diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/CREDITS linux.ac/CREDITS --- linux.vanilla/CREDITS Thu Oct 11 13:52:06 2001 +++ linux.ac/CREDITS Thu Oct 11 13:53:17 2001 @@ -969,6 +969,14 @@ S: 80050-430 - Curitiba - Paraná S: Brazil +N: Tom Gall +E: tom_gall@vnet.ibm.com +E: tgall@rochcivictheatre.org +D: ppc64, ppc +S: 710 Walnut St +S: Mantorville, MN 55955 +S: USA + N: Nigel Gamble E: nigel@nrg.org E: nigel@sgi.com diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/Configure.help linux.ac/Documentation/Configure.help --- linux.vanilla/Documentation/Configure.help Thu Oct 11 13:52:06 2001 +++ linux.ac/Documentation/Configure.help Thu Oct 11 21:07:07 2001 @@ -1,62 +1,71 @@ -# Maintained by Axel Boldt (axel@uni-paderborn.de) +# Maintained by: +# Eric S. Raymond +# Steven Cole +# +# Merged version 2.47: Current with 2.4.10-pre2 and 2.4.9-ac4. # # This version of the Linux kernel configuration help texts # corresponds to the kernel versions 2.4.x. # # Translations of this file available on the WWW: # -# - Japanese, maintained by the JF Project (JF@linux.or.jp), at -# http://www.linux.or.jp/JF/JFdocs/Configure.help/ -# - Russian, by kaf@linux.nevod.perm.su, at -# http://nevod.perm.su/service/linux/doc/kernel/Configure.help -# - French, by Pierre Tane (tanep@bigfoot.com), at -# http://www.traduc.org/kernelfr -# - Spanish, by Carlos Perelló Marín (fperllo@ehome.encis.es), at -# http://visar.csustan.edu/~carlos/ -# XXX: Site has moved, new location has no Configure.help trans. -# - Italian, by Alessandro Rubini (rubini@linux.it), at -# ftp://ftp-pavia1.linux.it/pub/linux/Configure.help -# XXX: ftp-pavia1.linux.it: Non-existent host/domain -# - Polish, by Cezar Cichocki (cezar@cs.net.pl), at -# http://www.cs.net.pl/~cezar/Kernel -# - German, by SuSE, at http://www.suse.de/~ke/kernel . This patch +# - Japanese, maintained by the JF Project , at +# +# - Russian, by , at +# +# - French, by Pierre Tane , at +# +# - Polish, by Dominik Mierzejewski , at +# +# - German, by SuSE, at . This patch # also includes infrastructure to support different languages. # # To access a document on the WWW, you need to have a direct Internet # connection and a browser program such as netscape or lynx. If you # only have email access, you can still use FTP and WWW servers: send -# an email to mail-server@rtfm.mit.edu with the text -# send usenet/news.answers/internet-services/access-via-email +# an email to with the text +# send usenet/news.answers/internet-services/access-via-email # in the body of the message. # # Information about what a kernel is, what it does, how to patch and # compile it and much more is contained in the Kernel-HOWTO, available -# at http://www.linuxdoc.org/docs.html#howto . Before you start +# at . Before you start # compiling, make sure that you have the necessary versions of all # programs and libraries required to compile and run this kernel; they -# are listed in the file Documentation/Changes. Make sure to read the +# are listed in the . Make sure to read the # toplevel kernel README file as well. # -# Format of this file: descriptionvariablehelp text. If -# the question being documented is of type "choice", we list only the -# first occurring config variable. The help texts may contain empty -# lines, but every non-empty line must be indented two positions. -# Order of the help texts does not matter, however, no variable should -# be documented twice: if it is, only the first occurrence will be -# used by Configure. We try to keep the help texts of related variables -# close together. Lines starting with `#' are ignored. To be nice to -# menuconfig, limit your line length to 70 characters. Use emacs' +# Format of this file: descriptionvariablehelp text. +# The help texts may contain empty lines, but every non-empty line must +# be indented two positions. Order of the help texts does not matter, +# however, no variable should be documented twice: if it is, only the +# first occurrence will be used. We try to keep the help texts of related +# variables close together. Lines starting with `#' are ignored. To be +# nice to menuconfig, limit your line length to 70 characters. Use emacs' # kfill.el to edit and ispell.el to spell check this file or you lose. # +# Comments of the form "# Choice:" followed by a menu name are used +# internally by the maintainers' consistency-checking tools. +# # If you add a help text to this file, please try to be as gentle as # possible. Don't use unexplained acronyms and generally write for the # hypothetical ignorant but intelligent user who has just bought a PC, # removed Windows, installed Linux and is now recompiling the kernel -# for the first time. Tell them what to do if they're unsure. Technical +# for the first time. Tell them what to do if they're unsure. Technical # information should go in a README in the Documentation directory. +# # Mention all the relevant READMEs and HOWTOs in the help text. +# Make them file URLs relative to the top level of the source tree so +# that help browsers can turn them into hotlinks. All URLs ahould be +# surrounded by <>. +# # Repetitions are fine since the help texts are not meant to be read -# in sequence. +# in sequence. It is good style to include URLs pointing to more +# detailed technical information, pictures of the hardware, etc. +# +# The most important thing to include in a help entry is *motivation*. +# Explain why someone configuring a kernel might want to select your +# option. # # All this was shamelessly stolen from several different sources. Many # thanks to all the contributors. Feel free to use these help texts in @@ -66,21 +75,22 @@ Prompt for development and/or incomplete code/drivers CONFIG_EXPERIMENTAL - Some of the various things that Linux supports (such as network - drivers, file systems, network protocols, etc.) can be in a state - of development where the functionality, stability, or the level of + Some of the various things that Linux supports (such as network + drivers, file systems, network protocols, etc.) can be in a state + of development where the functionality, stability, or the level of testing is not yet high enough for general use. This is usually - known as the "alpha-test" phase amongst developers. If a feature is - currently in alpha-test, then the developers usually discourage + known as the "alpha-test" phase among developers. If a feature is + currently in alpha-test, then the developers usually discourage uninformed widespread use of this feature by the general public to avoid "Why doesn't this work?" type mail messages. However, active testing and use of these systems is welcomed. Just be aware that it may not meet the normal level of reliability or it may fail to work in some special cases. Detailed bug reports from people familiar with the kernel internals are usually welcomed by the developers - (before submitting bug reports, please read the documents README, - MAINTAINERS, REPORTING-BUGS, Documentation/BUG-HUNTING, and - Documentation/oops-tracing.txt in the kernel source). + (before submitting bug reports, please read the documents + , , , + , and + in the kernel source). This option will also make obsoleted drivers available. These are drivers that have been replaced by something else, and/or are @@ -89,11 +99,18 @@ Unless you intend to help test and develop a feature or driver that falls into this category, or you have a situation that requires using these features, you should probably say N here, which will - cause this configure script to present you with fewer choices. If + cause the configurator to present you with fewer choices. If you say Y here, you will be offered the choice of using features or drivers that are currently considered to be in the alpha-test phase. -Symmetric Multi Processing +Prompt for drivers for obsolete features and hardware +CONFIG_OBSOLETE + Obsolete drivers have usually been replaced by more recent software + that can talk to the same hardware. Obsolete hardware is things + like MGA monitors that you are very unlikely to see on today's + systems. + +Symmetric Multi-Processing support CONFIG_SMP This enables support for systems with more than one CPU. If you have a system with only one CPU, like most personal computers, say N. If @@ -114,12 +131,106 @@ Y to "Enhanced Real Time Clock Support", below. The "Advanced Power Management" code will be disabled if you say Y here. - See also the files Documentation/smp.tex, Documentation/smp.txt, - Documentation/i386/IO-APIC.txt, Documentation/nmi_watchdog.txt and the - SMP-FAQ on the WWW at http://www.irisa.fr/prive/mentre/smp-faq/ . - + See also the , + , , + and the SMP-FAQ on the WWW at + . + If you don't know what to do here, say N. +Intel or compatible 80x86 processor +CONFIG_X86 + This is Linux's home port. Linux was originally native to the Intel + 386, and runs on all the later x86 processors including the Intel + 486, 586, Pentiums, and various instruction-set-compatible chips by + AMD, Cyrix, and others. + +Alpha processor +CONFIG_ALPHA + The Alpha is a 64-bit general-purpose processor designed and + marketed by the Digital Equipment Corporation of blessed memory, now + Compaq. Alpha Linux dates from 1995-1996 and was the first non-x86 + port. The Alpha Linux project has a home page at + . + +32-bit Sun Sparc +CONFIG_SPARC32 + SPARC is a family of RISC microprocessors designed and marketed by + Sun Microsystems, incorporated. They are very widely found in Sun + workstations and clones. This port covers the original 32-bit SPARC; + it is old and stable and usually considered one of the "big three" + along with the Intel and Alpha ports. The UltraLinux project + maintains both the SPARC32 and SPARC64 ports; its web page is + available at . + +64-bit Sun Sparc +CONFIG_SPARC64 + SPARC is a family of RISC microprocessors designed and marketed by + Sun Microsystems, incorporated. This port covers the newer 64-bit + UltraSPARC. The UltraLinux project maintains both the SPARC32 and + SPARC64 ports; its web page is available at + . + +Power PC processor +CONFIG_PPC + The PowerPC is a very capable 32-bit RISC processor from Motorola, + the successor to their 68000 and 88000 series. It powers recent + Macintoshes and also a widely-used series of single-board computers + from Motorola. The Linux PowerPC port has a home page at + . + +Motorola 68K processors +CONFIG_M68K + The Motorola 68K microprocessors are now obsolete, having been + superseded by the PowerPC line also from Motorola. But they powered + the first wave of workstation hardware in the 1980s, including Sun + workstations; they were also the basis of the original Amiga and + later Atari personal computers. A lot of this hardware is still + around. The m68k project has a home page at + . + +ARM processors +CONFIG_ARM + The ARM series is a line of low-power-consumption RISC chip designs + licensed by ARM ltd and targeted at embedded applications and + handhelds such as the Compaq IPAQ. ARM-based PCs are no longer + manufactured, but legacy ARM-based PC hardware remains popular in + Europe. There is an ARM Linux project with a web page at + . + +SuperH processors +CONFIG_SUPERH + The SuperH is a RISC processor targeted for use in embedded systems + and consumer electronics; it was also used in the Sega Dreamcast + gaming console. The SuperH port has a home page at + . + +IA64 processors, including Intel Itanium +CONFIG_IA64 + The Itanium is Intel's 64-bit successor to the 32-bit X86 line. As + of early 2001 it is not yet in widespread production use. The Linux + IA-64 project has a home page at . + +HP PA-RISC processor +CONFIG_PARISC + The PA-RISC microprocessor is a RISC chip designed by + Hewlett-Packard and used in their line of workstations. The PA-RISC + Linux project has a home page at . + +IBM System/390 +CONFIG_S390 + Linux now runs on the venerable System/390 mainframe from IBM, in a + guest partition under VM. In fact, over 40,000 simultaneous Linux + images have been run on a single mainframe! The S390 Linux project + has a home page at . + +Axis Communications ETRAX 100LX embedded network CPU +CONFIG_CRIS + Linux has been ported to run on the Axis Communications ETRAX 100LX + CPU and the single-board computers built around it, targeted for + network and embedded applications. For more information see the + Axis Communication site, . + Multiquad support for NUMA systems CONFIG_MULTIQUAD This option is used for getting Linux to run on a (IBM/Sequent) NUMA @@ -127,7 +238,7 @@ and uses Clustered Logical APIC addressing mode instead of Flat Logical. You will need a new lynxer.elf file to flash your firmware with - send email to Martin.Bligh@us.ibm.com - + IO-APIC Support on Uniprocessors CONFIG_X86_UP_IOAPIC An IO-APIC (I/O Advanced Programmable Interrupt Controller) is an @@ -160,7 +271,7 @@ a math coprocessor built in, 486SX and 386 do not, unless you added a 487DX or 387, respectively. (The messages during boot time can give you some hints here ["man dmesg"].) Everyone needs either a - coprocessor or this emulation. + coprocessor or this emulation. If you don't have a math coprocessor, you need to say Y here; if you say Y here even though you have a coprocessor, the coprocessor will @@ -172,7 +283,7 @@ intend to use this kernel on different machines. More information about the internals of the Linux math coprocessor - emulation can be found in arch/i386/math-emu/README. + emulation can be found in . If you are not sure, say Y; apart from resulting in a 66 KB bigger kernel, it won't hurt. @@ -211,12 +322,13 @@ functions. You may choose to use both, but the Timer LED function will overrule the CPU usage LED. -Kernel FP software completion (EXPERIMENTAL) +Kernel FP software completion CONFIG_MATHEMU This option is required for IEEE compliant floating point arithmetic on the Alpha. The only time you would ever not say Y is to say M in order to debug the code. Say Y unless you know what you are doing. +# Choice: himem High Memory support CONFIG_NOHIGHMEM Linux can use up to 64 Gigabytes of physical memory on x86 systems. @@ -248,23 +360,45 @@ auto detected or can be forced by using a kernel command line option such as "mem=256M". (Try "man bootparam" or see the documentation of your boot loader (lilo or loadlin) about how to pass options to the - kernel at boot time.) + kernel at boot time.) If unsure, say "off". +4GB +CONFIG_HIGHMEM4G + Select this if you have a 32-bit processor and between 1 and 4 + gigabytes of physical RAM. + +64GB +CONFIG_HIGHMEM64G + Select this if you have a 32-bit processor and more than 4 + gigabytes of physical RAM. + Normal PC floppy disk support CONFIG_BLK_DEV_FD If you want to use the floppy disk drive(s) of your PC under Linux, say Y. Information about this driver, especially important for IBM - Thinkpad users, is contained in Documentation/floppy.txt. That file - also contains the location of the Floppy driver FAQ as well as - location of the fdutils package used to configure additional + Thinkpad users, is contained in . + That file also contains the location of the Floppy driver FAQ as + well as location of the fdutils package used to configure additional parameters of the driver at run time. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called floppy.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . + +iSeries Virtual I/O Disk Support +CONFIG_VIODASD + If you are running on an iSeries system and you want to use + virtual disks created and managed by OS/400, say Y. + +iSeries Virtual I/O Disk IDE Emulation +CONFIG_VIODASD_IDE + This causes the iSeries virtual disks to look like IDE disks. + If you have programs or utilities that only support certain + kinds of disks, this option will cause iSeries virtual disks + to pretend to be IDE disks, which may satisfy the program. Support for PowerMac floppy CONFIG_MAC_FLOPPY @@ -278,15 +412,15 @@ write to it and do all the other things that you can do with normal block devices (such as hard drives). It is usually used to load and store a copy of a minimal root file system off of a floppy into RAM - during the initial install of Linux. + during the initial install of Linux. Note that the kernel command line option "ramdisk=XX" is now - obsolete. For details, read Documentation/ramdisk.txt. + obsolete. For details, read . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M and read Documentation/modules.txt. The module will be called - rd.o. + say M and read . The module will be + called rd.o. Most normal users won't need the RAM disk functionality, and can thus say N here. @@ -301,15 +435,15 @@ The initial RAM disk is a RAM disk that is loaded by the boot loader (loadlin or lilo) and that is mounted as root before the normal boot procedure. It is typically used to load modules needed to mount the - "real" root file system, etc. See Documentation/initrd.txt for - details. + "real" root file system, etc. See + for details. -Loop device support +Loopback device support CONFIG_BLK_DEV_LOOP Saying Y here will allow you to use a regular file as a block device; you can then create a file system on that block device and mount it just as you would mount other block devices such as hard - drive partitions, CDROM drives or floppy drives. The loop devices + drive partitions, CD-ROM drives or floppy drives. The loop devices are block special device files with major number 7 and typically called /dev/loop0, /dev/loop1 etc. @@ -318,7 +452,7 @@ writing them to floppy. Furthermore, some Linux distributions avoid the need for a dedicated Linux partition by keeping their complete root file system inside a DOS FAT file using this loop device - driver. + driver. The loop device driver can also be used to "hide" a file system in a disk partition, floppy, or regular file, either using encryption @@ -326,58 +460,58 @@ bits of, say, a sound file). This is also safe if the file resides on a remote file server. If you want to do this, you will first have to acquire and install a kernel patch from - ftp://ftp.kerneli.org/pub/kerneli/ , and then you need to + , and then you need to say Y to this option. Note that alternative ways to use encrypted file systems are provided by the cfs package, which can be gotten from - ftp://ftp.kerneli.org/pub/kerneli/net-source/ , and the newer tcfs - package, available at http://tcfs.dia.unisa.it/ . You do not need to - say Y here if you want to use one of these. However, using cfs + , and the newer tcfs + package, available at . You do not need + to say Y here if you want to use one of these. However, using cfs requires saying Y to "NFS file system support" below while using tcfs requires applying a kernel patch. An alternative steganography solution is provided by StegFS, also available from - ftp://ftp.kerneli.org/pub/kerneli/net-source/ . + . To use the loop device, you need the losetup utility and a recent version of the mount program, both contained in the util-linux package. The location and current version number of util-linux is - contained in the file Documentation/Changes. + contained in the file . Note that this loop device has nothing to do with the loopback device used for network connections from the machine to itself. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called loop.o. + say M here and read . The module + will be called loop.o. Most users will answer N here. -Network Block Device support +Network block device support CONFIG_BLK_DEV_NBD Saying Y here will allow your computer to be a client for network block devices, i.e. it will be able to use block devices exported by servers (mount file systems on them etc.). Communication between client and server works over TCP/IP networking, but to the client program this is hidden: it looks like a regular local file access to - a block device special file such as /dev/nd0. + a block device special file such as /dev/nd0. Network block devices also allows you to run a block-device in userland (making server and client physically the same computer, communicating using the loopback network device). - - Read Documentation/nbd.txt for more information, especially about - where to find the server code, which runs in user space and does not - need special kernel support. + + Read for more information, especially + about where to find the server code, which runs in user space and + does not need special kernel support. Note that this has nothing to do with the network file systems NFS or Coda; you can say N here even if you intend to use NFS or Coda. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called nbd.o. + say M here and read . The module + will be called nbd.o. If unsure, say N. @@ -385,16 +519,16 @@ CONFIG_IDE If you say Y here, your kernel will be able to manage low cost mass storage units such as ATA/(E)IDE and ATAPI units. The most common - cases are IDE hard drives and ATAPI CDROM drives. + cases are IDE hard drives and ATAPI CD-ROM drives. If your system is pure SCSI and doesn't use these interfaces, you can say N here. - + Integrated Disk Electronics (IDE aka ATA-1) is a connecting standard for mass storage units such as hard disks. It was designed by Western Digital and Compaq Computer in 1984. It was then named ST506. Quite a number of disks use the IDE interface. - + AT Attachment (ATA) is the superset of the IDE specifications. ST506 was also called ATA-1. @@ -407,22 +541,22 @@ ATA/IDE standards by means of fast DMA controllers. ATA Packet Interface (ATAPI) is a protocol used by EIDE tape and - CDROM drives, similar in many respects to the SCSI protocol. - + CD-ROM drives, similar in many respects to the SCSI protocol. + SMART IDE (Self Monitoring, Analysis and Reporting Technology) was designed in order to prevent data corruption and disk crash by detecting pre hardware failure conditions (heat, access time, and - the like...). Disks built since June 1995 may follow this - standard. The kernel itself don't manage this; however there are - quite a number of user programs such as smart that can query the - status of SMART parameters disk. + the like...). Disks built since June 1995 may follow this standard. + The kernel itself don't manage this; however there are quite a + number of user programs such as smart that can query the status of + SMART parameters disk. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called ide.o. + say M here and read . The module + will be called ide.o. - For further information, please read Documentation/ide.txt. + For further information, please read . If unsure, say Y. @@ -436,21 +570,21 @@ Useful information about large (>540 MB) IDE disks, multiple interfaces, what to do if ATA/IDE devices are not automatically detected, sound card ATA/IDE ports, module support, and other - topics, is contained in Documentation/ide.txt. For detailed + topics, is contained in . For detailed information about hard drives, consult the Disk-HOWTO and the Multi-Disk-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . To fine-tune ATA/IDE drive/interface parameters for improved performance, look for the hdparm package at - http://www.ibiblio.org/pub/Linux/system/hardware . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt and - Documentation/ide.txt. The module will be called ide-mod.o. Do not - compile this driver as a module if your root file system (the one - containing the directory /) is located on an IDE device. + say M here and read and + . The module will be called ide-mod.o. + Do not compile this driver as a module if your root file system (the + one containing the directory /) is located on an IDE device. If you have one or more IDE drives, say Y or M here. If your system has no IDE drives, or if memory requirements are really tight, you @@ -467,42 +601,42 @@ since it lacks the enhanced functionality of the new one. This makes it a good choice for systems with very tight memory restrictions, or for systems with only older MFM/RLL/ESDI drives. Choosing the old - driver can save 13 KB or so of kernel memory. + driver can save 13 KB or so of kernel memory. If you are unsure, then just choose the Enhanced IDE/MFM/RLL driver instead of this one. For more detailed information, read the Disk-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Use old disk-only driver on primary interface CONFIG_BLK_DEV_HD_IDE - There are two drivers for MFM/RLL/IDE disks. Most people use just - the new enhanced driver by itself. This option however installs the + There are two drivers for MFM/RLL/IDE disks. Most people use just + the new enhanced driver by itself. This option however installs the old hard disk driver to control the primary IDE/disk interface in the system, leaving the new enhanced IDE driver to take care of only - the 2nd/3rd/4th IDE interfaces. Doing this will prevent you from - having an IDE/ATAPI CDROM or tape drive connected to the primary IDE - interface. Choosing this option may be useful for older systems + the 2nd/3rd/4th IDE interfaces. Doing this will prevent you from + having an IDE/ATAPI CD-ROM or tape drive connected to the primary + IDE interface. Choosing this option may be useful for older systems which have MFM/RLL/ESDI controller+drives at the primary port address (0x1f0), along with IDE drives at the secondary/3rd/4th port - addresses. + addresses. Normally, just say N here; you will then use the new driver for all 4 interfaces. Include IDE/ATA-2 DISK support CONFIG_BLK_DEV_IDEDISK - This will include enhanced support for MFM/RLL/IDE hard disks. If + This will include enhanced support for MFM/RLL/IDE hard disks. If you have a MFM/RLL/IDE disk, and there is no special reason to use - the old hard disk driver instead, say Y. If you have an SCSI-only + the old hard disk driver instead, say Y. If you have an SCSI-only system, you can say N here. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called ide-disk.o. Do not compile this driver as a module if your - root file system (the one containing the directory /) is located on - the IDE disk. If unsure, say Y. + say M here and read . The module + will be called ide-disk.o. Do not compile this driver as a module + if your root file system (the one containing the directory /) is + located on the IDE disk. If unsure, say Y. Use multi-mode by default CONFIG_IDEDISK_MULTI_MODE @@ -513,38 +647,43 @@ If in doubt, say N. -Include IDE/ATAPI CDROM support +PCMCIA IDE support +CONFIG_BLK_DEV_IDECS + Support for outboard IDE disks, tape drives, and CD-ROM drives + connected through a PCMCIA card. + +Include IDE/ATAPI CD-ROM support CONFIG_BLK_DEV_IDECD - If you have a CDROM drive using the ATAPI protocol, say Y. ATAPI is - a newer protocol used by IDE CDROM and TAPE drives, similar to the - SCSI protocol. Most new CDROM drives use ATAPI, including the + If you have a CD-ROM drive using the ATAPI protocol, say Y. ATAPI is + a newer protocol used by IDE CD-ROM and TAPE drives, similar to the + SCSI protocol. Most new CD-ROM drives use ATAPI, including the NEC-260, Mitsumi FX400, Sony 55E, and just about all non-SCSI double(2X) or better speed drives. - If you say Y here, the CDROM drive will be identified at boot time + If you say Y here, the CD-ROM drive will be identified at boot time along with other IDE devices, as "hdb" or "hdc", or something similar (check the boot messages with dmesg). If this is your only - CDROM drive, you can say N to all other CDROM options, but be sure - to say Y or M to "ISO 9660 CDROM file system support". + CD-ROM drive, you can say N to all other CD-ROM options, but be sure + to say Y or M to "ISO 9660 CD-ROM file system support". - Read the CDROM-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto and the file - Documentation/cdrom/ide-cd. Note that older versions of lilo (the - Linux boot loader) cannot properly deal with IDE/ATAPI CDROMs, so - install lilo-16 or higher, available from - ftp://metalab.unc.edu/pub/Linux/system/boot/lilo . + Read the CD-ROM-HOWTO, available from + and + . Note that older versions of lilo + (the Linux boot loader) cannot properly deal with IDE/ATAPI CD-ROMs, + so install lilo-16 or higher, available from + . If you want to compile the driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called ide-cd.o. + say M here and read . The module + will be called ide-cd.o. Include IDE/ATAPI TAPE support CONFIG_BLK_DEV_IDETAPE If you have an IDE tape drive using the ATAPI protocol, say Y. - ATAPI is a newer protocol used by IDE tape and CDROM drives, similar - to the SCSI protocol. If you have an SCSI tape drive however, you - can say N here. + ATAPI is a newer protocol used by IDE tape and CD-ROM drives, + similar to the SCSI protocol. If you have an SCSI tape drive + however, you can say N here. You should also say Y if you have an OnStream DI-30 tape drive; this will not work with the SCSI protocol, until there is support for the @@ -553,27 +692,27 @@ If you say Y here, the tape drive will be identified at boot time along with other IDE devices, as "hdb" or "hdc", or something similar, and will be mapped to a character device such as "ht0" - (check the boot messages with dmesg). Be sure to consult the - drivers/ide/ide-tape.c and Documentation/ide.txt files for usage - information. + (check the boot messages with dmesg). Be sure to consult the + and files + for usage information. If you want to compile the driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called ide-tape.o. + say M here and read . The module + will be called ide-tape.o. Include IDE/ATAPI FLOPPY support CONFIG_BLK_DEV_IDEFLOPPY If you have an IDE floppy drive which uses the ATAPI protocol, - answer Y. ATAPI is a newer protocol used by IDE CDROM/tape/floppy - drives, similar to the SCSI protocol. + answer Y. ATAPI is a newer protocol used by IDE CD-ROM/tape/floppy + drives, similar to the SCSI protocol. The LS-120 and the IDE/ATAPI Iomega ZIP drive are also supported by this driver. For information about jumper settings and the question of when a ZIP drive uses a partition table, see - http://www.win.tue.nl/~aeb/linux/zip/zip-1.html . + . (ATAPI PD-CD/CDR drives are not supported by this driver; support - for PD-CD/CDR drives is available if you answer Y to + for PD-CD/CDR drives is available if you answer Y to "SCSI emulation support", below). If you say Y here, the FLOPPY drive will be identified along with @@ -582,8 +721,8 @@ If you want to compile the driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called ide-floppy.o. + say M here and read . The module + will be called ide-floppy.o. SCSI emulation support CONFIG_BLK_DEV_IDESCSI @@ -625,7 +764,7 @@ conditions. Say Y here to include code which tries to automatically detect and correct the problems under Linux. This option also enables access to the secondary IDE ports in some CMD640 based - systems. + systems. This driver will work automatically in PCI based systems (most new systems have PCI slots). But if your system uses VESA local bus @@ -636,15 +775,15 @@ The CMD640 chip is also used on add-in cards by Acculogic, and on the "CSA-6400E PCI to IDE controller" that some people have. For - details, read Documentation/ide.txt. + details, read . CMD640 enhanced support CONFIG_BLK_DEV_CMD640_ENHANCED This option includes support for setting/autotuning PIO modes and - prefetch on CMD640 IDE interfaces. For details, read - Documentation/ide.txt. If you have a CMD640 IDE interface and your - BIOS does not already do this for you, then say Y here. Otherwise - say N. + prefetch on CMD640 IDE interfaces. For details, read + . If you have a CMD640 IDE interface + and your BIOS does not already do this for you, then say Y here. + Otherwise say N. RZ1000 chipset bugfix/support CONFIG_BLK_DEV_RZ1000 @@ -654,14 +793,14 @@ severe data corruption under many conditions. Say Y here to include code which automatically detects and corrects the problem under Linux. This may slow disk throughput by a few percent, but at least - things will operate 100% reliably. + things will operate 100% reliably. Generic PCI IDE chipset support CONFIG_BLK_DEV_IDEPCI Say Y here for PCI systems which use IDE drive(s). This option helps the IDE driver to automatically detect and configure all PCI-based IDE interfaces in your system. - + Support for sharing PCI IDE interrupts CONFIG_IDEPCI_SHARE_IRQ Some ATA/IDE chipsets have hardware support which allows for @@ -681,14 +820,14 @@ for these drives, but you can change that by saying Y to the following question "Use DMA by default when available". You can get the latest version of the hdparm utility from - ftp://metalab.unc.edu/pub/Linux/system/hardware/ . + . - Read the comments at the beginning of drivers/ide/ide-dma.c and - the file Documentation/ide.txt for more information. + Read the comments at the beginning of + and the file for more information. It is safe to say Y to this question. -Good-Bad DMA Model-Firmware (EXPERIMENTAL) +Good-Bad DMA Model-Firmware (WIP) CONFIG_IDEDMA_NEW_DRIVE_LISTINGS If you say Y here, the model and firmware revision of your drive will be compared against a blacklist of buggy drives that claim to @@ -701,6 +840,16 @@ If in doubt, say N. +Attempt to HACK around Chipsets that TIMEOUT (WIP) +CONFIG_BLK_DEV_IDEDMA_TIMEOUT + If you say Y here, this is a NASTY UGLY HACK! + + We have to issue an abort and requeue the request DMA engine got + turned off by a goofy ASIC, and we have to clean up the mess, and + here is as good as any. Do it globally for all chipsets. + + If in doubt, say N. + Boot off-board chipsets first support CONFIG_BLK_DEV_OFFBOARD Normally, IDE controllers built into the motherboard (on-board @@ -722,7 +871,7 @@ If in doubt, say N. -Use DMA by default when available +Use PCI DMA by default when available CONFIG_IDEDMA_PCI_AUTO Prior to kernel version 2.1.112, Linux used to automatically use DMA for IDE drives and chipsets which support it. Due to concerns @@ -738,29 +887,39 @@ IGNORE word93 Validation BITS CONFIG_IDEDMA_IVB - Since various rules were applied and created ... et al. as it relates - the detection of valid cable signals. This is a result of unclear terms - in ATA-4 and ATA-5 standards. + Since various rules were applied and created ... et al. as it + relates the detection of valid cable signals. This is a result of + unclear terms in ATA-4 and ATA-5 standards. It is normally safe to answer Y; however, the default is N. -Various ATA, Work(s) In Progress (EXPERIMENTAL) +ATA Work(s) In Progress (EXPERIMENTAL)‹ CONFIG_IDEDMA_PCI_WIP If you enable this you will be able to use and test highly - developmental projects. If you say N, this configure script will + developmental projects. If you say N, the configurator will simply skip those options. It is SAFEST to say N to this question. +Asyncronious DMA support (EXPERIMENTAL) +CONFIG_BLK_DEV_ADMA + Please read the comments at the top of + . + +Pacific Digital A-DMA support (EXPERIMENTAL) +CONFIG_BLK_DEV_PDC_ADMA + Please read the comments at the top of . + 3ware Hardware ATA-RAID support CONFIG_BLK_DEV_3W_XXXX_RAID 3ware is the only hardware ATA-Raid product in Linux to date. This card is 2,4, or 8 channel master mode support only. SCSI support required!!! - http://www.3ware.com/ + - Please read the comments at the top of drivers/scsi/3w-xxxx.c + Please read the comments at the top of + . AEC62XX chipset support CONFIG_BLK_DEV_AEC62XX @@ -774,13 +933,13 @@ The ATP860 is an UltraDMA 66 chipset base. The ATP860M(acintosh) version is an UltraDMA 66 chipset base. - Please read the comments at the top of drivers/ide/aec62xx.c - If you say Y here, then say Y to "Use DMA by default when available" as - well. + Please read the comments at the top of . + If you say Y here, then say Y to "Use DMA by default when available" + as well. AEC62XX Tuning support CONFIG_AEC62XX_TUNING - Please read the comments at the top of drivers/ide/aec62xx.c + Please read the comments at the top of . If unsure, say N. ALI M15x3 chipset support @@ -790,8 +949,8 @@ normal dual channel support. If you say Y here, you also need to say Y to "Use DMA by default - when available", above. - Please read the comments at the top of drivers/ide/alim15x3.c + when available", above. Please read the comments at the top of + . If unsure, say N. @@ -806,20 +965,21 @@ Using this option can allow WDC drives to run at ATA-4/5 transfer rates with only an ATA-2 support structure. - SAY NO! + SAY N! -AMD7409 chipset support -CONFIG_BLK_DEV_AMD7409 - This driver ensures (U)DMA support for the AMD756 Viper chipset. +AMD Viper support +CONFIG_BLK_DEV_AMD74XX + This driver ensures (U)DMA support for the AMD756/760 Viper + chipsets. If you say Y here, you also need to say Y to "Use DMA by default when available", above. - Please read the comments at the top of drivers/ide/amd7409.c + Please read the comments at the top of . If unsure, say N. -AMD Viper ATA-66 Override support (WIP) -CONFIG_AMD7409_OVERRIDE +AMD Viper ATA-66 Override (WIP) +CONFIG_AMD74XX_OVERRIDE This option auto-forces the ata66 flag. This effect can be also invoked by calling "idex=ata66" If unsure, say N. @@ -858,8 +1018,8 @@ HPT34X AUTODMA support (WIP) CONFIG_HPT34X_AUTODMA This is a dangerous thing to attempt currently! Please read the - comments at the top of drivers/ide/hpt34x.c If you say Y here, - then say Y to "Use DMA by default when available" as well. + comments at the top of . If you say Y + here, then say Y to "Use DMA by default when available" as well. If unsure, say N. @@ -868,9 +1028,9 @@ HPT366 is an Ultra DMA chipset for ATA-66. HPT368 is an Ultra DMA chipset for ATA-66 RAID Based. HPT370 is an Ultra DMA chipset for ATA-100. - + This driver adds up to 4 more EIDE devices sharing a single - interrupt. + interrupt. The HPT366 chipset in its current form is bootable. One solution for this problem are special LILO commands for redirecting the @@ -884,21 +1044,22 @@ ide-probe at boot. It is reported to support DVD II drives, by the manufacturer. -NS87415 support (EXPERIMENTAL) +NS87415 chipset support (EXPERIMENTAL) CONFIG_BLK_DEV_NS87415 This driver adds detection and support for the NS87415 chip (used in SPARC64, among others). - Please read the comments at the top of drivers/ide/ns87415.c. + Please read the comments at the top of . -OPTi 82C621 enhanced support (EXPERIMENTAL) +OPTi 82C621 chipset enhanced support (EXPERIMENTAL) CONFIG_BLK_DEV_OPTI621 This is a driver for the OPTi 82C621 EIDE controller. - Please read the comments at the top of drivers/ide/opti621.c. + Please read the comments at the top of . -ServerWorks OSB4 chipset support (EXPERIMENTAL) -CONFIG_BLK_DEV_OSB4 - This driver adds PIO/DMA support for the Serverworks OSB4 chipset +ServerWorks OSB4/CSB5 chipset support +CONFIG_BLK_DEV_SVWKS + This driver adds PIO/(U)DMA support for the ServerWorks OSB4/CSB5 + chipsets. Intel PIIXn chipsets support CONFIG_BLK_DEV_PIIX @@ -907,7 +1068,7 @@ PIO 0-4 mode settings, this allows dynamic tuning of the chipset via the standard end-user tool 'hdparm'. - Please read the comments at the top of drivers/ide/piix.c. + Please read the comments at the top of . If you say Y here, you should also say Y to "PIIXn Tuning support", below. @@ -926,29 +1087,30 @@ If unsure, say N. -PROMISE PDC20246/PDC20262/PDC20267 support +PROMISE PDC20246/PDC20262/PDC20265/PDC20267/PDC20268 support CONFIG_BLK_DEV_PDC202XX Promise Ultra33 or PDC20246 Promise Ultra66 or PDC20262 - Promise Ultra100 or PDC20265/PDC20267 + Promise Ultra100 or PDC20265/PDC20267/PDC20268 This driver adds up to 4 more EIDE devices sharing a single interrupt. This add-on card is a bootable PCI UDMA controller. Since multiple cards can be installed and there are BIOS ROM problems that happen if the BIOS revisions of all installed cards (three-max) do not match, the driver attempts to do dynamic tuning of the chipset - at boot-time for max-speed. Ultra33 BIOS 1.25 or newer is required + at boot-time for max-speed. Ultra33 BIOS 1.25 or newer is required for more than one card. This card may require that you say Y to - "Special UDMA Feature (EXPERIMENTAL)". + "Special UDMA Feature". If you say Y here, you need to say Y to "Use DMA by default when available" as well. - Please read the comments at the top of drivers/ide/pdc202xx.c + Please read the comments at the top of + . If unsure, say N. -Special UDMA Feature (EXPERIMENTAL) +Special UDMA Feature CONFIG_PDC202XX_BURST For PDC20246, PDC20262, PDC20265 and PDC20267 Ultra DMA chipsets. Designed originally for PDC20246/Ultra33 that has BIOS setup @@ -956,10 +1118,15 @@ Unknown for PDC20265/PDC20267 Ultra DMA 100. - Please read the comments at the top of drivers/ide/pdc202xx.c + Please read the comments at the top of + . If unsure, say N. +Special FastTrak Feature +CONFIG_PDC202XX_FORCE + For FastTrak enable overriding BIOS. + SiS5513 chipset support CONFIG_BLK_DEV_SIS5513 This driver ensures (U)DMA support for SIS5513 chipset based @@ -969,20 +1136,21 @@ If you say Y here, you need to say Y to "Use DMA by default when available" as well. - Please read the comments at the top of drivers/ide/sis5513.c + Please read the comments at the top of . SLC90E66 chipset support CONFIG_BLK_DEV_SLC90E66 This driver ensures (U)DMA support for Victroy66 SouthBridges for SMsC with Intel NorthBridges. This is an Ultra66 based chipset. The nice thing about it is that you can mix Ultra/DMA/PIO devices - and it will handle timing cycles. Since this is an improved look-a-like - to the PIIX4 it should be a nice addition. + and it will handle timing cycles. Since this is an improved + look-a-like to the PIIX4 it should be a nice addition. If you say Y here, you need to say Y to "Use DMA by default when available" as well. - Please read the comments at the top of drivers/ide/slc90e66.c + Please read the comments at the top of + . Winbond SL82c105 support CONFIG_BLK_DEV_SL82C105 @@ -990,35 +1158,34 @@ special configuration for this chip. This is common on various CHRP motherboards, but could be used elsewhere. If in doubt, say Y. -Tekram TRM290 chipset support (EXPERIMENTAL) +Tekram TRM290 chipset support CONFIG_BLK_DEV_TRM290 This driver adds support for bus master DMA transfers using the Tekram TRM290 PCI IDE chip. Volunteers are needed for further tweaking and development. - Please read the comments at the top of drivers/ide/trm290.c. + Please read the comments at the top of . VIA82CXXX chipset support CONFIG_BLK_DEV_VIA82CXXX This allows you to configure your chipset for a better use while - running (U)DMA: it will allow you to enable efficiently the second - channel dma usage, as it may not be set by BIOS. It allows you to - pass a kernel command line at boot time in order to set fifo - config. If no command line is provided, it will try to set fifo - configuration at its best. It will allow you to get information from - /proc/ide/via provided you enabled "proc" support. + running PIO/(U)DMA, it will allow you to enable efficiently the + second channel dma usage, as it may not be set by BIOS. It will try + to set fifo configuration at its best. It will allow you to get + information from /proc/ide/via provided you enabled "/proc file + system" support. - Please read the comments at the top of drivers/ide/via82cxxx.c + Please read the comments at the top of + . If you say Y here, then say Y to "Use DMA by default when available" as well. If unsure, say N. -VIA82CXXX Tuning support (WIP) -CONFIG_VIA82CXXX_TUNING - Please read the comments at the top of drivers/ide/via82cxxx.c - - If unsure, say N. +RapIDE interface support +CONFIG_BLK_DEV_IDE_RAPIDE + Say Y here if you want to support the Yellowstone RapIDE controller + manufactured for use with Acorn computers. Other IDE chipset support CONFIG_IDE_CHIPSETS @@ -1030,9 +1197,9 @@ setting of higher speed I/O rates to improve system performance with these chipsets. Most of these also require special kernel boot parameters to actually turn on the support at runtime; you can find - a list of these in the file Documentation/ide.txt. - - People with SCSI-only systems can say N here. + a list of these in the file . + + People with SCSI-only systems can say N here. Generic 4 drives/port support CONFIG_BLK_DEV_4DRIVES @@ -1045,53 +1212,64 @@ ALI M14xx support CONFIG_BLK_DEV_ALI14XX This driver is enabled at runtime using the "ide0=ali14xx" kernel - boot parameter. It enables support for the secondary IDE interface + boot parameter. It enables support for the secondary IDE interface of the ALI M1439/1443/1445/1487/1489 chipsets, and permits faster - I/O speeds to be set as well. See the files Documentation/ide.txt - and drivers/ide/ali14xx.c for more info. + I/O speeds to be set as well. See the files + and for + more info. DTC-2278 support CONFIG_BLK_DEV_DTC2278 This driver is enabled at runtime using the "ide0=dtc2278" kernel boot parameter. It enables support for the secondary IDE interface of the DTC-2278 card, and permits faster I/O speeds to be set as - well. See the Documentation/ide.txt and drivers/ide/dtc2278.c - files for more info. + well. See the and + files for more info. Holtek HT6560B support CONFIG_BLK_DEV_HT6560B This driver is enabled at runtime using the "ide0=ht6560b" kernel boot parameter. It enables support for the secondary IDE interface of the Holtek card, and permits faster I/O speeds to be set as well. - See the Documentation/ide.txt and drivers/ide/ht6560b.c files for - more info. + See the and + files for more info. PROMISE DC4030 support (EXPERIMENTAL) CONFIG_BLK_DEV_PDC4030 This driver provides support for the secondary IDE interface and - cache of Promise IDE chipsets, e.g. DC4030 and DC5030. This driver + cache of Promise IDE chipsets, e.g. DC4030 and DC5030. This driver is known to incur timeouts/retries during heavy I/O to drives - attached to the secondary interface. CDROM and TAPE devices are not - supported yet. This driver is enabled at runtime using the - "ide0=dc4030" kernel boot parameter. See the Documentation/ide.txt - and drivers/ide/pdc4030.c files for more info. + attached to the secondary interface. CD-ROM and TAPE devices are + not supported yet. This driver is enabled at runtime using the + "ide0=dc4030" kernel boot parameter. See the + and files + for more info. +# This is for Linus's tree. QDI QD6580 support CONFIG_BLK_DEV_QD6580 This driver is enabled at runtime using the "ide0=qd6580" kernel - boot parameter. It permits faster I/O speeds to be set. See the - files Documentation/ide.txt and drivers/ide/qd6580.c for more - info. + boot parameter. It permits faster I/O speeds to be set. See the + and for + more info. + +# This is for Alan's tree. Note the name difference. +QDI QD65XX support +CONFIG_BLK_DEV_QD65XX + This driver is enabled at runtime using the "ide0=qd65xx" kernel + boot parameter. It permits faster I/O speeds to be set. See the + and for + more info. UMC 8672 support CONFIG_BLK_DEV_UMC8672 This driver is enabled at runtime using the "ide0=umc8672" kernel boot parameter. It enables support for the secondary IDE interface of the UMC-8672, and permits faster I/O speeds to be set as well. - See the files Documentation/ide.txt and drivers/ide/umc8672.c for - more info. + See the files and + for more info. -Amiga builtin Gayle IDE interface support +Amiga Gayle IDE interface support CONFIG_BLK_DEV_GAYLE This is the IDE driver for the builtin IDE interface on some Amiga models. It supports both the `A1200 style' (used in A600 and A1200) @@ -1107,11 +1285,11 @@ disks, CD-ROM drives, etc.) that are connected to the builtin IDE interface. -Amiga Buddha/Catweasel IDE interface support (EXPERIMENTAL) +Buddha/Catweasel IDE interface support (EXPERIMENTAL) CONFIG_BLK_DEV_BUDDHA This is the IDE driver for the IDE interfaces on the Buddha and - Catweasel expansion boards. It supports up to two interfaces on the - Buddha and three on the Catweasel. + Catweasel expansion boards for the Amiga. It supports up to two + interfaces on the Buddha and three on the Catweasel. Say Y if you have a Buddha or Catweasel expansion board and want to use IDE devices (hard disks, CD-ROM drives, etc.) that are connected @@ -1131,10 +1309,16 @@ Say Y if you have an IDE doubler. The driver is enabled at kernel runtime using the "ide=doubler" kernel boot parameter. -Support for PowerMac IDE devices (must also enable IDE) +WarpEngine SCSI support +CONFIG_WARPENGINE_SCSI + Support for MacroSystem Development's WarpEngine Amiga SCSI-2 + controller. Info at + . + +Builtin PowerMac IDE support CONFIG_BLK_DEV_IDE_PMAC - This driver provides support for the built-in IDE controller on most - of the recent Apple Power Macintoshes and PowerBooks. + This driver provides support for the built-in IDE controller on + most of the recent Apple Power Macintoshes and PowerBooks. If unsure, say Y. PowerMac IDE DMA support @@ -1164,29 +1348,6 @@ devices (hard disks, CD-ROM drives, etc.) that are connected to the builtin IDE interface. -MPC8xx IDE support -CONFIG_BLK_DEV_MPC8xx_IDE - This option provides support for IDE on Motorola MPC8xx Systems. - Please see 'Type of MPC8xx IDE interface' for details. - - If unsure, say N. - -Type of MPC8xx IDE interface -CONFIG_IDE_8xx_PCCARD - Select how the IDE devices are connected to the MPC8xx system: - - 8xx_PCCARD uses the 8xx internal PCMCIA interface in combination - with a PC Card (e.g. ARGOSY portable Hard Disk Adapter), - ATA PC Card HDDs or ATA PC Flash Cards (example: TQM8xxL - systems) - - 8xx_DIRECT is used for directly connected IDE devices using the 8xx - internal PCMCIA interface (example: IVMS8 systems) - - EXT_DIRECT is used for IDE devices directly connected to the 8xx - bus using some glue logic, but _not_ the 8xx internal - PCMCIA interface (example: IDIF860 systems) - ICS IDE interface support CONFIG_BLK_DEV_IDE_ICSIDE On Acorn systems, say Y here if you wish to use the ICS IDE @@ -1212,12 +1373,12 @@ XT hard disk support CONFIG_BLK_DEV_XD Very old 8 bit hard disk controllers used in the IBM XT computer - will be supported if you say Y here. + will be supported if you say Y here. If you want to compile the driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called xd.o. + say M here and read . The module + will be called xd.o. It's pretty unlikely that you have one of these: say N. @@ -1225,23 +1386,23 @@ CONFIG_BLK_DEV_PS2 Say Y here if you have a PS/2 machine with a MCA bus and an ESDI hard disk. - + If you want to compile the driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called ps2esdi.o. + say M here and read . The module + will be called ps2esdi.o. Mylex DAC960/DAC1100 PCI RAID Controller support CONFIG_BLK_DEV_DAC960 This driver adds support for the Mylex DAC960, AcceleRAID, and - eXtremeRAID PCI RAID controllers. See the file - Documentation/README.DAC960 for further information about this - driver. + eXtremeRAID PCI RAID controllers. See the file + for further information about + this driver. If you want to compile the driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called DAC960.o. + say M here and read . The module + will be called DAC960.o. Parallel port IDE device support CONFIG_PARIDE @@ -1249,7 +1410,7 @@ your computer's parallel port. Most of them are actually IDE devices using a parallel port IDE adapter. This option enables the PARIDE subsystem which contains drivers for many of these external drives. - Read Documentation/paride.txt for more information. + Read for more information. If you have said Y to the "Parallel-port support" configuration option, you may share a single port between your printer and other @@ -1270,13 +1431,13 @@ Parallel port IDE disks CONFIG_PARIDE_PD - This option enables the high-level driver for IDE-type disk devices - connected through a parallel port. If you chose to build PARIDE - support into your kernel, you may answer Y here to build in the - parallel port IDE driver, otherwise you should answer M to build - it as a loadable module. The module will be called pd.o. You - must also have at least one parallel port protocol driver in your - system. Among the devices supported by this driver are the SyQuest + This option enables the high-level driver for IDE-type disk devices + connected through a parallel port. If you chose to build PARIDE + support into your kernel, you may answer Y here to build in the + parallel port IDE driver, otherwise you should answer M to build + it as a loadable module. The module will be called pd.o. You + must also have at least one parallel port protocol driver in your + system. Among the devices supported by this driver are the SyQuest EZ-135, EZ-230 and SparQ drives, the Avatar Shark and the backpack hard drives from MicroSolutions. @@ -1291,8 +1452,8 @@ system. Among the devices supported by this driver are the MicroSolutions backpack CD-ROM drives and the Freecom Power CD. If you have such a CD-ROM drive, you should also say Y or M to "ISO - 9660 CDROM file system support" below, because that's the file - system used on CDROMs. + 9660 CD-ROM file system support" below, because that's the file + system used on CD-ROMs. Parallel port ATAPI disks CONFIG_PARIDE_PF @@ -1322,7 +1483,7 @@ This option enables a special high-level driver for generic ATAPI devices connected through a parallel port. The driver allows user programs, such as cdrecord, to send ATAPI commands directly to a - device. + device. If you chose to build PARIDE support into your kernel, you may answer Y here to build in the parallel port generic ATAPI driver, @@ -1333,10 +1494,10 @@ your system. This driver implements an API loosely related to the generic SCSI - driver. See include/linux/pg.h for details. + driver. See . for details. You can obtain the most recent version of cdrecord from - ftp://ftp.fokus.gmd.de/pub/unix/cdrecord/ . Versions 1.6.1a3 and + . Versions 1.6.1a3 and later fully support this driver. ATEN EH-100 protocol @@ -1352,39 +1513,40 @@ Micro Solutions BACKPACK Series 5 protocol CONFIG_PARIDE_BPCK - This option enables support for the Micro Solutions BACKPACK parallel - port Series 5 IDE protocol. (Most BACKPACK drives made before 1999 were - Series 5) Series 5 drives will NOT always have the Series noted on the - bottom of the drive. Series 6 drivers will. + This option enables support for the Micro Solutions BACKPACK + parallel port Series 5 IDE protocol. (Most BACKPACK drives made + before 1999 were Series 5) Series 5 drives will NOT always have the + Series noted on the bottom of the drive. Series 6 drivers will. In other words, if your BACKPACK drive dosen't say "Series 6" on the bottom, enable this option. - If you chose to build PARIDE support into your kernel, you may answer Y - here to build in the protocol driver, otherwise you should answer M to - build it as a loadable module. The module will be called bpck.o. You - must also have a high-level driver for the type of device that you want - to support. + If you chose to build PARIDE support into your kernel, you may + answer Y here to build in the protocol driver, otherwise you should + answer M to build it as a loadable module. The module will be + called bpck.o. You must also have a high-level driver for the type + of device that you want to support. Micro Solutions BACKPACK Series 6 protocol CONFIG_PARIDE_BPCK6 - This option enables support for the Micro Solutions BACKPACK parallel - port Series 6 IDE protocol. (Most BACKPACK drives made after 1999 were - Series 6) Series 6 drives will have the Series noted on the bottom of - the drive. Series 5 drivers don't always have it noted. + This option enables support for the Micro Solutions BACKPACK + parallel port Series 6 IDE protocol. (Most BACKPACK drives made + after 1999 were Series 6) Series 6 drives will have the Series noted + on the bottom of the drive. Series 5 drivers don't always have it + noted. - In other words, if your BACKPACK drive says "Series 6" on the bottom, - enable this option. + In other words, if your BACKPACK drive says "Series 6" on the + bottom, enable this option. - If you chose to build PARIDE support into your kernel, you may answer Y - here to build in the protocol driver, otherwise you should answer M to - build it as a loadable module. The module will be called bpck6.o. You - must also have a high-level driver for the type of device that you want - to support. + If you chose to build PARIDE support into your kernel, you may + answer Y here to build in the protocol driver, otherwise you should + answer M to build it as a loadable module. The module will be + called bpck6.o. You must also have a high-level driver for the type + of device that you want to support. DataStor Commuter protocol CONFIG_PARIDE_COMM - This option enables support for the Commuter parallel port IDE + This option enables support for the Commuter parallel port IDE protocol from DataStor. If you chose to build PARIDE support into your kernel, you may answer Y here to build in the protocol driver, otherwise you should answer M to build it as a loadable @@ -1393,7 +1555,7 @@ DataStor EP-2000 protocol CONFIG_PARIDE_DSTR - This option enables support for the EP-2000 parallel port IDE + This option enables support for the EP-2000 parallel port IDE protocol from DataStor. If you chose to build PARIDE support into your kernel, you may answer Y here to build in the protocol driver, otherwise you should answer M to build it as a loadable @@ -1447,12 +1609,12 @@ Freecom IQ ASIC-2 protocol CONFIG_PARIDE_FRIQ This option enables support for version 2 of the Freecom IQ parallel - port IDE adapter. This adapter is used by the Maxell Superdisk + port IDE adapter. This adapter is used by the Maxell Superdisk drive. If you chose to build PARIDE support into your kernel, you may answer Y here to build in the protocol driver, otherwise you should answer M to build it as a loadable module. The module will be called friq.o. You must also have a high-level driver for the type - of device that you want to support. + of device that you want to support. FreeCom power protocol CONFIG_PARIDE_FRPW @@ -1487,12 +1649,12 @@ OnSpec 90c20 protocol CONFIG_PARIDE_ON20 - This option enables support for the (obsolete) 90c20 parallel port + This option enables support for the (obsolete) 90c20 parallel port IDE protocol from OnSpec (often marketed under the ValuStore brand - name). If you chose to build PARIDE support into your kernel, you - may answer Y here to build in the protocol driver, otherwise you - should answer M to build it as a loadable module. The module will - be called on20.o. You must also have a high-level driver for the + name). If you chose to build PARIDE support into your kernel, you + may answer Y here to build in the protocol driver, otherwise you + should answer M to build it as a loadable module. The module will + be called on20.o. You must also have a high-level driver for the type of device that you want to support. OnSpec 90c26 protocol @@ -1516,13 +1678,19 @@ to new capacity needs. Logical volumes are accessed as block devices named /dev/VolumeGroupName/LogicalVolumeName. - For details see Documentation/LVM-HOWTO. You will need supporting - user space software; location is in Documentation/Changes. + For details see . You will need + supporting user space software; location is in + . If you want to compile this support as a module ( = code which can be inserted in and removed from the running kernel whenever you - want), say M here and read Documentation/modules.txt. The module - will be called lvm-mod.o. + want), say M here and read . The + module will be called lvm-mod.o. + +Multiple devices driver support (RAID and LVM) +CONFIG_MD + Support multiple physical spindles through a single logical device. + Required for RAID and logical volume management (LVM). Multiple devices driver support CONFIG_BLK_DEV_MD @@ -1537,7 +1705,7 @@ More information about Software RAID on Linux is contained in the Software-RAID mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . There you will also + . There you will also learn where to get the supporting user space utilities raidtools. If unsure, say N. @@ -1546,12 +1714,12 @@ CONFIG_MD_LINEAR If you say Y here, then your multiple devices driver will be able to use the so-called linear mode, i.e. it will combine the hard disk - partitions by simply appending one to the other. + partitions by simply appending one to the other. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called linear.o. + say M here and read . The module + will be called linear.o. If unsure, say Y. @@ -1561,40 +1729,40 @@ use the so-called raid0 mode, i.e. it will combine the hard disk partitions into one logical device in such a fashion as to fill them up evenly, one chunk here and one chunk there. This will increase - the throughput rate if the partitions reside on distinct disks. + the throughput rate if the partitions reside on distinct disks. Information about Software RAID on Linux is contained in the Software-RAID mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . There you will also + . There you will also learn where to get the supporting user space utilities raidtools. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called raid0.o. + say M here and read . The module + will be called raid0.o. If unsure, say Y. RAID-1 (mirroring) mode CONFIG_MD_RAID1 A RAID-1 set consists of several disk drives which are exact copies - of each other. In the event of a mirror failure, the RAID driver + of each other. In the event of a mirror failure, the RAID driver will continue to use the operational mirrors in the set, providing an error free MD (multiple device) to the higher levels of the - kernel. In a set with N drives, the available space is the capacity + kernel. In a set with N drives, the available space is the capacity of a single drive, and the set protects against a failure of (N - 1) - drives. + drives. Information about Software RAID on Linux is contained in the Software-RAID mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . There you will also + . There you will also learn where to get the supporting user space utilities raidtools. If you want to use such a RAID-1 set, say Y. This code is also available as a module called raid1.o ( = code which can be inserted - in and removed from the running kernel whenever you want). If you + in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . If unsure, say Y. @@ -1610,41 +1778,82 @@ Information about Software RAID on Linux is contained in the Software-RAID mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . There you will also + . There you will also learn where to get the supporting user space utilities raidtools. If you want to use such a RAID-4/RAID-5 set, say Y. This code is also available as a module called raid5.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . If unsure, say Y. +# AC tree only +Support for IDE Raid controllers +CONFIG_BLK_DEV_ATARAID + Say Y or M if you have an IDE Raid controller and want linux + to use its softwareraid feature. You must also select an + appropriate for your board low-level driver below. + + Note, that Linux does not use the Raid implemetation in BIOS, and + the main purpose for this feature is to retain compatibility and + data integrity with other OS-es, using the same disk array. Linux + has its own Raid drivers, which you should use if you need better + performance. + +# AC tree only +Support Promise software RAID (Fasttrak(tm)) +CONFIG_BLK_DEV_ATARAID_PDC + Say Y or M if you have a Promise Fasttrak (tm) Raid controller + and want linux to use the softwareraid feature of this card. + This driver uses /dev/ataraid/dXpY (X and Y numbers) as device + names. + + If you choose to compile this as a module, the module will be called + pdcraid.o. + +# AC tree only +Highpoint 370 software RAID +CONFIG_BLK_DEV_ATARAID_HPT + Say Y or M if you have a Highpoint HPT 370 Raid controller + and want linux to use the softwareraid feature of this card. + This driver uses /dev/ataraid/dXpY (X and Y numbers) as device + names. + + If you choose to compile this as a module, the module will be called + hptraid.o. + Support for Acer PICA 1 chipset CONFIG_ACER_PICA_61 This is a machine with a R4400 133/150 MHz CPU. To compile a Linux kernel that runs on these, say Y here. For details about Linux on the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at - http://oss.sgi.com/mips . + . Support for Algorithmics P4032 (EXPERIMENTAL) CONFIG_ALGOR_P4032 - This is an evaluation board of the British company Algorithmics. The - board uses the R4300 and a R5230 CPUs. For more information about - this board see http://www.algor.co.uk . + This is an evaluation board of the British company Algorithmics. + The board uses the R4300 and a R5230 CPUs. For more information + about this board see . Support for BAGET MIPS series CONFIG_BAGET_MIPS - This enables support for the Baget, a Russian embedded system. For - more details about the Baget see the Linux/MIPS FAQ on - http://oss.sgi.com/mips . + This enables support for the Baget, a Russian embedded system. For + more details about the Baget see the Linux/MIPS FAQ on + . + +Baget AMD LANCE support +CONFIG_BAGETLANCE + Say Y to enable kernel support for AMD Lance Ethernet cards on the + MIPS-32-based Baget embedded system. This chipset is better known + via the NE2100 cards. Support for DECstations CONFIG_DECSTATION This enables support for DEC's MIPS based workstations. For details - see the Linux/MIPS FAQ on http://oss.sgi.com/mips and the - DECstation porting pages on http://decstation.unix-ag.org . + see the Linux/MIPS FAQ on and the + DECstation porting pages on . If you have one of the following DECstation Models you definitely want to choose R4xx0 for the CPU Type: @@ -1656,6 +1865,25 @@ otherwise choose R3000. +Support for Cobalt Micro Server +CONFIG_COBALT_MICRO_SERVER + Support for MIPS-based Cobalt boxes (they have been bought by Sun + and are now the "Server Appliance Business Unit") including the 2700 + series -- versions 1 of the Qube and Raq. To compile a Linux kernel + for this hardware, say Y here. + +Support for Cobalt 2800 +CONFIG_COBALT_28 + Support for the second generation of MIPS-based Cobalt boxes (they + have been bought by Sun and are now the "Server Appliance Business + Unit") including the 2800 series -- versions 2 of the Qube and Raq. + To compile a Linux kernel for this hardware, say Y here. + +Support for the Momentum Computer Ocelot SBC +CONFIG_MOMENCO_OCELOT + The Ocelot is a MIPS-based Single Board Computer (SBC) made by + Momentum Computer . + Support for NEC DDB Vrc-5074 CONFIG_DDB5074 This enables support for the VR5000-based NEC DDB Vrc-5074 @@ -1667,10 +1895,18 @@ evaluation board. Features : kernel debugging, serial terminal, NFS root fs, on-board - ether port (with a patch to tulip driver), IDE controller, PS2 keyboard - PS2 mouse, etc. + ether port (Need an additional patch at ), + USB, AC97, PCI, PCI VGA card & framebuffer console, IDE controller, + PS2 keyboard, PS2 mouse, etc. + +Support for NEC DDB Vrc-5477 +CONFIG_DDB5477 + This enables support for the R5432-based NEC DDB Vrc-5477 + evaluation board. - TODO : USB, Compact-PCI interface. + Features : kernel debugging, serial terminal, NFS root fs, on-board + ether port (Need an additional patch at ), + USB, AC97, PCI, etc. Support for MIPS Atlas board CONFIG_MIPS_ATLAS @@ -1682,19 +1918,39 @@ This enables support for the VR5000-based MIPS Malta evaluation board. +Support for Galileo Evaluation board or CoSine Orion +CONFIG_ORION + Say Y if configuring for the Galileo evaluation board + or CoSine Orion. More information is available at + . + + Otherwise, say N. + Support for Mips Magnum 4000 CONFIG_MIPS_MAGNUM_4000 This is a machine with a R4000 100 MHz CPU. To compile a Linux kernel that runs on these, say Y here. For details about Linux on the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at - http://oss.sgi.com/mips. + . + +Enable Qtronix 990P Keyboard Support +CONFIG_QTRONIX_KEYBOARD + Images of Qtronix keyboards are at + . Support for Olivetti M700 CONFIG_OLIVETTI_M700 This is a machine with a R4000 100 MHz CPU. To compile a Linux kernel that runs on these, say Y here. For details about Linux on the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at - http://oss.sgi.com/mips. + . + +Support for SNI RM200 PCI +CONFIG_SNI_RM200_PCI + The SNI RM200 PCI was a MIPS-based platform manufactured by Siemens + Nixdorf Informationssysteme (SNI), parent company of Pyramid + Technology and now in turn merged with Fujitsu. Say Y here to + support this machine type. Support for SGI IP22 CONFIG_SGI_IP22 @@ -1703,6 +1959,7 @@ that runs on these, say Y here. Support for SGI IP27 +CONFIG_SGI_IP27 This are the SGI Origin 200, Origin 2000 and Onyx 2 Graphics workstations. To compile a Linux kernel that runs on these, say Y here. @@ -1725,11 +1982,49 @@ 4000, Acer PICA, Olivetti M700-10 and a few other identical OEM systems. +MIPS GT96100 support +CONFIG_MIPS_GT96100 + Say Y here to support the Galileo Technology GT96100 communications + controller card. There is a web page at . + +MIPS GT96100 Ethernet support +CONFIG_MIPS_GT96100ETH + Say Y here to support the Ethernet subsystem on your GT96100 card. + +Zalon SCSI support +CONFIG_SCSI_ZALON + The Zalon is an interface chip that sits between the PA-RISC + processor and the NCR 53c720 SCSI controller on K-series PA-RISC + boards (these are used, among other places, on some HP 780 + workstations). Say Y here to make sure it gets initialized + correctly before the Linux kernel tries to talk to the controller. + Kernel floating-point instruction emulation CONFIG_MIPS_FPU_EMULATOR - This option enables the MIPS software floatingpoint support. Due to the - way floatingpoint works you should always enable this option unless - you exactly know what you're doing. + This option enables the MIPS software floatingpoint support. Due to + the way floating point works you should always enable this option + unless you exactly know what you're doing. + +SGI PROM Console Support +CONFIG_SGI_PROM_CONSOLE + Say Y here to set up the boot console on serial port 0. + +DZ11 Serial Support +CONFIG_DZ + DZ11-family serial controllers for VAXstations, including the + DC7085, M7814, and M7819. + + +TURBOchannel support +CONFIG_TC + TurboChannel is a DEC (now Compaq) bus for Alpha and MIPS processors. + Documentation on writing device drivers for TurboChannel is available at: + . + +Z85C30 Serial Support +CONFIG_ZS + Documentation on the Zilog 85C350 serial communications controller + is downloadable at . PCMCIA SCSI adapter support CONFIG_SCSI_PCMCIA @@ -1738,9 +2033,30 @@ size devices often used with laptops. Note that the answer to this question won't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions PCMCIA SCSI host adapters. +Adaptec APA1480 CardBus support +CONFIG_PCMCIA_APA1480 + Say Y here if you intend to attach this type of CardBus SCSI host + adapter to your computer. + + This driver is also available as a module called apa1480_cb.o ( = + code which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read . + +NinjaSCSI-3 / NinjaSCSI-32Bi (16bit) PCMCIA support +CONFIG_PCMCIA_NINJA_SCSI + If you intend to attach this type of PCMCIA SCSI host adapter to + your computer, say Y here and read + . + + This driver is also available as a module called nsp_cs.o ( = + code which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read . + Adaptec AHA152X PCMCIA support CONFIG_PCMCIA_AHA152X Say Y here if you intend to attach this type of PCMCIA SCSI host @@ -1749,7 +2065,7 @@ This driver is also available as a module called aha152x_cs.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . Qlogic PCMCIA support CONFIG_PCMCIA_QLOGIC @@ -1758,8 +2074,8 @@ This driver is also available as a module called qlogic_cs.o ( = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + whenever you want). If you want to compile it as a module, say M + here and read . Future Domain PCMCIA support CONFIG_PCMCIA_FDOMAIN @@ -1768,27 +2084,96 @@ This driver is also available as a module called fdomain_cs.o ( = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. - -Adaptec APA1480 CardBus support -CONFIG_PCMCIA_APA1480 - Say Y here if you intend to attach this type of CardBus SCSI host - adapter to your computer. - - This driver is also available as a module called apa1480_cb.o ( = - code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + whenever you want). If you want to compile it as a module, say M + here and read . +# Choice: mipstype CPU type CONFIG_CPU_R3000 Please make sure to pick the right CPU type. Linux/MIPS is not designed to be generic, i.e. Kernels compiled for R3000 CPUs will - *not* work on R4000 Machines and vice versa. - However, since most the supported Machines have an R4000 (or - similar) CPU, R4xx0 might be a safe bet. - If the resulting Kernel does not work try to recompile with R3000. + *not* work on R4000 machines and vice versa. However, since most + of the supported machines have an R4000 (or similar) CPU, R4x00 + might be a safe bet. If the resulting kernel does not work, + try to recompile with R3000. + + R3000 MIPS Technologies R3000-series processors, + including the 3041, 3051, and 3081. + + R6000 MIPS Technologies R6000-series processors, + including the 64474, 64475, 64574 and 64575. + + R4300 MIPS Technologies R4300-series processors. + + R4x00 MIPS Technologies R4000-series processors other than 4300, + including the 4640, 4650, and 4700. + + R5000 MIPS Technologies R5000-series processors other than the + Nevada. + + R52xx MIPS Technologies R52xx-series ("Nevada") processors. + + R10000 MIPS Technologies R10000-series processors. + +R6000 +CONFIG_CPU_R6000 + MIPS Technologies R6000-series processors, including the 64474, + 64475, 64574 and 64575. + +R4300 +CONFIG_CPU_R4300 + MIPS Technologies R4300-series processors. + +R4x00 +CONFIG_CPU_R4X00 + MIPS Technologies R4000-series processors other than 4300, including + the 4640, 4650, and 4700. + +R5000 +CONFIG_CPU_R5000 + MIPS Technologies R5000-series processors other than the Nevada. + +R52x0 +CONFIG_CPU_NEVADA + MIPS Technologies R52x0-series ("Nevada") processors. + +R8000 +CONFIG_CPU_R8000 + MIPS Technologies R8000-series processors. + +R10000 +CONFIG_CPU_R10000 + MIPS Technologies R10000-series processors. + +Discontiguous Memory Support +CONFIG_DISCONTIGMEM + Say Y to upport efficient handling of discontiguous physical memory, + for architectures which are either NUMA (Non-Uniform Memory Access) + or have huge holes in the physical address space for other reasons. + See for more. + +Mapped kernel support +CONFIG_MAPPED_KERNEL + Change the way a Linux kernel is loaded unto memory on a MIPS64 + machine. This is required in order to support text replication and + NUMA. If you need to undersatand it, read the source code. + +Kernel text replication support +CONFIG_REPLICATE_KTEXT + Say Y here to enable replicating the kernel text across multiple + nodes in a NUMA cluster. This trades memory for speed. + +Exception handler replication support +CONFIG_REPLICATE_EXHANDLERS + Say Y here to enable replicating the kernel exception handlers + across multiple nodes in a NUMA cluster. This trades memory for + speed. + +NUMA support? +CONFIG_NUMA + Say Y to compile the kernel to support NUMA (Non-Uniform Memory + Access). This option is for configuring high-end multiprocessor + server machines. If in doubt, say N. CPU type CONFIG_CPU_VR41XX @@ -1799,14 +2184,18 @@ CPU feature configuration CONFIG_CPU_ADVANCED - Saying yes here allows you to select support for various features your - CPU may or may not have. Most people should say N here. + Saying yes here allows you to select support for various features + your CPU may or may not have. Most people should say N here. -ll and sc instructions +ll/sc Instructions available CONFIG_CPU_HAS_LLSC - Say Y here if your CPU has the ll and sc instructions. Say Y here for - better performance, N if you don't know. You must say Y here for - multiprocessor machines. + MIPS R4000 series and later provide the Load Linked (ll) + and Store Conditional (sc) instructions. More information is + available at . + + Say Y here if your CPU has the ll and sc instructions. Say Y here + for better performance, N if you don't know. You must say Y here + for multiprocessor machines. lld and scd instructions CONFIG_CPU_HAS_LLDSCD @@ -1814,7 +2203,7 @@ equivalents of ll and sc. Say Y here for better performance, N if you don't know. You must say Y here for multiprocessor machines. -Support for writebuffer flushing +Writeback Buffer available CONFIG_CPU_HAS_WB Say N here for slightly better performance. You must say Y here for machines which require flushing of write buffers in software. Saying @@ -1834,6 +2223,12 @@ byte order. These modes require different kernels. Say Y if your machine is little endian, N if it's a big endian machine. +Use power LED as a heartbeat +CONFIG_HEARTBEAT + Use the power-on LED on your machine as a load meter. The exact + behavior is platform-dependent, but normally the flash frequency is + a hyperbolic function of the 5-minute load average. + Networking support CONFIG_NET Unless you really know what you are doing, you should say Y here. @@ -1843,27 +2238,28 @@ should consider updating your networking tools too because changes in the kernel and the tools often go hand in hand. The tools are contained in the package net-tools, the location and version number - of which are given in Documentation/Changes. + of which are given in . For a general introduction to Linux networking, it is highly recommended to read the NET-3-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Socket filtering CONFIG_FILTER The Linux Socket Filter is derived from the Berkeley Packet Filter. If you say Y here, user-space programs can attach a filter to any socket and thereby tell the kernel that it should allow or disallow - certain types of data to get through the socket. Linux Socket - Filtering works on all socket types except TCP for now. See the text - file Documentation/networking/filter.txt for more information. + certain types of data to get through the socket. Linux Socket + Filtering works on all socket types except TCP for now. See the + text file for more + information. You need to say Y here if you want to use PPP packet filtering (see the CONFIG_PPP_FILTER option below). If unsure, say N. -Network packet filtering +Network packet filtering (replaces ipchains) CONFIG_NETFILTER Netfilter is a framework for filtering and mangling network packets that pass through your Linux box. @@ -1906,21 +2302,21 @@ Various modules exist for netfilter which replace the previous masquerading (ipmasqadm), packet filtering (ipchains), transparent proxying, and portforwarding mechanisms. Please see - Documentation/Changes under "iptables" for the location of these - packages. - + under "iptables" for the location of + these packages. + Make sure to say N to "Fast switching" below if you intend to say Y here, as Fast switching currently bypasses netfilter. - + Chances are that you should say Y here if you compile a kernel which will run as a router and N for regular hosts. If unsure, say N. - + Network packet filtering debugging CONFIG_NETFILTER_DEBUG You can say Y here if you want to get additional messages useful in - debugging the netfilter code. + debugging the netfilter code. -IP: connection tracking (required for masq/NAT) +Connection tracking (required for masq/NAT) CONFIG_IP_NF_CONNTRACK Connection tracking keeps a record of what packets have passed through your machine, in order to figure out how they are related @@ -1932,7 +2328,7 @@ below). If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. FTP protocol support CONFIG_IP_NF_FTP @@ -1941,17 +2337,17 @@ of Network Address Translation on them. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `Y'. + . If unsure, say `Y'. -IP: user space queueing via NETLINK (EXPERIMENTAL) +User space queueing via NETLINK CONFIG_IP_NF_QUEUE Netfilter has the ability to queue packets to user space: the netlink device can be used to access them using this driver. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. -IP: ip tables support (required for filtering/masq/NAT) +IP tables support (required for filtering/masq/NAT) CONFIG_IP_NF_IPTABLES iptables is a general, extensible packet identification framework. The packet filtering and full NAT (masquerading, port forwarding, @@ -1959,7 +2355,7 @@ either of those. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. limit match support CONFIG_IP_NF_MATCH_LIMIT @@ -1968,24 +2364,24 @@ target support", below) and to avoid some Denial of Service attacks. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. MAC address match support CONFIG_IP_NF_MATCH_MAC - mac matching allows you to match packets based on the source - ethernet address of the packet. + MAC matching allows you to match packets based on the source + Ethernet address of the packet. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. -netfilter mark match support +Netfilter MARK match support CONFIG_IP_NF_MATCH_MARK Netfilter mark matching allows you to match packets based on the `nfmark' value in the packet. This can be set by the MARK target (see below). If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. Multiple port match support CONFIG_IP_NF_MATCH_MULTIPORT @@ -1994,7 +2390,7 @@ match a single range of ports. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. TOS match support CONFIG_IP_NF_MATCH_TOS @@ -2002,7 +2398,7 @@ Service fields of the IP packet. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. Connection state match support CONFIG_IP_NF_MATCH_STATE @@ -2011,23 +2407,23 @@ is a powerful tool for packet classification. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. -Unclean match support (EXPERIMENTAL) +Unclean match support CONFIG_IP_NF_MATCH_UNCLEAN Unclean packet matching matches any strange or invalid packets, by looking at a series of fields in the IP, TCP, UDP and ICMP headers. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. -Owner match support (EXPERIMENTAL) +Owner match support CONFIG_IP_NF_MATCH_OWNER Packet owner matching allows you to match locally-generated packets based on who created them: the user, group, process or session. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. Packet filtering CONFIG_IP_NF_FILTER @@ -2036,7 +2432,7 @@ local output. See the man page for iptables(8). If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. REJECT target support CONFIG_IP_NF_TARGET_REJECT @@ -2045,24 +2441,24 @@ than silently being dropped. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. -MIRROR target support (EXPERIMENTAL) +MIRROR target support CONFIG_IP_NF_TARGET_MIRROR The MIRROR target allows a filtering rule to specify that an incoming packet should be bounced back to the sender. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. -Full NAT +Full NAT (Network Address Translation) CONFIG_IP_NF_NAT The Full NAT option allows masquerading, port forwarding and other forms of full Network Address Port Translation. It is controlled by the `nat' table in iptables: see the man page for iptables(8). If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. MASQUERADE target support CONFIG_IP_NF_TARGET_MASQUERADE @@ -2073,7 +2469,7 @@ address will be different on next dialup). If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. REDIRECT target support CONFIG_IP_NF_TARGET_REDIRECT @@ -2083,7 +2479,7 @@ useful for transparent proxies. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. Packet mangling CONFIG_IP_NF_MANGLE @@ -2092,7 +2488,7 @@ which can effect how the packet is routed. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. TOS target support CONFIG_IP_NF_TARGET_TOS @@ -2101,19 +2497,19 @@ packet prior to routing. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. MARK target support CONFIG_IP_NF_TARGET_MARK This option adds a `MARK' target, which allows you to create rules in the `mangle' table which alter the netfilter mark (nfmark) field - associated with the packet packet prior to routing. This can change - the routing method (see `IP: use netfilter MARK value as routing + associated with the packet prior to routing. This can change + the routing method (see `Use netfilter MARK value as routing key') and can also be used by other subsystems to change their behavior. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. TCPMSS target support CONFIG_IP_NF_TARGET_TCPMSS @@ -2138,16 +2534,16 @@ -j TCPMSS --clamp-mss-to-pmtu If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. -tcpmss match support +TCPMSS match support CONFIG_IP_NF_MATCH_TCPMSS This option adds a `tcpmss' match, which allows you to examine the MSS value of TCP SYN packets, which control the maximum packet size for that connection. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. LOG target support CONFIG_IP_NF_TARGET_LOG @@ -2155,7 +2551,7 @@ any iptables table which records the packet header to the syslog. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. ipchains (2.2-style) support CONFIG_IP_NF_COMPAT_IPCHAINS @@ -2166,7 +2562,7 @@ the ipchains tool exactly as in 2.2 kernels. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. ipfwadm (2.0-style) support CONFIG_IP_NF_COMPAT_IPFWADM @@ -2177,43 +2573,24 @@ the ipfwadm tool exactly as in 2.0 kernels. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -IP6 tables support (required for filtering/masq/NAT) -CONFIG_IP6_NF_IPTABLES - ip6tables is a general, extensible packet identification framework. - Currently only the packet filtering and packet mangling subsystem - for IPv6 use this, but connection tracking is going to follow. - Say 'Y' or 'M' here if you want to use either of those. - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -IPv6 limit match support -CONFIG_IP6_NF_MATCH_LIMIT - limit matching allows you to control the rate at which a rule can be - matched: mainly useful in combination with the LOG target ("LOG - target support", below) and to avoid some Denial of Service attacks. - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. MAC address match support CONFIG_IP6_NF_MATCH_MAC mac matching allows you to match packets based on the source - ethernet address of the packet. + Ethernet address of the packet. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. -netfilter mark match support +Netfilter MARK match support CONFIG_IP6_NF_MATCH_MARK Netfilter mark matching allows you to match packets based on the `nfmark' value in the packet. This can be set by the MARK target (see below). If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. Packet filtering CONFIG_IP6_NF_FILTER @@ -2222,7 +2599,7 @@ local output. See the man page for iptables(8). If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. Packet mangling CONFIG_IP6_NF_MANGLE @@ -2231,38 +2608,38 @@ which can effect how the packet is routed. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. MARK target support CONFIG_IP6_NF_TARGET_MARK This option adds a `MARK' target, which allows you to create rules in the `mangle' table which alter the netfilter mark (nfmark) field associated with the packet packet prior to routing. This can change - the routing method (see `IP: use netfilter MARK value as routing + the routing method (see `Use netfilter MARK value as routing key') and can also be used by other subsystems to change their behavior. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. TCP Explicit Congestion Notification support CONFIG_INET_ECN Explicit Congestion Notification (ECN) allows routers to notify clients about network congestion, resulting in fewer dropped packets - and increased network performance. This option adds ECN support to the - Linux kernel, as well as a sysctl (/proc/sys/net/ipv4/tcp_ecn) which - allows ECN support to be disabled at runtime. + and increased network performance. This option adds ECN support to + the Linux kernel, as well as a sysctl (/proc/sys/net/ipv4/tcp_ecn) + which allows ECN support to be disabled at runtime. Note that, on the Internet, there are many broken firewalls which refuse connections from ECN-enabled machines, and it may be a while - before these firewalls are fixed. Until then, to access a site behind - such a firewall (some of which are major sites, at the time of this - writing) you will have to disable this option, either by saying N now - or by using the sysctl. + before these firewalls are fixed. Until then, to access a site + behind such a firewall (some of which are major sites, at the time + of this writing) you will have to disable this option, either by + saying N now or by using the sysctl. If in doubt, say N. -IP6 tables support (required for filtering/masq/NAT) +IPv6 tables support (required for filtering/masq/NAT) CONFIG_IP6_NF_IPTABLES ip6tables is a general, extensible packet identification framework. Currently only the packet filtering and packet mangling subsystem @@ -2270,7 +2647,7 @@ Say 'Y' or 'M' here if you want to use either of those. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. IPv6 limit match support CONFIG_IP6_NF_MATCH_LIMIT @@ -2279,54 +2656,7 @@ target support", below) and to avoid some Denial of Service attacks. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -MAC address match support -CONFIG_IP6_NF_MATCH_MAC - mac matching allows you to match packets based on the source - ethernet address of the packet. - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -netfilter mark match support -CONFIG_IP6_NF_MATCH_MARK - Netfilter mark matching allows you to match packets based on the - `nfmark' value in the packet. This can be set by the MARK target - (see below). - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -Packet filtering -CONFIG_IP6_NF_FILTER - Packet filtering defines a table `filter', which has a series of - rules for simple packet filtering at local input, forwarding and - local output. See the man page for iptables(8). - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -Packet mangling -CONFIG_IP6_NF_MANGLE - This option adds a `mangle' table to iptables: see the man page for - iptables(8). This table is used for various packet alterations - which can effect how the packet is routed. - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -MARK target support -CONFIG_IP6_NF_TARGET_MARK - This option adds a `MARK' target, which allows you to create rules - in the `mangle' table which alter the netfilter mark (nfmark) field - associated with the packet packet prior to routing. This can change - the routing method (see `IP: use netfilter MARK value as routing - key') and can also be used by other subsystems to change their - behavior. - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. + . If unsure, say `N'. SYN flood protection CONFIG_SYN_COOKIES @@ -2342,8 +2672,7 @@ continue to connect, even when your machine is under attack. There is no need for the legitimate users to change their TCP/IP software; SYN cookies work transparently to them. For technical information - about SYN cookies, check out - ftp://koobera.math.uic.edu/syncookies.html . + about SYN cookies, check out . If you are SYN flooded, the source address reported by the kernel is likely to have been forged by the attacker; it is only reported as @@ -2358,12 +2687,21 @@ you can enable them by saying Y to "/proc file system support" and "Sysctl support" below and executing the command - echo 1 >/proc/sys/net/ipv4/tcp_syncookies + echo 1 >/proc/sys/net/ipv4/tcp_syncookies at boot time after the /proc file system has been mounted. - - If unsure, say Y. + If unsure, say N. + +HCI EMU (virtual device) driver +CONFIG_BLUEZ_HCIEMU + Bluetooth Virtual HCI device driver. + This driver is required if you want to use HCI Emulation software. + + Say Y here to compile support for Virtual HCI devices into the + kernel or say M to compile it as module (hci_usb.o). + +# Choice: alphatype Alpha system type CONFIG_ALPHA_GENERIC This is the system type of your hardware. A "generic" kernel will @@ -2372,7 +2710,7 @@ To find out what type of Alpha system you have, you may want to check out the Linux/Alpha FAQ, accessible on the WWW from - http://www.alphalinux.org . In summary: + . In summary: Alcor/Alpha-XLT AS 600 Alpha-XL XL-233, XL-266 @@ -2384,7 +2722,7 @@ EB64+ EB64+ 21064 evaluation board EB66 EB66 21066 evaluation board EB66+ EB66+ 21066 evaluation board - Jensen DECpc 150, DEC 2000 model 300, + Jensen DECpc 150, DEC 2000 model 300, DEC 2000 model 500 LX164 AlphaPC164-LX Miata Personal Workstation 433a, 433au, 500a, @@ -2404,21 +2742,159 @@ If you don't know what to do, choose "generic". -EV5 CPU daughtercard +# Most of the information on these variants is from +# +Alcor/Alpha-XLT +CONFIG_ALPHA_ALCOR + For systems using the Digital ALCOR chipset: 5 chips (4, 64-bit data + slices (Data Switch, DSW) - 208-pin PQFP and 1 control (Control, I/O + Address, CIA) - a 383 pin plastic PGA). It provides a DRAM + controller (256-bit memory bus) and a PCI interface. It also does + all the work required to support an external Bcache and to maintain + memory coherence when a PCI device DMAs into (or out of) memory. + +Alpha-XL +CONFIG_ALPHA_XL + XL-233 and XL-266-based Alpha systems. + +AlphaBook1 +CONFIG_ALPHA_BOOK1 + Dec AlphaBook1/Burns Alpha-based laptops. + +Avanti +CONFIG_ALPHA_AVANTI + Avanti AS 200, AS 205, AS 250, AS 255, AS 300, and AS 400-based + Alphas. Info at + . + +Cabriolet +CONFIG_ALPHA_CABRIOLET + Cabriolet AlphaPC64, AlphaPCI64 systems. Derived from EB64+ but now + baby-AT with Flash boot ROM, no on-board SCSI or Ethernet. 3 ISA + slots, 4 PCI slots (one pair are on a shared slot), uses plug-in + Bcache SIMMs. Requires power supply with 3.3V output. + +DP264 +CONFIG_ALPHA_DP264 + Hard Data HD-DP264-based Alpha systems. There is a Hard Data + website at . + +EB164 +CONFIG_ALPHA_EB164 + EB164 21164 evaluation board from DEC. Uses 21164 and ALCOR. Has + ISA and PCI expansion (3 ISA slots, 2 64-bit PCI slots (one is + shared with an ISA slot) and 2 32-bit PCI slots. Uses plus-in + Bcache SIMMs. I/O sub-system provides SuperI/O (2S, 1P, FD), KBD, + MOUSE (PS2 style), RTC/NVRAM. Boot ROM is Flash. PC-AT-sized + motherboard. Requires power supply with 3.3V output. + +EB64+ +CONFIG_ALPHA_EB64P + Uses 21064 or 21064A and APECs. Has ISA and PCI expansion (3 ISA, + 2 PCI, one pair are on a shared slot). Supports 36-bit DRAM SIMs. + ISA bus generated by Intel SaturnI/O PCI-ISA bridge. On-board SCSI + (NCR 810 on PCI) Ethernet (Digital 21040), KBD, MOUSE (PS2 style), + SuperI/O (2S, 1P, FD), RTC/NVRAM. Boot ROM is EPROM. PC-AT size. + Runs from standard PC power supply. + +EB66 +CONFIG_ALPHA_EB66 + A Digital DS group board. Uses 21066 or 21066A. I/O sub-system is + identical to EB64+. Baby PC-AT size. Runs from standard PC power + supply. The EB66 schematic was published as a marketing poster + advertising the 21066 as "the first microprocessor in the world with + embedded PCI". + +EB66+ +CONFIG_ALPHA_EB66P + Later variant of the EB66 board. + +Eiger +CONFIG_ALPHA_EIGER + Apparently an obscure OEM single-board computer based on the + Typhoon/Tsunami chipset family. Information on it is scanty. + +Jensen +CONFIG_ALPHA_JENSEN + DEC PC 150 AXP (aka Jensen): This is a very old Digital system - one + of the first-generation Alpha systems. A number of these systems + seem to be available on the second- hand market. The Jensen is a + floor-standing tower system which originally used a 150MHz 21064 It + used programmable logic to interface a 486 EISA I/O bridge to the + CPU. + +LX164 +CONFIG_ALPHA_LX164 + A technical overview of this board is available at + . + +Miata +CONFIG_ALPHA_MIATA + The Digital PersonalWorkStation (PWS 433a, 433au, 500a, 500au, 600a, + or 600au). There is an Installation HOWTO for this hardware at + . + +Mikasa +CONFIG_ALPHA_MIKASA + AlphaServer 1000-based Alpha systems. + +Nautilus +CONFIG_ALPHA_NAUTILUS + Alpha systems based on the AMD 751 & ALI 1543C chipsets. + +Noname +CONFIG_ALPHA_NONAME + The AXPpci33 (aka NoName), is based on the EB66 (includes the Multia + UDB). This design was produced by Digital's Technical OEM (TOEM) + group. It uses the 21066 processor running at 166MHz or 233MHz. It + is a baby-AT size, and runs from a standard PC power supply. It has + 5 ISA slots and 3 PCI slots (one pair are a shared slot). There are + 2 versions, with either PS/2 or large DIN connectors for the + keyboard. + +Noritake +CONFIG_ALPHA_NORITAKE + AlphaServer 1000A, AlphaServer 600A, and AlphaServer 800-based + systems. + +Rawhide +CONFIG_ALPHA_RAWHIDE + AlphaServer 1200, AlphaServer 4000 and AlphaServer 4100 machines. + See HOWTO at + . + +Ruffian +CONFIG_ALPHA_RUFFIAN + Samsung APC164UX. There is a page on known problems and workarounds + at . + +Sable +CONFIG_ALPHA_SABLE + Digital AlphaServer 2000 and 2100-based systems. + +Takara +CONFIG_ALPHA_TAKARA + Alpha 11164-based OEM single-board computer. + +Wildfire +CONFIG_ALPHA_WILDFIRE + AlphaServer GS 40/80/160/320 SMP based on the EV67 core. + +EV5 CPU daughtercard (model 5/xxx) CONFIG_ALPHA_PRIMO Say Y if you have an AS 1000 5/xxx or an AS 1000A 5/xxx. -EV5 CPU(s) +EV5 CPU(s) (model 5/xxx) CONFIG_ALPHA_GAMMA Say Y if you have an AS 2000 5/xxx or an AS 2100 5/xxx. -Using SRM as bootloader +Use SRM as bootloader CONFIG_ALPHA_SRM There are two different types of booting firmware on Alphas: SRM, which is command line driven, and ARC, which uses menus and arrow keys. Details about the Linux/Alpha booting process are contained in the Linux/Alpha FAQ, accessible on the WWW from - http://www.alphalinux.org . + . The usual way to load Linux on an Alpha machine is to use MILO (a bootloader that lets you pass command line parameters to the @@ -2429,7 +2905,7 @@ here. If MILO doesn't work on your system (true for Jensen motherboards), you can bypass it altogether and boot Linux directly from an SRM console; say Y here in order to do that. Note that you - won't be able to boot from an IDE disk using SRM. + won't be able to boot from an IDE disk using SRM. If unsure, say N. @@ -2438,20 +2914,20 @@ The 2.4 kernel changed the kernel start address from 0x310000 to 0x810000 to make room for the Wildfire's larger SRM console. - If you're using aboot 0.7 or later, the bootloader will examine - the ELF headers to determine where to transfer control. Unfortunately, - most older bootloaders -- APB or MILO -- hardcoded the kernel - start address rather than examining the ELF headers, and the result - is a hard lockup. + If you're using aboot 0.7 or later, the bootloader will examine the + ELF headers to determine where to transfer control. Unfortunately, + most older bootloaders -- APB or MILO -- hardcoded the kernel start + address rather than examining the ELF headers, and the result is a + hard lockup. - Say Y if you have a broken bootloader. Say N if you do not, or - if you wish to run on Wildfire. + Say Y if you have a broken bootloader. Say N if you do not, or if + you wish to run on Wildfire. Large VMALLOC support CONFIG_ALPHA_LARGE_VMALLOC - Process creation and other aspects of virtual memory management - can be streamlined if we restrict the kernel to one PGD for all - vmalloc allocations. This equates to about 8GB. + Process creation and other aspects of virtual memory management can + be streamlined if we restrict the kernel to one PGD for all vmalloc + allocations. This equates to about 8GB. Under normal circumstances, this is so far and above what is needed as to be laughable. However, there are certain applications (such @@ -2467,11 +2943,11 @@ This includes intelligent serial boards such as Cyclades, Digiboards, etc. These are usually used for systems that need many serial ports because they serve many terminals or dial-in - connections. + connections. Note that the answer to this question won't directly affect the - kernel: saying N will just cause this configure script to skip all - the questions about non-standard serial boards. + kernel: saying N will just cause the configurator to skip all + the questions about non-standard serial boards. Most people can say N here. @@ -2480,10 +2956,10 @@ If you wish to use any non-standard features of the standard "dumb" driver, say Y here. This includes HUB6 support, shared serial interrupts, special multiport support, support for more than the - four COM 1/2/3/4 boards, etc. + four COM 1/2/3/4 boards, etc. Note that the answer to this question won't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions about serial driver options. If unsure, say N. Support more than 4 serial ports @@ -2491,7 +2967,7 @@ Say Y here if you have dumb serial boards other than the four standard COM 1/2/3/4 ports. This may happen if you have an AST FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto ), or other custom + from ), or other custom serial port hardware which acts similar to standard serial port hardware. If you only use the standard COM 1/2/3/4 ports, you can say N here to save some memory. You can also say Y if you have an @@ -2503,10 +2979,10 @@ serial ports on the same board to share a single IRQ. To enable support for this in the serial driver, say Y here. -Auto detect IRQ on standard ports (unsafe) +Auto-detect IRQ on standard ports (unsafe) CONFIG_SERIAL_DETECT_IRQ Say Y here if you want the kernel to try to guess which IRQ - to use for your serial port. + to use for your serial port. This is considered unsafe; it is far better to configure the IRQ in a boot script using the setserial command. @@ -2520,16 +2996,12 @@ servicing. Say Y here to enable the serial driver to take advantage of those special I/O ports. -SGI PROM Console Support -CONFIG_ARC_CONSOLE - Say Y here if you want to use the PROMs for console I/O. - SGI Zilog85C30 serial support CONFIG_SGI_SERIAL If you want to use your SGI's built-in serial ports under Linux, answer Y. -SGI Newport Graphics support (EXPERIMENTAL) +SGI Newport Graphics support CONFIG_SGI_NEWPORT_GFX If you have an SGI machine and you want to compile the graphics drivers, say Y here. This will include the code for the @@ -2559,26 +3031,48 @@ CONFIG_PCMCIA_SERIAL_CS Say Y here to enable support for 16-bit PCMCIA serial devices, including serial port cards, modems, and the modem functions of - multi-function ethernet/modem cards. (PCMCIA- or PC-cards are + multi-function Ethernet/modem cards. (PCMCIA- or PC-cards are credit-card size devices often used with laptops.) This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called serial_cs.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. If unsure, - say N. + The module will be called serial_cs.o. If you want to compile it as + a module, say M here and read . + If unsure, say N. + +ACP Modem (Mwave) support +CONFIG_MWAVE + The ACP modem (Mwave) for Linux is a WinModem. It is composed of a + kernel driver and a user level application. Together these components + support direct attachment to public switched telephone networks (PSTNs) + and support selected world wide countries. -/dev/agpgart (AGP Support) (EXPERIMENTAL) + This version of the ACP Modem driver supports the IBM Thinkpad 600E, + 600, and 770 that include on board ACP modem hardware. + + The modem also supports the standard communications port interface + (ttySx) and is compatible with the Hayes AT Command Set. + + The user level application needed to use this driver can be found at + the IBM Linux Technology Center (LTC) web site: + http://www.ibm.com/linux/ltc/ + + If you own one of the above IBM Thinkpads which has the Mwave chipset + in it, say Y. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called mwave.o. If you want to compile it as + a module, say M here and read Documentation/modules.txt. + +/dev/agpgart (AGP Support) CONFIG_AGP AGP (Accelerated Graphics Port) is a bus system mainly used to - connect graphics cards to the rest of the system. + connect graphics cards to the rest of the system. If you have an AGP system and you say Y here, it will be possible to use the AGP features of your 3D rendering video card. This code acts - as a sort of "AGP driver" for the motherboard's chipset. The glx - module will then be able to program the GART (graphics aperture - relocation table) registers with appropriate values to transfer - commands to the card. + as a sort of "AGP driver" for the motherboard's chipset. If you need more texture memory than you can get with the AGP GART (theoretically up to 256 MB, but in practice usually 64 or 128 MB @@ -2589,47 +3083,42 @@ write-combining with MTRR support on the AGP bus. Without it, OpenGL direct rendering will be a lot slower but still faster than PIO. - For the moment, you should probably say N, unless you want to test - the GLX component for XFree86 3.3.6, which can be downloaded from - http://utah-glx.sourceforge.net/ , or need to use the 810 Xserver in - XFree 3.3.6. - - This driver is available as a module. If you want to compile it as a - module, say M here and read Documentation/modules.txt. The module - will be called agpgart.o. + You should say Y here if you use XFree86 3.3.6 or 4.x and want to + use GLX or DRI. If unsure, say N. -Intel 440LX/BX/GX/815/830M/840/850 support + This driver is available as a module. If you want to compile it as + a module, say M here and read . The + module will be called agpgart.o. + +Intel 440LX/BX/GX/815/830/840/850/860 support CONFIG_AGP_INTEL This option gives you AGP support for the GLX component of the - XFree86 4.x on Intel 440LX/BX/GX, 815, 830M, 840 and 850 chipsets. + XFree86 4.x on Intel 440LX/BX/GX, 815, 830, 840, 850 and 860 chipsets. - For the moment, you should probably say N, unless you want to test - the GLX component for XFree86 3.3.6, which can be downloaded from - http://utah-glx.sourceforge.net/ . + You should say Y here if you use XFree86 3.3.6 or 4.x and want to + use GLX or DRI. If unsure, say N. -Intel I810/I810 DC100/I810e support +Intel I810/I815 DC100/I810e support CONFIG_AGP_I810 - This option gives you AGP support for the Xserver on the Intel 810, - 830M and 815 chipset boards for their on-board integrated graphics. - This is required to do any useful video modes with these boards. + This option gives you AGP support for the Xserver on the Intel 810 + 815 and 830m chipset boards for their on-board integrated graphics. This + is required to do any useful video modes with these boards. VIA chipset support CONFIG_AGP_VIA This option gives you AGP support for the GLX component of the XFree86 4.x on VIA MPV3/Apollo Pro chipsets. - For the moment, you should probably say N, unless you want to test - the GLX component for XFree86 3.3.6, which can be downloaded from - http://utah-glx.sourceforge.net/ . + You should say Y here if you use XFree86 3.3.6 or 4.x and want to + use GLX or DRI. If unsure, say N. AMD Irongate, 761, and 762 support CONFIG_AGP_AMD This option gives you AGP support for the GLX component of the XFree86 4.x on AMD Irongate, 761, and 762 chipsets. - For the moment, you should probably say N, unless you want to test - the GLX component for XFree86 3.3.6, which can be downloaded from - http://utah-glx.sourceforge.net/ . + You should say Y here if you use XFree86 3.3.6 or 4.x and want to + use GLX or DRI. If unsure, say N. Generic SiS support CONFIG_AGP_SIS @@ -2639,33 +3128,47 @@ Note that 5591/5592 AGP chipsets are NOT supported. - For the moment, you should probably say N, unless you want to test - the GLX component for XFree86 3.3.6, which can be downloaded from - http://utah-glx.sourceforge.net/ . + You should say Y here if you use XFree86 3.3.6 or 4.x and want to + use GLX or DRI. If unsure, say N. + +Serverworks LE/HE support +CONFIG_AGP_SWORKS + Say Y here to support the Serverworks AGP card. See + for product descriptions and images. -ALI M1541 support +ALI chipset support CONFIG_AGP_ALI This option gives you AGP support for the GLX component of the - XFree86 4.x on the ALi M1541 chipset. + XFree86 4.x on the following ALi chipsets. The supported chipsets + include M1541, M1621, M1631, M1632, M1641,M1647,and M1651. + For the ALi-chipset question, ALi suggests you refer to + . - This chipset can do AGP 1x and 2x, but note that there is an + The M1541 chipset can do AGP 1x and 2x, but note that there is an acknowledged incompatibility with Matrox G200 cards. Due to timing issues, this chipset cannot do AGP 2x with the G200. This is a hardware limitation. AGP 1x seems to be fine, though. - For the moment, you should probably say N, unless you want to test - the GLX component for XFree86 3.3.6, which can be downloaded from - http://utah-glx.sourceforge.net/ . + You should say Y here if you use XFree86 3.3.6 or 4.x and want to + use GLX or DRI. If unsure, say N. -PCI support +Support for ISA-bus hardware +CONFIG_ISA + Find out whether you have ISA slots on your motherboard. ISA is the + name of a bus system, i.e. the way the CPU talks to the other stuff + inside your box. Other bus systems are PCI, EISA, MicroChannel + (MCA) or VESA. ISA is an older system, now being displaced by PCI; + newer boards don't support it. If you have ISA, say Y, otherwise N. + +Support for PCI bus hardware CONFIG_PCI Find out whether you have a PCI motherboard. PCI is the name of a bus system, i.e. the way the CPU talks to the other stuff inside - your box. Other bus systems are ISA, EISA, Microchannel (MCA) or - VESA. If you have PCI, say Y, otherwise N. + your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or + VESA. If you have PCI, say Y, otherwise N. The PCI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , contains valuable + , contains valuable information about which PCI hardware does work under Linux and which doesn't. @@ -2673,11 +3176,11 @@ CONFIG_PCI_INTEGRATOR Find out whether you have a PCI motherboard. PCI is the name of a bus system, i.e. the way the CPU talks to the other stuff inside - your box. Other bus systems are ISA, EISA, Microchannel (MCA) or - VESA. If you have PCI, say Y, otherwise N. + your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or + VESA. If you have PCI, say Y, otherwise N. The PCI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , contains valuable + , contains valuable information about which PCI hardware does work under Linux and which doesn't. @@ -2685,14 +3188,15 @@ CONFIG_PCI_QSPAN Find out whether you have a PCI motherboard. PCI is the name of a bus system, i.e. the way the CPU talks to the other stuff inside - your box. Other bus systems are ISA, EISA, Microchannel (MCA) or - VESA. If you have PCI, say Y, otherwise N. + your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or + VESA. If you have PCI, say Y, otherwise N. The PCI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , contains valuable + , contains valuable information about which PCI hardware does work under Linux and which doesn't. +# Choice: pci_access PCI access mode CONFIG_PCI_GOBIOS On PCI systems, the BIOS can be used to detect the PCI devices and @@ -2724,19 +3228,19 @@ MCA support CONFIG_MCA MicroChannel Architecture is found in some IBM PS/2 machines and - laptops. It is a bus system similar to PCI or ISA. See - Documentation/mca.txt (and especially the web page given there) - before attempting to build an MCA bus kernel. + laptops. It is a bus system similar to PCI or ISA. See + (and especially the web page given + there) before attempting to build an MCA bus kernel. -EISA support +Support for EISA-bus hardware CONFIG_EISA The Extended Industry Standard Architecture (EISA) bus was developed as an open alternative to the IBM MicroChannel bus. The EISA bus provided some of the features of the IBM MicroChannel bus while maintaining backward compatibility with cards made for - the older ISA bus. The EISA bus saw limited use between 1988 and 1995 - when it was made obsolete by the PCI bus. + the older ISA bus. The EISA bus saw limited use between 1988 and + 1995 when it was made obsolete by the PCI bus. Say Y here if you are building a kernel for an EISA-based machine. @@ -2749,7 +3253,7 @@ Say Y here to create a kernel to run on the SGI 320 or 540. A kernel compiled for the Visual Workstation will not run on other PC boards and vice versa. - See Documentation/sgi-visws.txt for more. + See for more. SGI Visual Workstation framebuffer support CONFIG_FB_SGIVW @@ -2774,8 +3278,8 @@ This support is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. You will get modules called i2o_core.o - and i2o_config.o. + . You will get modules called + i2o_core.o and i2o_config.o. If unsure, say N. @@ -2787,7 +3291,7 @@ This support is also available as a module called i2o_pci.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . I2O Block OSM CONFIG_I2O_BLOCK @@ -2797,7 +3301,7 @@ This support is also available as a module called i2o_block.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . I2O LAN OSM CONFIG_I2O_LAN @@ -2808,7 +3312,7 @@ This support is also available as a module called i2o_lan.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . I2O SCSI OSM CONFIG_I2O_SCSI @@ -2819,7 +3323,7 @@ This support is also available as a module called i2o_scsi.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . I2O /proc support CONFIG_I2O_PROC @@ -2830,7 +3334,7 @@ This support is also available as a module called i2o_proc.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . Plug and Play support CONFIG_PNP @@ -2842,24 +3346,41 @@ Say Y here if you would like Linux to configure your Plug and Play devices. You should then also say Y to "ISA Plug and Play support", - below. Alternatively, you can say N here and configure your PnP + below. Alternatively, you can say N here and configure your PnP devices using the user space utilities contained in the isapnptools package. - + This support is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . ISA Plug and Play support CONFIG_ISAPNP Say Y here if you would like support for ISA Plug and Play devices. - Some information is in Documentation/isapnp.txt. - + Some information is in . + This support is also available as a module called isapnp.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . + + If unsure, say Y. + +PNPBIOS support +CONFIG_PNPBIOS + Linux uses the PNPBIOS as defined in "Plug and Play BIOS + Specification Version 1.0A May 5, 1994" to autodetect built-in + mainboard resources (e.g. parallel port resources). + + Other features (e.g. change resources, ESCD, event notification, + Docking station information, ISAPNP services) are not used. + + Note: ACPI is expected to supersede PNPBIOS some day, currently it + co-exists nicely. + + See latest pcmcia-cs (stand-alone package) for a nice "lspnp" tools, + or have a look at /proc/bus/pnp. If unsure, say Y. @@ -2875,50 +3396,50 @@ example, used on modern desktops as well as laptops, is USB. Enable HOTPLUG and KMOD, and build a modular kernel. Get agent - software (at http://linux-hotplug.sourceforge.net) and install it. + software (at ) and install it. Then your kernel will automatically call out to a user mode "policy agent" (/sbin/hotplug) to load modules and set up software needed to use devices as you hotplug them. -PCMCIA/Cardbus support +PCMCIA/CardBus support CONFIG_PCMCIA Say Y here if you want to attach PCMCIA- or PC-cards to your Linux - computer. These are credit-card size devices such as network cards, - modems or hard drives often used with laptops computers. There are + computer. These are credit-card size devices such as network cards, + modems or hard drives often used with laptops computers. There are actually two varieties of these cards: the older 16 bit PCMCIA cards - and the newer 32 bit CardBus cards. If you want to use CardBus + and the newer 32 bit CardBus cards. If you want to use CardBus cards, you need to say Y here and also to "CardBus support" below. - To use your PC-cards, you will need supporting software from David - Hinds' pcmcia-cs package (see the file Documentation/Changes for - location). Please also read the PCMCIA-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto + To use your PC-cards, you will need supporting software from David + Hinds' pcmcia-cs package (see the file + for location). Please also read the PCMCIA-HOWTO, available from + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). When compiled this way, there will be modules called pcmcia_core.o and ds.o. If you want to compile it as a module, say M here and - read Documentation/modules.txt. + read . CardBus support CONFIG_CARDBUS - CardBus is a bus mastering architecture for PC-cards, which allows - for 32 bit PC-cards (the original PCMCIA standard specifies only + CardBus is a bus mastering architecture for PC-cards, which allows + for 32 bit PC-cards (the original PCMCIA standard specifies only a 16 bit wide bus). Many newer PC-cards are actually CardBus cards. - To use your PC-cards, you will need supporting software from David - Hinds' pcmcia-cs package (see the file Documentation/Changes for - location). + To use your PC-cards, you will need supporting software from David + Hinds' pcmcia-cs package (see the file + for location). If unsure, say Y. -i82365/Yenta compatible bridge support +i82365 compatible bridge support CONFIG_I82365 - Say Y here to include support for PCMCIA and CardBus host bridges - that are register compatible with the Intel i82365 and/or the Yenta - specification: this includes virtually all modern PCMCIA bridges. - "Bridge" is the name used for the hardware inside your computer that - PCMCIA cards are plugged into. If unsure, say Y. + Say Y here to include support for PCMCIA bridges that are register + compatible with the Intel i82365 specification: this includes + virtually all common PCMCIA bridges that are not 'Yenta' Cardbus + bridges. "Bridge" is the name used for the hardware inside your + computer that PCMCIA cards are plugged into. If unsure, say Y. Databook TCIC host bridge support CONFIG_TCIC @@ -2935,60 +3456,67 @@ and some programs won't run unless you say Y here. In particular, if you want to run the DOS emulator dosemu under Linux (read the DOSEMU-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto ), you'll need to say Y + ), you'll need to say Y here. - + You can find documentation about IPC with "info ipc" and also in section 6.4 of the Linux Programmer's Guide, available from - http://www.linuxdoc.org/docs.html#guide . + . BSD Process Accounting CONFIG_BSD_PROCESS_ACCT If you say Y here, a user level program will be able to instruct the kernel (via a special system call) to write process accounting information to a file: whenever a process exits, information about - that process will be appended to the file by the kernel. The + that process will be appended to the file by the kernel. The information includes things such as creation time, owning user, command name, memory usage, controlling terminal etc. (the complete - list is in the struct acct in include/linux/acct.h). It is up to the - user level program to do useful things with this information. This - is generally a good idea, so say Y. - + list is in the struct acct in ). It is + up to the user level program to do useful things with this + information. This is generally a good idea, so say Y. + Sysctl support CONFIG_SYSCTL The sysctl interface provides a means of dynamically changing certain kernel parameters and variables on the fly without requiring - a recompile of the kernel or reboot of the system. The primary + a recompile of the kernel or reboot of the system. The primary interface consists of a system call, but if you say Y to "/proc file system support", a tree of modifiable sysctl entries will be generated beneath the /proc/sys directory. They are explained in the - files in Documentation/sysctl/. Note that enabling this option will - enlarge the kernel by at least 8 KB. + files in . Note that enabling this + option will enlarge the kernel by at least 8 KB. As it is generally a good thing, you should say Y here unless building a kernel for install/rescue disks or your system is very limited in memory. +# Choice: kcore Kernel core (/proc/kcore) format CONFIG_KCORE_ELF - If you enabled support for /proc file system then the file - /proc/kcore will contain the kernel core image. This can be used + If you enabled support for /proc file system then the file + /proc/kcore will contain the kernel core image. This can be used in gdb: $ cd /usr/src/linux ; gdb vmlinux /proc/kcore - You have two choices here: ELF and A.OUT. Selecting ELF will make + You have two choices here: ELF and A.OUT. Selecting ELF will make /proc/kcore appear in ELF core format as defined by the Executable and Linking Format specification. Selecting A.OUT will choose the old "a.out" format which may be necessary for some old versions of binutils or on some architectures. - This is especially useful if you have compiled the kernel with the - "-g" option to preserve debugging information. It is mainly used - for examining kernel data structures on the live kernel so if you - don't understand what this means or are not a kernel hacker, just + This is especially useful if you have compiled the kernel with the + "-g" option to preserve debugging information. It is mainly used + for examining kernel data structures on the live kernel so if you + don't understand what this means or are not a kernel hacker, just leave it at its default value ELF. +# Choice: kcore +Select a.out format for /proc/kcore +CONFIG_KCORE_AOUT + Not necessary unless you're using a very out-of-date binutils + version. You probably want KCORE_ELF. + Kernel support for ELF binaries CONFIG_BINFMT_ELF ELF (Executable and Linkable Format) is a format for libraries and @@ -3003,21 +3531,21 @@ want to say Y here. Information about ELF is contained in the ELF HOWTO available from - http://www.linuxdoc.org/docs.html#howto . + . If you find that after upgrading from Linux kernel 1.2 and saying Y here, you still can't run any ELF binaries (they just crash), then you'll have to install the newest ELF runtime libraries, including - ld.so (check the file Documentation/Changes for location and latest - version). + ld.so (check the file for location and + latest version). If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called binfmt_elf.o. Saying M or N here is dangerous because some - crucial programs on your system might be in ELF format. + say M here and read . The module + will be called binfmt_elf.o. Saying M or N here is dangerous because + some crucial programs on your system might be in ELF format. -Kernel support for A.OUT binaries +Kernel support for a.out binaries CONFIG_BINFMT_AOUT A.out (Assembler.OUTput) is a set of formats for libraries and executables used in the earliest versions of UNIX. Linux used the @@ -3045,12 +3573,17 @@ this to work, you need to have the emulator /usr/bin/em86 in place. You can get the same functionality by saying N here and saying Y to - "Kernel support for MISC binaries". + "Kernel support for MISC binaries". You may answer M to compile the emulation support as a module and later load the module when you want to use a Linux/Intel binary. The module will be called binfmt_em86.o. If unsure, say Y. +Kernel support for SOM binaries +CONFIG_BINFMT_SOM + SOM is a binary executable format inherited from HP/UX. Say Y here + to be able to load and execute SOM binaries directly. + Kernel support for MISC binaries CONFIG_BINFMT_MISC If you say Y here, it will be possible to plug wrapper-driven binary @@ -3058,15 +3591,15 @@ programs that need an interpreter to run like Java, Python or Emacs-Lisp. It's also useful if you often run DOS executables under the Linux DOS emulator DOSEMU (read the DOSEMU-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto ). Once you have + ). Once you have registered such a binary class with the kernel, you can start one of those programs simply by typing in its name at a shell prompt; Linux will automatically feed it to the correct interpreter. You can do other nice things, too. Read the file - Documentation/binfmt_misc.txt to learn how to use this feature, and - Documentation/java.txt for information about how to include Java - support. + to learn how to use this + feature, and for information about how + to include Java support. You must say Y to "/proc file system support" (CONFIG_PROC_FS) to use this part of the kernel. @@ -3075,16 +3608,33 @@ you have use for it; the module is called binfmt_misc.o. If you don't know what to answer at this point, say Y. -Solaris binary emulation (EXPERIMENTAL) +Kernel support for JAVA binaries +CONFIG_BINFMT_JAVA + If you say Y here, the kernel will load and execute Java J-code + binaries directly. Note: this option is obsolete and scheduled for + removal, use CONFIG_BINFMT_MISC instead. + +Solaris binary emulation CONFIG_SOLARIS_EMUL This is experimental code which will enable you to run (many) - Solaris binaries on your SPARC Linux machine. + Solaris binaries on your SPARC Linux machine. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called solaris.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . +SUN SME environment monitoring +CONFIG_ENVCTRL + Kernel support for temperature and fan monitoring on Sun SME + machines. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called envctrl.o. If you want to compile it as a + module, say M here and read . + +# Choice: x86 Processor family CONFIG_M386 This is the processor type of your CPU. This information is used for @@ -3098,17 +3648,17 @@ Here are the settings recommended for greatest speed: - "386" for the AMD/Cyrix/Intel 386DX/DXL/SL/SLC/SX, Cyrix/TI - 486DLC/DLC2, UMC 486SX-S and NexGen Nx586. Only "386" kernels will - run on a 386 class machine. + 486DLC/DLC2, UMC 486SX-S and NexGen Nx586. Only "386" kernels + will run on a 386 class machine. - "486" for the AMD/Cyrix/IBM/Intel 486DX/DX2/DX4 or SL/SLC/SLC2/SLC3/SX/SX2 and UMC U5D or U5S. - - "586" for generic Pentium CPUs, possibly lacking the TSC + - "586" for generic Pentium CPUs, possibly lacking the TSC (time stamp counter) register. - "Pentium-Classic" for the Intel Pentium. - "Pentium-MMX" for the Intel Pentium MMX. - "Pentium-Pro" for the Intel Pentium Pro/Celeron/Pentium II. - "Pentium-III" for the Intel Pentium III - and Celerons based on the coppermine core. + and Celerons based on the Coppermine core. - "Pentium-4" for the Intel Pentium 4. - "K6" for the AMD K6, K6-II and K6-III (aka K6-3D). - "Athlon" for the AMD K7 family (Athlon/Duron/Thunderbird). @@ -3120,18 +3670,54 @@ If you don't know what to do, choose "386". +486 +CONFIG_M486 + Select this for a x486 processor, ether Intel or one of the + compatible processors from AMD, Cyrix, IBM, or Intel. Includes DX, + DX2, and DX4 variants; also SL/SLC/SLC2/SLC3/SX/SX2 and UMC U5D or + U5S. + +586/K5/5x86/6x86/6x86MX +CONFIG_M586 + Select this for an x586 or x686 processor such as the AMD K5, the + Intel 5x86 or 6x86, or the Intel 6x86MX. This choice does not + assume the RDTSC instruction. + +Pentium Classic +CONFIG_M586TSC + Select this for a Pentium Classic processor with the RDTSC (Read + Time Stamp Counter) instruction for benchmarking. + +32-bit PDC +CONFIG_PDC_NARROW + Saying Y here will allow developers with a C180, C200, C240, C360, + J200, J210, and/or a J2240 to test 64-bit kernels by providing a + wrapper for the 32-bit PDC calls. Since the machines which require + this option do not support over 4G of RAM, this option is targeted + for developers of these machines wishing to test changes on both + 32-bit and 64-bit configurations. + + If unsure, say N. + VGA text console CONFIG_VGA_CONSOLE Saying Y here will allow you to use Linux in text mode through a display that complies with the generic VGA standard. Virtually - everyone wants that. + everyone wants that. The program SVGATextMode can be used to utilize SVGA video cards to their full potential in text mode. Download it from - ftp://metalab.unc.edu/pub/Linux/utils/console . + . Say Y. +Distribute interrupts on all CPUs by default +CONFIG_IRQ_ALL_CPUS + This option gives the kernel permission to distribute IRQs across + multiple CPUs. Saying N here will route all IRQs to the first + CPU. Generally SMP PowerMacs can answer Y. SMP IBM CHRP boxes or + Power3 boxes should say N for now. + Video mode selection support CONFIG_VIDEO_SELECT This enables support for text mode selection on kernel startup. If @@ -3143,16 +3729,16 @@ "man bootparam" or see the documentation of your boot loader about how to pass options to the kernel.) - Read the file Documentation/svga.txt for more information about the - Video mode selection support. If unsure, say N. + Read the file for more information + about the Video mode selection support. If unsure, say N. -Support for frame buffer devices (EXPERIMENTAL) +Support for frame buffer devices CONFIG_FB The frame buffer device provides an abstraction for the graphics hardware. It represents the frame buffer of some video hardware and allows application software to access the graphics hardware through a well-defined interface, so the software doesn't need to know - anything about the low-level (hardware register) stuff. + anything about the low-level (hardware register) stuff. Frame buffer devices work identically across the different architectures supported by Linux and make the implementation of @@ -3160,14 +3746,14 @@ server exists which uses the frame buffer device exclusively. On several non-X86 architectures, the frame buffer device is the only way to use the graphics hardware. - + The device is accessed through special device nodes, usually located in the /dev directory, i.e. /dev/fb*. You need an utility program called fbset to make full use of frame - buffer devices. Please read Documentation/fb/framebuffer.txt and the - Framebuffer-HOWTO at - http://www.tahallah.demon.co.uk/programming/prog.html for more + buffer devices. Please read + and the Framebuffer-HOWTO at + for more information. Say Y here and to the driver for your graphics board below if you @@ -3185,6 +3771,27 @@ hardware found in Acorn RISC PCs and other ARM-based machines. If unsure, say N. +Permedia2 support +CONFIG_FB_PM2 + This is the frame buffer device driver for the Permedia2 AGP frame + buffer card from ASK, aka `Graphic Blaster Exxtreme'. There is a + product page at + . + +Enable FIFO disconnect feature +CONFIG_FB_PM2_FIFO_DISCONNECT + Support the Permedia2 FIFOI disconnect feature (see CONFIG_FB_PM2). + +Generic Permedia2 PCI board support +CONFIG_FB_PM2_PCI + Say Y to enable support for Permedia2 AGP frame buffer card from + 3Dlabs (aka `Graphic Blaster Exxtreme') on the PCI bus. + +Phase5 CVisionPPC/BVisionPPC support +CONFIG_FB_PM2_CVPPC + Say Y to enable support for the Amiga Phase 5 CVisionPPC BVisionPPC + framebuffer cards. Phase 5 is no longer with us, alas. + Amiga native chipset support CONFIG_FB_AMIGA This is the frame buffer device driver for the builtin graphics @@ -3193,7 +3800,7 @@ The driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). The module will be called amifb.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . Amiga OCS chipset support CONFIG_FB_AMIGA_OCS @@ -3231,7 +3838,7 @@ Say Y if you have a NetWinder or a graphics card containing this device, otherwise say N. -Amiga CyberVision3D support (EXPERIMENTAL) +Amiga CyberVision3D support CONFIG_FB_VIRGE This enables support for the Cybervision 64/3D graphics card from Phase5. Please note that its use is not all that intuitive (i.e. if @@ -3240,13 +3847,13 @@ kernel. Please note that this driver DOES NOT support the older Cybervision 64 card, as they use incompatible video chips. -Amiga RetinaZ3 support (EXPERIMENTAL) +Amiga RetinaZ3 support CONFIG_FB_RETINAZ3 This enables support for the Retina Z3 graphics card. Say N unless you have a Retina Z3 or plan to get one before you next recompile the kernel. -Cirrus Logic generic driver (EXPERIMENTAL) +Cirrus Logic generic driver CONFIG_FB_CLGEN This enables support for Cirrus Logic GD542x/543x based boards on Amiga: SD64, Piccolo, Picasso II/II+, Picasso IV, or EGS Spectrum. @@ -3254,21 +3861,17 @@ If you have a PCI-based system, this enables support for these chips: GD-543x, GD-544x, GD-5480. - Please read the file Documentation/fb/clgenfb.txt. + Please read the file . Say N unless you have such a graphics board or plan to get one before you next recompile the kernel. -Permedia2 support (EXPERIMENTAL) -CONFIG_FB_PM2 - Say Y here if this is your graphics board. - Apollo support CONFIG_APOLLO Say Y here if you want to run Linux on an MC680x0-based Apollo Domain workstation such as the DN3500. -Apollo 3c505 support +Apollo 3c505 "EtherLink Plus" support CONFIG_APOLLO_ELPLUS Say Y or M here if your Apollo has a 3Com 3c505 ISA Ethernet card. If you don't have one made for Apollos, you can use one from a PC, @@ -3280,16 +3883,21 @@ This is the frame buffer device driver for the builtin graphics chipset found in Ataris. -Open Firmware frame buffer device support +Amiga FrameMaster II/Rainbow II support +CONFIG_FB_FM2 + This is the frame buffer device driver for the Amiga FrameMaster + card from BSC (exhibited 1992 but not shipped as a CBM product). + +Open Firmware frame buffer device support CONFIG_FB_OF Say Y if you want support with Open Firmware for your graphics board. -S3 Trio frame buffer device support +S3 Trio frame buffer device support CONFIG_FB_S3TRIO - If you have a S3 Trio say Y. Say N for S3 Virge. + If you have a S3 Trio say Y. Say N for S3 Virge. -3Dfx Banshee/Voodoo3 display support (EXPERIMENTAL) +3Dfx Banshee/Voodoo3 display support CONFIG_FB_3DFX This driver supports graphics boards with the 3Dfx Banshee/Voodoo3 chips. Say Y if you have such a graphics board. @@ -3297,9 +3905,9 @@ The driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). The module will be called tdfxfb.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . -nVidia Riva support (EXPERIMENTAL) +nVidia Riva support CONFIG_FB_RIVA This driver supports graphics boards with the nVidia Riva/Geforce chips. @@ -3308,9 +3916,9 @@ The driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). The module will be called rivafb.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . -ATI Mach64 display support (EXPERIMENTAL) +ATI Mach64 display support CONFIG_FB_ATY This driver supports graphics boards with the ATI Mach64 chips. Say Y if you have such a graphics board. @@ -3318,30 +3926,54 @@ The driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). The module will be called atyfb.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. - -ATI Mach64 GX display support (EXPERIMENTAL) -CONFIG_FB_ATY_GX - This options adds support for the first generation ATI Mach64 - graphics chips, i.e. the Mach64 GX and CX. Note that this support is - limited. - -ATI Mach64 CT/VT/GT/LT display support (EXPERIMENTAL) -CONFIG_FB_ATY_CT - This option adss support for ATI Mach64 graphics chips starting - with the Mach64 CT family. This includes the Mach64 VT (limited - support), GT (3D RAGE family), and LT. + module, say M here and read . -ATI Rage128 display support (EXPERIMENTAL) +ATI Rage128 display support CONFIG_FB_ATY128 This driver supports graphics boards with the ATI Rage128 chips. Say Y if you have such a graphics board and read - Documentation/fb/aty128fb.txt. + . The driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). The module will be called aty128fb.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . + +# AC tree only +Maxine (Personal DECstation) onboard framebuffer support +CONFIG_FB_MAXINE + Say Y here to directly support the on-board framebuffer in the + Maxine (5000/20, /25, /33) version of the DECstation. There is a + page dedicated to Linux on DECstations at . + +# AC tree only +PMAG-BA TURBOchannel framebuffer support +CONFIG_FB_PMAG_BA + Say Y here to directly support the on-board PMAG-BA framebuffer in + the 5000/1xx versions of the DECstation. There is a page dedicated + to Linux on DECstations at . + +# AC tree only +PMAGB-B TURBOchannel framebuffer support +CONFIG_FB_PMAGB_B + Say Y here to directly support the on-board PMAGB-B framebuffer in + the 5000/1xx versions of the DECstation. There is a page dedicated + to Linux on DECstations at . + +FutureTV PCI card +CONFIG_ARCH_FTVPCI + Say Y here if you intend to run this kernel on a FutureTV (nee Nexus + Electronics) StrongARM PCI card. + +P720T +CONFIG_ARCH_P720T + Say Y here if you intend to run this kernel on the ARM Prospector + 720T. + +Link-Up Systems LCD support +CONFIG_FB_L7200 + This driver supports the L7200 Color LCD. + Say Y if you want graphics support. PowerMac "control" frame buffer device support CONFIG_FB_CONTROL @@ -3373,13 +4005,13 @@ This is the frame buffer device driver for generic VESA 2.0 compliant graphic cards. The older VESA 1.2 cards are not supported. You will get a boot time penguin logo at no additional cost. Please - read Documentation/fb/vesafb.txt. If unsure, say Y. + read . If unsure, say Y. VGA 16-color planar support CONFIG_FBCON_VGA_PLANES This low level frame buffer console driver enable the kernel to use the 16-color planar modes of the old VGA cards where the bits of - each pixel are separated into 4 planes. + each pixel are separated into 4 planes. Only answer Y here if you have a (very old) VGA card that isn't VESA 2 compatible. @@ -3392,7 +4024,29 @@ This code is also available as a module. If you want to compile it as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read - Documentation/modules.txt. The module will be called vga16fb.o. + . The module will be called + vga16fb.o. + +Generic STI frame buffer device support +CONFIG_FB_STI + STI refers to the HP "Standard Text Interface" which is a set of + BIOS routines contained in a ROM chip in HP PA-RISC based machines. + Enabling this option will implement the linux framebuffer device and + an fbcon color text console using calls to the STI BIOS routines. + The HP framebuffer device is usually planar, uses a strange memory + layout, and changing the plane mask to create colored pixels + requires a call to the STI routines, so do not expect /dev/fb to + actually be useful. However, it is the best we have as far as + graphics on the HP chipsets due to lack of hardware level + documentation for the various on-board HP chipsets used in these + systems. It is sufficient for basic text console functions, + including fonts. + + You should probably enable this option, unless you are having + trouble getting video when booting the kernel (make sure it isn't + just that you are running the console on the serial port, though). + Really old HP boxes may not have STI, and must use the PDC BIOS + console or the IODC BIOS. Select other compiled-in fonts CONFIG_FBCON_FONTS @@ -3400,7 +4054,7 @@ your frame buffer console usually use. Note that the answer to this question won't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions about foreign fonts. If unsure, say N (the default choices are safe). @@ -3432,14 +4086,29 @@ VGA 8x8 font CONFIG_FONT_8x8 This is the "high resolution" font for the VGA frame buffer (the one - provided by the text console 80x50 (and higher) modes). + provided by the text console 80x50 (and higher) modes). Note that this is a poor quality font. The VGA 8x16 font is quite a - lot more readable. + lot more readable. Given the resolution provided by the frame buffer device, answer N here is safe. +Mac console 6x11 font (not supported by all drivers) +CONFIG_FONT_6x11 + Small console font with Macintosh-style high-half glyphs. Some Mac + framebuffer drivers don't support this one at all. + +Pearl (old m68k) console 8x8 font +CONFIG_FONT_PEARL_8x8 + Small console font with PC-style control-character and high-half + glyphs. + +Acorn console 8x8 font +CONFIG_FONT_ACORN_8x8 + Small console font with PC-style control characters and high-half + glyphs. + Backward compatibility mode for Xpmac CONFIG_FB_COMPAT_XPMAC If you use the Xpmac X server (common with mklinux), you'll need to @@ -3447,35 +4116,61 @@ includes a server that supports the frame buffer device directly (XF68_FBDev). -HGA monochrome support (EXPERIMENTAL) +Hercules (HGA) mono graphics support CONFIG_FB_HGA Say Y here if you have a Hercules mono graphics card. This driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). The module will be called hgafb.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . As this card technology is 15 years old, most people will answer N here. +Epson 1355 framebuffer support +CONFIG_FB_E1355 + Build in support for the SED1355 Epson Research Embedded RAMDAC + LCD/CRT Controller (since redesignated as the S1D13505) as a + framebuffer. Product specs at + . + +Dreamcast Frame Buffer support +CONFIG_FB_DC + Say Y here to enable support for the framebuffer on the Sega + Dreamcast. This driver is also available as a module, dcfb.o. + +Register Base Address +CONFIG_E1355_REG_BASE + Epson SED1355/S1D13505 LCD/CRT controller register base address. + See the manuals at + for + discussion. + +Framebuffer Base Address +CONFIG_E1355_FB_BASE + Epson SED1355/S1D13505 LCD/CRT controller memory base address. See + the manuals at + for + discussion. + NEC PowerVR 2 display support CONFIG_FB_PVR2 - Say Y here if you have a PowerVR 2 card in your box. If you plan to - run linux on your Dreamcast, you will have to say Y here. This driver - may or may not work on other PowerVR 2 cards, but is totally untested. - Use at your own risk. If unsure, say N. + Say Y here if you have a PowerVR 2 card in your box. If you plan to + run linux on your Dreamcast, you will have to say Y here. + This driver may or may not work on other PowerVR 2 cards, but is + totally untested. Use at your own risk. If unsure, say N. This driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). - The module will be called pvr2fb.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + The module will be called pvr2fb.o. If you want to compile it as + a module, say M here and read . You can pass several parameters to the driver at boot time or at - module load time. The parameters look like "video=pvr2:XXX", where + module load time. The parameters look like "video=pvr2:XXX", where the meaning of XXX can be found at the end of the main source file - (drivers/video/pvr2fb.c). Please see the file - Documentation/fb/pvr2fb.txt. + (). Please see the file + . Debug pvr2fb CONFIG_FB_PVR2_DEBUG @@ -3483,26 +4178,26 @@ messages. Most people will want to say N here. If unsure, you will also want to say N. -Matrox unified accelerated driver (EXPERIMENTAL) +Matrox unified accelerated driver CONFIG_FB_MATROX Say Y here if you have a Matrox Millennium, Matrox Millennium II, Matrox Mystique, Matrox Mystique 220, Matrox Productiva G100, Matrox Mystique G200, Matrox Millennium G200, Matrox Marvel G200 video, - Matrox G400 or G450 card in your box. At this time, support for the G100 - is untested and support for G450 is highly experimental. + Matrox G400 or G450 card in your box. At this time, support for the + G100 is untested and support for G450 is highly experimental. This driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). The module will be called matroxfb.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . You can pass several parameters to the driver at boot time or at module load time. The parameters look like "video=matrox:XXX", where the meaning of XXX can be found at the end of the main source file - (drivers/video/matroxfb.c). Please see the file - Documentation/fb/matroxfb.txt. + (). Please see + . -Matrox Millennium support +Matrox Millennium I/II support CONFIG_FB_MATROX_MILLENIUM Say Y here if you have a Matrox Millennium or Matrox Millennium II video card. If you select "Advanced lowlevel driver options" below, @@ -3521,9 +4216,9 @@ Matrox G100/G200/G400/G450 support CONFIG_FB_MATROX_G100 Say Y here if you have a Matrox G100, G200, G400 or G450 based - video card. If you select "Advanced lowlevel driver options", you - should check 8 bpp packed pixel, 16 bpp packed pixel, 24 bpp packed - pixel and 32 bpp packed pixel. You can also use font widths + video card. If you select "Advanced lowlevel driver options", you + should check 8 bpp packed pixel, 16 bpp packed pixel, 24 bpp packed + pixel and 32 bpp packed pixel. You can also use font widths different from 8. If you need support for G400 secondary head, you must first say Y to @@ -3537,14 +4232,14 @@ DDC (I2C) bus present on all Matroxes, an I2C bus which interconnects Matrox optional devices, like MGA-TVO on G200 and G400, and the secondary head DDC bus, present on G400 only. - - You can say Y or M here if you want to experiment with monitor + + You can say Y or M here if you want to experiment with monitor detection code. You must say Y or M here if you want to use either second head of G400 or MGA-TVO on G200 or G400. - + If you compile it as module, it will create a module named i2c-matroxfb.o. - + Matrox G400 second head support CONFIG_FB_MATROX_MAVEN WARNING !!! This support does not work with G450 !!! @@ -3555,26 +4250,24 @@ secondary head output is blanked while you are in X. With XFree 3.9.17 preview you can use both heads if you use SVGA over fbdev or the fbdev driver on first head and the fbdev driver on second head. - + If you compile it as module, two modules are created, matroxfb_crtc2.o and matroxfb_maven.o. Matroxfb_maven is needed for both G200 and G400, matroxfb_crtc2 is needed only by G400. You must also load i2c-matroxfb to get it to run. - + The driver starts in monitor mode and you must use the matroxset - tool (available at ftp://platan.vc.cvut.cz/pub/linux/matrox-latest) - to switch it to PAL or NTSC or to swap primary and secondary head - outputs. Secondary head driver also always start in 640x480 - resolution, you must use fbset to change it. - + tool (available at + ) to switch it to + PAL or NTSC or to swap primary and secondary head outputs. + Secondary head driver also always start in 640x480 resolution, you + must use fbset to change it. + Also do not forget that second head supports only 16 and 32 bpp packed pixels, so it is a good idea to compile them into the kernel - too. You can use only some font widths, as the driver uses generic + too. You can use only some font widths, as the driver uses generic painting procedures (the secondary head does not use acceleration engine). - - There is no need for enabling 'Matrox multihead support' if you have - only one Matrox card in the box. Matrox G450 second head support CONFIG_FB_MATROX_G450 @@ -3586,10 +4279,10 @@ want two independent display devices. The driver starts in monitor mode and currently does not support - output in TV modes. You must use the matroxset tool (available - at ftp://platan.vc.cvut.cz/pub/linux/matrox-latest) to swap primary - and secondary head outputs. Secondary head driver always start in - 640x480 resolution and you must use fbset to change it. + output in TV modes. You must use the matroxset tool (available + at ) to swap + primary and secondary head outputs. Secondary head driver always + start in 640x480 resolution and you must use fbset to change it. Also do not forget that second head supports only 16 and 32 bpp packed pixels, so it is a good idea to compile them into the kernel @@ -3597,9 +4290,6 @@ painting procedures (the secondary head does not use acceleration engine). - There is no need for enabling 'Matrox multihead support' if you have - only one Matrox card in the box. - Matrox unified driver multihead support CONFIG_FB_MATROX_MULTIHEAD Say Y here if you have more than one (supported) Matrox device in @@ -3615,6 +4305,24 @@ for the different Matrox devices. This method is slightly faster but uses 40 KB of kernel memory per Matrox card. + There is no need for enabling 'Matrox multihead support' if you have + only one Matrox card in the box. + +3Dfx Voodoo Graphics / Voodoo2 frame buffer support +CONFIG_FB_VOODOO1 + Say Y here if you have a 3Dfx Voodoo Graphics (Voodoo1/sst1) or + Voodoo2 (cvg) based graphics card. + + This driver is also available as a module ( = code which can be + inserted and removed from the running kernel whenever you want). + The module will be called sstfb.o. If you want to compile it as + a module, say M here and read Documentation/modules.txt. + + WARNING: Do not use any application that uses the 3D engine + (namely glide) while using this driver. + Please read the file Documentation/fb/README-sstfb.txt for supported + options and other important info support. + MDA text console (dual-headed) CONFIG_MDA_CONSOLE Say Y here if you have an old MDA or monochrome Hercules graphics @@ -3622,15 +4330,15 @@ will then be able to use two monitors with your Linux system. Do not say Y here if your MDA card is the primary card in your system; the normal VGA driver will handle it. - + This driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). The module will be called mdacon.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. - + a module, say M here and read . + If unsure, say N. -SBUS and UPA frame buffers +SBUS and UPA framebuffers CONFIG_FB_SBUS Say Y if you want support for SBUS or UPA based frame buffer device. @@ -3652,11 +4360,59 @@ CONFIG_FB_CGTHREE This is the frame buffer device driver for the CGthree frame buffer. +CGfourteen (SX) support +CONFIG_FB_CGFOURTEEN + This is the frame buffer device driver for the CGfourteen frame + buffer on Desktop SPARCsystems with the SX graphics option. + +P9100 (Sparcbook 3 only) support +CONFIG_FB_P9100 + This is the frame buffer device driver for the P9100 card + supported on Sparcbook 3 machines. + +Leo (ZX) support +CONFIG_FB_LEO + This is the frame buffer device driver for the SBUS-based Sun ZX + (leo) frame buffer cards. + +IGA 168x display support +CONFIG_FB_IGA + This is the framebuffer device for the INTERGRAPHICS 1680 and + successor frame buffer cards. + TCX (SS4/SS5 only) support CONFIG_FB_TCX This is the frame buffer device driver for the TCX 24/8bit frame buffer. +HD64461 Frame Buffer support +CONFIG_FB_HIT + This is the frame buffer device driver for the Hitachi HD64461 LCD + frame buffer card. + +SIS 630/540 display support +CONFIG_FB_SIS + This is the frame buffer device driver for the SiS 630 and 640 Super + Socket 7 UMA cards. Specs available at . + +# AC tree only +SIS 630/540/730 support +CONFIG_FB_SIS_300 + This is the frame buffer device driver for the SiS 630 and related + Super Socket 7 UMA cards. Specs available at + . + +# AC tree only +SIS 315H/315 support +CONFIG_FB_SIS_315 + This is the frame buffer device driver for the SiS 315 graphics + card. Specs available at . + +IMS Twin Turbo display support +CONFIG_FB_IMSTT + The IMS Twin Turbo is a PCI-based frame buffer card bundled with + many Macintosh and compatible computers. + Virtual Frame Buffer support (ONLY FOR TESTING!) CONFIG_FB_VIRTUAL This is a `virtual' frame buffer device. It operates on a chunk of @@ -3671,13 +4427,48 @@ This driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). The module will be called vfb.o. If you want to compile it as a module, - say M here and read Documentation/modules.txt. - + say M here and read . + If unsure, say N. +Mach64 CT/VT/GT/LT (incl. 3D RAGE) support +CONFIG_FB_ATY_CT + Say Y here to support use of ATI's 64-bit Rage boards (or other + boards based on the Mach64 CT, VT, GT, and LT chipsets) as a + framebuffer device. The ATI product support page for these boards + is at . + +Sony Vaio Picturebook laptop LCD panel support +CONFIG_FB_ATY_CT_VAIO_LCD + Say Y here if you want to use the full width of the Sony Vaio + Picturebook laptops LCD panels (you will get a 128x30 console). + + Note that you need to activate this mode using the 'vga=0x301' + option from your boot loader (lilo or loadlin). See the + documentation of your boot loader about how to pass options to the + kernel. + +Mach64 GX support +CONFIG_FB_ATY_GX + Say Y here to support use of the ATI Mach64 Graphics Expression + board (or other boards based on the Mach64 GX chipset) as a + framebuffer device. The ATI product support page for these boards + is at + . + +# AC tree only +ATI Radeon display support +CONFIG_FB_RADEON + Choose this option if you want to use an ATI Radeon graphics card as + a framebuffer device. There are both PCI and AGP versions. You + don't need to choose this to run the Radeon in plain VGA mode. + There is a product page at + . + +SA-1100 LCD support CONFIG_FB_SA1100 This is a framebuffer device for the SA-1100 LCD Controller. - See http://www.linux-fbdev.org/ for information on framebuffer + See for information on framebuffer devices. If you plan to use the LCD display with your SA-1100 system, say @@ -3702,8 +4493,8 @@ Low level frame buffer console drivers can be modules ( = code which can be inserted and removed from the running kernel whenever you want). The modules will be called fbcon-*.o. If you want to compile - (some of) them as modules, read Documentation/modules.txt. - + (some of) them as modules, read . + If unsure, say N. Monochrome support @@ -3773,10 +4564,10 @@ This is the low level frame buffer console driver for 1/2/4/8/16/32 bits per pixel packed pixels on Mac. It supports variable font widths for low resolution screens. - -HGA monochrome support (EXPERIMENTAL) + +HGA monochrome support CONFIG_FBCON_HGA - This is the low level frame buffer console driver for Hercules mono + This is the low level frame buffer console driver for Hercules mono graphics cards. VGA characters/attributes support @@ -3792,21 +4583,21 @@ drive, PLIP link (Parallel Line Internet Protocol is mainly used to create a mini network by connecting the parallel ports of two local machines) etc., then you need to say Y here; please read - Documentation/parport.txt and drivers/parport/BUGS-parport. + and drivers/parport/BUGS-parport. For extensive information about drivers for many devices attaching - to the parallel port see http://www.torque.net/linux-pp.html on the - WWW. + to the parallel port see on + the WWW. It is possible to share a single parallel port among several devices and it is safe to compile all the corresponding drivers into the - kernel. If you want to compile parallel port support as a module ( = - code which can be inserted in and removed from the running kernel - whenever you want), say M here and read Documentation/modules.txt. - The module will be called parport.o. If you have more than one - parallel port and want to specify which port and IRQ to be used by - this driver at module load time, take a look at - Documentation/parport.txt. + kernel. If you want to compile parallel port support as a module + ( = code which can be inserted in and removed from the running + kernel whenever you want), say M here and read + . The module will be called + parport.o. If you have more than one parallel port and want to + specify which port and IRQ to be used by this driver at module load + time, take a look at . If unsure, say Y. @@ -3814,20 +4605,21 @@ CONFIG_PARPORT_PC You should say Y here if you have a PC-style parallel port. All IBM PC compatible computers and some Alphas have PC-style parallel - ports. + ports. - This code is also available as a module. If you want to compile it + This code is also available as a module. If you want to compile it as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read - Documentation/modules.txt. The module will be called parport_pc.o. - + . The module will be called + parport_pc.o. + If unsure, say Y. Parallel+serial PCI card support CONFIG_PARPORT_SERIAL - This adds support for multi-IO PCI cards that have parallel and serial - ports. You should say Y or M here. If you say M, the module will be - called parport_serial.o. + This adds support for multi-IO PCI cards that have parallel and + serial ports. You should say Y or M here. If you say M, the module + will be called parport_serial.o. Use FIFO/DMA if available CONFIG_PARPORT_PC_FIFO @@ -3835,12 +4627,12 @@ printing. Say Y here if you want to take advantage of that. As well as actually having a FIFO, or DMA capability, the kernel - will need to know which IRQ the parallel port has. By default, + will need to know which IRQ the parallel port has. By default, parallel port interrupts will not be used, and so neither will the - FIFO. See Documentation/parport.txt to find out how to specify - which IRQ/DMA to use. + FIFO. See to find out how to + specify which IRQ/DMA to use. -SuperIO chipset support (EXPERIMENTAL) +SuperIO chipset support CONFIG_PARPORT_PC_SUPERIO Saying Y here enables some probes for Super-IO chipsets in order to find out things like base addresses, IRQ lines and DMA channels. It @@ -3857,12 +4649,6 @@ other non-standard types of parallel ports. This causes a performance loss, so most people say N. -Sun Ultra/AX-style hardware -CONFIG_PARPORT_AX - Say Y here if you need support for the parallel port hardware on Sun - Ultra/AX machines. This code is also available as a module (say M), - called parport_ax.o. If in doubt, saying N is the safe plan. - Amiga built-in parallel port support CONFIG_PARPORT_AMIGA Say Y here if you need support for the parallel port hardware on @@ -3875,51 +4661,51 @@ Atari machines. This code is also available as a module (say M), called parport_atari.o. If in doubt, saying N is the safe plan. -Multiface 3 parallel port card support +Multiface III parallel port support CONFIG_PARPORT_MFC3 Say Y here if you need parallel port support for the MFC3 card. This code is also available as a module (say M), called parport_mfc3.o. If in doubt, saying N is the safe plan. -Support IEEE1284 status readback +Support IEEE 1284 status readback CONFIG_PRINTER_READBACK If you have a device on your parallel port that support this protocol, this option will allow the device to report its status. It is safe to say Y. -IEEE1284 transfer modes +IEEE 1284 transfer modes CONFIG_PARPORT_1284 If you have a printer that supports status readback or device ID, or want to use a device that uses enhanced parallel port transfer modes such as EPP and ECP, say Y here to enable advanced IEEE 1284 transfer modes. Also say Y if you want device ID information to appear in /proc/sys/dev/parport/*/autoprobe*. It is safe to say N. - + Enable loadable module support CONFIG_MODULES Kernel modules are small pieces of compiled code which can be inserted in or removed from the running kernel, using the programs insmod and rmmod. This is described in the file - Documentation/modules.txt, including the fact that you have to say - "make modules" in order to compile the modules that you chose during - kernel configuration. Modules can be device drivers, file systems, - binary executable formats, and so on. If you think that you may want - to make use of modules with this kernel in the future, then say Y - here. If unsure, say Y. + , including the fact that you have + to say "make modules" in order to compile the modules that you chose + during kernel configuration. Modules can be device drivers, file + systems, binary executable formats, and so on. If you think that you + may want to make use of modules with this kernel in the future, then + say Y here. If unsure, say Y. Set version information on all symbols for modules CONFIG_MODVERSIONS Usually, modules have to be recompiled whenever you switch to a new - kernel. Saying Y here makes it possible, and safe, to use the + kernel. Saying Y here makes it possible, and safe, to use the same modules even after compiling a new kernel; this requires the program modprobe. All the software needed for module support is in - the modutils package (check the file Documentation/Changes for - location and latest version). NOTE: if you say Y here but don't + the modutils package (check the file + for location and latest version). NOTE: if you say Y here but don't have the program genksyms (which is also contained in the above mentioned modutils package), then the building of your kernel will - fail. If you are going to use modules that are generated from - non-kernel sources, you would benefit from this option. Otherwise - it's not that important. So, N ought to be a safe bet. + fail. If you are going to use modules that are generated from + non-kernel sources, you would benefit from this option. Otherwise + it's not that important. So, N ought to be a safe bet. Kernel module loader support CONFIG_KMOD @@ -3931,11 +4717,11 @@ kernel needs a module, it runs modprobe with the appropriate arguments, thereby loading the module if it is available. (This is a replacement for kerneld.) Say Y here and read about configuring it - in Documentation/kmod.txt. + in . -ARP daemon support (EXPERIMENTAL) +ARP daemon support CONFIG_ARPD - Normally, the kernel maintains an internal cache which maps IP + Normally, the kernel maintains an internal cache which maps IP addresses to hardware addresses on the local network, so that Ethernet/Token Ring/ etc. frames are sent to the proper address on the physical networking layer. For small networks having a few @@ -3943,7 +4729,7 @@ resolution (ARP) cache inside the kernel works well. However, maintaining an internal ARP cache does not work well for very large switched networks, and will use a lot of kernel memory if TCP/IP - connections are made to many machines on the network. + connections are made to many machines on the network. If you say Y here, the kernel's internal ARP cache will never grow to more than 256 entries (the oldest entries are expired in a LIFO @@ -3967,35 +4753,35 @@ For an excellent introduction to Linux networking, please read the NET-3-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This option is also necessary if you want to use the full power of term (term is a program which gives you almost full Internet connectivity if you have a regular dial up shell account on some Internet connected Unix computer; for more information, read - http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html ). - + ). + If you say Y here and also to "/proc file system support" and "Sysctl support" below, you can change various aspects of the behavior of the TCP/IP code by writing to the (virtual) files in /proc/sys/net/ipv4/*; the options are explained in the file - Documentation/networking/ip-sysctl.txt. + . Short answer: say Y. -IP: multicasting +IP multicasting CONFIG_IP_MULTICAST This is code for addressing several networked computers at once, enlarging your kernel by about 2 KB. You need multicasting if you intend to participate in the MBONE, a high bandwidth network on top of the Internet which carries audio and video broadcasts. More information about the MBONE is on the WWW at - http://www-itg.lbl.gov/mbone/ . Information about the multicast + . Information about the multicast capabilities of the various network cards is contained in - Documentation/networking/multicast.txt. For most people, it's safe - to say N. + . For most people, it's + safe to say N. -IP: advanced router +Advanced router CONFIG_IP_ADVANCED_ROUTER If you intend to run your Linux box mostly as a router, i.e. as a computer that forwards and redistributes network packets, say Y; you @@ -4003,7 +4789,7 @@ control about the routing process. The answer to this question won't directly affect the kernel: - answering N will just cause this configure script to skip all the + answering N will just cause the configurator to skip all the questions about advanced routing. Note that your box can only act as a router if you enable IP @@ -4013,7 +4799,7 @@ echo "1" > /proc/sys/net/ipv4/ip_forward - at boot time after the /proc file system has been mounted. + at boot time after the /proc file system has been mounted. If you turn on IP forwarding, you will also get the rp_filter, which automatically rejects incoming packets if the routing table entry @@ -4031,26 +4817,27 @@ If unsure, say N here. -IP: policy routing +Policy routing CONFIG_IP_MULTIPLE_TABLES Normally, a router decides what to do with a received packet based solely on the packet's final destination address. If you say Y here, the Linux router will also be able to take the packet's source - address into account. Furthermore, if you also say Y to "IP: use TOS + address into account. Furthermore, if you also say Y to "Use TOS value as routing key" below, the TOS (Type-Of-Service) field of the packet can be used for routing decisions as well. In addition, if - you say Y here and to "IP: fast network address translation" below, + you say Y here and to "Fast network address translation" below, the router will also be able to modify source and destination addresses of forwarded packets. If you are interested in this, please see the preliminary - documentation at http://www.compendium.com.ar/policy-routing.txt and - ftp://post.tepkom.ru/pub/vol2/Linux/docs/advanced-routing.tex . You - will need supporting software from ftp://ftp.inr.ac.ru/ip-routing/ - + documentation at + and . + You will need supporting software from + . + If unsure, say N. - -IP: equal cost multipath + +Equal cost multipath CONFIG_IP_ROUTE_MULTIPATH Normally, the routing tables specify a single action to be taken in a deterministic manner for a given packet. If you say Y here @@ -4060,20 +4847,20 @@ equal "cost" and chooses one of them in a non-deterministic fashion if a matching packet arrives. -IP: use TOS value as routing key +Use TOS value as routing key CONFIG_IP_ROUTE_TOS The header of every IP packet carries a TOS (Type Of Service) value - with which the packet requests a certain treatment, e.g. low latency - (for interactive traffic), high throughput, or high reliability. If - you say Y here, you will be able to specify different routes for - packets with different TOS values. + with which the packet requests a certain treatment, e.g. low + latency (for interactive traffic), high throughput, or high + reliability. If you say Y here, you will be able to specify + different routes for packets with different TOS values. -IP: use netfilter MARK value as routing key +Use netfilter MARK value as routing key CONFIG_IP_ROUTE_FWMARK If you say Y here, you will be able to specify different routes for packets with different mark values (see iptables(8), MARK target). -IP: verbose route monitoring +Verbose route monitoring CONFIG_IP_ROUTE_VERBOSE If you say Y here, which is recommended, then the kernel will print verbose messages regarding the routing, for example warnings about @@ -4082,20 +4869,20 @@ handled by the klogd daemon which is responsible for kernel messages ("man klogd"). -IP: large routing tables +Large routing tables CONFIG_IP_ROUTE_LARGE_TABLES If you have routing zones that grow to more than about 64 entries, you may want to say Y here to speed up the routing process. -IP: fast network address translation +Fast network address translation CONFIG_IP_ROUTE_NAT If you say Y here, your router will be able to modify source and destination addresses of packets that pass through it, in a manner - you specify. General information about Network Address Translation + you specify. General information about Network Address Translation can be gotten from the document - http://www.csn.tu-chemnitz.de/~mha/linux-ip-nat/diplom/nat.html + . -IP: kernel level autoconfiguration +Kernel level IP autoconfiguration CONFIG_IP_PNP This enables automatic configuration of IP addresses of devices and of the routing table during kernel boot, based on either information @@ -4116,7 +4903,22 @@ does BOOTP itself, providing all necessary information on the kernel command line, you can say N here. If unsure, say Y. Note that if you want to use BOOTP, a BOOTP server must be operating on your network. - Read Documentation/nfsroot.txt for details. + Read for details. + +DHCP support +CONFIG_IP_PNP_DHCP + If you want your Linux box to mount its whole root file system (the + one containing the directory /) from some other computer over the + net via NFS and you want the IP address of your computer to be + discovered automatically at boot time using the DHCP protocol (a + special protocol designed for doing this job), say Y here. In case + the boot ROM of your network card was designed for booting Linux and + does DHCP itself, providing all necessary information on the kernel + command line, you can say N here. + + If unsure, say Y. Note that if you want to use DHCP, a DHCP server + must be operating on your network. Read + for details. RARP support CONFIG_IP_PNP_RARP @@ -4126,10 +4928,10 @@ discovered automatically at boot time using the RARP protocol (an older protocol which is being obsoleted by BOOTP and DHCP), say Y here. Note that if you want to use RARP, a RARP server must be - operating on your network. Read Documentation/nfsroot.txt for + operating on your network. Read for details. -IP: tunneling +IP tunneling CONFIG_NET_IPIP Tunneling means encapsulating data of one protocol type within another protocol and sending it over a channel that understands the @@ -4139,13 +4941,13 @@ appear on a different network than it physically is, or to use mobile-IP facilities (allowing laptops to seamlessly move between networks without changing their IP addresses; check out - http://anchor.cs.binghamton.edu/~mobileip/LJ/index.html ). + ). Saying Y to this option will produce two modules ( = code which can be inserted in and removed from the running kernel whenever you want). Most people won't need this and can say N. -IP: GRE tunnels over IP +GRE tunnels over IP CONFIG_NET_IPGRE Tunneling means encapsulating data of one protocol type within another protocol and sending it over a channel that understands the @@ -4153,18 +4955,18 @@ GRE (Generic Routing Encapsulation) and at this time allows encapsulating of IPv4 or IPv6 over existing IPv4 infrastructure. This driver is useful if the other endpoint is a Cisco router: Cisco - likes GRE much better than the other Linux tunneling driver ("IP: + likes GRE much better than the other Linux tunneling driver ("IP tunneling" above). In addition, GRE allows multicast redistribution through the tunnel. -IP: broadcast GRE over IP +Broadcast GRE over IP CONFIG_NET_IPGRE_BROADCAST One application of GRE/IP is to construct a broadcast WAN (Wide Area Network), which looks like a normal Ethernet LAN (Local Area Network), but can be distributed all over the Internet. If you want - to do that, say Y here and to "IP: multicast routing" below. + to do that, say Y here and to "IP multicast routing" below. -IP: multicast routing +IP multicast routing CONFIG_IP_MROUTE This is used if you want your machine to act as a router for IP packets that have several destination addresses. It is needed on the @@ -4172,118 +4974,69 @@ audio and video broadcasts. In order to do that, you would most likely run the program mrouted. Information about the multicast capabilities of the various network cards is contained in - Documentation/networking/multicast.txt. If you haven't heard about - it, you don't need it. + . If you haven't heard + about it, you don't need it. -IP: PIM-SM version 1 support +PIM-SM version 1 support CONFIG_IP_PIMSM_V1 Kernel side support for Sparse Mode PIM (Protocol Independent Multicast) version 1. This multicast routing protocol is used widely because Cisco supports it. You need special software to use it - (pimd-v1). Please see http://netweb.usc.edu/pim/ for more + (pimd-v1). Please see for more information about PIM. Say Y if you want to use PIM-SM v1. Note that you can say N here if you just want to use Dense Mode PIM. -IP: PIM-SM version 2 support +PIM-SM version 2 support CONFIG_IP_PIMSM_V2 Kernel side support for Sparse Mode PIM version 2. In order to use this, you need an experimental routing daemon supporting it (pimd or gated-5). This routing protocol is not used widely, so say N unless you want to play with it. -PC/TCP compatibility mode -CONFIG_INET_PCTCP - If you have been having difficulties telnetting to your Linux - machine from a DOS system that uses (broken) PC/TCP networking - software (all versions up to OnNet 2.0) over your local Ethernet try - saying Y here. Everyone else says N. - - People having problems with NCSA telnet should see the file - Documentation/networking/ncsa-telnet. - -Path MTU Discovery (normally enabled) -CONFIG_PATH_MTU_DISCOVERY - MTU (maximal transfer unit) is the size of the chunks we send out - over the net. "Path MTU Discovery" means that, instead of always - sending very small chunks, we start out sending big ones and if we - then discover that some host along the way likes its chunks smaller, - we adjust to a smaller size. This is good, so most people say Y - here. - - However, some DOS software (versions of DOS NCSA telnet and Trumpet - Winsock in PPP mode) is broken and won't be able to connect to your - Linux machine correctly in all cases (especially through a terminal - server) unless you say N here. See - Documentation/networking/ncsa-telnet for the location of fixed NCSA - telnet clients. If in doubt, say Y. - -Disable NAGLE algorithm (normally enabled) -CONFIG_TCP_NAGLE_OFF - The NAGLE algorithm works by requiring an acknowledgment before - sending small IP frames (packets). This keeps tiny telnet and - rlogin packets from congesting Wide Area Networks. Most people - strongly recommend to say N here, thereby leaving NAGLE - enabled. Those programs that would benefit from disabling this - facility can do it on a per connection basis themselves. - -IP: Allow large windows (not recommended if <16 MB of memory) -CONFIG_SKB_LARGE - On high speed, long distance networks the performance limit on - networking becomes the amount of data the sending machine can buffer - until the other end confirms its reception. (At 45 Mbit/second there - are a lot of bits between New York and London ...). If you say Y - here, bigger buffers can be used which allows larger amounts of data - to be "in flight" at any given time. It also means a user process - can require a lot more memory for network buffers and thus this - option is best used only on machines with 16 MB of memory or higher. - Unless you are using long links with end to end speeds of over 2 - Mbit a second or satellite links this option will make no difference - to performance. - Unix domain sockets CONFIG_UNIX If you say Y here, you will include support for Unix domain sockets; sockets are the standard Unix mechanism for establishing and - accessing network connections. Many commonly used programs such as + accessing network connections. Many commonly used programs such as the X Window system and syslog use these sockets even if your - machine is not connected to any network. Unless you are working on + machine is not connected to any network. Unless you are working on an embedded system or something similar, you therefore definitely want to say Y here. However, the socket support is also available as a module ( = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. The module will be called - unix.o. If you try building this as a module and you have said Y to - "Kernel module loader support" above, be sure to add 'alias net-pf-1 - unix' to your /etc/modules.conf file. Note that several important - services won't work correctly if you say M here and then neglect to - load the module. + whenever you want). If you want to compile it as a module, say M + here and read . The module will be + called unix.o. If you try building this as a module and you have + said Y to "Kernel module loader support" above, be sure to add + 'alias net-pf-1 unix' to your /etc/modules.conf file. Note that + several important services won't work correctly if you say M here + and then neglect to load the module. Say Y unless you know what you are doing. -The IPv6 protocol (EXPERIMENTAL) +The IPv6 protocol CONFIG_IPV6 This is experimental support for the next version of the Internet Protocol: IP version 6 (also called IPng "IP next generation"). Features of this new protocol include: expanded address space, authentication and privacy, and seamless interoperability with the current version of IP (IP version 4). For general information about - IPv6, see http://playground.sun.com/pub/ipng/html/ipng-main.html ; + IPv6, see ; for specific information about IPv6 under Linux read the HOWTO at - http://www.bieringer.de/linux/IPv6/ and the file net/ipv6/README in - the kernel source. + and the file net/ipv6/README + in the kernel source. If you want to use IPv6, please upgrade to the newest net-tools as - given in Documentation/Changes. You will still be able to do regular - IPv4 networking as well. + given in . You will still be able to do + regular IPv4 networking as well. This protocol support is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called ipv6.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + as a module, say M here and read . It is safe to say N here for now. @@ -4293,8 +5046,8 @@ through the old netlink interface. However, a better option is to say Y to "Kernel/User network link driver" and to "Routing messages" instead. - -Kernel httpd acceleration (EXPERIMENTAL) + +Kernel httpd acceleration CONFIG_KHTTPD The kernel httpd acceleration daemon (kHTTPd) is a (limited) web server built into the kernel. It is limited since it can only serve @@ -4313,17 +5066,17 @@ The kHTTPd is experimental. Be careful when using it on a production machine. Also note that kHTTPd doesn't support virtual servers yet. -IPX networking +The IPX protocol CONFIG_IPX This is support for the Novell networking protocol, IPX, commonly - used for local networks of Windows machines. You need it if you want - to access Novell NetWare file or print servers using the Linux + used for local networks of Windows machines. You need it if you + want to access Novell NetWare file or print servers using the Linux Novell client ncpfs (available from - ftp://metalab.unc.edu/pub/Linux/system/filesystems/ ) or from within - the Linux DOS emulator DOSEMU (read the DOSEMU-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto ). In order to do the - former, you'll also have to say Y to "NCP file system support", - below. + ) or from + within the Linux DOS emulator DOSEMU (read the DOSEMU-HOWTO, + available from ). In order + to do the former, you'll also have to say Y to "NCP file system + support", below. IPX is similar in scope to IP, while SPX, which runs on top of IPX, is similar to TCP. There is also experimental support for SPX in @@ -4331,22 +5084,22 @@ To turn your Linux box into a fully featured NetWare file server and IPX router, say Y here and fetch either lwared from - ftp://metalab.unc.edu/pub/Linux/system/network/daemons/ or mars_nwe - from ftp://ftp.gwdg.de/pub/linux/misc/ncpfs . For more information, - read the IPX-HOWTO available from - http://www.linuxdoc.org/docs.html#howto . + or + mars_nwe from . For more + information, read the IPX-HOWTO available from + . General information about how to connect Linux, Windows machines and - Macs is on the WWW at http://www.eats.com/linux_mac_win.html . + Macs is on the WWW at . The IPX driver would enlarge your kernel by about 16 KB. This driver is also available as a module ( = code which can be inserted in and - removed from the running kernel whenever you want). The module will - be called ipx.o. If you want to compile it as a module, say M here - and read Documentation/modules.txt. Unless you want to integrate - your Linux box with a local Novell network, say N. + removed from the running kernel whenever you want). The module will + be called ipx.o. If you want to compile it as a module, say M here + and read . Unless you want to + integrate your Linux box with a local Novell network, say N. -IPX: Full internal IPX network +Full internal IPX network CONFIG_IPX_INTERN Every IPX network has an address that identifies it. Sometimes it is useful to give an IPX "network" address to your Linux box as well @@ -4355,7 +5108,7 @@ same address). The way this is done is to create a virtual internal "network" inside your box and to assign an IPX address to this network. Say Y here if you want to do this; read the IPX-HOWTO at - http://www.linuxdoc.org/docs.html#howto for details. + for details. The full internal IPX network enables you to allocate sockets on different virtual nodes of the internal network. This is done by @@ -4368,12 +5121,17 @@ 'special' sockets to sockets listening on the primary network is disabled. This might break existing applications, especially RIP/SAP daemons. A RIP/SAP daemon that works well with the full internal net - can be found on ftp://ftp.gwdg.de/pub/linux/misc/ncpfs . + can be found on . If you don't know what you are doing, say N. -IPX: SPX networking (EXPERIMENTAL) +#(We're told this will come back someday) + +SPX networking CONFIG_SPX + * Orphaned entry retained 20 April 2001 by Petr Vandrovec * + * If you read this note from the configurator, please contact * + * the Configure.help maintainers. * The Sequenced Packet eXchange protocol is a transport layer protocol built on top of IPX. It is used in Novell NetWare systems for client-server applications and is similar to TCP (which runs on top @@ -4385,26 +5143,26 @@ space programs lwared or mars_nwe for the server side). Say Y here if you have use for SPX; read the IPX-HOWTO at - http://www.linuxdoc.org/docs.html#howto for details. + for details. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called af_spx.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . -DECnet networking (EXPERIMENTAL) +DECnet networking CONFIG_DECNET The DECnet networking protocol was used in many products made by - Digital (now Compaq). It provides reliable stream and sequenced + Digital (now Compaq). It provides reliable stream and sequenced packet communications over which run a variety of services similar to those which run over TCP/IP. To find some tools to use with the kernel layer support, please - look at Patrick Caulfield's web site: - http://linux.dreamtime.org/decnet/ + look at Patrick Caulfield's web site: + . - More detailed documentation is available in the - Documentation/networking/decnet.txt file. + More detailed documentation is available in + . Be sure to say Y to "/proc file system support" and "Sysctl support" below when using DECnet, since you will need sysctl support to aid @@ -4416,27 +5174,27 @@ DECnet SIOCFIGCONF support CONFIG_DECNET_SIOCGIFCONF - This option should only be turned on if you are really sure that - you know what you are doing. It can break other applications which - use this system call and the proper way to get the information - provided by this call is to use rtnetlink. - - If unsure, say N. + This option should only be turned on if you are really sure that + you know what you are doing. It can break other applications which + use this system call and the proper way to get the information + provided by this call is to use rtnetlink. -DECnet Router Support (EXPERIMENTAL) + If unsure, say N. + +DECnet router support CONFIG_DECNET_ROUTER - Add support for turning your DECnet Endnode into a level 1 or 2 - router. This is an unfinished option for developers only. If you do - say Y here, then make sure that you also say Y to "Kernel/User - network link driver", "Routing messages" and "Network packet - filtering". The first two are required to allow configuration via - rtnetlink (currently you need Alexey Kuznetsov's iproute2 package - from ftp://ftp.inr.ac.ru). The "Network packet filtering" option - will be required for the forthcoming routing daemon to work. + Add support for turning your DECnet Endnode into a level 1 or 2 + router. This is an unfinished option for developers only. If you + do say Y here, then make sure that you also say Y to "Kernel/User + network link driver", "Routing messages" and "Network packet + filtering". The first two are required to allow configuration via + rtnetlink (currently you need Alexey Kuznetsov's iproute2 package + from ). The "Network packet filtering" option + will be required for the forthcoming routing daemon to work. - See Documentation/networking/decnet.txt for more information. + See for more information. -DECnet: use FWMARK value as routing key +Use FWMARK value as DECnet routing key CONFIG_DECNET_ROUTE_FWMARK If you say Y here, you will be able to specify different routes for packets with different FWMARK ("firewalling mark") values @@ -4451,28 +5209,29 @@ AppleTalk protocol support CONFIG_ATALK AppleTalk is the way Apple computers speak to each other on a - network. If your Linux box is connected to such a network and you - want to join the conversation, say Y. You will need to use the + network. If your Linux box is connected to such a network and you + want to join the conversation, say Y. You will need to use the netatalk package so that your Linux box can act as a print and file - server for Macs as well as access AppleTalk printers. Check out - http://www.zettabyte.net/netatalk/ on the WWW for details. EtherTalk - is the name used for AppleTalk over Ethernet and the cheaper and - slower LocalTalk is AppleTalk over a proprietary Apple network using - serial links. EtherTalk and LocalTalk are fully supported by Linux. + server for Macs as well as access AppleTalk printers. Check out + on the WWW for details. + EtherTalk is the name used for AppleTalk over Ethernet and the + cheaper and slower LocalTalk is AppleTalk over a proprietary Apple + network using serial links. EtherTalk and LocalTalk are fully + supported by Linux. General information about how to connect Linux, Windows machines and - Macs is on the WWW at http://www.eats.com/linux_mac_win.html . The + Macs is on the WWW at . The NET-3-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , contains valuable + , contains valuable information as well. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module is called appletalk.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. You almost - certainly want to compile it as a module so you can restart your - AppleTalk stack without rebooting your machine. I hear that the - GNU boycott of Apple is over, so even politically correct people + The module is called appletalk.o. If you want to compile it as a + module, say M here and read . You + almost certainly want to compile it as a module so you can restart + your AppleTalk stack without rebooting your machine. I hear that + the GNU boycott of Apple is over, so even politically correct people are allowed to say Y here. AppleTalk-IP driver support @@ -4483,7 +5242,7 @@ box is stuck on an AppleTalk only network) or decapsulate (e.g. if you want your Linux box to act as an Internet gateway for a zoo of AppleTalk connected Macs). Please see the file - Documentation/networking/ipddp.txt for more information. + for more information. If you say Y here, the AppleTalk-IP support will be compiled into the kernel. In this case, you can either use encapsulation or @@ -4492,11 +5251,11 @@ If you say M here, the AppleTalk-IP support will be compiled as a module ( = code which can be inserted in and removed from the - running kernel whenever you want, read Documentation/modules.txt). - The module is called ipddp.o. In this case, you will be able to use - both encapsulation and decapsulation simultaneously, by loading two - copies of the module and specifying different values for the module - option ipddp_mode. + running kernel whenever you want, read + ). The module is called ipddp.o. + In this case, you will be able to use both encapsulation and + decapsulation simultaneously, by loading two copies of the module + and specifying different values for the module option ipddp_mode. IP to AppleTalk-IP Encapsulation support CONFIG_IPDDP_ENCAP @@ -4504,20 +5263,20 @@ IP packets inside AppleTalk frames; this is useful if your Linux box is stuck on an AppleTalk network (which hopefully contains a decapsulator somewhere). Please see - Documentation/networking/ipddp.txt for more information. If you said - Y to "AppleTalk-IP driver support" above and you say Y here, then - you cannot say Y to "AppleTalk-IP to IP Decapsulation support", - below. + for more information. If + you said Y to "AppleTalk-IP driver support" above and you say Y + here, then you cannot say Y to "AppleTalk-IP to IP Decapsulation + support", below. AppleTalk-IP to IP Decapsulation support CONFIG_IPDDP_DECAP If you say Y here, the AppleTalk-IP code will be able to decapsulate AppleTalk-IP frames to IP packets; this is useful if you want your Linux box to act as an Internet gateway for an AppleTalk network. - Please see Documentation/networking/ipddp.txt for more information. - If you said Y to "AppleTalk-IP driver support" above and you say Y - here, then you cannot say Y to "IP to AppleTalk-IP Encapsulation - support", above. + Please see for more + information. If you said Y to "AppleTalk-IP driver support" above + and you say Y here, then you cannot say Y to "IP to AppleTalk-IP + Encapsulation support", above. Apple/Farallon LocalTalk PC card support CONFIG_LTPC @@ -4526,7 +5285,7 @@ If you are in doubt, this card is the one with the 65C02 chip on it. You also need version 1.3.3 or later of the netatalk package. This driver is experimental, which means that it may not work. - See the file Documentation/networking/ltpc.txt. + See the file . COPS LocalTalk PC card support CONFIG_COPS @@ -4535,7 +5294,7 @@ package. This driver is experimental, which means that it may not work. This driver will only work if you choose "AppleTalk DDP" networking support, above. - Please read the file Documentation/networking/cops.txt. + Please read the file . Dayna firmware support CONFIG_COPS_DAYNA @@ -4551,15 +5310,15 @@ Amateur Radio support CONFIG_HAMRADIO If you want to connect your Linux box to an amateur radio, answer Y - here. You want to read http://www.tapr.org/tapr/html/pkthome.html + here. You want to read and the HAM-HOWTO and the AX25-HOWTO, both available from - http://www.linuxdoc.org/docs.html#howto . + . Note that the answer to this question won't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions about amateur radio. -Amateur Radio AX.25 Level 2 +Amateur Radio AX.25 Level 2 protocol CONFIG_AX25 This is the protocol used for computer communication over amateur radio. It is either used by itself for point-to-point links, or to @@ -4577,15 +5336,16 @@ Information about where to get supporting software for Linux amateur radio as well as information about how to configure an AX.25 port is contained in the AX25-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . You might also want to - check out the file Documentation/networking/ax25.txt in the kernel - source. More information about digital amateur radio in general is - on the WWW at http://www.tapr.org/tapr/html/pkthome.html . + . You might also want to + check out the file in the + kernel source. More information about digital amateur radio in + general is on the WWW at + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called ax25.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . AX.25 DAMA Slave support CONFIG_AX25_DAMA_SLAVE @@ -4605,7 +5365,7 @@ slaves. If you say Y here, your Linux box will act as a DAMA server. If unsure, say N. -Amateur Radio NET/ROM +Amateur Radio NET/ROM support CONFIG_NETROM NET/ROM is a network layer protocol on top of AX.25 useful for routing. @@ -4613,15 +5373,15 @@ A comprehensive listing of all the software for Linux amateur radio users as well as information about how to configure an AX.25 port is contained in the AX25-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . You also might want to - check out the file Documentation/networking/ax25.txt. More + . You also might want to + check out the file . More information about digital amateur radio in general is on the WWW at - http://www.tapr.org/tapr/html/pkthome.html . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called netrom.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . Amateur Radio X.25 PLP (Rose) CONFIG_ROSE @@ -4632,15 +5392,15 @@ A comprehensive listing of all the software for Linux amateur radio users as well as information about how to configure an AX.25 port is contained in the AX25-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . You also might want to - check out the file Documentation/networking/ax25.txt. More + . You also might want to + check out the file . More information about digital amateur radio in general is on the WWW at - http://www.tapr.org/tapr/html/pkthome.html . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called rose.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . Serial port KISS driver for AX.25 CONFIG_MKISS @@ -4656,8 +5416,8 @@ If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called mkiss.o. + say M here and read . The module + will be called mkiss.o. Serial port 6PACK driver for AX.25 CONFIG_6PACK @@ -4670,12 +5430,12 @@ Note that this driver is still experimental and might cause problems. For details about the features and the usage of the - driver, read Documentation/networking/6pack.txt. + driver, read . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called 6pack.o. + say M here and read . The module + will be called 6pack.o. BPQ Ethernet driver CONFIG_BPQETHER @@ -4696,7 +5456,7 @@ Currently, this driver supports Ottawa PI/PI2, Paccomm/Gracilis PackeTwin, and S5SCC/DMA boards. They are detected automatically. If you have one of these cards, say Y here and read the AX25-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . This driver can operate multiple boards simultaneously. If you compile it as a module (by saying M instead of Y), it will be called @@ -4712,7 +5472,7 @@ certain parameters, such as channel access timing, clock mode, and DMA channel. This is accomplished with a small utility program, dmascc_cfg, available at - http://www.nt.tuwien.ac.at/~kkudielk/Linux/ . Please be sure to get + . Please be sure to get at least version 1.27 of dmascc_cfg, as older versions will not work with the current driver. @@ -4720,27 +5480,28 @@ CONFIG_SCC These cards are used to connect your Linux box to an amateur radio in order to communicate with other computers. If you want to use - this, read Documentation/networking/z8530drv.txt and the AX25-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . Also - make sure to say Y to "Amateur Radio AX.25 Level 2" support. + this, read and the + AX25-HOWTO, available from + . Also make sure to say Y + to "Amateur Radio AX.25 Level 2" support. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called scc.o. + say M here and read . The module + will be called scc.o. + +Support for TRX that feedback the tx signal to rx +CONFIG_SCC_TRXECHO + Some transmitters feed the transmitted signal back to the receive + line. Say Y here to foil this by explicitly disabling the receiver + during data transmission. If in doubt, say Y. -additional delay for PA0HZP OptoSCC compatible boards +Additional delay for PA0HZP OptoSCC compatible boards CONFIG_SCC_DELAY Say Y here if you experience problems with the SCC driver not - working properly; please read Documentation/networking/z8530drv.txt - for details. If unsure, say N. - -#support for TRX that feedback the tx signal to rx -#CONFIG_SCC_TRXECHO -### -### Don't know what's going on here. -### -# + working properly; please read + for details. If unsure, + say N. YAM driver for AX.25 CONFIG_YAM @@ -4750,7 +5511,7 @@ If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. + say M here and read . BAYCOM picpar and par96 driver for AX.25 CONFIG_BAYCOM_PAR @@ -4758,13 +5519,13 @@ connect to a parallel interface. The driver supports the picpar and par96 designs. To configure the driver, use the sethdlc utility available in the standard ax25 utilities package. For information on - the modems, see http://www.baycom.de and the file - Documentation/networking/baycom.txt. + the modems, see and the file + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called baycom_par.o. + say M here and read . This is + recommended. The module will be called baycom_par.o. BAYCOM EPP driver for AX.25 CONFIG_BAYCOM_EPP @@ -4772,48 +5533,49 @@ connect to a parallel interface. The driver supports the EPP designs. To configure the driver, use the sethdlc utility available in the standard ax25 utilities package. For information on the - modems, see http://www.baycom.de and the file - Documentation/networking/baycom.txt. + modems, see and the file + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called baycom_par.o. + say M here and read . This is + recommended. The module will be called baycom_par.o. -BAYCOM ser12 full duplex driver for AX.25 +BAYCOM ser12 full-duplex driver for AX.25 CONFIG_BAYCOM_SER_FDX This is one of two drivers for Baycom style simple amateur radio modems that connect to a serial interface. The driver supports the - ser12 design in full duplex mode. In addition, it allows the + ser12 design in full-duplex mode. In addition, it allows the baudrate to be set between 300 and 4800 baud (however not all modems support all baudrates). This is the preferred driver. The next - driver, "BAYCOM ser12 half duplex driver for AX.25" is the old + driver, "BAYCOM ser12 half-duplex driver for AX.25" is the old driver and still provided in case this driver does not work with your serial interface chip. To configure the driver, use the sethdlc utility available in the standard ax25 utilities package. For - information on the modems, see http://www.baycom.de and - Documentation/networking/baycom.txt. + information on the modems, see and + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called baycom_ser_fdx.o. + say M here and read . This is + recommended. The module will be called baycom_ser_fdx.o. -BAYCOM ser12 half duplex driver for AX.25 +BAYCOM ser12 half-duplex driver for AX.25 CONFIG_BAYCOM_SER_HDX This is one of two drivers for Baycom style simple amateur radio modems that connect to a serial interface. The driver supports the - ser12 design in full duplex mode. This is the old driver. It is + ser12 design in full-duplex mode. This is the old driver. It is still provided in case your serial interface chip does not work with - the full duplex driver. This driver is depreciated. To configure the - driver, use the sethdlc utility available in the standard ax25 + the full-duplex driver. This driver is depreciated. To configure + the driver, use the sethdlc utility available in the standard ax25 utilities package. For information on the modems, see - http://www.baycom.de and Documentation/networking/baycom.txt. + and + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called baycom_ser_hdx.o. + say M here and read . This is + recommended. The module will be called baycom_ser_hdx.o. Sound card modem driver for AX.25 CONFIG_SOUNDMODEM @@ -4825,13 +5587,13 @@ To configure the driver, use the sethdlc, smdiag and smmixer utilities available in the standard ax25 utilities package. For information on how to key the transmitter, see - http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html and - Documentation/networking/soundmodem.txt. + and + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called soundmodem.o. + say M here and read . This is + recommended. The module will be called soundmodem.o. Sound card modem support for Sound Blaster and compatible cards CONFIG_SOUNDMODEM_SBC @@ -4840,7 +5602,7 @@ with a Sound Blaster emulation) you should say N here and Y to "Sound card modem support for WSS and Crystal cards", below, because this usually results in better performance. This option also - supports SB16/32/64 in full duplex mode. + supports SB16/32/64 in full-duplex mode. Sound card modem support for WSS and Crystal cards CONFIG_SOUNDMODEM_WSS @@ -4848,8 +5610,8 @@ compatible cards. These cards feature a codec chip from either Analog Devices (such as AD1848, AD1845, AD1812) or Crystal Semiconductors (such as CS4248, CS423x). This option also supports - the WSS full duplex operation which currently works with Crystal - CS423x chips. If you don't need full duplex operation, do not enable + the WSS full-duplex operation which currently works with Crystal + CS423x chips. If you don't need full-duplex operation, do not enable it to save performance. Sound card modem support for 1200 baud AFSK modulation @@ -4909,7 +5671,7 @@ can only use one protocol at a time, depending on what the other end can understand). -CCITT X.25 Packet Layer (EXPERIMENTAL) +CCITT X.25 Packet Layer CONFIG_X25 X.25 is a set of standardized network protocols, similar in scope to frame relay; the one physical line from your box to the X.25 network @@ -4920,13 +5682,13 @@ countries have public X.25 networks. X.25 consists of two protocols: the higher level Packet Layer Protocol (PLP) (say Y here if you want that) and the lower level data link layer protocol LAPB - (say Y to "LAPB Data Link Driver" below if you want that). + (say Y to "LAPB Data Link Driver" below if you want that). - You can read more about X.25 at http://www.sangoma.com/x25.htm and - http://www.cisco.com/univercd/data/doc/software/11_0/rpcg/cx25.htm . + You can read more about X.25 at and + . Information about X.25 for Linux is contained in the files - Documentation/networking/x25.txt and - Documentation/networking/x25-iface.txt. + and + . One connects to an X.25 network either with a dedicated network card using the X.21 protocol (not yet supported by Linux) or one can do @@ -4934,14 +5696,14 @@ to "X.25 async driver" below) or over Ethernet using an ordinary Ethernet card and either the 802.2 LLC protocol (say Y to "802.2 LLC" below) or LAPB over Ethernet (say Y to "LAPB Data Link Driver" - and "LAPB over Ethernet driver" below). + and "LAPB over Ethernet driver" below). If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called x25.o. If unsure, say N. + say M here and read . The module + will be called x25.o. If unsure, say N. -LAPB Data Link Driver (EXPERIMENTAL) +LAPB Data Link Driver CONFIG_LAPB Link Access Procedure, Balanced (LAPB) is the data link layer (i.e. the lower) part of the X.25 protocol. It offers a reliable @@ -4952,41 +5714,42 @@ currently supports LAPB only over Ethernet connections. If you want to use LAPB connections over Ethernet, say Y here and to "LAPB over Ethernet driver" below. Read - Documentation/networking/lapb-module.txt for technical details. + for technical + details. If you want to compile this driver as a module though ( = code which can be inserted in and removed from the running kernel whenever you - want), say M here and read Documentation/modules.txt. The module - will be called lapb.o. If unsure, say N. + want), say M here and read . The + module will be called lapb.o. If unsure, say N. -802.2 LLC (EXPERIMENTAL) +802.2 LLC CONFIG_LLC This is a Logical Link Layer protocol used for X.25 connections over - Ethernet, using ordinary Ethernet cards. + Ethernet, using ordinary Ethernet cards. - -Frame Diverter (EXPERIMENTAL) +Frame Diverter CONFIG_NET_DIVERT The Frame Diverter allows you to divert packets from the - network, that are not aimed at the interface receiving it (in - promisc. mode). Typically, a Linux box setup as an ethernet bridge - with the Frames Diverter on, can do some *really* transparent www + network, that are not aimed at the interface receiving it (in + promisc. mode). Typically, a Linux box setup as an Ethernet bridge + with the Frames Diverter on, can do some *really* transparent www caching using a Squid proxy for example. This is very useful when you don't want to change your router's config (or if you simply don't have access to it). - The other possible usages of diverting Ethernet Frames are numberous: + The other possible usages of diverting Ethernet Frames are + numberous: - reroute smtp traffic to another interface - traffic-shape certain network streams - transparently proxy smtp connections - etc... For more informations, please refer to: - http://www.freshmeat.net/projects/etherdivert - http://perso.wanadoo.fr/magpie/EtherDivert.html + + - If unsure, say N + If unsure, say N. 802.1d Ethernet Bridging CONFIG_BRIDGE @@ -4996,21 +5759,22 @@ Several such bridges can work together to create even larger networks of Ethernets using the IEEE 802.1 spanning tree algorithm. As this is a standard, Linux bridges will cooperate properly with - other third party bridge products. + other third party bridge products. + + In order to use the Ethernet bridge, you'll need the bridge + configuration tools; see + for location. Please read the Bridge mini-HOWTO for more + information. - In order to use the ethernet bridge, you'll need the bridge - configuration tools; see Documentation/networking/bridge.txt for - location. Please read the Bridge mini-HOWTO for more information. - Note that if your box acts as a bridge, it probably contains several Ethernet devices, but the kernel is not able to recognize more than one at boot time without help; for details read the Ethernet-HOWTO, - available from in http://www.linuxdoc.org/docs.html#howto . + available from in . If you want to compile this code as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called bridge.o. + say M here and read . The module + will be called bridge.o. If unsure, say N. @@ -5018,15 +5782,15 @@ CONFIG_PACKET The Packet protocol is used by applications which communicate directly with network devices without an intermediate network - protocol implemented in the kernel, e.g. tcpdump. If you want them - to work, choose Y. + protocol implemented in the kernel, e.g. tcpdump. If you want them + to work, choose Y. This driver is also available as a module called af_packet.o ( = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt; if you use modprobe or - kmod, you may also want to add "alias net-pf-17 af_packet" to - /etc/modules.conf. + whenever you want). If you want to compile it as a module, say M + here and read ; if you use modprobe + or kmod, you may also want to add "alias net-pf-17 af_packet" to + /etc/modules.conf. If unsure, say Y. @@ -5040,17 +5804,17 @@ Kernel/User network link driver CONFIG_NETLINK This driver allows for two-way communication between the kernel and - user processes. It does so by creating a new socket family, PF_NETLINK. - Over this socket, the kernel can send and receive datagrams carrying - information. It is documented on many systems in netlink(7), a HOWTO is - provided as well, for example on - http://snafu.freedom.org/linux2.2/docs/netlink-HOWTO.html + user processes. It does so by creating a new socket family, + PF_NETLINK. Over this socket, the kernel can send and receive + datagrams carrying information. It is documented on many systems in + netlink(7), a HOWTO is provided as well, for example on + . So far, the kernel uses this feature to publish some network related information if you say Y to "Routing messages", below. You also need to say Y here if you want to use arpd, a daemon that helps keep the internal ARP cache (a mapping between IP addresses and hardware - addresses on the local network) small. The ethertap device, which + addresses on the local network) small. The ethertap device, which lets user space programs read and write raw Ethernet frames, also needs the network link driver. @@ -5060,7 +5824,7 @@ CONFIG_RTNETLINK If you say Y here, user space programs can receive some network related routing information over the netlink. 'rtmon', supplied - with the iproute2 package (ftp://ftp.inr.ac.ru), can read and + with the iproute2 package (), can read and interpret this data. Information sent to the kernel over this link is ignored. @@ -5075,17 +5839,17 @@ Asynchronous Transfer Mode (ATM) CONFIG_ATM ATM is a high-speed networking technology for Local Area Networks - and Wide Area Networks. It uses a fixed packet size and is + and Wide Area Networks. It uses a fixed packet size and is connection oriented, allowing for the negotiation of minimum bandwidth requirements. - + In order to participate in an ATM network, your Linux box needs an ATM networking card. If you have that, say Y here and to the driver of your ATM card below. Note that you need a set of user-space programs to actually make use - of ATM. See the file Documentation/networking/atm.txt for further - details. + of ATM. See the file for + further details. Classical IP over ATM CONFIG_ATM_CLIP @@ -5130,9 +5894,9 @@ The driver works with MMF (-MF or ...F) and UTP-5 (-U5 or ...D) adapters. - This driver is also available as a module. If you want to compile - it as a module, say M here and read Documentation/modules.txt. The - module will be called eni.o. + This driver is also available as a module. If you want to compile + it as a module, say M here and read + . The module will be called eni.o. Enable extended debugging CONFIG_ATM_ENI_DEBUG @@ -5179,7 +5943,7 @@ Burst four words at once in the send direction. You may want to try this if you have disabled 8W bursts. Enabling 4W if 8W is also set may or may not improve throughput. - + Enable 2W TX bursts (optional) CONFIG_ATM_ENI_BURST_TX_2W Burst two words at once in the send direction. You may want to try @@ -5214,9 +5978,9 @@ Driver for the ZeitNet ZN1221 (MMF) and ZN1225 (UTP-5) 155 Mbps ATM adapters. - This driver is also available as a module. If you want to compile - it as a module, say M here and read Documentation/modules.txt. The - module will be called zatm.o. + This driver is also available as a module. If you want to compile + it as a module, say M here and read + . The module will be called zatm.o. Enable extended debugging CONFIG_ATM_ZATM_DEBUG @@ -5228,12 +5992,13 @@ Fujitsu FireStream (FS50/FS155) CONFIG_ATM_FIRESTREAM - Driver for the Fujitsu FireStream 155 (MB86697) and + Driver for the Fujitsu FireStream 155 (MB86697) and FireStream 50 (MB86695) ATM PCI chips. - This driver is also available as a module. If you want to compile - it as a module, say M here and read Documentation/modules.txt. The - module will be called firestream.o. + This driver is also available as a module. If you want to compile + it as a module, say M here and read + . The module will be called + firestream.o. Enable usec resolution timestamps CONFIG_ATM_ZATM_EXACT_TS @@ -5251,21 +6016,22 @@ 25 and for 155 Mbps, including IDT cards and the Fore ForeRunnerLE series. Say Y if you have one of those. - This driver is also available as a module. If you want to compile - it as a module, say M here and read Documentation/modules.txt. The - module will be called nicstar.o. + This driver is also available as a module. If you want to compile + it as a module, say M here and read + . The module will be called + nicstar.o. -ForeRunner LE155 PHYsical layer +Use suni PHY driver (155Mbps) CONFIG_ATM_NICSTAR_USE_SUNI Support for the S-UNI and compatible PHYsical layer chips. These are found in most 155Mbps NICStAR based ATM cards, namely in the - ForeRunner LE155 cards. This driver provides detection of cable + ForeRunner LE155 cards. This driver provides detection of cable~ removal and reinsertion and provides some statistics. This driver doesn't have removal capability when compiled as a module, so if you need that capability don't include S-UNI support (it's not needed to make the card work). -ForeRunner LE25 PHYsical layer +Use IDT77015 PHY driver (25Mbps) CONFIG_ATM_NICSTAR_USE_IDT77105 Support for the PHYsical layer chip in ForeRunner LE25 cards. In addition to cable removal/reinsertion detection, this driver allows @@ -5282,11 +6048,12 @@ Enable debugging messages CONFIG_ATM_AMBASSADOR_DEBUG Somewhat useful debugging messages are available. The choice of - messages is controlled by a bitmap. This may be specified as a + messages is controlled by a bitmap. This may be specified as a module argument (kernel command line argument as well?), changed dynamically using an ioctl (not yet) or changed by sending the - string "Dxxxx" to VCI 1023 (where x is a hex digit). See the file - drivers/atm/ambassador.h for the meanings of the bits in the mask. + string "Dxxxx" to VCI 1023 (where x is a hex digit). See the file + for the meanings of the bits in the + mask. When active, these messages can have a significant impact on the speed of the driver, and the size of your syslog files! When @@ -5301,28 +6068,29 @@ Enable debugging messages CONFIG_ATM_HORIZON_DEBUG Somewhat useful debugging messages are available. The choice of - messages is controlled by a bitmap. This may be specified as a + messages is controlled by a bitmap. This may be specified as a module argument (kernel command line argument as well?), changed dynamically using an ioctl (not yet) or changed by sending the - string "Dxxxx" to VCI 1023 (where x is a hex digit). See the file - drivers/atm/horizon.h for the meanings of the bits in the mask. + string "Dxxxx" to VCI 1023 (where x is a hex digit). See the file + for the meanings of the bits in the + mask. When active, these messages can have a significant impact on the speed of the driver, and the size of your syslog files! When inactive, they will have only a modest impact on performance. -Interphase ATM PCI x575/x525/x531 +Interphase ATM PCI x575/x525/x531 CONFIG_ATM_IA This is a driver for the Interphase (i)ChipSAR adapter cards - which include a variety of variants in term of the size of the - control memory (128K-1KVC, 512K-4KVC), the size of the packet - memory (128K, 512K, 1M), and the PHY type (Single/Multi mode OC3, + which include a variety of variants in term of the size of the + control memory (128K-1KVC, 512K-4KVC), the size of the packet + memory (128K, 512K, 1M), and the PHY type (Single/Multi mode OC3, UTP155, UTP25, DS3 and E3). Go to: www.iphase.com/products/ClassSheet.cfm?ClassID=ATM - for more info about the cards. Say Y (or M to compile as a module + for more info about the cards. Say Y (or M to compile as a module named iphase.o) here if you have one of these cards. - See the file Documentation/networking/iphase.txt for further + See the file for further details. Enable debugging messages @@ -5330,9 +6098,11 @@ Somewhat useful debugging messages are available. The choice of messages is controlled by a bitmap. This may be specified as a module argument (kernel command line argument as well?), changed - dynamically using an ioctl (Get the debug utility, iadbg, from - ftp.iphase.com/pub/atm/pci). See the file drivers/atm/iphase.h - for the meanings of the bits in the mask. + dynamically using an ioctl (Get the debug utility, iadbg, from + ). + + See the file for the meanings of the + bits in the mask. When active, these messages can have a significant impact on the speed of the driver, and the size of your syslog files! When @@ -5341,36 +6111,37 @@ Linux telephony support CONFIG_PHONE Say Y here if you have a telephony card, which for example allows - you to use a regular phone for voice-over-IP applications. + you to use a regular phone for voice-over-IP applications. - Note: this has nothing to do with modems. You do not need to say Y + Note: this has nothing to do with modems. You do not need to say Y here in order to be able to use a modem under Linux. - This support is also available as a module. If you want to compile - it as a module, say M here and read Documentation/modules.txt. The - module will be called phonedev.o. + This support is also available as a module. If you want to compile + it as a module, say M here and read + . The module will be called + phonedev.o. Compaq Smart Array support CONFIG_BLK_CPQ_CISS_DA - This is the driver for Compaq Smart Array 5xxx controllers. - Everyone using these boards should say Y here. - See Documentation/cciss.txt for the current list of - boards supported by this driver, and for further information - on the use of this driver. + This is the driver for Compaq Smart Array 5xxx controllers. + Everyone using these boards should say Y here. + See for the current list of + boards supported by this driver, and for further information + on the use of this driver. QuickNet Internet LineJack/PhoneJack support CONFIG_PHONE_IXJ Say M if you have a telephony card manufactured by Quicknet Technologies, Inc. These include the Internet PhoneJACK and Internet LineJACK Telephony Cards. You will get a module called - ixj.o. + ixj.o. For the ISA versions of these products, you can configure the cards using the isapnp tools (pnpdump/isapnp) or you can use the - isapnp support. Please read Documentation/telephony/ixj.txt. + isapnp support. Please read . For more information on these cards, see Quicknet's web site at: - http://www.quicknet.net/ . + . If you do not have any Quicknet telephony cards, you can safely say N here. @@ -5381,66 +6152,66 @@ cards. It simultaneously supports PCA-200E and SBA-200E models on PCI and SBUS hosts. Say Y (or M to compile as a module named fore_200e.o) here if you have one of these ATM adapters. - + Note that the driver will actually be compiled only if you additionally enable the support for PCA-200E and/or SBA-200E cards. - See the file Documentation/networking/fore200e.txt for further - details. - + See the file for + further details. + Enable PCA-200E card support on PCI-based hosts CONFIG_ATM_FORE200E_PCA Say Y here if you want your PCA-200E cards to be probed. - + Use default PCA-200E firmware CONFIG_ATM_FORE200E_PCA_DEFAULT_FW Use the default PCA-200E firmware data shipped with the driver. - + Normal users do not have to deal with the firmware stuff, so they should say Y here. - + Pathname of user-supplied binary firmware CONFIG_ATM_FORE200E_PCA_FW This defines the pathname of an alternative PCA-200E binary firmware image supplied by the user. This pathname may be absolute or relative to the drivers/atm directory. - + The driver comes with an adequate firmware image, so normal users do not have to supply an alternative one. They just say Y to "Use default PCA-200E firmware" instead. - + Enable SBA-200E card support on SBUS-based hosts CONFIG_ATM_FORE200E_SBA Say Y here if you want your SBA-200E cards to be probed. - + Use default SBA-200E firmware CONFIG_ATM_FORE200E_SBA_DEFAULT_FW Use the default SBA-200E firmware data shipped with the driver. - + Normal users do not have to deal with the firmware stuff, so they should say Y here. - + Pathname of user-supplied binary firmware CONFIG_ATM_FORE200E_SBA_FW This defines the pathname of an alternative SBA-200E binary firmware image supplied by the user. This pathname may be absolute or relative to the drivers/atm directory. - + The driver comes with an adequate firmware image, so normal users do not have to supply an alternative one. They just say Y to "Use default SBA-200E firmware", above. - + Maximum number of tx retries CONFIG_ATM_FORE200E_TX_RETRY Specifies the number of times the driver attempts to transmit a message before giving up, if the transmit queue of the ATM card is transiently saturated. - + Saturation of the transmit queue may occur only under extreme conditions, e.g. when a fast host continuously submits very small frames (<64 bytes) or raw AAL0 cells (48 bytes) to the ATM adapter. - + Note that under common conditions, it is unlikely that you encounter a saturation of the transmit queue, so the retry mechanism never comes into play. @@ -5450,54 +6221,168 @@ Specifies the level of debugging messages issued by the driver. The verbosity of the driver increases with the value of this parameter. - + When active, these messages can have a significant impact on the performances of the driver, and the size of your syslog files! Keep the debugging level to 0 during normal operations. -SCSI support? +Fusion MPT device support +CONFIG_FUSION + LSI Logic Fusion(TM) Message Passing Technology (MPT) device support + provides high performance SCSI host initiator, and LAN [1] interface + services to a host system. The Fusion architecture is capable of + duplexing these protocols on high-speed Fibre Channel + (up to 2 GHz x 2 ports = 4 GHz) and parallel SCSI (up to Ultra-320) + physical medium. + + [1] LAN is not supported on parallel SCSI medium. + + These drivers require a Fusion MPT compatible PCI adapter installed + in the host system. MPT adapters contain specialized I/O processors + to handle I/O workload, and more importantly to offload this work + from the host CPU(s). + + If you have Fusion MPT hardware and want to use it, you can say + Y or M here to add MPT (base + ScsiHost) drivers. + = build lib (fusion.o), and link [static] into the kernel [2] + proper + = compiled as [dynamic] modules [3] named: (mptbase.o, + mptscsih.o) + + [2] In order enable capability to boot the linux kernel + natively from a Fusion MPT target device, you MUST + answer Y here! (currently requires CONFIG_BLK_DEV_SD) + [3] This support is also available as a module ( = code + which can be inserted in and removed from the running + kernel whenever you want). If you want to compile as + modules, say M here and read + . + + If unsure, say N. + + If you say Y or M here you will get a choice of these + additional protocol and support module options: Module Name: + Enhanced SCSI error reporting (isense.o) + Fusion MPT misc device (ioctl) driver (mptctl.o) + Fusion MPT LAN driver (mptlan.o) + + --- + Fusion MPT is trademark of LSI Logic Corporation, and its + architecture is based on LSI Logic's Message Passing Interface (MPI) + specification. + +Fusion MPT enhanced SCSI error reporting [optional] module +CONFIG_FUSION_ISENSE + The isense module (roughly stands for Interpret SENSE data) is + completely optional. It simply provides extra English readable + strings in SCSI Error Report(s) that might be generated from the + Fusion MPT SCSI Host driver, for example when a target device + returns a SCSI check condition on a I/O. Without this module + loaded you might see: + + SCSI Error Report =-=-= (ioc0,scsi5:0) + SCSI_Status=02h (CHECK_CONDITION) + Original_CDB[]: 2A 00 00 00 00 41 00 00 02 00 + SenseData[12h]: 70 00 02 00 00 00 00 0A 00 00 00 00 04 02 02 00 00 00 + SenseKey=2h (NOT READY); FRU=02h + ASC/ASCQ=29h/00h + + Where otherwise, if this module had been loaded, you would see: + + SCSI Error Report =-=-= (ioc0,scsi5:0) + SCSI_Status=02h (CHECK_CONDITION) + Original_CDB[]: 2A 00 00 00 00 41 00 00 02 00 - "WRITE(10)" + SenseData[12h]: 70 00 02 00 00 00 00 0A 00 00 00 00 04 02 02 00 00 00 + SenseKey=2h (NOT READY); FRU=02h + ASC/ASCQ=29h/00h "LOGICAL UNIT NOT READY, INITIALIZING CMD. REQUIRED" + + Say M for "Enhanced SCSI error reporting" to compile this optional module, + creating a driver named: isense.o. + + NOTE: Support for building this feature into the kernel is not + available, due to kernel size considerations. + +Fusion MPT misc device (ioctl) driver [optional] module +CONFIG_FUSION_CTL + The Fusion MPT misc device driver provides specialized control + of MPT adapters via system ioctl calls. Use of ioctl calls to + the MPT driver requires that you create and use a misc device + node ala: + mknod /dev/mptctl c 10 240 + + One use of this ioctl interface is to perform an upgrade (reflash) + of the MPT adapter firmware. Refer to readme file(s) distributed + with the Fusion MPT linux driver for additional details. + + If enabled by saying M to this, a driver named: mptctl.o + will be compiled. + + If unsure whether you really want or need this, say N. + +Fusion MPT LAN driver [optional] +CONFIG_FUSION_LAN + This module supports LAN IP traffic over Fibre Channel port(s) + on Fusion MPT compatible hardware (LSIFC9xx chips). + The physical interface used is defined in RFC 2625. + Please refer to that document for details. + + Installing this driver requires the knowledge to configure and + activate a new network interface, "fc0", using standard Linux tools. + + If enabled by saying M to this, a driver named: mptlan.o + will be compiled. + + If unsure whether you really want or need this, say N. + + NOTES: This feature is NOT available nor supported for linux-2.2.x + kernels. You must be building a linux-2.3.x or linux-2.4.x kernel + in order to configure this option. + Support for building this feature into the linux kernel is not + yet available. + +SCSI support CONFIG_SCSI - If you want to use a SCSI hard disk, SCSI tape drive, SCSI CDROM or + If you want to use a SCSI hard disk, SCSI tape drive, SCSI CD-ROM or any other SCSI device under Linux, say Y and make sure that you know the name of your SCSI host adapter (the card inside your computer that "speaks" the SCSI protocol, also called SCSI controller), - because you will be asked for it. + because you will be asked for it. You also need to say Y here if you want support for the parallel port version of the 100 MB IOMEGA ZIP drive. Please read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . The + . The SCSI-Programming-HOWTO contains information about how to add or remove an SCSI device from a running Linux machine without rebooting. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called scsi_mod.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt and - Documentation/scsi.txt. However, do not compile this as a module if - your root file system (the one containing the directory /) is - located on a SCSI device. + The module will be called scsi_mod.o. If you want to compile it as + a module, say M here and read and + . However, do not compile this as a + module if your root file system (the one containing the directory /) + is located on a SCSI device. SCSI disk support CONFIG_BLK_DEV_SD If you want to use a SCSI hard disk or the SCSI or parallel port version of the IOMEGA ZIP drive under Linux, say Y and read the SCSI-HOWTO, the Disk-HOWTO and the Multi-Disk-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . This is NOT for SCSI - CDROMs. + . This is NOT for SCSI + CD-ROMs. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called sd_mod.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt and - Documentation/scsi.txt. Do not compile this driver as a module if - your root file system (the one containing the directory /) is - located on a SCSI disk. In this case, do not compile the driver for - your SCSI host adapter (below) as a module either. + The module will be called sd_mod.o. If you want to compile it as a + module, say M here and read and + . Do not compile this driver as a + module if your root file system (the one containing the directory /) + is located on a SCSI disk. In this case, do not compile the driver + for your SCSI host adapter (below) as a module either. -Extra SCSI Disks +Maximum number of SCSI disks that can be loaded as modules CONFIG_SD_EXTRA_DEVS This controls the amount of additional space allocated in tables for drivers that are loaded as modules after the kernel is booted. In @@ -5511,63 +6396,76 @@ If you don't understand what's going on, go with the default. +Maximum number of SCSI tapes that can be loaded as modules +CONFIG_ST_EXTRA_DEVS + This controls the amount of additional space allocated in tables for + drivers that are loaded as modules after the kernel is booted. In + the event that the SCSI core itself was loaded as a module, this + value is the number of additional tapes that can be loaded after the + first host driver is loaded. + + Admittedly this isn't pretty, but there are tons of race conditions + involved with resizing the internal arrays on the fly. Someday this + flag will go away, and everything will work automatically. + + If you don't understand what's going on, go with the default. + SCSI tape support CONFIG_CHR_DEV_ST If you want to use a SCSI tape drive under Linux, say Y and read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , and - drivers/scsi/README.st in the kernel source. This is NOT for SCSI - CDROMs. + , and + in the kernel source. This is NOT for + SCSI CD-ROMs. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called st.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt and - Documentation/scsi.txt . + module, say M here and read and + . OnStream SC-x0 SCSI tape support CONFIG_CHR_DEV_OSST - The OnStream SC-x0 SCSI tape drives can not be driven by the - standard st driver, but instead need this special osst driver and - use the /dev/osstX char device nodes (major 206). - Via usb-storage and ide-scsi, you may be able to drive the USB-x0 - and DI-x0 drives as well. Note that there is also a second generation - of OnStream tape drives (ADR-x0) that supports the standard SCSI-2 - commands for tapes (QIC-157) and can be driven by the standard - driver st. + The OnStream SC-x0 SCSI tape drives can not be driven by the + standard st driver, but instead need this special osst driver and + use the /dev/osstX char device nodes (major 206). Via usb-storage + and ide-scsi, you may be able to drive the USB-x0 and DI-x0 drives + as well. Note that there is also a second generation of OnStream + tape drives (ADR-x0) that supports the standard SCSI-2 commands for + tapes (QIC-157) and can be driven by the standard driver st. For more information, you may have a look at the SCSI-HOWTO - ftp://metalab.unc.edu/pub/Linux/docs/HOWTO and - drivers/scsi/README.osst in the kernel source. - More info on the OnStream driver may be found on - http://linux1.onstream.nl/test/ + and + in the kernel source. + More info on the OnStream driver may be found on + Please also have a look at the standard st docu, as most of it applies to osst as well. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called osst.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt and - Documentation/scsi.txt . + module, say M here and read and + . -SCSI CDROM support +SCSI CD-ROM support CONFIG_BLK_DEV_SR - If you want to use a SCSI CDROM under Linux, say Y and read the - SCSI-HOWTO and the CDROM-HOWTO at - http://www.linuxdoc.org/docs.html#howto . Also make sure to say Y - or M to "ISO 9660 CDROM file system support" later. + If you want to use a SCSI CD-ROM under Linux, say Y and read the + SCSI-HOWTO and the CD-ROM-HOWTO at + . Also make sure to say Y + or M to "ISO 9660 CD-ROM file system support" later. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called sr_mod.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt and - Documentation/scsi.txt . + module, say M here and read and + . -Extra SCSI CDROMs +Maximum number of CD-ROM devices that can be loaded as modules CONFIG_SR_EXTRA_DEVS This controls the amount of additional space allocated in tables for drivers that are loaded as modules after the kernel is booted. In the event that the SCSI core itself was loaded as a module, this - value is the number of additional CDROMs that can be loaded after + value is the number of additional CD-ROMs that can be loaded after the first host driver is loaded. Admittedly this isn't pretty, but there are tons of race conditions @@ -5576,7 +6474,7 @@ If you don't understand what's going on, go with the default. -Enable vendor-specific extensions (for SCSI CDROM) +Enable vendor-specific extensions (for SCSI CD-ROM) CONFIG_BLK_DEV_SR_VENDOR This enables the usage of vendor specific SCSI commands. This is required to support multisession CDs with old NEC/TOSHIBA cdrom @@ -5587,27 +6485,27 @@ CONFIG_CHR_DEV_SG If you want to use SCSI scanners, synthesizers or CD-writers or just about anything having "SCSI" in its name other than hard disks, - CDROMs or tapes, say Y here. These won't be supported by the kernel + CD-ROMs or tapes, say Y here. These won't be supported by the kernel directly, so you need some additional software which knows how to talk to these devices using the SCSI protocol: - For scanners, look at SANE (http://www.mostang.com/sane). For CD + For scanners, look at SANE (). For CD writer software look at cdrecord - (http://www.fokus.gmd.de/research/cc/glone/employees/joerg.schilling/private/cdrecord.html) + () and for burning a "disk at once": cdrdao - (http://www.ping.de/sites/daneb/cdrdao.html). Cdparanoia is a high - quality digital reader of audio CDs (http://www.xiph.org/paranoia). + (). Cdparanoia is a high + quality digital reader of audio CDs (). For other devices, it's possible that you'll have to write the driver software yourself. Please read the file - Documentation/scsi-generic.txt for more information. + for more information. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt and - Documentation/scsi.txt. The module will be called sg.o. If unsure, + say M here and read and + . The module will be called sg.o. If unsure, say N. -Debug new queueing code for SCSI +Enable extra checks in SCSI queueing code CONFIG_SCSI_DEBUG_QUEUES This option turns on a lot of additional consistency checking for the new queueing code. This will adversely affect performance, but @@ -5615,14 +6513,14 @@ This will typically cause the kernel to panic if an error is detected, but it would have probably crashed if the panic weren't there. Comments/questions/problems to linux-scsi mailing list - please. See http://www.andante.org/scsi_queue.html for more + please. See for more up-to-date information. Probe all LUNs on each SCSI device CONFIG_SCSI_MULTI_LUN If you have a SCSI device that supports more than one LUN (Logical Unit Number), e.g. a CD jukebox, and only one LUN is detected, you - can say Y here to force the SCSI driver to probe for multiple LUNs. + can say Y here to force the SCSI driver to probe for multiple LUNs. A SCSI device with multiple LUNs acts logically like multiple SCSI devices. The vast majority of SCSI devices have only one LUN, and so most people can say N here and should in fact do so, because it @@ -5637,7 +6535,7 @@ SCSI logging facility CONFIG_SCSI_LOGGING This turns on a logging facility that can be used to debug a number - of SCSI related problems. + of SCSI related problems. If you say Y here, no logging output will appear by default, but you can enable logging by saying Y to "/proc file system support" and @@ -5648,25 +6546,37 @@ at boot time after the /proc file system has been mounted. There are a number of things that can be used for 'token' (you can - find them in the source: drivers/scsi/scsi.c), and this allows you - to select the types of information you want, and the level allows - you to select the level of verbosity. + find them in the source: ), and this + allows you to select the types of information you want, and the + level allows you to select the level of verbosity. If you say N here, it may be harder to track down some types of SCSI problems. If you say Y here your kernel will be somewhat larger, but there should be no noticeable performance impact as long as you have logging turned off. +SGI WD93C93 SCSI Driver +CONFIG_SCSI_SGIWD93 + Say Y here to support the on-board WD93C93 SCSI controller found (a) + on the Indigo2 and other MIPS-based SGI machines, and (b) on ARCS + ARM-based machines. + +DEC NCR53C94 SCSI Driver +CONFIG_SCSI_DECNCR + Say Y here to support the NCR53C94 SCSI controller chips on IOASIC + based TURBOchannel DECstations and TURBOchannel PMAZ-A cards. + AdvanSys SCSI support CONFIG_SCSI_ADVANSYS This is a driver for all SCSI host adapters manufactured by AdvanSys. It is documented in the kernel source in - drivers/scsi/advansys.c. + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. The module will be called advansys.o. + . The module will be called + advansys.o. Adaptec AHA152X/2825 support CONFIG_SCSI_AHA152X @@ -5675,49 +6585,61 @@ must be manually specified in this case. It is explained in section 3.3 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . You might also want to - read the file drivers/scsi/README.aha152x. + . You might also want to + read the file . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called aha152x.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . Adaptec AHA1542 support CONFIG_SCSI_AHA1542 - This is support for a SCSI host adapter. It is explained in section + This is support for a SCSI host adapter. It is explained in section 3.4 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Note that Trantor was + . Note that Trantor was purchased by Adaptec, and some former Trantor products are being - sold under the Adaptec name. If it doesn't work out of the box, you - may have to change some settings in drivers/scsi/aha1542.h. - + sold under the Adaptec name. If it doesn't work out of the box, you + may have to change some settings in . + If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called aha1542.o. + say M here and read . The module + will be called aha1542.o. Adaptec AHA1740 support CONFIG_SCSI_AHA1740 - This is support for a SCSI host adapter. It is explained in section + This is support for a SCSI host adapter. It is explained in section 3.5 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . If it doesn't work out + . If it doesn't work out of the box, you may have to change some settings in - drivers/scsi/aha1740.h. + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called aha1740.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called aha1740.o. If you want to compile it as a + module, say M here and read . -Adaptec AIC7xxx chipset SCSI controller support +Adaptec AIC7xxx support CONFIG_SCSI_AIC7XXX - This driver supports all of Adaptec's PCI based SCSI controllers (not - the hardware RAID controllers though) as well as the aic7770 based - EISA and VLB SCSI controllers (the 274x and 284x series). This is - an Adaptec sponsored driver written by Justin Gibbs. It is intended - to replace the previous aic7xxx driver maintained by Doug Ledford since - Doug is no longer maintaining that driver. + This driver supports all of Adaptec's PCI based SCSI controllers + (not the hardware RAID controllers though) as well as the aic7770 + based EISA and VLB SCSI controllers (the 274x and 284x series). + This is an Adaptec sponsored driver written by Justin Gibbs. It is + intended to replace the previous aic7xxx driver maintained by Doug + Ledford since Doug is no longer maintaining that driver. + +Adaptec I2O RAID controllers +CONFIG_SCSI_DPT_I2O + This driver supports all of Adaptec's I2O based RAID controllers as + well as the DPT SmartRaid V cards. This is an Adaptec maintained + driver by Deanna Bonds. See . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + If you want to compile it as a module, say M here and read + . The module will be called + dpt_i2o.o. Default number of TCQ commands per device CONFIG_AIC7XXX_CMDS_PER_DEVICE @@ -5735,21 +6657,31 @@ Default: 253 -Initial Bus Reset Settle Delay +Delay in seconds after SCSI bus reset CONFIG_AIC7XXX_RESET_DELAY_MS The number of milliseconds to delay after an initial bus reset. The bus settle delay following all error recovery actions is dictated by the SCSI layer and is not affected by this value. - Default: 5000 (5 seconds) + Default: 15000 (15 seconds) + +Build Adapter Firmware with Kernel Build +CONFIG_AIC7XXX_BUILD_FIRMWARE + This option should only be enabled if you are modifying the firmware + source to the aic7xxx driver and wish to have the generated firmware + include files updated during a normal kernel build. The assembler + for the firmware requires lex and yacc or their equivalents, as well + as the db v1 library. You may have to install additional packages + or modify the assembler make file or the files it includes if your + build environment is different than that of the author. -Old Adaptec AIC7xxx chipset SCSI controller support +Old Adaptec AIC7xxx support CONFIG_SCSI_AIC7XXX_OLD - WARNING This driver is an older aic7xxx driver and is no longer under - active development. Adaptec, Inc. is writing a new driver to take the - place of this one, and it is recommended that whenever possible, people - should use the new Adaptec written driver instead of this one. This - driver will eventually be phased out entirely. + WARNING This driver is an older aic7xxx driver and is no longer + under active development. Adaptec, Inc. is writing a new driver to + take the place of this one, and it is recommended that whenever + possible, people should use the new Adaptec written driver instead + of this one. This driver will eventually be phased out entirely. This is support for the various aic7xxx based Adaptec SCSI controllers. These include the 274x EISA cards; 284x VLB cards; @@ -5772,47 +6704,47 @@ Information on the configuration options for this controller can be found by checking the help file for each of the available - configuration options. You should read drivers/scsi/aic7xxx_old/README.aic7xxx - at a minimum before contacting the maintainer with any questions. - The SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , can also be of great - help. + configuration options. You should read + at a minimum before + contacting the maintainer with any questions. The SCSI-HOWTO, + available from , can also + be of great help. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called aic7xxx_old.o. + say M here and read . The module + will be called aic7xxx_old.o. -Enable or Disable Tagged Command Queueing by default +Enable tagged command queueing (TCQ) by default CONFIG_AIC7XXX_OLD_TCQ_ON_BY_DEFAULT This option causes the aic7xxx driver to attempt to use Tagged - Command Queueing (TCQ) on all devices that claim to support it. + Command Queueing (TCQ) on all devices that claim to support it. TCQ is a feature of SCSI-2 which improves performance: the host adapter can send several SCSI commands to a device's queue even if - previous commands haven't finished yet. Because the device is + previous commands haven't finished yet. Because the device is intelligent, it can optimize its operations (like head positioning) - based on its own request queue. Not all devices implement this - correctly. + based on its own request queue. Not all devices implement this + correctly. If you say Y here, you can still turn off TCQ on troublesome devices - with the use of the tag_info boot parameter. See the file - drivers/scsi/README.aic7xxx for more information on that and other - aic7xxx setup commands. If this option is turned off, you may still - enable TCQ on known good devices by use of the tag_info boot + with the use of the tag_info boot parameter. See the file + for more information on that and + other aic7xxx setup commands. If this option is turned off, you may + still enable TCQ on known good devices by use of the tag_info boot parameter. If you are unsure about your devices then it is safest to say N here. - + However, TCQ can increase performance on some hard drives by as much as 50% or more, so it is recommended that if you say N here, you - should at least read the README.aic7xxx file so you will know how to - enable this option manually should your drives prove to be safe in - regards to TCQ. + should at least read the file so + you will know how to enable this option manually should your drives + prove to be safe in regards to TCQ. Conversely, certain drives are known to lock up or cause bus resets - when TCQ is enabled on them. If you have a Western Digital + when TCQ is enabled on them. If you have a Western Digital Enterprise SCSI drive for instance, then don't even bother to enable TCQ on it as the drive will become unreliable, and it will actually reduce performance. @@ -5830,7 +6762,7 @@ eventually have their command depth reduced, but is a waste of memory if all of your devices end up reducing this number down to a more reasonable figure. - + NOTE: Certain very broken drives are known to lock up when given more commands than they like to deal with. Quantum Fireball drives are the most common in this category. For the Quantum Fireball @@ -5847,14 +6779,14 @@ small amount of overhead to each and every SCSI command the aic7xxx driver handles, so if you aren't really interested in this information, it is best to leave it disabled. This will only work if - you also say Y to "/proc file system support", below. + you also say Y to "/proc file system support", below. If unsure, say N. -IBM ServeRAID Support +IBM ServeRAID support CONFIG_SCSI_IPS This is support for the IBM ServeRAID hardware RAID controllers. - See http://www.developer.ibm.com/welcome/netfinity/serveraid.html + See for more information. If this driver does not work correctly without modification please contact the author by email at ipslinux@us.ibm.com. @@ -5862,14 +6794,14 @@ You can build this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), but only a single instance may be loaded. If you want to compile it - as a module, say M here and read Documentation/modules.txt. The - module will be called ips.o. + as a module, say M here and read . + The module will be called ips.o. BusLogic SCSI support CONFIG_SCSI_BUSLOGIC This is support for BusLogic MultiMaster and FlashPoint SCSI Host Adapters. Consult the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , and the files + , and the files README.BusLogic and README.FlashPoint in drivers/scsi for more information. If this driver does not work correctly without modification, please contact the author, Leonard N. Zubkoff, by @@ -5878,8 +6810,8 @@ You can also build this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), but only a single instance may be loaded. If you want to compile it - as a module, say M here and read Documentation/modules.txt. The - module will be called BusLogic.o. + as a module, say M here and read . + The module will be called BusLogic.o. Omit BusLogic SCSI FlashPoint support CONFIG_SCSI_OMIT_FLASHPOINT @@ -5888,81 +6820,86 @@ substantial, so users of MultiMaster Host Adapters may wish to omit it. +Compaq Fibre Channel 64-bit/66Mhz HBA support +CONFIG_SCSI_CPQFCTS + Say Y here to compile in support for the Compaq StorageWorks Fibre + Channel 64-bit/66Mhz Host Bus Adapter. + DMX3191D SCSI support CONFIG_SCSI_DMX3191D This is support for Domex DMX3191D SCSI Host Adapters. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called dmx3191d.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called dmx3191d.o. If you want to compile it as + a module, say M here and read . DTC3180/3280 SCSI support CONFIG_SCSI_DTC3280 - This is support for DTC 3180/3280 SCSI Host Adapters. Please read + This is support for DTC 3180/3280 SCSI Host Adapters. Please read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , and the file - drivers/scsi/README.dtc3x80. + , and the file + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called dtc.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called dtc.o. If you want to compile it as a + module, say M here and read . EATA-DMA [Obsolete] (DPT, NEC, AT&T, SNI, AST, Olivetti, Alphatronix) support CONFIG_SCSI_EATA_DMA This is support for the EATA-DMA protocol compliant SCSI Host Adapters like the SmartCache III/IV, SmartRAID controller families - and the DPT PM2011B and PM2012B controllers. + and the DPT PM2011B and PM2012B controllers. - Note that this driver is obsolete; if you have one of the above SCSI - Host Adapters, you should normally say N here and Y to "EATA - ISA/EISA/PCI support", below. Please read the SCSI-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + Note that this driver is obsolete; if you have one of the above + SCSI Host Adapters, you should normally say N here and Y to "EATA + ISA/EISA/PCI support", below. Please read the SCSI-HOWTO, available + from . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called eata_dma.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called eata_dma.o. If you want to compile it as + a module, say M here and read . EATA-PIO (old DPT PM2001, PM2012A) support CONFIG_SCSI_EATA_PIO This driver supports all EATA-PIO protocol compliant SCSI Host - Adapters like the DPT PM2001 and the PM2012A. EATA-DMA compliant + Adapters like the DPT PM2001 and the PM2012A. EATA-DMA compliant host adapters could also use this driver but are discouraged from doing so, since this driver only supports hard disks and lacks - numerous features. You might want to have a look at the SCSI-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + numerous features. You might want to have a look at the SCSI-HOWTO, + available from . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called eata_pio.o. + say M here and read . The module + will be called eata_pio.o. UltraStor 14F/34F support CONFIG_SCSI_U14_34F This is support for the UltraStor 14F and 34F SCSI-2 host adapters. - The source at drivers/scsi/u14-34f.c contains some information about - this hardware. If the driver doesn't work out of the box, you may - have to change some settings in drivers/scsi/u14-34f.c. Read the - SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Note that there is also + The source at contains some + information about this hardware. If the driver doesn't work out of + the box, you may have to change some settings in + . Read the SCSI-HOWTO, available from + . Note that there is also another driver for the same hardware: "UltraStor SCSI support", - below. You should say Y to both only if you want 24F support as + below. You should say Y to both only if you want 24F support as well. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called u14-34f.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called u14-34f.o. If you want to compile it as a + module, say M here and read . enable elevator sorting CONFIG_SCSI_U14_34F_LINKED_COMMANDS - This option enables elevator sorting for all probed SCSI disks and - CDROMs. It definitely reduces the average seek distance when doing + This option enables elevator sorting for all probed SCSI disks and + CD-ROMs. It definitely reduces the average seek distance when doing random seeks, but this does not necessarily result in a noticeable performance improvement: your mileage may vary... - + The safe answer is N. maximum number of queued commands @@ -5981,7 +6918,7 @@ other adapters based on the Future Domain chipsets (Quantum ISA-200S, ISA-250MG; Adaptec AHA-2920A; and at least one IBM board). It is explained in section 3.7 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . NOTE: Newer Adaptec AHA-2920C boards use the Adaptec AIC-7850 chip and should use the aic7xxx driver ("Adaptec AIC7xxx chipset SCSI @@ -5991,7 +6928,7 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called fdomain.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . Future Domain MCS-600/700 SCSI support CONFIG_SCSI_FD_MCS @@ -6004,71 +6941,90 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called fd_mcs.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . Generic NCR5380/53c400 SCSI support CONFIG_SCSI_GENERIC_NCR5380 This is the generic NCR family of SCSI controllers, not to be - confused with the NCR 53c7 or 8xx controllers. It is explained in + confused with the NCR 53c7 or 8xx controllers. It is explained in section 3.8 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . If it doesn't work out + . If it doesn't work out of the box, you may have to change some settings in - drivers/scsi/g_NCR5380.h. + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called g_NCR5380.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + The module will be called g_NCR5380.o. If you want to compile it as + a module, say M here and read . Enable NCR53c400 extensions CONFIG_SCSI_GENERIC_NCR53C400 - This enables certain optimizations for the NCR53c400 SCSI cards. You - might as well try it out. Note that this driver will only probe for - the Trantor T130B in its default configuration; you might have to - pass a command line option to the kernel at boot time if it doesn't - detect your card. See the file drivers/scsi/README.g_NCR5380 for - details. + This enables certain optimizations for the NCR53c400 SCSI cards. + You might as well try it out. Note that this driver will only probe + for the Trantor T130B in its default configuration; you might have + to pass a command line option to the kernel at boot time if it does + not detect your card. See the file + for details. +# Choice: ncr5380 NCR5380/53c400 mapping method (use Port for T130B) CONFIG_SCSI_G_NCR5380_PORT The NCR5380 and NCR53c400 SCSI controllers come in two varieties: port or memory mapped. You should know what you have. The most common card, Trantor T130B, uses port mapped mode. +NCR Dual 700 MCA SCSI support +CONFIG_SCSI_NCR_D700 + This is a driver for the MicroChannel Dual 700 card produced by + NCR and commonly used in 345x/35xx/4100 class machines. It always + tries to negotiate sync and uses tag command queueing. + + Unless you have an NCR manufactured machine, the chances are that + you do not have this SCSI card, so say N. + +HP LASI SCSI support for 53c700 +CONFIG_SCSI_LASI700 + This is a driver for the lasi baseboard in some parisc machines + which is based on the 53c700 chip. Will also support LASI subsystems + based on the 710 chip using 700 emulation mode. + + Unless you know you have a 53c700 or 53c710 based lasi, say N here + NCR53c7,8xx SCSI support CONFIG_SCSI_NCR53C7xx This is a driver for the 53c7 and 8xx NCR family of SCSI - controllers, not to be confused with the NCR 5380 controllers. It is - explained in section 3.8 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . If it doesn't work out + controllers, not to be confused with the NCR 5380 controllers. It + is explained in section 3.8 of the SCSI-HOWTO, available from + . If it doesn't work out of the box, you may have to change some settings in - drivers/scsi/53c7,8xx.h. Please read drivers/scsi/README.ncr53c7xx - for the available boot time command line options. + . Please read + for the available boot time + command line options. Note: there is another driver for the 53c8xx family of controllers - ("NCR53C8XX SCSI support" below). If you want to use them both, you + ("NCR53C8XX SCSI support" below). If you want to use them both, you need to say M to both and build them as modules, but only one may be active at a time. If you have a 53c8xx board, it's better to use the other driver. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called 53c7,8xx.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called 53c7,8xx.o. If you want to compile it as + a module, say M here and read . -always negotiate synchronous transfers +Always negotiate synchronous transfers CONFIG_SCSI_NCR53C7xx_sync In general, this is good; however, it is a bit dangerous since there are some broken SCSI devices out there. Take your chances. Safe bet is N. -allow FAST-SCSI [10MHz] +Allow FAST-SCSI [10MHz] CONFIG_SCSI_NCR53C7xx_FAST This will enable 10MHz FAST-SCSI transfers with your host adapter. Some systems have problems with that speed, so it's safest to say N here. -allow DISCONNECT +Allow DISCONNECT CONFIG_SCSI_NCR53C7xx_DISCONNECT This enables the disconnect/reconnect feature of the NCR SCSI controller. When you say Y here, a slow SCSI device will not lock @@ -6083,7 +7039,7 @@ NCR53C8XX SCSI support CONFIG_SCSI_NCR53C8XX This is the BSD ncr driver adapted to Linux for the NCR53C8XX family - of PCI-SCSI controllers. This driver supports parity checking, + of PCI-SCSI controllers. This driver supports parity checking, tagged command queuing and fast synchronous data transfers up to 80 MB/s with wide FAST-40 LVD devices and controllers. @@ -6091,12 +7047,13 @@ option "SYM53C8XX SCSI support", below. Note: there is yet another driver for the 53c8xx family of - controllers ("NCR53c7,8xx SCSI support" above). If you want to use + controllers ("NCR53c7,8xx SCSI support" above). If you want to use them both, you need to say M to both and build them as modules, but - only one may be active at a time. If you have a 53c8xx board, you + only one may be active at a time. If you have a 53c8xx board, you probably do not want to use the "NCR53c7,8xx SCSI support". - Please read drivers/scsi/README.ncr53c8xx for more information. + Please read for more + information. SYM53C8XX SCSI support CONFIG_SCSI_SYM53C8XX @@ -6105,7 +7062,7 @@ feature of the SYM53C896. Older versions of the 53C8XX chips are not supported by this - driver. If your system uses either a 810 rev. < 16, a 815, or a 825 + driver. If your system uses either a 810 rev. < 16, a 815, or a 825 rev. < 16 PCI SCSI processor, you must use the generic NCR53C8XX driver ("NCR53C8XX SCSI support" above) or configure both the NCR53C8XX and this SYM53C8XX drivers either as module or linked to @@ -6114,32 +7071,33 @@ When both drivers are linked into the kernel, the SYM53C8XX driver is called first at initialization and you can use the 'excl=ioaddr' driver boot option to exclude attachment of adapters by the - SYM53C8XX driver. For example, entering + SYM53C8XX driver. For example, entering 'sym53c8xx=excl:0xb400,excl=0xc000' at the lilo prompt prevents adapters at io address 0xb400 and 0xc000 from being attached by the SYM53C8XX driver, thus allowing the NCR53C8XX driver to attach them. The 'excl' option is also supported by the NCR53C8XX driver. - - Please read drivers/scsi/README.ncr53c8xx for more information. -synchronous data transfers frequency + Please read for more + information. + +Synchronous transfer frequency in MHz CONFIG_SCSI_NCR53C8XX_SYNC The SCSI Parallel Interface-2 Standard defines 5 classes of transfer - rates: FAST-5, FAST-10, FAST-20, FAST-40 and FAST-80. The numbers are - respectively the maximum data transfer rates in mega-transfers per - second for each class. For example, a FAST-20 Wide 16 device is able - to transfer data at 20 million 16 bit packets per second for a total - rate of 40 MB/s. + rates: FAST-5, FAST-10, FAST-20, FAST-40 and FAST-80. The numbers + are respectively the maximum data transfer rates in mega-transfers + per second for each class. For example, a FAST-20 Wide 16 device is + able to transfer data at 20 million 16 bit packets per second for a + total rate of 40 MB/s. You may specify 0 if you want to only use asynchronous data transfers. This is the safest and slowest option. Otherwise, specify a value between 5 and 80, depending on the capability of your SCSI - controller. The higher the number, the faster the data transfer. + controller. The higher the number, the faster the data transfer. Note that 80 should normally be ok since the driver decreases the value automatically according to the controller's capabilities. Your answer to this question is ignored for controllers with NVRAM, - since the driver will get this information from the user set-up. It + since the driver will get this information from the user set-up. It also can be overridden using a boot setup option, as follows (example): 'ncr53c8xx=sync:12' will allow the driver to negotiate for FAST-20 synchronous data transfer (20 mega-transfers per @@ -6153,18 +7111,18 @@ There is no safe option other than using good cabling, right terminations and SCSI conformant devices. -use normal IO +Use normal IO CONFIG_SCSI_NCR53C8XX_IOMAPPED If you say Y here, the driver will use normal IO, as opposed to memory mapped IO. Memory mapped IO has less latency than normal IO and works for most Intel-based hardware. Under Linux/Alpha only normal IO is currently supported by the driver and so, this option - has no effect on those systems. + has no effect on those systems. The normal answer therefore is N; try Y only if you encounter SCSI related problems. -not allow targets to disconnect +Not allow targets to disconnect CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT This option is only provided for safety if you suspect some SCSI device of yours to not support properly the target-disconnect @@ -6172,7 +7130,7 @@ not allow targets to disconnect is not reasonable if there is more than 1 device on a SCSI bus. The normal answer therefore is N. -default tagged command queue depth +Default tagged command queue depth CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS "Tagged command queuing" is a feature of SCSI-2 which improves performance: the host adapter can send several SCSI commands to a @@ -6183,33 +7141,33 @@ feature, enter 0 or 1 here (it doesn't matter which). The default value is 8 and should be supported by most hard disks. - This value can be overridden from the boot command line using the + This value can be overridden from the boot command line using the 'tags' option as follows (example): 'ncr53c8xx=tags:4/t2t3q16/t0u2q10' will set default queue depth to 4, set queue depth to 16 for target 2 and target 3 on controller 0 and set queue depth to 10 for target 0 / lun 2 on controller 1. - The normal answer therefore is to go with the default 8 and to use - a boot command line option for devices that need to use a different + The normal answer therefore is to go with the default 8 and to use + a boot command line option for devices that need to use a different command queue depth. There is no safe option other than using good SCSI devices. -maximum number of queued commands +Maximum number of queued commands CONFIG_SCSI_NCR53C8XX_MAX_TAGS This option allows you to specify the maximum number of commands that can be queued to any device, when tagged command queuing is possible. The default value is 32. Minimum is 2, maximum is 64. - Modern hard disks are able to support 64 tags and even more, but + Modern hard disks are able to support 64 tags and even more, but do not seem to be faster when more than 32 tags are being used. - + So, the normal answer here is to go with the default value 32 unless you are using very large hard disks with large cache (>= 1 MB) that are able to take advantage of more than 32 tagged commands. There is no safe option and the default answer is recommended. -assume boards are SYMBIOS compatible (EXPERIMENTAL) +Assume boards are SYMBIOS compatible CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT This option allows you to enable some features depending on GPIO wiring. These General Purpose Input/Output pins can be used for @@ -6218,7 +7176,7 @@ controller LED and GPIO3 bit as a flag indicating singled-ended/differential interface. The Tekram DC-390U/F boards uses a different GPIO wiring. - + Your answer to this question is ignored if all your controllers have NVRAM, since the driver is able to detect the board type from the NVRAM format. @@ -6227,16 +7185,16 @@ use BIOS and drivers from SYMBIOS, you would want to say Y here, otherwise N. N is the safe answer. -enable profiling statistics gathering +Enable traffic profiling CONFIG_SCSI_NCR53C8XX_PROFILE This option allows you to enable profiling information gathering. - These statistics are not very accurate due to the low frequency - of the kernel clock (100 Hz on i386) and have performance impact + These statistics are not very accurate due to the low frequency + of the kernel clock (100 Hz on i386) and have performance impact on systems that use very fast devices. The normal answer therefore is N. -include support for the NCR PQS/PDS SCSI card +Include support for the NCR PQS/PDS SCSI card CONFIG_SCSI_NCR53C8XX_PQS_PDS Say Y here if you have a special SCSI adapter produced by NCR corporation called a PCI Quad SCSI or PCI Dual SCSI. You do not need @@ -6249,23 +7207,24 @@ IBMMCA SCSI support CONFIG_SCSI_IBMMCA This is support for the IBM SCSI adapter found in many of the PS/2 - series computers. These machines have an MCA bus, so you need to - answer Y to "MCA support" as well and read Documentation/mca.txt. + series computers. These machines have an MCA bus, so you need to + answer Y to "MCA support" as well and read + . If the adapter isn't found during boot (a common problem for models 56, 57, 76, and 77) you'll need to use the 'ibmmcascsi=' kernel option, where is the id of the SCSI subsystem (usually 7, but - if that doesn't work check your reference diskette). Owners of model - 95 with a LED-matrix-display can in addition activate some activity - info like under OS/2, but more informative, by setting - 'ibmmcascsi=display' as an additional kernel parameter. Try "man + if that doesn't work check your reference diskette). Owners of + model 95 with a LED-matrix-display can in addition activate some + activity info like under OS/2, but more informative, by setting + 'ibmmcascsi=display' as an additional kernel parameter. Try "man bootparam" or see the documentation of your boot loader about how to pass options to the kernel. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called ibmmca.o. + say M here and read . The module + will be called ibmmca.o. Standard SCSI-order CONFIG_IBMMCA_SCSI_ORDER_STANDARD @@ -6282,7 +7241,7 @@ highest priority. This must therefore be the disk with the highest SCSI-id (e.g. 6) and not the one with the lowest! IBM-BIOS kept the original definition of the SCSI-standard as also industrial- and - process-control-machines, like VME-CPUs running under realtime-OSs + process-control-machines, like VME-CPUs running under realtime-OSes (e.g. LynxOS, OS9) do. If you like to run Linux on your MCA-machine with the same @@ -6311,96 +7270,96 @@ you know that one of your older devices needs it; N is the safe answer. -NCR 53C9x MCA support +NCR MCA 53C9x SCSI support CONFIG_SCSI_MCA_53C9X - Some Microchannel machines, notably the NCR 35xx line, use a SCSI + Some MicroChannel machines, notably the NCR 35xx line, use a SCSI controller based on the NCR 53C94. This driver will allow use of - the controller on the 3550, and very possibly others. + the controller on the 3550, and very possibly others. If you want to compile this as a module (= code which can be inserted and removed from the running kernel whenever you want), say - M here and read Documentation/modules.txt. The module will be called - mca_53c9x.o. - + M here and read . The module will + be called mca_53c9x.o. + Always IN2000 SCSI support CONFIG_SCSI_IN2000 - This is support for an ISA bus SCSI host adapter. You'll find more - information in drivers/scsi/in2000.readme. If it doesn't work out of - the box, you may have to change the jumpers for IRQ or address - selection. + This is support for an ISA bus SCSI host adapter. You'll find more + information in . If it doesn't work + out of the box, you may have to change the jumpers for IRQ or + address selection. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called in2000.o. + say M here and read . The module + will be called in2000.o. Initio 91XXU(W) SCSI support CONFIG_SCSI_INITIO - This is support for the Initio 91XXU(W) SCSI host adapter. Please + This is support for the Initio 91XXU(W) SCSI host adapter. Please read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called initio.o + say M here and read . The module + will be called initio.o. PAS16 SCSI support CONFIG_SCSI_PAS16 - This is support for a SCSI host adapter. It is explained in section + This is support for a SCSI host adapter. It is explained in section 3.10 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . If it doesn't work out + . If it doesn't work out of the box, you may have to change some settings in - drivers/scsi/pas16.h. - + . + This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called pas16.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called pas16.o. If you want to compile it as a + module, say M here and read . Initio INI-A100U2W SCSI support CONFIG_SCSI_INIA100 - This is support for the Initio INI-A100U2W SCSI host adapter. Please - read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + This is support for the Initio INI-A100U2W SCSI host adapter. + Please read the SCSI-HOWTO, available from + . - If you want to compile this as a module ( = code which can be + If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called a100u2w.o + say M here and read . The module + will be called a100u2w.o. PCI2000 support CONFIG_SCSI_PCI2000 This is support for the PCI2000I EIDE interface card which acts as a - SCSI host adapter. Please read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + SCSI host adapter. Please read the SCSI-HOWTO, available from + . This driver is also available as a module called pci2000.o ( = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + whenever you want). If you want to compile it as a module, say M + here and read . PCI2220i support CONFIG_SCSI_PCI2220I This is support for the PCI2220i EIDE interface card which acts as a - SCSI host adapter. Please read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + SCSI host adapter. Please read the SCSI-HOWTO, available from + . This driver is also available as a module called pci2220i.o ( = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + whenever you want). If you want to compile it as a module, say M + here and read . PSI240i support CONFIG_SCSI_PSI240I This is support for the PSI240i EIDE interface card which acts as a - SCSI host adapter. Please read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + SCSI host adapter. Please read the SCSI-HOWTO, available from + . This driver is also available as a module called psi240i.o ( = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + whenever you want). If you want to compile it as a module, say M + here and read . Qlogic FAS SCSI support CONFIG_SCSI_QLOGIC_FAS @@ -6413,31 +7372,32 @@ SCSI support"), below. Information about this driver is contained in - drivers/scsi/README.qlogicfas. You should also read the SCSI-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + . You should also read the + SCSI-HOWTO, available from + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called qlogicfas.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . -Qlogic ISP SCSI support (EXPERIMENTAL) +Qlogic ISP SCSI support CONFIG_SCSI_QLOGIC_ISP This driver works for all QLogic PCI SCSI host adapters (IQ-PCI, - IQ-PCI-10, IQ_PCI-D) except for the PCI-basic card. (This latter - card is supported by the "AM53/79C974 PCI SCSI" driver). + IQ-PCI-10, IQ_PCI-D) except for the PCI-basic card. (This latter + card is supported by the "AM53/79C974 PCI SCSI" driver.) If you say Y here, make sure to choose "BIOS" at the question "PCI access mode". - Please read the file drivers/scsi/README.qlogicisp. You should also - read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + Please read the file . You + should also read the SCSI-HOWTO, available from + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called qlogicisp.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + The module will be called qlogicisp.o. If you want to compile it as + a module, say M here and read . Qlogic ISP FC SCSI support CONFIG_SCSI_QLOGIC_FC @@ -6445,8 +7405,8 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called qlogicfc.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + The module will be called qlogicfc.o. If you want to compile it as + a module, say M here and read . Qlogic QLA 1280 SCSI support CONFIG_SCSI_QLOGIC_1280 @@ -6455,61 +7415,63 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called qla1280.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . Seagate ST-02 and Future Domain TMC-8xx SCSI support CONFIG_SCSI_SEAGATE These are 8-bit SCSI controllers; the ST-01 is also supported by - this driver. It is explained in section 3.9 of the SCSI-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . If it + this driver. It is explained in section 3.9 of the SCSI-HOWTO, + available from . If it doesn't work out of the box, you may have to change some settings in - drivers/scsi/seagate.h. + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called seagate.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called seagate.o. If you want to compile it as a + module, say M here and read . Trantor T128/T128F/T228 SCSI support CONFIG_SCSI_T128 This is support for a SCSI host adapter. It is explained in section 3.11 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . If it doesn't work out + . If it doesn't work out of the box, you may have to change some settings in - drivers/scsi/t128.h. Note that Trantor was purchased by Adaptec, and - some former Trantor products are being sold under the Adaptec name. + . Note that Trantor was purchased by + Adaptec, and some former Trantor products are being sold under the + Adaptec name. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called t128.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called t128.o. If you want to compile it as a + module, say M here and read . UltraStor SCSI support CONFIG_SCSI_ULTRASTOR This is support for the UltraStor 14F, 24F and 34F SCSI-2 host - adapter family. This driver is explained in section 3.12 of the + adapter family. This driver is explained in section 3.12 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . If it doesn't work out + . If it doesn't work out of the box, you may have to change some settings in - drivers/scsi/ultrastor.h. - + . + Note that there is also another driver for the same hardware: "UltraStor 14F/34F support", above. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called ultrastor.o. + say M here and read . The module + will be called ultrastor.o. 7000FASST SCSI support CONFIG_SCSI_7000FASST This driver supports the Western Digital 7000 SCSI host adapter - family. Some information is in the source: drivers/scsi/wd7000.c. + family. Some information is in the source: + . This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you - want). The module will be called wd7000.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + inserted in and removed from the running kernel whenever you want). + The module will be called wd7000.o. If you want to compile it as a + module, say M here and read . ACARD SCSI support CONFIG_SCSI_ACARD @@ -6518,40 +7480,41 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called atp870u.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support CONFIG_SCSI_EATA - This driver supports all EATA/DMA-compliant SCSI host adapters. DPT - ISA and all EISA i/o addresses are probed looking for the "EATA" - signature. If you chose "BIOS" at the question "PCI access mode", + This driver supports all EATA/DMA-compliant SCSI host adapters. DPT + ISA and all EISA I/O addresses are probed looking for the "EATA" + signature. If you chose "BIOS" at the question "PCI access mode", the addresses of all the PCI SCSI controllers reported by the PCI subsystem are probed as well. - You want to read the start of drivers/scsi/eata.c and the + You want to read the start of and the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Note that there is also another driver for the same hardware - available: "EATA-DMA support". You should say Y to only one of them. + available: "EATA-DMA [Obsolete] (DPT, NEC, AT&T, SNI, AST, Olivetti, + Alphatronix) support". You should say Y to only one of them. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called eata.o. + say M here and read . The module + will be called eata.o. -enable tagged command queuing +enable tagged command queueing CONFIG_SCSI_EATA_TAGGED_QUEUE This is a feature of SCSI-2 which improves performance: the host adapter can send several SCSI commands to a device's queue even if previous commands haven't finished yet. Most EATA adapters negotiate this feature automatically with the device, even if your answer is N. The safe answer is N. - + enable elevator sorting CONFIG_SCSI_EATA_LINKED_COMMANDS - This option enables elevator sorting for all probed SCSI disks and - CDROMs. It definitely reduces the average seek distance when doing + This option enables elevator sorting for all probed SCSI disks and + CD-ROMs. It definitely reduces the average seek distance when doing random seeks, but this does not necessarily result in a noticeable performance improvement: your mileage may vary... The safe answer is N. @@ -6567,21 +7530,21 @@ NCR53c406a SCSI support CONFIG_SCSI_NCR53C406A - This is support for the NCR53c406a SCSI host adapter. For user - configurable parameters, check out drivers/scsi/NCR53c406.c in the - kernel source. Also read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + This is support for the NCR53c406a SCSI host adapter. For user + configurable parameters, check out + in the kernel source. Also read the SCSI-HOWTO, available from + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called NCR53c406.o. + say M here and read . The module + will be called NCR53c406.o. -Symbios Logic sym53c416 support +Symbios 53c416 SCSI support CONFIG_SCSI_SYM53C416 This is support for the sym53c416 SCSI host adapter, the SCSI adapter that comes with some HP scanners. This driver requires that - the sym53c416 is configured first using some sort of pnp + the sym53c416 is configured first using some sort of PnP configuration program (e.g. isapnp) or by a PnP aware BIOS. If you are using isapnp then you need to compile this driver as a module and then load it using insmod after isapnp has run. The parameters @@ -6593,8 +7556,8 @@ There is support for up to four adapters. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and - read Documentation/modules.txt. The module will be called - sym53c416.o. + read . The module will be called + sym53c416.o. Simple 53c710 SCSI support (Compaq, NCR machines) CONFIG_SCSI_SIM710 @@ -6603,46 +7566,46 @@ More complex drivers for this chip are available ("NCR53c7,8xx SCSI support", above), but they require that the scsi chip be able to do DMA block moves between memory and on-chip registers, which can - cause problems under certain conditions. This driver is designed to + cause problems under certain conditions. This driver is designed to avoid these problems and is intended to work with any Intel machines using 53c710 chips, including various Compaq and NCR machines. Please read the comments at the top of the file - drivers/scsi/sim710.c for more information. + for more information. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called sim710.o. + say M here and read . The module + will be called sim710.o. -Tekram DC390(T) and Am53/79C974 (PCscsi) SCSI support +Tekram DC390(T) and Am53/79C974 SCSI support CONFIG_SCSI_DC390T This driver supports PCI SCSI host adapters based on the Am53C974A chip, e.g. Tekram DC390(T), DawiControl 2974 and some onboard PCscsi/PCnet (Am53/79C974) solutions. - Documentation can be found in drivers/scsi/README.tmscsim. - + Documentation can be found in . + Note that this driver does NOT support Tekram DC390W/U/F, which are based on NCR/Symbios chips. Use "NCR53C8XX SCSI support" for those. Also note that there is another generic Am53C974 driver, - "AM53/79C974 PCI SCSI support" below. You can pick either one. + "AM53/79C974 PCI SCSI support" below. You can pick either one. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called tmscsim.o. + say M here and read . The module + will be called tmscsim.o. Omit support for other Am53/79C974 based SCSI adapters CONFIG_SCSI_DC390T_NOGENSUPP If you say N here, the DC390(T) SCSI driver relies on the DC390 EEPROM to get initial values for its settings, such as speed, - termination, etc. If it can't find this EEPROM, it will use defaults - or the user supplied boot/module parameters. For details on driver - configuration see drivers/scsi/README.tmscsim. + termination, etc. If it can't find this EEPROM, it will use + defaults or the user supplied boot/module parameters. For details + on driver configuration see . If you say Y here and if no EEPROM is found, the driver gives up and - thus only supports Tekram DC390(T) adapters. This can be useful if + thus only supports Tekram DC390(T) adapters. This can be useful if you have a DC390(T) and another Am53C974 based adapter, which, for some reason, you want to drive with the other AM53C974 driver. @@ -6650,40 +7613,42 @@ AM53/79C974 PCI SCSI support CONFIG_SCSI_AM53C974 - This is support for the AM53/79C974 SCSI host adapters. Please read - drivers/scsi/README.AM53C974 for details. Also, the SCSI-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto , is for - you. + This is support for the AM53/79C974 SCSI host adapters. Please read + for details. Also, the + SCSI-HOWTO, available from + , is for you. Note that there is another driver for AM53C974 based adapters: - "Tekram DC390(T) and Am53/79C974 (PCscsi) SCSI support", above. You + "Tekram DC390(T) and Am53/79C974 (PCscsi) SCSI support", above. You can pick either one. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called AM53C974.o. + say M here and read . The module + will be called AM53C974.o. AMI MegaRAID support CONFIG_SCSI_MEGARAID This driver supports the AMI MegaRAID 418, 428, 438, 466, 762, 490 - and 467 SCSI host adapters. + and 467 SCSI host adapters. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called megaraid.o. + say M here and read . The module + will be called megaraid.o. -GDT SCSI Disk Array Controller support +Intel/ICP (former GDT SCSI Disk Array) RAID Controller Support CONFIG_SCSI_GDTH - This is a driver for all SCSI Disk Array Controllers (EISA/ISA/PCI) - manufactured by ICP vortex. It is documented in the kernel source in - drivers/scsi/gdth.c and drivers/scsi/gdth.h. + Formerly called GDT SCSI Disk Array Controller Support. + + This is a driver for RAID/SCSI Disk Array Controllers (EISA/ISA/PCI) + manufactured by Intel/ICP vortex (an Intel Company). It is documented + in the kernel source in drivers/scsi/gdth.c and drivers/scsi/gdth.h. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . IOMEGA parallel port (ppa - older drives) CONFIG_SCSI_PPA @@ -6700,17 +7665,17 @@ newer drives)", below. For more information about this driver and how to use it you should - read the file drivers/scsi/README.ppa. You should also read the - SCSI-HOWTO, which is available from - http://www.linuxdoc.org/docs.html#howto . If you use this driver, + read the file . You should also read + the SCSI-HOWTO, which is available from + . If you use this driver, you will still be able to use the parallel port for other tasks, such as a printer; it is safe to compile both drivers into the kernel. This driver is also available as a module which can be inserted in - and removed from the running kernel whenever you want. To compile + and removed from the running kernel whenever you want. To compile this driver as a module, say M here and read - Documentation/modules.txt. The module will be called ppa.o. + . The module will be called ppa.o. IOMEGA parallel port (imm - newer drives) CONFIG_SCSI_IMM @@ -6727,17 +7692,17 @@ here and Y to "IOMEGA Parallel Port (ppa - older drives)", above. For more information about this driver and how to use it you should - read the file drivers/scsi/README.ppa. You should also read the - SCSI-HOWTO, which is available from - http://www.linuxdoc.org/docs.html#howto . If you use this driver, + read the file . You should also read + the SCSI-HOWTO, which is available from + . If you use this driver, you will still be able to use the parallel port for other tasks, such as a printer; it is safe to compile both drivers into the kernel. This driver is also available as a module which can be inserted in - and removed from the running kernel whenever you want. To compile + and removed from the running kernel whenever you want. To compile this driver as a module, say M here and read - Documentation/modules.txt. The module will be called imm.o. + . The module will be called imm.o. Force the Iomega ZIP drivers to use EPP-16 CONFIG_SCSI_IZIP_EPP16 @@ -6749,7 +7714,7 @@ so we have to control the state of the chipset's FIFO queue every now and then to avoid data loss. This will be done if you say Y here. - + Generally, saying Y is the safe option and slows things down a bit. Assume slow parallel port control register @@ -6764,67 +7729,7 @@ Generally, saying N is fine. -Parallel port SCSI device support -CONFIG_PPSCSI - There are many external CD-ROM and disk devices that connect through - your computer's parallel port. Lots of them are actually SCSI - devices using a parallel port SCSI adapter. This option enables the - ppSCSI subsystem which contains drivers for many of these external - drives. You may also want to look at CONFIG_PARIDE (Parallel port - IDE device support). - - If you built ppSCSI support into your kernel, you may still build - the individual protocol modules and high-level drivers as loadable - modules. If you build this support as a module, it will be called - ppscsi.o. - - To use the ppSCSI support, you must say Y or M here and also to at - least one protocol driver (e.g. "Shuttle EPST adapter", "Iomega VPI0 - adapter", "Shining ScarSCI adapter" etc.). - -Adaptec APA-348 adapter -CONFIG_PPSCSI_T348 - This option enables support for the APA-348 adapter from Adaptec - (also known as Trantor T348). If you build this as a module it will - be called t348.o. - -Adaptec APA-358 adapter -CONFIG_PPSCSI_T358 - This option enables support for the APA-358 adapter from Adaptec - (also known as Trantor T358). If you build this as a module it will - be called t358.o. - -Iomega VPI0 adapter -CONFIG_PPSCSI_VPI0 - This option enables support for the Iomega VPI0 adapter found in the - original ZIP-100 drives and the Jaz Traveller. If you build this as - a module it will be called vpi0.o. - -OnSpec 90c26 adapter -CONFIG_PPSCSI_ONSCSI - This option enables support for the OnSpec 90c26 in its SCSI adapter - mode. If you build this as a module it will be called onscsi.o. - -Shining SparSCI adapter -CONFIG_PPSCSI_SPARCSI - This option enables support for the WBS-11A parallel port SCSI - adapter. This adapter has been marketed by LinkSys as the - "ParaSCSI+" and by Shining Technologies as the "SparCSI". If you - build this as a module it will be called sparcsi.o. - -Shuttle EPSA-2 adapter -CONFIG_PPSCSI_EPSA2 - This option enables support for the Shuttle Technologies EPSA2 - parallel port SCSI adapter. EPAS2 is a predecessor to the EPST. If - you build this as a module it will be called epsa2.o. - -Shuttle EPST adapter -CONFIG_PPSCSI_EPST - This option enables support for the Shuttle Technologies EPST - parallel port SCSI adapter. If you build this as a module is will - be called epst.o. - -SCSI Debug host simulator. (EXPERIMENTAL) +SCSI debugging host simulator CONFIG_SCSI_DEBUG This is a host adapter simulator that can be programmed to simulate a large number of conditions that could occur on a real bus. The @@ -6833,7 +7738,7 @@ important data. This is primarily of use to people trying to debug the middle and upper layers of the SCSI subsystem. If unsure, say N. -Fibre Channel support and FC4 SCSI support +Fibre Channel and FC4 SCSI support CONFIG_FC4 Fibre Channel is a high speed serial protocol mainly used to connect large storage devices to the computer; it is compatible with @@ -6849,7 +7754,7 @@ If unsure, say N. -Sun SOC +Sun SOC/Sbus CONFIG_FC4_SOC Serial Optical Channel is an interface card with one or two Fibre Optic ports, each of which can be connected to a disk array. Note @@ -6859,7 +7764,7 @@ This support is also available as a module called soc.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . Sun SOC+ (aka SOCAL) CONFIG_FC4_SOCAL @@ -6872,16 +7777,16 @@ This support is also available as a module called socal.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . SparcSTORAGE Array 100 and 200 series CONFIG_SCSI_PLUTO - If you never bought a disk array made by Sun, go with N. + If you never bought a disk array made by Sun, go with N. This support is also available as a module called pluto.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . Sun Enterprise Network Array (A5000 and EX500) CONFIG_SCSI_FCAL @@ -6893,14 +7798,14 @@ This support is also available as a module called fcal.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. If unsure, say N. + here and read . If unsure, say N. -AcornSCSI support +Acorn SCSI card (aka30) support CONFIG_SCSI_ACORNSCSI_3 This enables support for the Acorn SCSI card (aka30). If you have an Acorn system with one of these, say Y. If unsure, say N. -Acorn SCSI tagged queue support +Support SCSI 2 Tagged queueing CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE Say Y here to enable tagged queuing support on the Acorn SCSI card. @@ -6909,7 +7814,7 @@ previous commands haven't finished yet. Some SCSI devices don't implement this properly, so the safe answer is N. -Acorn SCSI Synchronous transfers support +Support SCSI 2 Synchronous Transfers CONFIG_SCSI_ACORNSCSI_SYNC Say Y here to enable synchronous transfer negotiation with all targets on the Acorn SCSI card. @@ -6917,6 +7822,16 @@ In general, this improves performance; however some SCSI devices don't implement it properly, so the safe answer is N. +ARXE SCSI support +CONFIG_SCSI_ARXESCSI + Around 1991, Arxe Systems Limited released a high density floppy + disc interface for the Acorn Archimedes range, to allow the use of + HD discs from the then new A5000 on earlier models. This interface + was either sold on its own or with an integral SCSI controller. + Technical details on this NCR53c94-based device are available at + + Say Y here to compile in support for the SCSI controller. + Oak SCSI support CONFIG_SCSI_OAK1 This enables support for the Oak SCSI card. If you have an Acorn @@ -6927,7 +7842,7 @@ This enables support for the Cumana SCSI I card. If you have an Acorn system with one of these, say Y. If unsure, say N. -Cumana SCSI II support (EXPERIMENTAL) +Cumana SCSI II support CONFIG_SCSI_CUMANA_2 This enables support for the Cumana SCSI II card. If you have an Acorn system with one of these, say Y. If unsure, say N. @@ -6938,17 +7853,17 @@ in the Econet socket. If you have an Acorn system with one of these, say Y. If unsure, say N. -EESOX SCSI support (EXPERIMENTAL) +EESOX SCSI support CONFIG_SCSI_EESOXSCSI This enables support for the EESOX SCSI card. If you have an Acorn system with one of these, say Y, otherwise say N. -Powertec SCSI support (EXPERIMENTAL) +PowerTec SCSI support CONFIG_SCSI_POWERTECSCSI This enables support for the Powertec SCSI card on Acorn systems. If you have one of these, say Y. If unsure, say N. -IEEE 1394 (aka FireWire) support +IEEE 1394 (FireWire) support CONFIG_IEEE1394 IEEE 1394 describes a high performance serial bus, which is also known as FireWire(tm) or i.Link(tm) and is used for connecting all @@ -6961,10 +7876,10 @@ If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called ieee1394.o. + say M here and read . The module + will be called ieee1394.o. -TI PCILynx IEEE 1394 support +Texas Instruments PCILynx support CONFIG_IEEE1394_PCILYNX Say Y here if you have an IEEE-1394 controller with the Texas Instruments PCILynx chip. Note: this driver is written for revision @@ -6972,8 +7887,8 @@ If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called pcilynx.o. + say M here and read . The module + will be called pcilynx.o. Use local RAM on PCILynx board CONFIG_IEEE1394_PCILYNX_LOCALRAM @@ -6983,7 +7898,7 @@ computer's motherboard. Local RAM may speed up command processing because no PCI transfers are necessary during use of the Packet Control Lists. - + Note that there are no known PCILynx systems providing local RAM except for the evaluation boards by Texas Instruments and that the PCILynx does not reliably report missing RAM. This means that it is @@ -6997,32 +7912,43 @@ This option enables driver code to access the RAM, ROM and AUX ports of the PCILynx through character devices in /dev. If you don't know what this is about then you won't need it. - + If unsure, say N. +#Adaptec AIC-5800 IEEE 1394 support +#CONFIG_IEEE1394_AIC5800 +# Say Y here if you have a IEEE 1394 controller using the Adaptec +# AIC-5800 chip. All Adaptec host adapters (89xx series) use this +# chip, as well as miro's DV boards. +# +# If you want to compile this as a module ( = code which can be +# inserted in and removed from the running kernel whenever you want), +# say M here and read . The module +# will be called aic5800.o. +# OHCI-1394 support CONFIG_IEEE1394_OHCI1394 Enable this driver if you have an IEEE 1394 controller based on the OHCI-1394 specification. The current driver is only tested with OHCI chipsets made by Texas Instruments and NEC. Most third-party vendors - use one of these chipsets. It should work with any OHCI-1394 compliant - card, however. + use one of these chipsets. It should work with any OHCI-1394 + compliant card, however. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called ohci1394.o. + say M here and read . The module + will be called ohci1394.o. OHCI-1394 Video support CONFIG_IEEE1394_VIDEO1394 - This option enables video device usage for OHCI-1394 cards. Enable this - option only if you have an IEEE 1394 video device connected to an - OHCI-1394 card. + This option enables video device usage for OHCI-1394 cards. Enable + this option only if you have an IEEE 1394 video device connected to + an OHCI-1394 card. SBP-2 support (Harddisks etc.) CONFIG_IEEE1394_SBP2 - This option enables you to use SBP-2 devices connected to your IEEE 1394 - bus. SBP-2 devices include harddrives and DVD devices. + This option enables you to use SBP-2 devices connected to your IEEE + 1394 bus. SBP-2 devices include harddrives and DVD devices. Raw IEEE 1394 I/O support CONFIG_IEEE1394_RAWIO @@ -7033,8 +7959,8 @@ If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called raw1394.o. + say M here and read . The module + will be called raw1394.o. Excessive debugging output CONFIG_IEEE1394_VERBOSEDEBUG @@ -7047,18 +7973,18 @@ Say Y if you really want or need the debugging output, everyone else says N. -Network device support? +Network device support CONFIG_NETDEVICES You can say N here if you don't intend to connect your Linux box to any other computer at all or if all your connections will be over a telephone line with a modem either via UUCP (UUCP is a protocol to forward mail and news between unix hosts over telephone lines; read the UUCP-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto ) or dialing up a shell + ) or dialing up a shell account or a BBS, even using term (term is a program which gives you almost full Internet connectivity if you have a regular dial up shell account on some Internet connected Unix computer. Read - http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html ). + ). You'll have to say Y if your computer contains a network card that you want to use under Linux (make sure you know its name because you @@ -7074,7 +8000,7 @@ Make sure to read the NET-3-HOWTO. Eventually, you will have to read Olaf Kirch's excellent and free book "Network Administrator's - Guide", to be found in http://www.linuxdoc.org/docs.html#guide . If + Guide", to be found in . If unsure, say Y. Dummy net driver support @@ -7087,14 +8013,15 @@ thing often comes in handy, the default is Y. It won't enlarge your kernel either. What a deal. Read about it in the Network Administrator's Guide, available from - http://www.linuxdoc.org/docs.html#guide . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called dummy.o. If you want to use more than one dummy device at a - time, you need to compile this driver as a module. Instead of - 'dummy', the devices will then be called 'dummy0', 'dummy1' etc. + say M here and read . The module + will be called dummy.o. If you want to use more than one dummy + device at a time, you need to compile this driver as a module. + Instead of 'dummy', the devices will then be called 'dummy0', + 'dummy1' etc. Bonding driver support CONFIG_BONDING @@ -7102,7 +8029,7 @@ Channels together. This is called 'Etherchannel' by Cisco, 'Trunking' by Sun, and 'Bonding' in Linux. - If you have two ethernet connections to some other computer, you can + If you have two Ethernet connections to some other computer, you can make them behave like one double speed connection using this driver. Naturally, this has to be supported at the other end as well, either with a similar Bonding Linux driver, a Cisco 5500 switch or a @@ -7113,8 +8040,8 @@ If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called bonding.o. + say M here and read . The module + will be called bonding.o. SLIP (serial line) support CONFIG_SLIP @@ -7130,23 +8057,23 @@ Normally, your access provider has to support SLIP in order for you to be able to use it, but there is now a SLIP emulator called SLiRP around (available via FTP (user: anonymous) from - ftp://metalab.unc.edu/pub/Linux/system/network/serial/ ) which + ) which allows you to use SLIP over a regular dial up shell connection. If you plan to use SLiRP, make sure to say Y to CSLIP, below. The NET-3-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , explains how to + , explains how to configure SLIP. Note that you don't need this option if you just want to run term (term is a program which gives you almost full Internet connectivity if you have a regular dial up shell account on some Internet connected Unix computer. Read - http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html ). SLIP + ). SLIP support will enlarge your kernel by about 4 KB. If unsure, say N. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - slip.o. + say M here and read as well as + . The module will be + called slip.o. CSLIP compressed headers CONFIG_SLIP_COMPRESSED @@ -7155,10 +8082,10 @@ on both ends. Ask your access provider if you are not sure and answer Y, just in case. You will still be able to use plain SLIP. If you plan to use SLiRP, the SLIP emulator (available from - ftp://metalab.unc.edu/pub/Linux/system/network/serial/ ) which + ) which allows you to use SLIP over a regular dial up shell connection, you definitely want to say Y here. The NET-3-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , explains how to configure + , explains how to configure CSLIP. This won't enlarge your kernel. Keepalive and linefill @@ -7179,24 +8106,24 @@ PPP (point-to-point protocol) support CONFIG_PPP - PPP (Point to Point Protocol) is a newer and better SLIP. It serves + PPP (Point to Point Protocol) is a newer and better SLIP. It serves the same purpose: sending Internet traffic over telephone (and other - serial) lines. Ask your access provider if they support it, because - otherwise you can't use it; most internet access providers these + serial) lines. Ask your access provider if they support it, because + otherwise you can't use it; most Internet access providers these days support PPP rather than SLIP. To use PPP, you need an additional program called pppd as described - in Documentation/networking/ppp.txt and in the PPP-HOWTO, available - at http://www.linuxdoc.org/docs.html#howto . If you upgrade - from an older kernel, you might need to upgrade pppd as well. The - PPP option enlarges your kernel by about 16 KB. + in the PPP-HOWTO, available at + . Make sure that you have + the version of pppd recommended in . + The PPP option enlarges your kernel by about 16 KB. There are actually two versions of PPP: the traditional PPP for asynchronous lines, such as regular analog phone lines, and synchronous PPP which can be used over digital ISDN lines for - example. If you want to use PPP over phone lines or other + example. If you want to use PPP over phone lines or other asynchronous serial lines, you need to say Y (or M) here and also to - the next option, "PPP support for async serial ports". For PPP over + the next option, "PPP support for async serial ports". For PPP over synchronous lines, you should say Y (or M) here and to "Support synchronous PPP", below. @@ -7204,12 +8131,12 @@ inserted in and removed from the running kernel whenever you want). If you said Y to "Version information on all symbols" above, then you cannot compile the PPP driver into the kernel; you can then only - compile it as a module. The module will be called ppp_generic.o. If - you want to compile it as a module, say M here and read - Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + compile it as a module. The module will be called ppp_generic.o. + If you want to compile it as a module, say M here and read + as well as + . -PPP multilink support (EXPERIMENTAL) +PPP multilink support CONFIG_PPP_MULTILINK PPP multilink is a protocol (defined in RFC 1990) which allows you to combine several (logical or physical) lines into one logical PPP @@ -7220,7 +8147,7 @@ If unsure, say N. -PPP filtering (EXPERIMENTAL) +PPP filtering CONFIG_PPP_FILTER Say Y here if you want to be able to filter the packets passing over PPP interfaces. This allows you to control which packets count as @@ -7240,7 +8167,7 @@ This code is also available as a module (code which can be inserted into and removed from the running kernel). If you want to compile - it as a module, say M here and read Documentation/modules.txt. + it as a module, say M here and read . If unsure, say Y. @@ -7252,20 +8179,22 @@ This code is also available as a module (code which can be inserted into and removed from the running kernel). If you want to compile - it as a module, say M here and read Documentation/modules.txt. + it as a module, say M here and read + . PPP Deflate compression CONFIG_PPP_DEFLATE Support for the Deflate compression method for PPP, which uses the Deflate algorithm (the same algorithm that gzip uses) to compress - each PPP packet before it is sent over the wire. The machine at the + each PPP packet before it is sent over the wire. The machine at the other end of the PPP link (usually your ISP) has to support the - Deflate compression method as well for this to be useful. Even if + Deflate compression method as well for this to be useful. Even if they don't support it, it is safe to say Y here. This code is also available as a module (code which can be inserted into and removed from the running kernel). If you want to compile - it as a module, say M here and read Documentation/modules.txt. + it as a module, say M here and read + . PPP BSD-Compress compression CONFIG_PPP_BSDCOMP @@ -7284,14 +8213,14 @@ module; it is called bsd_comp.o and will show up in the directory modules once you have said "make modules". If unsure, say N. -PPP over Ethernet (EXPERIMENTAL) +PPP over Ethernet CONFIG_PPPOE Support for PPP over Ethernet. This driver requires a specially patched pppd daemon. The patch to pppd, along with binaries of a patched pppd package can be found at: - http://www.shoshin.uwaterloo.ca/~mostrows - + . + Wireless LAN (non-hamradio) CONFIG_NET_RADIO Support for wireless LANs and everything having to do with radio, @@ -7308,18 +8237,18 @@ driver (or Linux). If you wish to use Wireless Extensions with wireless PCMCIA (PC-) cards, you need to say Y here; you can fetch the tools from - http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html . + . Some user-level drivers for scarab devices which don't require special kernel support are available from - ftp://shadow.cabi.net/pub/Linux . + . STRIP (Metricom Starmode radio IP) CONFIG_STRIP Say Y if you have a Metricom radio and intend to use Starmode Radio IP. STRIP is a radio protocol developed for the MosquitoNet project - (on the WWW at http://mosquitonet.stanford.edu/ ) to send Internet - traffic using Metricom radios. Metricom radios are small, battery + (on the WWW at ) to send Internet + traffic using Metricom radios. Metricom radios are small, battery powered, 100kbit/sec packet radio transceivers, about the size and weight of a cellular telephone. (You may also have heard them called "Metricom modems" but we avoid the term "modem" because it misleads @@ -7330,50 +8259,51 @@ it is obviously most useful for people with laptop computers. If you think you might get a Metricom radio in the future, there is no harm in saying Y to STRIP now, except that it makes the kernel a bit - bigger. + bigger. You can also compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M - here and read Documentation/modules.txt. The module will be called - strip.o. + here and read . The module will be + called strip.o. AT&T WaveLAN & DEC RoamAbout DS support CONFIG_WAVELAN The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is a Radio LAN (wireless Ethernet-like Local Area Network) using the radio frequencies 900 MHz and 2.4 GHz. - - This driver support the ISA version of the WaveLAN card. A separate + + This driver support the ISA version of the WaveLAN card. A separate driver for the PCMCIA (PC-card) hardware is available in David - Hinds' pcmcia-cs package (see the file Documentation/Changes for - location). + Hinds' pcmcia-cs package (see the file + for location). If you want to use an ISA WaveLAN card under Linux, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Some more specific - information is contained in Documentation/networking/wavelan.txt and - in the source code drivers/net/wavelan.p.h. + . Some more specific + information is contained in + and in the source code + . You will also need the wireless tools package available from - http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html . + . Please read the man pages contained therein. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called wavelan.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called wavelan.o. If you want to compile it as a + module, say M here and read as well + as . Aironet Arlan 655 & IC2200 DS support CONFIG_ARLAN Aironet makes Arlan, a class of wireless LAN adapters. These use the www.Telxon.com chip, which is also used on several similar cards. This driver is tested on the 655 and IC2200 series cards. Look at - http://www.ylenurme.ee/~elmer/655/ for the latest information. - + for the latest information. + The driver is built as two modules, arlan and arlan-proc. The latter is the /proc interface and is not needed most of time. - + On some computers the card ends up in non-valid state after some time. Use a ping-reset script to clear it. @@ -7382,27 +8312,27 @@ www.aironet.com (recently bought by Cisco) makes these 802.11 DS adapters. Driver by Elmer Joandi (elmer@ylenurme.ee). - Say Y here if you have such an adapter, and then say Y below to - the option that applies to your particular type of card (PCI, ISA, + Say Y here if you have such an adapter, and then say Y below to + the option that applies to your particular type of card (PCI, ISA, or PCMCIA). This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called aironet4500_core.o. If you want to - compile it as a module, say M here and read - Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called aironet4500_core.o. If you want to + compile it as a module, say M here and read + as well as + . - quick config parameters: + quick config parameters: SSID=tsunami - "The Password" adhoc=1 there are no Access Points around - master=1 Adhoc master (the one who creates network + master=1 Adhoc master (the one who creates network sync) - slave=1 Adhoc slave (btw, it is still forming own net + slave=1 Adhoc slave (btw, it is still forming own net sometimes, and has problems with firmware... change IbssJoinNetTimeout from /proc...) channel=1..? meaningful in adhoc mode - + If you have problems with screwing up card, both_bap_lock=1 is a conservative value (performance hit 15%). @@ -7410,107 +8340,107 @@ Aironet 4500/4800 ISA/PCI/PNP/365 support CONFIG_AIRONET4500_NONCS - If you have an ISA, PCI or PCMCIA Aironet 4500/4800 wireless LAN - card, say Y here, and then also to the options below that apply + If you have an ISA, PCI or PCMCIA Aironet 4500/4800 wireless LAN + card, say Y here, and then also to the options below that apply to you. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called aironet4500_card.o. If you want to - compile it as a module, say M here and read - Documentation/modules.txt + The module will be called aironet4500_card.o. If you want to + compile it as a module, say M here and read + . Aironet 4500/4800 PNP support CONFIG_AIRONET4500_PNP - If you have an ISA Aironet 4500/4800 card which you want to use in - PNP (Plug and Play) mode, say Y here. This is the recommended mode - for ISA cards. Remember however to enable the PNP jumper on the + If you have an ISA Aironet 4500/4800 card which you want to use in + PnP (Plug and Play) mode, say Y here. This is the recommended mode + for ISA cards. Remember however to enable the PnP jumper on the board if you say Y here. Aironet 4500/4800 PCI support CONFIG_AIRONET4500_PCI If you have an PCI Aironet 4500/4800 card, say Y here. -Aironet 4500/4800 ISA broken support (EXPERIMENTAL) +Aironet 4500/4800 ISA broken support CONFIG_AIRONET4500_ISA If you have an ISA Aironet 4500/4800 card which you want to run in - non-PNP mode, say Y here. This is not recommended and does not work + non-PnP mode, say Y here. This is not recommended and does not work correctly at this point. Say N. -Aironet 4500/4800 I365 broken support (EXPERIMENTAL) +Aironet 4500/4800 I365 broken support CONFIG_AIRONET4500_I365 - If you have a PCMCIA Aironet 4500/4800 card which you want to use - without the standard PCMCIA cardservices provided by the pcmcia-cs + If you have a PCMCIA Aironet 4500/4800 card which you want to use + without the standard PCMCIA cardservices provided by the pcmcia-cs package, say Y here. This is not recommended, so say N. Aironet 4500/4800 PCMCIA support CONFIG_AIRONET4500_CS - Say Y here if you have a PCMCIA Aironet 4500/4800 card which you + Say Y here if you have a PCMCIA Aironet 4500/4800 card which you want to use with the standard PCMCIA cardservices provided by the pcmcia-cs package. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called aironet4500_cs.o. If you want to - compile it as a module, say M here and read - Documentation/modules.txt. - + The module will be called aironet4500_cs.o. If you want to + compile it as a module, say M here and read + . + Aironet 4500/4800 PROC interface CONFIG_AIRONET4500_PROC - If you say Y here (and to the "/proc file system" below), you will - be able to configure your Aironet card via the + If you say Y here (and to the "/proc file system" below), you will + be able to configure your Aironet card via the /proc/sys/aironet4500 interface. - Additional info: look in drivers/net/aironet4500_rids.c. + Additional info: look in . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called aironet4500_proc.o. If you want to - compile it as a module, say M here and read - Documentation/modules.txt. + The module will be called aironet4500_proc.o. If you want to + compile it as a module, say M here and read + . NOTE: the proc interface uses a lot of memory, so it is recommended - to compile it as a module and remove the module after + to compile it as a module and remove the module after configuration. LAPB over Ethernet driver CONFIG_LAPBETHER This is a driver for a pseudo device (typically called /dev/lapb0) which allows you to open an LAPB point-to-point connection to some - other computer on your Ethernet network. In order to do this, you + other computer on your Ethernet network. In order to do this, you need to say Y or M to the driver for your Ethernet card as well as - to "LAPB Data Link Driver". + to "LAPB Data Link Driver". If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called lapbether.o. If unsure, say N. + say M here and read . The module + will be called lapbether.o. If unsure, say N. X.25 async driver CONFIG_X25_ASY This is a driver for sending and receiving X.25 frames over regular asynchronous serial lines such as telephone lines equipped with - ordinary modems. Experts should note that this driver doesn't + ordinary modems. Experts should note that this driver doesn't currently comply with the asynchronous HDLS framing protocols in - CCITT recommendation X.25. + CCITT recommendation X.25. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called x25_asy.o. If unsure, say N. + say M here and read . The module + will be called x25_asy.o. If unsure, say N. PCMCIA network device support CONFIG_NET_PCMCIA Say Y if you would like to include support for any PCMCIA or CardBus network adapters, then say Y to the driver for your particular card - below. PCMCIA- or PC-cards are credit-card size devices often used + below. PCMCIA- or PC-cards are credit-card size devices often used with laptops computers; CardBus is the newer and faster version of - PCMCIA. + PCMCIA. To use your PC-cards, you will need supporting software from David - Hinds' pcmcia-cs package (see the file Documentation/Changes for - location). You also want to check out the PCMCIA-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + Hinds' pcmcia-cs package (see the file + for location). You also want to check out the PCMCIA-HOWTO, + available from . If unsure, say N. @@ -7521,9 +8451,9 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called 3c589_cs.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. If unsure, - say N. + The module will be called 3c589_cs.o. If you want to compile it as + a module, say M here and read . If + unsure, say N. 3Com 3c574 PCMCIA support CONFIG_PCMCIA_3C574 @@ -7532,9 +8462,9 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called 3c574_cs.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. If unsure, - say N. + The module will be called 3c574_cs.o. If you want to compile it as + a module, say M here and read . If + unsure, say N. Fujitsu FMV-J18x PCMCIA support CONFIG_PCMCIA_FMVJ18X @@ -7543,9 +8473,9 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called fmvj18x_cs.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. If unsure, - say N. + The module will be called fmvj18x_cs.o. If you want to compile it + as a module, say M here and read . + If unsure, say N. NE2000 compatible PCMCIA support CONFIG_PCMCIA_PCNET @@ -7554,9 +8484,9 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called pcnet_cs.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. If unsure, - say N. + The module will be called pcnet_cs.o. If you want to compile it as + a module, say M here and read . If + unsure, say N. New Media PCMCIA support CONFIG_PCMCIA_NMCLAN @@ -7565,9 +8495,9 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called nmclan_cs.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. If unsure, - say N. + The module will be called nmclan_cs.o. If you want to compile it as + a module, say M here and read . If + unsure, say N. SMC 91Cxx PCMCIA support CONFIG_PCMCIA_SMC91C92 @@ -7576,20 +8506,20 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called smc91c92_cs.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. If - unsure, say N. + The module will be called smc91c92_cs.o. If you want to compile it + as a module, say M here and read . + If unsure, say N. Xircom 16-bit PCMCIA support CONFIG_PCMCIA_XIRC2PS - Say Y here if you intend to attach a Xircom 16-bit PCMCIA - (PC-card) Ethernet or Fast Ethernet card to your computer. + Say Y here if you intend to attach a Xircom 16-bit PCMCIA (PC-card) + Ethernet or Fast Ethernet card to your computer. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called xirc2ps_cs.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. If unsure, - say N. + The module will be called xirc2ps_cs.o. If you want to compile it + as a module, say M here and read . + If unsure, say N. COM20020 ARCnet PCMCIA support CONFIG_ARCNET_COM20020_CS @@ -7598,20 +8528,20 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called com20020_cs.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. If - unsure, say N. + The module will be called com20020_cs.o. If you want to compile it + as a module, say M here and read . + If unsure, say N. IBM PCMCIA Token Ring adapter support CONFIG_PCMCIA_IBMTR Say Y here if you intend to attach this type of Token Ring PCMCIA card to your computer. You then also need to say Y to "Token Ring - driver support". + driver support". This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called ibmtr_cs.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + The module will be called ibmtr_cs.o. If you want to compile it as + a module, say M here and read . Xircom Tulip-like CardBus support CONFIG_PCMCIA_XIRTULIP @@ -7622,20 +8552,20 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called xircom_tulip_cb.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. If - unsure, say N. + The module will be called xircom_tulip_cb.o. If you want to compile + it as a module, say M here and read + . If unsure, say N. -Pcmcia Wireless LAN +PCMCIA Wireless LAN CONFIG_NET_PCMCIA_RADIO Say Y here if you would like to use a PCMCIA (PC-card) device to connect to a wireless local area network. Then say Y to the driver for your particular card below. To use your PC-cards, you will need supporting software from David - Hinds' pcmcia-cs package (see the file Documentation/Changes for - location). You also want to check out the PCMCIA-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + Hinds' pcmcia-cs package (see the file + for location). You also want to check out the PCMCIA-HOWTO, + available from . Hermes chipset 802.11b support (Orinoco/Prism2/Symbol cards) CONFIG_HERMES @@ -7675,37 +8605,28 @@ Support for these adaptors is so far still incomplete and buggy. You have been warned. -Hermes PCMCIA card support -CONFIG_PCMCIA_HERMES - Enable support for PCMCIA 802.11b cards using the Hermes or Intersil - HFA384x (Prism 2) chipset. To use your PC-cards, you will need - supporting software from David Hinds' pcmcia-cs package (see the - file Documentation/Changes for location). You also want to check out - the PCMCIA-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . - Hermes support (Orinoco/WavelanIEEE/PrismII/Symbol 802.11b cards) CONFIG_PCMCIA_HERMES A driver for "Hermes" chipset based PCMCIA wireless adaptors, such as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ - EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and others). - It should also be usable on various Prism II based cards such as the - Linksys, D-Link and Farallon Skyline. It should also work on Symbol - cards such as the 3Com AirConnect and Ericsson WLAN. + EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and + others). It should also be usable on various Prism II based cards + such as the Linksys, D-Link and Farallon Skyline. It should also + work on Symbol cards such as the 3Com AirConnect and Ericsson WLAN. To use your PC-cards, you will need supporting software from David - Hinds' pcmcia-cs package (see the file Documentation/Changes for - location). You also want to check out the PCMCIA-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + Hinds' pcmcia-cs package (see the file + for location). You also want to check out the PCMCIA-HOWTO, + available from . You will also very likely also need the Wireless Tools in order to - configure your card and that /etc/pcmcia/wireless.opts works : - http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html + configure your card and that /etc/pcmcia/wireless.opts works: + . Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards CONFIG_AIRO - This is the standard Linux driver to support Cisco/Aironet ISA - and PCI 802.11 wireless cards. + This is the standard Linux driver to support Cisco/Aironet ISA and + PCI 802.11 wireless cards. It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X - with or without encryption) as well as card before the Cisco aquisition (Aironet 4500, Aironet 4800, Aironet 4800B). @@ -7719,7 +8640,7 @@ Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards CONFIG_AIRO_CS This is the standard Linux driver to support Cisco/Aironet PCMCIA - 802.11 wireless cards. This driver is the same as the Aironet + 802.11 wireless cards. This driver is the same as the Aironet driver part of the Linux Pcmcia package. It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X - with or without encryption) as well as card before the Cisco @@ -7732,22 +8653,28 @@ Cisco Linux utilities can be used to configure the card. To use your PC-cards, you will need supporting software from David - Hinds' pcmcia-cs package (see the file Documentation/Changes for - location). You also want to check out the PCMCIA-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + Hinds' pcmcia-cs package (see the file + for location). You also want to check out the PCMCIA-HOWTO, + available from . Aviator/Raytheon 2.4MHz wireless support CONFIG_PCMCIA_RAYCS Say Y here if you intend to attach an Aviator/Raytheon PCMCIA (PC-card) wireless Ethernet networking card to your computer. - Please read the file Documentation/networking/ray_cs.txt for + Please read the file for details. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called ray_cs.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. If unsure, - say N. + The module will be called ray_cs.o. If you want to compile it as a + module, say M here and read . If + unsure, say N. + +Apple Airport support (built-in) +CONFIG_APPLE_AIRPORT + Say Y here to support the Airport 802.11b wireless Ethernet hardware + built into the Macintosh iBook and other recent PowerPC-based + Macintosh machines. Xircom Netwave AirSurfer wireless support CONFIG_PCMCIA_NETWAVE @@ -7756,9 +8683,9 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called netwave_cs.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. If unsure, - say N. + The module will be called netwave_cs.o. If you want to compile it + as a module, say M here and read . + If unsure, say N. AT&T/Lucent Wavelan wireless support CONFIG_PCMCIA_WAVELAN @@ -7768,44 +8695,45 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called wavelan_cs.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. If unsure, - say N. + The module will be called wavelan_cs.o. If you want to compile it + as a module, say M here and read . + If unsure, say N. PLIP (parallel port) support CONFIG_PLIP PLIP (Parallel Line Internet Protocol) is used to create a reasonably fast mini network consisting of two (or, rarely, more) - local machines. A PLIP link from a Linux box is a popular means to - install a Linux distribution on a machine which doesn't have a CDROM - drive (a minimal system has to be transferred with floppies first). - The kernels on both machines need to have this PLIP option enabled - for this to work. - - The PLIP driver has two modes, mode 0 and mode 1. The parallel ports - (the connectors at the computers with 25 holes) are connected with - "null printer" or "Turbo Laplink" cables which can transmit 4 bits - at a time (mode 0) or with special PLIP cables, to be used on + local machines. A PLIP link from a Linux box is a popular means to + install a Linux distribution on a machine which doesn't have a + CD-ROM drive (a minimal system has to be transferred with floppies + first). The kernels on both machines need to have this PLIP option + enabled for this to work. + + The PLIP driver has two modes, mode 0 and mode 1. The parallel + ports (the connectors at the computers with 25 holes) are connected + with "null printer" or "Turbo Laplink" cables which can transmit 4 + bits at a time (mode 0) or with special PLIP cables, to be used on bidirectional parallel ports only, which can transmit 8 bits at a time (mode 1); you can find the wiring of these cables in - Documentation/networking/PLIP.txt. The cables can be up to 15m long. - Mode 0 works also if one of the machines runs DOS/Windows and has - some PLIP software installed, e.g. the Crynwr PLIP packet driver - (http://oak.oakland.edu/simtel.net/msdos/pktdrvr-pre.html ) and - winsock or NCSA's telnet. + . The cables can be up to + 15m long. Mode 0 works also if one of the machines runs DOS/Windows + and has some PLIP software installed, e.g. the Crynwr PLIP packet + driver () + and winsock or NCSA's telnet. If you want to use PLIP, say Y and read the PLIP mini-HOWTO as well as the NET-3-HOWTO, both available from - http://www.linuxdoc.org/docs.html#howto . Note that the PLIP + . Note that the PLIP protocol has been changed and this PLIP driver won't work together - with the PLIP support in Linux versions 1.0.x. This option enlarges + with the PLIP support in Linux versions 1.0.x. This option enlarges your kernel by about 8 KB. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - plip.o. If unsure, say Y or M, in case you buy a laptop later. + say M here and read as well as + . The module will be + called plip.o. If unsure, say Y or M, in case you buy a laptop + later. EQL (serial line load balancing) support CONFIG_EQUALIZER @@ -7813,39 +8741,41 @@ usually requires two modems and two telephone lines) and you use SLIP (the protocol for sending Internet traffic over telephone lines) or PPP (a better SLIP) on them, you can make them behave like - one double speed connection using this driver. Naturally, this has + one double speed connection using this driver. Naturally, this has to be supported at the other end as well, either with a similar EQL - Linux driver or with a Livingston Portmaster 2e. + Linux driver or with a Livingston Portmaster 2e. - Say Y if you want this and read Documentation/networking/eql.txt. - You may also want to read section 6.2 of the NET-3-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + Say Y if you want this and read + . You may also want to read + section 6.2 of the NET-3-HOWTO, available from + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called eql.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. If unsure, - say N. + The module will be called eql.o. If you want to compile it as a + module, say M here and read . If + unsure, say N. -Universal TUN/TAP device driver. +Universal TUN/TAP device driver support CONFIG_TUN - TUN/TAP provides packet reception and transmission for user space programs. - It can be viewed as a simple Point-to-Point or Ethernet device, which - instead of receiving packets from a physical media, receives them from - user space program and instead of sending packets via physical media - writes them to the user space program. + TUN/TAP provides packet reception and transmission for user space + programs. It can be viewed as a simple Point-to-Point or Ethernet + device, which instead of receiving packets from a physical media, + receives them from user space program and instead of sending packets + via physical media writes them to the user space program. + + When a program opens /dev/net/tun, driver creates and registers + corresponding net device tunX or tapX. After a program closed above + devices, driver will automatically delete tunXX or tapXX device and + all routes corresponding to it. - When a program opens /dev/net/tun, driver creates and registers - corresponding net device tunX or tapX. After a program closed above - devices, driver will automatically delete tunXX or tapXX device and all - routes corresponding to it. - - Please read Documentation/networking/tuntap.txt for more information. + Please read for more + information. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called tun.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called tun.o. If you want to compile it as a + module, say M here and read . If you don't know what to use this for, you don't need it. @@ -7855,7 +8785,7 @@ driver", above) and create a character special file /dev/tap0 with major number 36 and minor number 16 using mknod ("man mknod"), you will be able to have a user space program read and write raw - Ethernet frames from/to that special file. tap0 can be configured + Ethernet frames from/to that special file. tap0 can be configured with ifconfig and route like any other Ethernet device but it is not connected to any physical LAN; everything written by the user to /dev/tap0 is treated by the kernel as if it had come in from a LAN @@ -7863,19 +8793,19 @@ device tap0 can instead be read by the user from /dev/tap0: the user mode program replaces the LAN that would be attached to an ordinary Ethernet device. Please read the file - Documentation/networking/ethertap.txt for more information. + for more information. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called ethertap.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . If you don't know what to use this for, you don't need it. Sealevel Systems 4021 support CONFIG_SEALEVEL_4021 This is a driver for the Sealevel Systems ACB 56 serial I/O adapter. - + This driver can only be compiled as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to do that, say M here. The module will be called @@ -7889,6 +8819,21 @@ of the Cisco HDLC/PPP driver (syncppp.c). The SyncLink WAN driver (in character devices) must also be enabled. +FarSync T-Series support +CONFIG_FARSYNC + This driver supports the FarSync T-Series X.21 (and V.35/V.24) cards + from FarSite Communications Ltd. + Synchronous communication is supported on all ports at speeds up to + 8Mb/s (128K on V.24) using synchronous PPP or Cisco HDLC. + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want) + say M here and read . + The module will be called farsync.o and if you want the module to be + automatically loaded when the interface is referenced then you + should add "alias syncX farsync" to /etc/modules.conf for each + interface, where X is 0, 1, 2, ... + Frame Relay (DLCI) support CONFIG_DLCI This is support for the frame relay protocol; frame relay is a fast @@ -7898,15 +8843,15 @@ network, usually at the phone company) can carry several logical point-to-point connections to other computers connected to the frame relay network. For a general explanation of the protocol, check out - http://www.frforum.com/ on the WWW. To use frame relay, you need + on the WWW. To use frame relay, you need supporting hardware (called FRAD) and certain programs from the net-tools package as explained in - Documentation/networking/framerelay.txt. + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called dlci.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . Max open DLCI CONFIG_DLCI_COUNT @@ -7921,19 +8866,19 @@ handled by each of your hardware frame relay access devices. Go with the default. -Sangoma S502A FRAD support +SDLA (Sangoma S502/S508) support CONFIG_SDLA Say Y here if you need a driver for the Sangoma S502A, S502E, and S508 Frame Relay Access Devices. These are multi-protocol cards, but only frame relay is supported by the driver at this time. Please - read Documentation/framerelay.txt. + read . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called sdla.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . -Acorn Econet/AUN protocols (EXPERIMENTAL) +Acorn Econet/AUN protocols CONFIG_ECONET Econet is a fairly old and slow networking protocol mainly used by Acorn computers to access file and print servers. It uses native @@ -7949,7 +8894,7 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called econet.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . AUN over UDP CONFIG_ECONET_AUNUDP @@ -7962,7 +8907,7 @@ Say Y here if you have a native Econet network card installed in your computer. -WAN Router +WAN router CONFIG_WAN_ROUTER Wide Area Networks (WANs), such as X.25, frame relay and leased lines, are used to interconnect Local Area Networks (LANs) over vast @@ -7974,16 +8919,17 @@ As an alternative, WAN routing can be built into the Linux kernel. With relatively inexpensive WAN interface cards available on the market, a perfectly usable router can be built for less than half - the price of an external router. If you have one of those cards and + the price of an external router. If you have one of those cards and wish to use your Linux box as a WAN router, say Y here and also to - the WAN driver for your card, below. You will then need the - wan-tools package which is available from ftp://ftp.sangoma.com . - Read Documentation/networking/wan-router.txt for more information. + the WAN driver for your card, below. You will then need the + wan-tools package which is available from . + Read for more + information. The WAN routing support is also available as a module called wanrouter.o ( = code which can be inserted in and removed from the - running kernel whenever you want). If you want to compile it as a - module, say M here and read Documentation/modules.txt. + running kernel whenever you want). If you want to compile it as a + module, say M here and read . If unsure, say N. @@ -7995,13 +8941,13 @@ IMPORTANT NOTE: This option is NOT COMPATIBLE with "Network packet filtering" (CONFIG_NETFILTER). Say N here if you say Y there. - However, it will work with all options in the "IP: advanced router" - section (except for "IP: use TOS value as routing key" and - "IP: use FWMARK value as routing key"). + However, it will work with all options in the "Advanced router" + section (except for "Use TOS value as routing key" and + "Use FWMARK value as routing key"). At the moment, few devices support fast switching (tulip is one of them, a modified 8390 driver can be found at - ftp://ftp.inr.ac.ru/ip-routing/fastroute/fastroute-8390.tar.gz ). + ). If unsure, say N. @@ -8011,7 +8957,7 @@ during periods of extremal congestion. At the moment only a couple of device drivers support it (really only one -- tulip, a modified 8390 driver can be found at - ftp://ftp.inr.ac.ru/ip-routing/fastroute/fastroute-8390.tar.gz ). + ). Really, this option is applicable to any machine attached to a fast enough network, and even a 10 Mb NIC is able to kill a not very slow @@ -8038,68 +8984,75 @@ This code is considered to be experimental. To administer these schedulers, you'll need the user-level utilities - from the package iproute2+tc at ftp://ftp.inr.ac.ru/ip-routing/ . + from the package iproute2+tc at . That package also contains some documentation; for more, check out - http://snafu.freedom.org/linux2.2/iproute-notes.html . + . This Quality of Service (QoS) support will enable you to use Differentiated Services (diffserv) and Resource Reservation Protocol (RSVP) on your Linux router if you also say Y to "QoS support", "Packet classifier API" and to some classifiers below. Documentation - and software is at http://icawww1.epfl.ch/linux-diffserv/ . + and software is at . If you say Y here and to "/proc file system" below, you will be able to read status information about packet schedulers from the file /proc/net/psched. - + The available schedulers are listed in the following questions; you can say Y to as many as you like. If unsure, say N now. CBQ packet scheduler CONFIG_NET_SCH_CBQ Say Y here if you want to use the Class-Based Queueing (CBQ) packet - scheduling algorithm for some of your network devices. This + scheduling algorithm for some of your network devices. This algorithm classifies the waiting packets into a tree-like hierarchy of classes; the leaves of this tree are in turn scheduled by separate algorithms (called "disciplines" in this context). - See the top of net/sched/sch_cbq.c for references about the CBQ - algorithm. - + See the top of for references about the + CBQ algorithm. + CBQ is a commonly used scheduler, so if you're unsure, you should say Y here. Then say Y to all the queueing algorithms below that you - want to use as CBQ disciplines. Then say Y to "Packet classifier + want to use as CBQ disciplines. Then say Y to "Packet classifier API" and say Y to all the classifiers you want to use; a classifier is a routine that allows you to sort your outgoing traffic into classes based on a certain criterion. This code is also available as a module called sch_cbq.o ( = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + whenever you want). If you want to compile it as a module, say M + here and read . CSZ packet scheduler CONFIG_NET_SCH_CSZ Say Y here if you want to use the Clark-Shenker-Zhang (CSZ) packet - scheduling algorithm for some of your network devices. At the + scheduling algorithm for some of your network devices. At the moment, this is the only algorithm that can guarantee service for - real-time applications (see the top of net/sched/sch_csz.c for - details and references about the algorithm). - + real-time applications (see the top of + for details and references about the algorithm). + Note: this scheduler is currently broken. This code is also available as a module called sch_csz.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . -#ATM pseudo-scheduler -#CONFIG_NET_SCH_ATM -# -# ??? -# - -The simplest PRIO pseudo scheduler +ATM pseudo-scheduler +CONFIG_NET_SCH_ATM + Say Y here if you want to use the ATM pseudo-scheduler. This + provides a framework for invoking classifiers (aka "filters"), which + in turn select classes of this queuing discipline. Each class maps + the flow(s) it is handling to a given virtual circuit (see the top of + ). + + This code is also available as a module called sch_atm.o ( = code + which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read . + +The simplest PRIO pseudo-scheduler CONFIG_NET_SCH_PRIO Say Y here if you want to use an n-band priority queue packet "scheduler" for some of your network devices or as a leaf discipline @@ -8108,32 +9061,56 @@ This code is also available as a module called sch_prio.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . + +Diffserv field marker +CONFIG_NET_SCH_DSMARK + Say Y if you want to schedule packets avccording to the + Differentiated Services architecture proposed in RFC 2475. + Technical information on this method, with pointers to associated + RFCs, is available at . + + This code is also available as a module called sch_dsmark.o ( = code + which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read . + +GRED queue +CONFIG_NET_SCH_GRED + Say Y here if you want to use the Generic Random Early Detection + (RED) packet scheduling algorithm for some of your network devices + (see the top of for details and + references about the algorithm). + + This code is also available as a module called sch_gred.o ( = code + which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read . RED queue CONFIG_NET_SCH_RED Say Y here if you want to use the Random Early Detection (RED) packet scheduling algorithm for some of your network devices (see - the top of net/sched/sch_red.c for details and references about the - algorithm). + the top of for details and references + about the algorithm). This code is also available as a module called sch_red.o ( = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + whenever you want). If you want to compile it as a module, say M + here and read . SFQ queue CONFIG_NET_SCH_SFQ Say Y here if you want to use the Stochastic Fairness Queueing (SFQ) packet scheduling algorithm for some of your network devices or as a leaf discipline for the CBQ scheduling algorithm (see the top of - net/sched/sch_sfq.c for details and references about the SFQ - algorithm). + for details and references about the SFQ + algorithm). This code is also available as a module called sch_sfq.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . TEQL queue CONFIG_NET_SCH_TEQL @@ -8141,30 +9118,36 @@ scheduling algorithm for some of your network devices or as a leaf discipline for the CBQ scheduling algorithm. This queueing discipline allows the combination of several physical devices into - one virtual device. (see the top of net/sched/sch_teql.c for + one virtual device. (see the top of for details). This code is also available as a module called sch_teql.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . TBF queue CONFIG_NET_SCH_TBF Say Y here if you want to use the Simple Token Bucket Filter (TBF) packet scheduling algorithm for some of your network devices or as a leaf discipline for the CBQ scheduling algorithm (see the top of - net/sched/sch_tbf.c for a description of the TBF algorithm). + for a description of the TBF algorithm). This code is also available as a module called sch_tbf.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . -### Add these -#+tristate ' GRED queue' CONFIG_NET_SCH_GRED -#+tristate ' Diffserv field marker' CONFIG_NET_SCH_DSMARK -#+tristate ' Ingress Qdisc' CONFIG_NET_SCH_INGRESS +Ingress Qdisc +CONFIG_NET_SCH_INGRESS + If you say Y here, you will be able to police incoming bandwidth + and drop packets when this bandwidth exceeds your desired rate. + If unsure, say Y. + + This code is also available as a module called cls_ingress.o + ( = code which can be inserted in and removed from the running + kernel whenever you want). If you want to compile it as a module, + say M here and read . QoS support CONFIG_NET_QOS @@ -8176,10 +9159,10 @@ Differentiated Services (diffserv) and Resource Reservation Protocol (RSVP) on your Linux router if you also say Y to "Packet classifier API" and to some classifiers below. Documentation and software is at - http://icawww1.epfl.ch/linux-diffserv/ . + . Note that the answer to this question won't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions about QoS support. Rate estimator @@ -8199,10 +9182,24 @@ This will enable you to use Differentiated Services (diffserv) and Resource Reservation Protocol (RSVP) on your Linux router. Documentation and software is at - http://icawww1.epfl.ch/linux-diffserv/ . + . + +Traffic policing (needed for in/egress) +CONFIG_NET_CLS_POLICE + Say Y to support traffic policing (bandwidth limits). Needed for + ingress and egress rate limiting. -### Add -#tristate ' TC index classifier' CONFIG_NET_CLS_TCINDEX +TC index classifier +CONFIG_NET_CLS_TCINDEX + If you say Y here, you will be able to classify outgoing packets + according to the tc_index field of the skb. You will want this + feature if you want to implement Differentiated Services using + sch_dsmark. If unsure, say Y. + + This code is also available as a module called cls_tcindex.o + ( = code which can be inserted in and removed from the running + kernel whenever you want). If you want to compile it as a module, + say M here and read . Routing tables based classifier CONFIG_NET_CLS_ROUTE4 @@ -8211,8 +9208,8 @@ This code is also available as a module called cls_route.o ( = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + whenever you want). If you want to compile it as a module, say M + here and read . Firewall based classifier CONFIG_NET_CLS_FW @@ -8221,8 +9218,8 @@ This code is also available as a module called cls_fw.o ( = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + whenever you want). If you want to compile it as a module, say M + here and read . U32 classifier CONFIG_NET_CLS_U32 @@ -8231,8 +9228,8 @@ This code is also available as a module called cls_u32.o ( = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt + whenever you want). If you want to compile it as a module, say M + here and read . Special RSVP classifier CONFIG_NET_CLS_RSVP @@ -8245,8 +9242,8 @@ This code is also available as a module called cls_rsvp.o ( = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt + whenever you want). If you want to compile it as a module, say M + here and read . Special RSVP classifier for IPv6 CONFIG_NET_CLS_RSVP6 @@ -8260,15 +9257,8 @@ This code is also available as a module called cls_rsvp6.o ( = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt - -# -# Traffic policing (needed for in/egress) -# CONFIG_NET_CLS_POLICE -### -### Some expert please fill these in -### + whenever you want). If you want to compile it as a module, say M + here and read . Network code profiler CONFIG_NET_PROFILE @@ -8297,10 +9287,10 @@ CONFIG_HOSTESS_SV11 This is a network card for low speed synchronous serial links, at up to 256Kbps. It supports both PPP and Cisco HDLC. - + At this point, the driver can only be compiled as a module. -COSA/SRP sync serial boards support +COSA/SRP sync serial board support CONFIG_COSA This is a driver for COSA and SRP synchronous serial boards. These boards allow to connect synchronous serial devices (for example @@ -8311,59 +9301,60 @@ To actually use the COSA or SRP board, you will need user-space utilities for downloading the firmware to the cards and to set them - up. Look at the http://www.fi.muni.cz/~kas/cosa/ for more + up. Look at the for more information about the cards (including the pointer to the user-space utilities). You can also read the comment at the top of the - drivers/net/cosa.c for details about the cards and the driver + for details about the cards and the driver itself. The driver will be compiled as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called cosa.o. For general information about - modules read Documentation/modules.txt. + modules read . -Etinc PCISYNC serial boards support +Etinc PCISYNC serial board support CONFIG_DSCC4 This is a driver for Etinc PCISYNC boards based on the Infineon (ex. Siemens) DSCC4 chipset. It is supposed to work with the four - ports card. Take a look at http://www.cogenit.fr/dscc4 + ports card. Take a look at for further informations about the driver and his configuration. The driver will be compiled as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called dscc4.o. For general information about - modules read Documentation/modules.txt. + modules read . -Lan Media sync serial boards support +LanMedia Corp. serial boards (SSI/V.35, T1/E1, HSSI, T3) CONFIG_LANMEDIA - This is a driver for the following Lan Media family of serial boards. - + This is a driver for the following Lan Media family of serial + boards. + LMC 1000 board allows you to connect synchronous serial devices (for example base-band modems, or any other device with the X.21, V.24, V.35 or V.36 interface) to your Linux box. - + LMC 1200 with on board DSU board allows you to connect your Linux box dirrectly to a T1 or E1 circuit. - - LMC 5200 board provides a HSSI interface capable of runnig up to + + LMC 5200 board provides a HSSI interface capable of running up to 52 mbits per second. - + LMC 5245 board connects directly to a T3 circuit saving the additional external hardware. - + To change setting such as syncPPP vs cisco HDLC or clock source you - will need lmcctl. It it available at ftp.lanmedia.com. - + will need lmcctl. It is available at . + This code is also available as a module called lmc.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . Fibre Channel driver support CONFIG_NET_FC Fibre Channel is a high speed serial protocol mainly used to connect large storage devices to the computer; it is compatible with and - intended to replace SCSI. + intended to replace SCSI. If you intend to use Fibre Channel, you need to have a Fibre channel adaptor card in your computer; say Y here and to the driver for your @@ -8377,9 +9368,9 @@ The driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called iph5526.o. For general information about - modules read Documentation/modules.txt. - -Red Creek Hardware VPN (EXPERIMENTAL) + modules read . + +Red Creek Hardware VPN CONFIG_RCPCI This is a driver for hardware which provides a Virtual Private Network (VPN). Say Y if you have it. @@ -8387,19 +9378,28 @@ This code is also available as a module called rcpci.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. - -SBNI12-xx support + here and read . + +Granch SBNI12 Leased Line adapter driver CONFIG_SBNI This is a driver for ISA SBNI12-xx cards which are low cost alternatives to leased line modems. Say Y if you want to insert the driver into the kernel or say M to compile it as a module (the - module will be called sbni.o). - - You can find more information and last versions of drivers and - utilities at http://www.granch.ru . If you have any question you + module will be called sbni.o). + + You can find more information and last versions of drivers and + utilities at . If you have any question you can send email to sbni@granch.ru. - + + Say N if unsure. + +SBNI Adapters Multiline feature +CONFIG_SBNI_MULTILINE + Schedule traffic for some parallel lines, via SBNI12 adapters. + If you have two computers connected with two parallel lines it's + possible to increase transfer rate nearly twice. You should have + a program named 'sbniconfig' to configure adapters. + Say N if unsure. WAN router drivers @@ -8411,27 +9411,26 @@ Router". You will need the wan-tools package which is available from - ftp://ftp.sangoma.com . Read Documentation/networking/wan-router.txt - for more information. + . Read + for more information. Note that the answer to this question won't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions about WAN router drivers. If unsure, say N. Sangoma WANPIPE(tm) multiprotocol cards CONFIG_VENDOR_SANGOMA + WANPIPE from Sangoma Technologies Inc. () + is a family of intelligent multiprotocol WAN adapters with data + transfer rates up to 4Mbps. They are also known as Synchronous + Data Link Adapters (SDLA) and are designated as S514-PCI or + S508-ISA. These cards support + + - X.25, Frame Relay, PPP, Cisco HDLC protocols. + + - API support for protocols like HDLC (LAPB), + HDLC Streaming, X.25, Frame Relay and BiSync. - WANPIPE from Sangoma Technologies Inc. (http://www.sangoma.com) - is a family of intelligent multiprotocol WAN adapters with data - transfer rates up to 4Mbps. They are also known as Synchronous - Data Link Adapters (SDLA) and are designated as S514-PCI or S508-ISA. - These cards support - - - X.25, Frame Relay, PPP, Cisco HDLC protocols. - - - API support for protocols like HDLC (LAPB), - HDLC Streaming, X.25, Frame Relay and BiSync. - - Ethernet Bridging over Frame Relay protocol. - MULTILINK PPP @@ -8439,32 +9438,32 @@ - Async PPP (Modem Dialup) If you have one or more of these cards, say M to this option; you - may then also want to read the file - Documentation/networking/wanpipe.txt. The next questions will ask - you about the protocols you want the driver to support. + may then also want to read the file + . The next questions + will ask you about the protocols you want the driver to support. The driver will be compiled as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called wanpipe.o. For general information about - modules read Documentation/modules.txt. + The module will be called wanpipe.o. For general information about + modules read . WANPIPE X.25 support CONFIG_WANPIPE_X25 Say Y to this option if you are planning to connect a WANPIPE card to an X.25 network. Note, this feature also includes the X.25 API support used to develope custom applications over the X.25 protocol. - If you say N, the X.25 support will not be included in the driver. + If you say N, the X.25 support will not be included in the driver. The X.25 option is supported on S514-PCI and S508-ISA cards. WANPIPE Frame Relay support CONFIG_WANPIPE_FR Say Y to this option if you are planning to connect a WANPIPE card - to a frame relay network, or use frame relay API to develope - custom applications over the Frame Relay protocol. + to a frame relay network, or use frame relay API to develope + custom applications over the Frame Relay protocol. This feature also contains the Ethernet Bridging over Frame Relay, - where a WANPIPE frame relay link can be directly connected to the Linux - kernel bridge. If you say N, the frame relay support will - not be included in the driver. The Frame Relay option is + where a WANPIPE frame relay link can be directly connected to the + Linux kernel bridge. If you say N, the frame relay support will + not be included in the driver. The Frame Relay option is supported on S514-PCI and S508-ISA cards. WANPIPE PPP support @@ -8474,70 +9473,71 @@ the PPP support will not be included in the driver. The PPP option is supported on S514-PCI/S508-ISA cards. -WANPIPE MultiPort PPP support +WANPIPE Multi-Port PPP support CONFIG_WANPIPE_MULTPPP Say Y to this option if you are planning to connect a WANPIPE card - to a leased line using Point-to-Point protocol (PPP). Note, the - MultiPort PPP uses the Linux Kernel SyncPPP protocol over the Sangoma - HDLC Streaming adapter. In this case each Sangoma adapter port - can support an independent PPP connection. For example, a single - Quad-Port PCI adapter can support up to four independent - PPP links. If you say N,the PPP support will not be included - in the driver. The PPP option is supported on S514-PCI/S508-ISA cards. - + to a leased line using Point-to-Point protocol (PPP). Note, the + MultiPort PPP uses the Linux Kernel SyncPPP protocol over the + Sangoma HDLC Streaming adapter. In this case each Sangoma adapter + port can support an independent PPP connection. For example, a + single Quad-Port PCI adapter can support up to four independent + PPP links. If you say N,the PPP support will not be included in the + driver. The PPP option is supported on S514-PCI/S508-ISA cards. + WANPIPE Cisco HDLC support CONFIG_WANPIPE_CHDLC Say Y to this option if you are planning to connect a WANPIPE card to a leased line using the Cisco HDLC protocol. This now supports - Dual Port Cisco HDLC on the S514-PCI/S508-ISA cards. - This support also allows user to build applications using the - HDLC streaming API. - + Dual Port Cisco HDLC on the S514-PCI/S508-ISA cards. + This support also allows user to build applications using the + HDLC streaming API. + CHDLC Streaming driver also supports MULTILINK PPP support that can bind multiple WANPIPE T1 cards into a single logical channel. - - If you say N, the Cisco HDLC support and - HDLC streaming API and MULTILINK PPP will not be + + If you say N, the Cisco HDLC support and + HDLC streaming API and MULTILINK PPP will not be included in the driver. -MultiGate/COMX support +MultiGate (COMX) synchronous serial board support CONFIG_COMX Say Y if you want to use any board from the MultiGate (COMX) family. These boards are synchronous serial adapters for the PC, manufactured by ITConsult-Pro Co, Hungary. - Read linux/Documentation/networking/comx.txt for help on configuring - and using COMX interfaces. Further info on these cards can be found - at http://www.itc.hu or . + Read for help on + configuring and using COMX interfaces. Further info on these cards + can be found at or . You must say Y to "/proc file system support" (CONFIG_PROC_FS) to use this driver. If you want to compile this as a module, say M and read - Documentation/modules.txt. The module will be called comx.o. + . The module will be called comx.o. -COMX/CMX/HiCOMX board support +Support for COMX/CMX/HiCOMX boards CONFIG_COMX_HW_COMX Hardware driver for the 'CMX', 'COMX' and 'HiCOMX' boards from the - MultiGate family. Say Y if you have one of these. + MultiGate family. Say Y if you have one of these. You will need additional firmware to use these cards, which are - downloadable from ftp://ftp.itc.hu/. + downloadable from . If you want to compile this as a module, say M and read - Documentation/modules.txt. The module will be called comx-hw-comx.o. + . The module will be called + comx-hw-comx.o. -LoCOMX board support +Support for LoCOMX board CONFIG_COMX_HW_LOCOMX Hardware driver for the 'LoCOMX' board from the MultiGate family. Say Y if you have a board like this. If you want to compile this as a module, say M and read - Documentation/modules.txt. The module will be called + . The module will be called comx-hw-locomx.o. -MixCOM board support +Support for MixCOM board CONFIG_COMX_HW_MIXCOM Hardware driver for the 'MixCOM' board from the MultiGate family. Say Y if you have a board like this. @@ -8547,63 +9547,63 @@ configuration. The ISDN interface of this card is Teles 16.3 compatible, you should enable it in the ISDN configuration menu. The driver for the flash ROM of this card is available separately on - ftp://ftp.itc.hu/. + . If you want to compile this as a module, say M and read - Documentation/modules.txt. The module will be called + . The module will be called comx-hw-mixcom.o. -i810 TCO support +i810 TCO timer/watchdog support CONFIG_I810_TCO Hardware driver for the TCO timer built into the Intel i810 and i815 - chipset family. The TCO (Total Cost of Ownership) timer is a watchdog - timer that will reboot the machine after it's second expiration. The - expiration time can be configured by commandline argument - "i810_margin=" where is the counter initial value. It is - decremented every 0.6 secs, the default is 50 which gives a timeout - of 30 seconds and one minute until reset. + chipset family. The TCO (Total Cost of Ownership) timer is a + watchdog timer that will reboot the machine after its second + expiration. The expiration time can be configured by commandline + argument "i810_margin=" where is the counter initial value. + It is decremented every 0.6 secs, the default is 50 which gives a + timeout of 30 seconds and one minute until reset. On some motherboards the driver may fail to reset the chipset's - NO_REBOOT flag which prevents the watchdog from rebooting the machine. - If this is the case you will get a kernel message like + NO_REBOOT flag which prevents the watchdog from rebooting the + machine. If this is the case you will get a kernel message like "i810tco init: failed to reset NO_REBOOT flag". If you want to compile this as a module, say M and read - Documentation/modules.txt. The module will be called + . The module will be called i810-tco.o. -MultiGate Cisco-HDLC and synchronous PPP protocol support +Support for HDLC and syncPPP protocols on MultiGate boards CONFIG_COMX_PROTO_PPP Cisco-HDLC and synchronous PPP protocol driver for all MultiGate boards. Say Y if you want to use either protocol on your MultiGate boards. If you want to compile this as a module, say M and read - Documentation/modules.txt. The module will be called + . The module will be called comx-proto-ppp.o. -MultiGate LAPB protocol support +Support for LAPB protocol on MultiGate boards CONFIG_COMX_PROTO_LAPB - LAPB protocol driver for all MultiGate boards. Say Y if you + LAPB protocol driver for all MultiGate boards. Say Y if you want to use this protocol on your MultiGate boards. If you want to compile this as a module, say M and read - Documentation/modules.txt. The module will be called + . The module will be called comx-proto-lapb.o. -MultiGate Frame Relay protocol support +Support for Frame Relay on MultiGate boards CONFIG_COMX_PROTO_FR - Frame Relay protocol driver for all MultiGate boards. Say Y if you + Frame Relay protocol driver for all MultiGate boards. Say Y if you want to use this protocol on your MultiGate boards. If you want to compile this as a module, say M and read - Documentation/modules.txt. The module will be called + . The module will be called comx-proto-fr.o. -Cyclom 2X(tm) multiprotocol cards (EXPERIMENTAL) +Cyclom 2X(tm) multiprotocol cards CONFIG_CYCLADES_SYNC - Cyclom 2X from Cyclades Corporation (http://www.cyclades.com and - http://www.cyclades.com.br) is an intelligent multiprotocol WAN + Cyclom 2X from Cyclades Corporation ( and + is an intelligent multiprotocol WAN adapter with data transfer rates up to 512 Kbps. These cards support the X.25 and SNA related protocols. If you have one or more of these cards, say Y to this option. The next questions will ask you about @@ -8611,10 +9611,11 @@ supported). While no documentation is available at this time please grab the - wanconfig tarball in http://www.conectiva.com.br/~acme/cycsyn-devel - (with minor changes to make it compile with the current wanrouter - include files; efforts are being made to use the original package - available at ftp://ftp.sangoma.com ). + wanconfig tarball in + (with minor changes + to make it compile with the current wanrouter include files; efforts + are being made to use the original package available at + ). Feel free to contact me or the cycsyn-devel mailing list at acme@conectiva.com.br and cycsyn-devel@bazar.conectiva.com.br for @@ -8624,12 +9625,12 @@ The driver will be compiled as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called cyclomx.o. For general information about - modules read Documentation/modules.txt. + modules read . Cyclom 2X X.25 support CONFIG_CYCLOMX_X25 Say Y to this option if you are planning to connect a Cyclom 2X card - to an X.25 network. + to an X.25 network. If you say N, the X.25 support will not be included in the driver (saves about 11 KB of kernel memory). @@ -8639,41 +9640,69 @@ Say Y to this option if your Linux box contains a WAN card supported by this driver and you are planning to connect the box to a WAN ( = Wide Area Network). You will need supporting software from - http://hq.pm.waw.pl/hdlc/. + . Generic HDLC driver currently supports raw HDLC, Cisco HDLC, Frame Relay, synchronous Point-to-Point Protocol (PPP) and X.25. If unsure, say N here. -Synchronous Point-to-Point Protocol (PPP) +Raw HDLC support +CONFIG_HDLC_RAW + Say Y to this option if you want generic HDLC driver to support + raw HDLC over WAN (Wide Area Network) connections. + + If unsure, say N here. + +Cisco HDLC support +CONFIG_HDLC_CISCO + Say Y to this option if you want generic HDLC driver to support + Cisco HDLC over WAN (Wide Area Network) connections. + + If unsure, say N here. + +Frame-Relay HDLC support +CONFIG_HDLC_FR + Say Y to this option if you want generic HDLC driver to support + Frame-Relay protocol over WAN (Wide Area Network) connections. + + If unsure, say N here. + +Frame-Relay bridging support +CONFIG_HDLC_FR_BRIDGE + Say Y to this option if you want generic HDLC driver to support + bridging LAN frames over Frame-Relay links. + + If unsure, say N here. + +Synchronous Point-to-Point Protocol (PPP) support CONFIG_HDLC_PPP Say Y to this option if you want generic HDLC driver to support PPP over WAN (Wide Area Network) connections. If unsure, say N here. -CCITT X.25 protocol +CCITT X.25 over HDLC support CONFIG_HDLC_X25 Say Y to this option if you want generic HDLC driver to support X.25 protocol over WAN (Wide Area Network) connections. If unsure, say N here. -SDL RISCom/N2 driver +SDL RISCom/N2 support CONFIG_N2 This driver is for RISCom/N2 single or dual channel ISA cards - made by SDL Communications Inc. If you have such a card, - say Y here and see http://hq.pm.waw.pl/pub/hdlc/ + made by SDL Communications Inc. If you have such a card, + say Y here and see . Note that N2csu and N2dds cards are not supported by this driver. If unsure, say N here. -Moxa C101 driver +Moxa C101 support CONFIG_C101 This driver is for C101 SuperSync ISA cards made by Moxa Technologies Co., Ltd. If you have such a card, - say Y here and see http://hq.pm.waw.pl/pub/hdlc/ + say Y here and see If unsure, say N here. @@ -8695,21 +9724,21 @@ If your Linux machine will be connected to an Ethernet and you have an Ethernet network interface card (NIC) installed in your computer, say Y here and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . You will then also have + . You will then also have to say Y to the driver for your particular NIC. Note that the answer to this question won't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions about Ethernet network cards. If unsure, say N. Western Digital/SMC cards CONFIG_NET_VENDOR_SMC If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions about Western Digital cards. If you say Y, you will be asked for your specific card in the following questions. @@ -8717,32 +9746,32 @@ CONFIG_WD80x3 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called wd.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called wd.o. If you want to compile it as a + module, say M here and read as well + as . SMC Ultra MCA support CONFIG_ULTRAMCA If you have a network (Ethernet) card of this type and are running an MCA based system (PS/2), say Y and read the Ethernet-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called smc-mca.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called smc-mca.o. If you want to compile it as a + module, say M here and read as well + as . SMC Ultra support CONFIG_ULTRA If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . - + . + Important: There have been many reports that, with some motherboards mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible, such as some BusLogic models) causes corruption problems with many @@ -8752,44 +9781,44 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called smc-ultra.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called smc-ultra.o. If you want to compile it as + a module, say M here and read as + well as . SMC Ultra32 EISA support CONFIG_ULTRA32 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called smc-ultra32.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt as well - as Documentation/networking/net-modules.txt. + The module will be called smc-ultra32.o. If you want to compile it + as a module, say M here and read as + well as . -SMC 9194 Support +SMC 9194 support CONFIG_SMC9194 This is support for the SMC9xxx based Ethernet cards. Choose this option if you have a DELL laptop with the docking station, or - another SMC9192/9194 based chipset. Say Y if you want it compiled + another SMC9192/9194 based chipset. Say Y if you want it compiled into the kernel, and read the file - Documentation/networking/smc9.txt and the Ethernet-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + and the Ethernet-HOWTO, + available from . This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you - want). The module will be called smc9194.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt as - well as Documentation/networking/net-modules.txt. + inserted in and removed from the running kernel whenever you want). + The module will be called smc9194.o. If you want to compile it as a + module, say M here and read as well + as . -PCI NE2000 support +PCI NE2000 and clones support CONFIG_NE2K_PCI This driver is for NE2000 compatible PCI cards. It will not work with ISA NE2000 cards (they have their own driver, "NE2000/NE1000 support" below). If you have a PCI NE2000 network (Ethernet) card, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver also works for the following NE2000 clone cards: RealTek RTL-8029 Winbond 89C940 Compex RL2000 KTI ET32P2 @@ -8798,77 +9827,79 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called ne2k-pci.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called ne2k-pci.o. If you want to compile it as + a module, say M here and read as + well as . Racal-Interlan (Micom) NI cards CONFIG_NET_VENDOR_RACAL If you have a network (Ethernet) card belonging to this class, such as the NI5010, NI5210 or NI6210, say Y and read the Ethernet-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions about NI cards. If you say Y, you will be asked for your specific card in the following questions. -NI5010 support (EXPERIMENTAL) +NI5010 support CONFIG_NI5010 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Note that this is still + . Note that this is still experimental code. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called ni5010.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called ni5010.o. If you want to compile it as a + module, say M here and read as well + as . NI5210 support CONFIG_NI52 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called ni52.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called ni52.o. If you want to compile it as a + module, say M here and read as well + as . NI6510 support CONFIG_NI65 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called ni65.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called ni65.o. If you want to compile it as a + module, say M here and read as well + as . RealTek RTL-8139 PCI Fast Ethernet Adapter support CONFIG_8139TOO This is a driver for the Fast Ethernet PCI network cards based on the RTL8139 chips. If you have one of those, say Y and read - Documentation/networking/8139too.txt as well as the Ethernet-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + as well as the + Ethernet-HOWTO, available from + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called 8139too.o. + say M here and read . This is + recommended. The module will be called 8139too.o. Use PIO instead of MMIO CONFIG_8139TOO_PIO This instructs the driver to use programmed I/O ports (PIO) instead - of PCI shared memory (MMIO). This can possibly solve some problems in - case your mainboard has memory consistency issues. If unsure, say N. + of PCI shared memory (MMIO). This can possibly solve some problems + in case your mainboard has memory consistency issues. If unsure, + say N. -Support for automatic channel equalization (EXPERIMENTAL) +Support for automatic channel equalization CONFIG_8139TOO_TUNE_TWISTER This implements a function which might come in handy in case you are using low quality on long cabling. It tries to match the transceiver @@ -8878,27 +9909,27 @@ Support for older RTL-8129/8130 boards CONFIG_8139TOO_8129 This enables support for the older and uncommon RTL-8129 and - RTL-8130 chips, which support MII via an external transceiver, instead - of an internal one. Disabling this option will save some memory - by making the code size smaller. If unsure, say Y. + RTL-8130 chips, which support MII via an external transceiver, + instead of an internal one. Disabling this option will save some + memory by making the code size smaller. If unsure, say Y. -SiS 900 PCI Fast Ethernet Adapter support +SiS 900/7016 PCI Fast Ethernet Adapter support CONFIG_SIS900 This is a driver for the Fast Ethernet PCI network cards based on the SiS 900 and SiS 7016 chips. The SiS 900 core is also embedded in - SiS 630 and SiS 540 chipsets. If you have one of those, say Y and + SiS 630 and SiS 540 chipsets. If you have one of those, say Y and read the Ethernet-HOWTO, available at - http://www.linuxdoc.org/docs.html#howto . Please read - Documentation/networking/sis900.txt and comments at the beginning of - drivers/net/sis900.c for more information. + . Please read + and comments at the + beginning of for more information. This driver also supports AMD 79C901 HomePNA so that you can use your phone line as a network cable. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called sis900.o. + say M here and read . This is + recommended. The module will be called sis900.o. Packet Engines Yellowfin Gigabit-NIC / Symbios 53c885 support CONFIG_YELLOWFIN @@ -8910,8 +9941,8 @@ If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called yellowfin.o. + say M here and read . This is + recommended. The module will be called yellowfin.o. General Instruments Surfboard 1000 CONFIG_NET_SB1000 @@ -8924,29 +9955,29 @@ At present this driver only compiles as a module, so say M here if you have this card. The module will be called sb1000.o. Then read - Documentation/networking/README.sb1000 for information on how to use - this module, as it needs special ppp scripts for establishing a - connection. Further documentation and the necessary scripts can be + for information on how + to use this module, as it needs special ppp scripts for establishing + a connection. Further documentation and the necessary scripts can be found at: - http://www.jacksonville.net/~fventuri/ - http://home.adelphia.net/~siglercm/sb1000.html - http://linuxpower.cx/~cable/ + + + If you don't have this card, of course say N. -Adaptec Starfire support (EXPERIMENTAL) +Adaptec Starfire support CONFIG_ADAPTEC_STARFIRE Say Y here if you have an Adaptec Starfire (or DuraLAN) PCI network adapter. The DuraLAN chip is used on the 64 bit PCI boards from Adaptec e.g. the ANA-6922A. The older 32 bit boards use the tulip driver. - + If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called starfire.o. - + say M here and read . This is + recommended. The module will be called starfire.o. + Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support CONFIG_ACENIC Say Y here if you have an Alteon AceNIC, 3Com 3C985(B), NetGear @@ -8958,16 +9989,16 @@ If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called acenic.o. + say M here and read . This is + recommended. The module will be called acenic.o. -Omit support for older Tigon I based AceNICs +Omit support for old Tigon I based AceNICs CONFIG_ACENIC_OMIT_TIGON_I Say Y here if you only have Tigon II based AceNICs and want to leave out support for the older Tigon I based cards which are no longer being sold (ie. the original Alteon AceNIC and 3Com 3C985 (non B - version)). This will reduce the size of the driver object by - app. 100KB. If you are not sure whether your card is a Tigon I or a + version)). This will reduce the size of the driver object by + app. 100KB. If you are not sure whether your card is a Tigon I or a Tigon II, say N here. The safe and default value for this is N. @@ -8987,148 +10018,170 @@ The driver also supports the following adapters from Allied Telesyn: - AT2970... - The dual link adapters support a link-failover feature. - Read Documentation/networking/sk98lin.txt for information about + The dual link adapters support a link-failover feature. Read + for information about optional driver parameters. Questions concerning this driver may be addressed to: linux@syskonnect.de If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called sk98lin.o. + say M here and read . This is + recommended. The module will be called sk98lin.o. + +Sun GEM support +CONFIG_SUNGEM + Support for the Sun GEM chip, aka Sun GigabitEthernet/P 2.0. See also + . MyriCOM Gigabit Ethernet support CONFIG_MYRI_SBUS - This driver supports MyriCOM Sbus gigabit ethernet cards. + This driver supports MyriCOM Sbus gigabit Ethernet cards. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called myri_sbus.o. + say M here and read . This is + recommended. The module will be called myri_sbus.o. D-Link 2000-based Gigabit Ethernet support CONFIG_DL2K This driver supports D-Link 2000-based gigabit ethernet cards, which includes - D-Link DGE-550T Gigabit Ethernet Adapter. - D-Link DL2000-based Gigabit Ethernet Adapter. + D-Link DGE-550T Gigabit Ethernet Adapter. + D-Link DL2000-based Gigabit Ethernet Adapter. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called dl2k.o. + say M here and read . This is + recommended. The module will be called dl2k.o. AMD LANCE and PCnet (AT1500 and NE2100) support CONFIG_LANCE If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Some LinkSys cards are + . Some LinkSys cards are of this type. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called lance.o. + say M here and read . This is + recommended. The module will be called lance.o. SGI IOC3 Ethernet CONFIG_SGI_IOC3_ETH If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . + +National Semiconductor DP83902AV support +CONFIG_STNIC + Support for cards based on the National Semiconductor DP83902AV + ST-NIC Serial Network Interface Controller for Twisted Pair. This + is a 10Mbit/sec Ethernet controller. Product overview and specs at + . + +CompactFlash Connection Area +CONFIG_CF_AREA5 + If your board has "Directly Connected" CompactFlash, You should + select the area where your CF is connected to. + + - "Area5" if CompactFlash is connected to Area 5 (0x14000000) + - "Area6" if it is connected to Area 6 (0x18000000) + + "Area6" will work for most boards. For ADX, select "Area5". 3COM cards CONFIG_NET_VENDOR_3COM If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions about 3COM cards. If you say Y, you will be asked for your specific card in the following questions. -3c501 support +3c501 "EtherLink" support CONFIG_EL1 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Also, consider buying a + . Also, consider buying a new card, since the 3c501 is slow, broken, and obsolete: you will - have problems. Some people suggest to ping ("man ping") a nearby + have problems. Some people suggest to ping ("man ping") a nearby machine every minute ("man cron") when using this card. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called 3c501.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called 3c501.o. If you want to compile it as a + module, say M here and read as well + as . -3c503 support +3c503 "EtherLink II" support CONFIG_EL2 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called 3c503.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called 3c503.o. If you want to compile it as a + module, say M here and read as well + as . -3c505 support +3c505 "EtherLink Plus" support CONFIG_ELPLUS Information about this network (Ethernet) card can be found in - Documentation/networking/3c505.txt. If you have a card of this type, - say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you have a card of + this type, say Y and read the Ethernet-HOWTO, available from + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - 3c505.o. + say M here and read as well as + . The module will be + called 3c505.o. -3c507 support (EXPERIMENTAL) +3c507 (EtherLink 16) support CONFIG_EL16 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called 3c507.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called 3c507.o. If you want to compile it as a + module, say M here and read as well + as . -3c523 support +3c523 "EtherlinkMC" support CONFIG_ELMC If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called 3c523.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called 3c523.o. If you want to compile it as a + module, say M here and read as well + as . -3c527 support +3c527 "EtherLink/MC 32" support CONFIG_ELMC_II If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called 3c527.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called 3c527.o. If you want to compile it as a + module, say M here and read as well + as . -3c509/3c579 support +3c509/3c529 (MCA)/3c579 "EtherLink III" support CONFIG_EL3 If you have a network (Ethernet) card belonging to the 3Com EtherLinkIII series, say Y and read the Ethernet-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + from . If your card is not working you may need to use the DOS setup disk to disable Plug & Play mode, and to select the default @@ -9136,23 +10189,23 @@ If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - 3c509.o. + say M here and read as well as + . The module will be + called 3c509.o. 3c515 ISA Fast EtherLink CONFIG_3C515 If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet network card, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - 3c515.o. + say M here and read as well as + . The module will be + called 3c515.o. -3c59x/3c90x/3c575_Cardbus series "Vortex/Boomerang/Cyclone" support +3c590/3c900 series (592/595/597) "Vortex/Boomerang/Cyclone" support CONFIG_VORTEX This option enables driver support for a large number of 10mbps and 10/100mbps EISA, PCI and PCMCIA 3Com network cards: @@ -9163,15 +10216,16 @@ "Tornado" (3c905) PCI "Hurricane" (3c555/3cSOHO) PCI - If you have such a card, say Y and read the Ethernet-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . More specific - information is in Documentation/networking/vortex.txt and in the - comments at the beginning of drivers/net/3c59x.c. + If you have such a card, say Y and read the Ethernet-HOWTO, + available from . More + specific information is in + and in the comments at + the beginning of . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + say M here and read as well as + . Other ISA cards CONFIG_NET_ISA @@ -9179,19 +10233,20 @@ bus system (that's the way the cards talks to the other components of your computer) is ISA (as opposed to EISA, VLB or PCI), say Y. Make sure you know the name of your card. Read the Ethernet-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . If unsure, say Y. Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the remaining ISA network card questions. If you say Y, you will be asked for your specific card in the following questions. Generic ARCnet support CONFIG_ARCNET If you have a network card of this type, say Y and check out the - (arguably) beautiful poetry in Documentation/networking/arcnet.txt. + (arguably) beautiful poetry in + . You need both this driver, and the driver for the particular ARCnet chipset of your card. If you don't know, then it's probably a @@ -9199,26 +10254,14 @@ support" below. You might also want to have a look at the Ethernet-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto (even though ARCnet + from (even though ARCnet is not really Ethernet). This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called arcnet.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. - -Enable arc0e (ARCnet "ether-encap" packet format) -CONFIG_ARCNET_ETH - This allows you to use "Ethernet encapsulation" with your ARCnet - card via the virtual arc0e device. You only need arc0e if you want - to talk to nonstandard ARCnet software, specifically, - DOS/Windows-style "NDIS" drivers. You do not need to say Y here to - communicate with industry-standard RFC1201 implementations, like the - arcether.com packet driver or most DOS/Windows ODI drivers. RFC1201 - is included automatically as the arc0 device. Please read the - ARCnet documentation in Documentation/networking/arcnet.txt for more - information about using arc0e and arc0s. + The module will be called arcnet.o. If you want to compile it as a + module, say M here and read as well + as . Enable old ARCNet packet format (RFC 1051) CONFIG_ARCNET_1051 @@ -9230,33 +10273,48 @@ industry-standard RFC1201 implementations, like the arcether.com packet driver or most DOS/Windows ODI drivers. RFC1201 is included automatically as the arc0 device. Please read the ARCnet - documentation in Documentation/networking/arcnet.txt for more + documentation in for more information about using arc0e and arc0s. +Enable standard ARCNet packet format (RFC 1201) +CONFIG_ARCNET_1201 + This allows you to use RFC1201 with your ARCnet card via the virtual + arc0 device. You need to say Y here to communicate with + industry-standard RFC1201 implementations, like the arcether.com + packet driver or most DOS/Windows ODI drivers. Please read the + ARCnet documentation in + for more information about using arc0. + +Enable raw mode packet interface +CONFIG_ARCNET_RAW + ARCnet "raw mode" packet encapsulation, no soft headers. Unlikely + to work unless talking to a copy of the same Linux arcnet driver, + but perhaps marginally faster in that case. + ARCnet COM90xx (normal) chipset driver CONFIG_ARCNET_COM90xx This is the chipset driver for the standard COM90xx cards. If you have always used the old ARCnet driver without knowing what type of - card you had, this is probably the one for you. + card you had, this is probably the one for you. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called com90xx.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called com90xx.o. If you want to compile it as a + module, say M here and read as well + as . ARCnet COM90xx (IO mapped) chipset driver CONFIG_ARCNET_COM90xxIO This is the chipset driver for the COM90xx cards, using them in IO-mapped mode instead of memory-mapped mode. This is slower than the normal driver. Only use it if your card doesn't support shared - memory. + memory. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called com90io.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called com90io.o. If you want to compile it as a + module, say M here and read as well + as . ARCnet COM90xx (RIM I) chipset driver CONFIG_ARCNET_RIM_I @@ -9267,197 +10325,198 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you - want). The module will be called arc-rimi.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt as - well as Documentation/networking/net-modules.txt. + want). The module will be called arc-rimi.o. If you want to compile + it as a module, say M here and read + as well as . ARCnet COM20020 chipset driver CONFIG_ARCNET_COM20020 This is the driver for the new COM20020 chipset. It supports such things as promiscuous mode, so packet sniffing is possible, and - extra diagnostic information. + extra diagnostic information. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called com20020.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called com20020.o. If you want to compile it as + a module, say M here and read as + well as . Cabletron E21xx support CONFIG_E2100 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called e2100.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called e2100.o. If you want to compile it as a + module, say M here and read as well + as . -CS89x0 support +CS89x0 support (Daynaport CS and LC cards) CONFIG_CS89x0 Support for CS89x0 chipset based Ethernet cards. If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto as well as - Documentation/networking/cs89x0.txt. + as well as + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - cs89x.o. + say M here and read as well as + . The module will be + called cs89x.o. -DEPCA support +DEPCA, DE10x, DE200, DE201, DE202, DE422 support CONFIG_DEPCA If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto as well as - drivers/net/depca.c. + as well as + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called + say M here and read as well as + . The module will be + called depca.o. -EtherWorks 3 support +EtherWORKS 3 (DE203, DE204, DE205) support CONFIG_EWRK3 This driver supports the DE203, DE204 and DE205 network (Ethernet) cards. If this is for you, say Y and read - Documentation/networking/ewrk3.txt in the kernel source as well as - the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + in the kernel source as + well as the Ethernet-HOWTO, available from + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - ewrk3.o. + say M here and read as well as + . The module will be + called ewrk3.o. SEEQ8005 support CONFIG_SEEQ8005 - This is a driver for the SEEQ 8005 network (Ethernet) card. If this + This is a driver for the SEEQ 8005 network (Ethernet) card. If this is for you, read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - ewrk3.o. + say M here and read as well as + . The module will be + called ewrk3.o. AT1700/1720 support CONFIG_AT1700 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - at1700.o. + as well as + . The module will be + called at1700.o. FMV-181/182/183/184 support CONFIG_FMV18X If you have a Fujitsu FMV-181/182/183/184 network (Ethernet) card, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you use an FMV-183 or FMV-184 and it is not working, you may need to disable Plug & Play mode of the card. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called fmv18x.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called fmv18x.o. If you want to compile it as a + module, say M here and read as well + as . -EtherExpress PRO support +EtherExpressPro support/EtherExpress 10 (i82595) support CONFIG_EEXPRESS_PRO If you have a network (Ethernet) card of this type, say Y. This - driver supports intel i82595{FX,TX} based boards. Note however + driver supports intel i82595{FX,TX} based boards. Note however that the EtherExpress PRO/100 Ethernet card has its own separate - driver. Please read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + driver. Please read the Ethernet-HOWTO, available from + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called eepro.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called eepro.o. If you want to compile it as a + module, say M here and read as well + as . -EtherExpress support +EtherExpress 16 support CONFIG_EEXPRESS If you have an EtherExpress16 network (Ethernet) card, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Note that the Intel + . Note that the Intel EtherExpress16 card used to be regarded as a very poor choice because the driver was very unreliable. We now have a new driver that should do better. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - eexpress.o. + say M here and read as well as + . The module will be + called eexpress.o. Packet Engines Hamachi GNIC-II support CONFIG_HAMACHI If you have a Gigabit Ethernet card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - hamachi.o. + say M here and read as well as + . The module will be + called hamachi.o. HP PCLAN+ (27247B and 27252A) support CONFIG_HPLAN_PLUS If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called hp-plus.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called hp-plus.o. If you want to compile it as a + module, say M here and read as well + as . HP PCLAN (27245 and other 27xxx series) support CONFIG_HPLAN If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called hp.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called hp.o. If you want to compile it as a + module, say M here and read as well + as . HP 10/100VG PCLAN (ISA, EISA, PCI) support CONFIG_HP100 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - hp100.o. + say M here and read as well as + . The module will be + called hp100.o. NE2000/NE1000 support CONFIG_NE2000 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Many Ethernet cards + . Many Ethernet cards without a specific driver are compatible with NE2000. If you have a PCI NE2000 card however, say N here and Y to "PCI @@ -9468,74 +10527,76 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called ne.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called ne.o. If you want to compile it as a + module, say M here and read as well + as . -National Semiconductor DP83810 series PCI Ethernet support +National Semiconductor DP8381x series PCI Ethernet support CONFIG_NATSEMI This driver is for the National Semiconductor DP83810 series, - including the 83815 chip. - More specific information and updates are available from - http://www.scyld.com/network/natsemi.html + which is used in cards from PureData, NetGear, Linksys + and others, including the 83815 chip. + More specific information and updates are available from + . SK_G16 support CONFIG_SK_G16 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . NE/2 (ne2000 MCA version) support CONFIG_NE2_MCA If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called ne2.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called ne2.o. If you want to compile it as a + module, say M here and read as well + as . SKnet MCA support CONFIG_SKMC - These are Micro Channel ethernet adapters. You need to say Y to "MCA - support" in order to use this driver. Supported cards are the SKnet - Junior MC2 and the SKnet MC2(+). The driver automatically + These are Micro Channel Ethernet adapters. You need to say Y to "MCA + support" in order to use this driver. Supported cards are the SKnet + Junior MC2 and the SKnet MC2(+). The driver automatically distinguishes between the two cards. Note that using multiple boards - of different type hasn't been tested with this driver. Say Y if you - have one of these ethernet adapters. + of different type hasn't been tested with this driver. Say Y if you + have one of these Ethernet adapters. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module is called sk_mca.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module is called sk_mca.o. If you want to compile it as a + module, say M here and read as well + as . IBM LAN Adapter/A support CONFIG_IBMLANA - This is a Micro Channel ethernet adapter. You need to set CONFIG_MCA - to use this driver. It is both available as an in-kernel driver and - as a module ( = code which can be inserted in and removed from the - running kernel whenever you want). If you want to compile it as a module, - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more than - one network card under linux, read the Multiple-Ethernet-mini-HOWTO, - available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. The only - currently supported card is the IBM LAN Adapter/A for Ethernet. It will - both support 16K and 32K memory windows, however a 32K window gives - a better security against packet losses. Usage of multiple boards with - this driver should be possible, but has not been tested up to now due - to lack of hardware. + This is a Micro Channel Ethernet adapter. You need to set + CONFIG_MCA to use this driver. It is both available as an in-kernel + driver and as a module ( = code which can be inserted in and removed + from the running kernel whenever you want). If you want to compile + it as a module, say M here and read + as well as . + If you plan to use more than one network card under linux, read the + Multiple-Ethernet-mini-HOWTO, available from + . The only + currently supported card is the IBM LAN Adapter/A for Ethernet. It + will both support 16K and 32K memory windows, however a 32K window + gives a better security against packet losses. Usage of multiple + boards with this driver should be possible, but has not been tested + up to now due to lack of hardware. EISA, VLB, PCI and on board controllers CONFIG_NET_PCI This is another class of network cards which attach directly to the bus. If you have one of those, say Y and read the Ethernet-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions about this class of network cards. If you say Y, you will be asked for your specific card in the following questions. If you are unsure, say Y. @@ -9544,98 +10605,100 @@ CONFIG_PCNET32 If you have a PCnet32 or PCnetPCI based network (Ethernet) card, answer Y here and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called pcnet32.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called pcnet32.o. If you want to compile it as a + module, say M here and read as well + as . Ansel Communications EISA 3200 support CONFIG_AC3200 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called ac3200.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called ac3200.o. If you want to compile it as a + module, say M here and read as well + as . -Mylex EISA LNE390A/LNE390B support (EXPERIMENTAL) +Mylex EISA LNE390A/LNE390B support CONFIG_LNE390 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called lne390.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called lne390.o. If you want to compile it as a + module, say M here and read as well + as . Novell/Eagle/Microdyne NE3210 EISA support CONFIG_NE3210 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Note that this driver + . Note that this driver will NOT WORK for NE3200 cards as they are completely different. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called ne3210.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called ne3210.o. If you want to compile it as a + module, say M here and read as well + as . Apricot Xen-II on board Ethernet CONFIG_APRICOT If you have a network (Ethernet) controller of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - apricot.o. + say M here and read as well as + . The module will be + called apricot.o. Generic DECchip & DIGITAL EtherWORKS PCI/EISA CONFIG_DE4X5 This is support for the DIGITAL series of PCI/EISA Ethernet cards. - These include the DE425, DE434, DE435, DE450 and DE500 models. If + These include the DE425, DE434, DE435, DE450 and DE500 models. If you have a network card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . More specific - information is contained in Documentation/networking/de4x5.txt. + . More specific + information is contained in + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called de4x5.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called de4x5.o. If you want to compile it as a + module, say M here and read as well + as . DECchip Tulip (dc21x4x) PCI support CONFIG_TULIP This driver is developed for the SMC EtherPower series Ethernet - cards and also works with cards based on the DECchip - 21040/21041/21140 (Tulip series) chips. Some LinkSys PCI cards are - of this type. (If your card is NOT SMC EtherPower 10/100 PCI + cards and also works with cards based on the DECchip + 21040/21041/21140 (Tulip series) chips. Some LinkSys PCI cards are + of this type. (If your card is NOT SMC EtherPower 10/100 PCI (smc9332dst), you can also try the driver for "Generic DECchip" - cards, above. However, most people with a network card of this type + cards, above. However, most people with a network card of this type will say Y here.) Do read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . More specific - information is contained in Documentation/networking/tulip.txt. + . More specific + information is contained in + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called tulip.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called tulip.o. If you want to compile it as a + module, say M here and read as well + as . -New Tulip bus configuration (EXPERIMENTAL) -CONFIG_TULIP_MWI +New Tulip bus configuration +CONFIG_TULIP_MWI This configures your Tulip card specifically for the card and system cache line size type you are using. @@ -9643,34 +10706,34 @@ If unsure, say N. -Digi Intl. RightSwitch support +Digi Intl. RightSwitch SE-X support CONFIG_DGRS This is support for the Digi International RightSwitch series of PCI/EISA Ethernet switch cards. These include the SE-4 and the SE-6 - models. If you have a network card of this type, say Y and read the + models. If you have a network card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . More specific - information is contained in Documentation/networking/dgrs.txt. + . More specific + information is contained in . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called dgrs.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called dgrs.o. If you want to compile it as a + module, say M here and read as well + as . -EtherExpress PRO/100 support +EtherExpress Pro/100 support CONFIG_EEPRO100 If you have an Intel EtherExpress PRO/100 PCI network (Ethernet) card, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called eepro100.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called eepro100.o. If you want to compile it as + a module, say M here and read as + well as . -Enable Power Management (EXPERIMENTAL) +Enable Power Management CONFIG_EEPRO100_PM Many Intel EtherExpress PRO/100 PCI network cards are capable of providing power management capabilities. To make use of these @@ -9682,34 +10745,40 @@ It is recommended to say N here. -ICL EtherTeam 16i/32 support (EXPERIMENTAL) +Myson MTD-8xx PCI Ethernet support +CONFIG_FEALNX + Say Y here to support the Mysom MTD-800 family of PCI-based Ethernet + cards. Specifications and data at + . + +ICL EtherTeam 16i/32 support CONFIG_ETH16I If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called eth16i.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called eth16i.o. If you want to compile it as a + module, say M here and read as well + as . -TI ThunderLAN support (EXPERIMENTAL) +TI ThunderLAN support CONFIG_TLAN If you have a PCI Ethernet network card based on the ThunderLAN chip which is supported by this driver, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Devices currently supported by this driver are Compaq Netelligent, - Compaq NetFlex and Olicom cards. Please read the file - Documentation/networking/tlan.txt for more details. + Compaq NetFlex and Olicom cards. Please read the file + for more details. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called tlan.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called tlan.o. If you want to compile it as a + module, say M here and read as well + as . Please email feedback to torben.mathiasen@compaq.com. @@ -9720,131 +10789,164 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called via-rhine.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called via-rhine.o. If you want to compile it as + a module, say M here and read as + well as . -PCI DM9102(A)/DM9132/DM9801 support +Davicom DM910x/DM980x support CONFIG_DM9102 This driver is for DM9102(A)/DM9132/DM9801 compatible PCI cards from - Davicom ( http://www.davicom.com.tw ). If you have such a network - (Ethernet) card, say Y. Some information is contained in the file - Documentation/networking/dmfe.txt. - + Davicom (). If you have such a network + (Ethernet) card, say Y. Some information is contained in the file + . + This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called dmfe.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called dmfe.o. If you want to compile it as a + module, say M here and read as well + as . -Racal-Interlan EISA ES3210 support (EXPERIMENTAL) +Racal-Interlan EISA ES3210 support CONFIG_ES3210 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called es3210.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + The module will be called es3210.o. If you want to compile it as a + module, say M here and read as well + as . SMC EtherPower II CONFIG_EPIC100 This driver is for the SMC EtherPower II 9432 PCI Ethernet NIC, which is based on the SMC83c17x (EPIC/100). More specific information and updates are available from - http://www.scyld.com/network/epic100.html + . + +DEC LANCE Ethernet controller support +CONFIG_DECLANCE + This driver is for the series of Ethernet controllers produced by + DEC (now Compaq) based on the AMD Lance chipset, including the + DEPCA series. (This chipset is better known via the NE2100 cards.) -SGI Seeq ethernet controller support +SGI Seeq Ethernet controller support CONFIG_SGISEEQ Say Y here if you have an Seeq based Ethernet network card. This is used in many Silicon Graphics machines. -Sundance "Alta" PCI Ethernet support +Sundance Alta PCI Ethernet support CONFIG_SUNDANCE This driver is for the Sundance "Alta" chip. More specific information and updates are available from - http://www.scyld.com/network/sundance.html + . + +Sun3/Sun3x on-board LANCE support +CONFIG_SUN3LANCE + Most Sun3 and Sun3x motherboards (including the 3/50, 3/60 and 3/80) + featured an AMD Lance 10Mbit Ethernet controller on board; say Y + here to compile in the Linux driver for this and enable Ethernet. + General Linux information on the Sun 3 and 3x series (now + discontinued) is at + . + + If you're not building a kernel for a Sun 3, say N. + +Sun3 on-board Intel 82586 support +CONFIG_SUN3_82586 + This driver enables support for the on-board Intel 82586 based + Ethernet adapter found on Sun 3/1xx and 3/2xx motherboards. Note + that this driver does not support 82586-based adapters on additional + VME boards. Winbond W89c840 PCI Ethernet support CONFIG_WINBOND_840 This driver is for the Winbond W89c840 chip. It also works with the TX9882 chip on the Compex RL100-ATX board. More specific information and updates are available from - http://www.scyld.com/network/drivers.html + . -Zenith Z-Note support (EXPERIMENTAL) +Zenith Z-Note support CONFIG_ZNET The Zenith Z-Note notebook computer has a built-in network (Ethernet) card, and this is the Linux driver for it. Note that the IBM Thinkpad 300 is compatible with the Z-Note and is also supported by this driver. Read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . + +Philips SAA9730 Ethernet support +CONFIG_LAN_SAA9730 + The SAA9730 is a combined multimedia and peripheral controller used + in thin clients, Internet access terminals, and diskless + workstations. + See . Pocket and portable adapters CONFIG_NET_POCKET Cute little network (Ethernet) devices which attach to the parallel port ("pocket adapters"), commonly used with laptops. If you have one of those, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to plug a network (or some other) card into the PCMCIA (or PC-card) slot of your laptop instead (PCMCIA is the standard for credit card size extension cards used by all modern laptops), you need the pcmcia-cs package (location contained in the file - Documentation/Changes) and you can say N here. + ) and you can say N here. Laptop users should read the Linux Laptop home page at - http://www.cs.utexas.edu/users/kharker/linux-laptop/ . + . Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions about this class of network devices. If you say Y, you will be asked for your specific device in the following questions. AT-LAN-TEC/RealTek pocket adapter support CONFIG_ATP This is a network (Ethernet) device which attaches to your parallel - port. Read drivers/net/atp.c as well as the Ethernet-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto , if you - want to use this. If you intend to use this driver, you should have + port. Read as well as the Ethernet-HOWTO, + available from , if you + want to use this. If you intend to use this driver, you should have said N to the "Parallel printer support", because the two drivers don't like each other. If you want to compile this driver as a module however ( = code which can be inserted in and removed from the running kernel - whenever you want), say M here and read Documentation/modules.txt. - The module will be called atp.o. + whenever you want), say M here and read + . The module will be called atp.o. D-Link DE600 pocket adapter support CONFIG_DE600 This is a network (Ethernet) device which attaches to your parallel - port. Read Documentation/networking/DLINK.txt as well as the + port. Read as well as the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , if you want to use + , if you want to use this. It is possible to have several devices share a single parallel port and it is safe to compile the corresponding drivers into the kernel. If you want to compile this driver as a module however ( = code which can be inserted in and removed from the running kernel - whenever you want), say M here and read Documentation/modules.txt. + whenever you want), say M here and read + . The module will be called de600.o. D-Link DE620 pocket adapter support CONFIG_DE620 This is a network (Ethernet) device which attaches to your parallel - port. Read Documentation/networking/DLINK.txt as well as the + port. Read as well as the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , if you want to use + , if you want to use this. It is possible to have several devices share a single parallel port and it is safe to compile the corresponding drivers into the kernel. If you want to compile this driver as a module however ( = code which can be inserted in and removed from the running kernel - whenever you want), say M here and read Documentation/modules.txt. + whenever you want), say M here and read + . The module will be called de620.o. Token Ring driver support @@ -9855,41 +10957,41 @@ connected to such a Token Ring network and want to use your Token Ring card under Linux, say Y here and to the driver for your particular card below and read the Token-Ring mini-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . Most people can + from . Most people can say N here. IBM Tropic chipset based adapter support CONFIG_IBMTR This is support for all IBM Token Ring cards that don't use DMA. If you have such a beast, say Y and read the Token-Ring mini-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . Warning: this driver will almost definitely fail if more than one - active Token Ring card is present. + active Token Ring card is present. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called ibmtr.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . IBM Olympic chipset PCI adapter support CONFIG_IBMOL - This is support for all non-Lanstreamer IBM PCI Token Ring Cards. + This is support for all non-Lanstreamer IBM PCI Token Ring Cards. Specifically this is all IBM PCI, PCI Wake On Lan, PCI II, PCI II Wake On Lan, and PCI 100/16/4 adapters. If you have such an adapter, say Y and read the Token-Ring mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called olympic.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + as a module, say M here and read . - Also read the file Documentation/networking/olympic.txt or check the + Also read or check the Linux Token Ring Project site for the latest information at - http://www.linuxtr.net . + . IBM Lanstreamer chipset PCI adapter support CONFIG_IBMLS @@ -9897,12 +10999,12 @@ If you have such an adapter, say Y and read the Token-Ring mini-HOWTO available via FTP (user:anonymous) from - ftp://metalab.unc/edu/pub/Linux/docs/HOWTO. + . This driver is also available as a modules ( = code which can be inserted in and removed from the running kernel whenever you want). The modules will be called lanstreamer.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + as a module, say M here and read . Generic TMS380 Token Ring ISA/PCI/MCA/EISA adapter support CONFIG_TMS380TR @@ -9917,15 +11019,15 @@ If you have such an adapter and would like to use it, say Y and read the Token-Ring mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . - Also read the file Documentation/networking/tms380tr.txt or - check http://www.auk.cx/tms380tr/ . + Also read the file or + check . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called tms380tr.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + as a module, say M here and read . Generic TMS380 PCI support CONFIG_TMSPCI @@ -9940,19 +11042,31 @@ This driver is available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called tmspci.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + as a module, say M here and read . -Madge Smart 16/4 PCI Mk2 support -CONFIG_ABYSS - This tms380 module supports the Madge Smart 16/4 PCI Mk2 - cards (51-02). +Generic TMS380 ISA support +CONFIG_TMSISA + This tms380 module supports generic TMS380-based ISA cards. + + These cards are known to work: + - SysKonnect TR4/16 ISA (SK-4190) + + This driver is available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called tmsisa.o. If you want to compile it + as a module, say M here and read . + +Madge Smart 16/4 PCI Mk2 support +CONFIG_ABYSS + This tms380 module supports the Madge Smart 16/4 PCI Mk2 + cards (51-02). This driver is available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called abyss.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + as a module, say M here and read . -Madge Smart 16/4 Ringode MicroChannel +Madge Smart 16/4 Ringnode MicroChannel CONFIG_MADGEMC This tms380 module supports the Madge Smart 16/4 MC16 and MC32 MicroChannel adapters. @@ -9960,9 +11074,9 @@ This driver is available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called madgemc.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + as a module, say M here and read . -SMC ISA TokenRing adapter support +SMC ISA/MCA Token Ring adapter support CONFIG_SMCTR This is support for the ISA and MCA SMC Token Ring cards, specifically SMC TokenCard Elite (8115T) and SMC TokenCard Elite/A @@ -9970,13 +11084,13 @@ If you have such an adapter and would like to use it, say Y or M and read the Token-Ring mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto and the file - Documentation/networking/smctr.txt. + and the file + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called smctr.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + as a module, say M here and read . Sun Happy Meal 10/100baseT support CONFIG_HAPPYMEAL @@ -9988,60 +11102,62 @@ This support is also available as a module called sunhme.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . Sun Lance support CONFIG_SUNLANCE This driver supports the "le" interface present on all 32-bit Sparc - systems, on some older Ultra systems and as an Sbus option. + systems, on some older Ultra systems and as an Sbus option. These + cards are based on the AMD Lance chipset, which is better known + via the NE2100 cards. This support is also available as a module called sunlance.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . -Sun BigMAC 10/100baseT support (EXPERIMENTAL) +Sun BigMAC 10/100baseT support CONFIG_SUNBMAC This driver supports the "be" interface available as an Sbus option. - This is Sun's older 100baseT ethernet device. + This is Sun's older 100baseT Ethernet device. This support is also available as a module called sunbmac.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . Sun QuadEthernet support CONFIG_SUNQE - This driver supports the "qe" 10baseT ethernet device, available as + This driver supports the "qe" 10baseT Ethernet device, available as an Sbus option. Note that this is not the same as Quad FastEthernet "qfe" which is supported by the Happy Meal driver instead. This support is also available as a module called sunqe.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . -Traffic Shaper (EXPERIMENTAL) +Traffic Shaper CONFIG_SHAPER The traffic shaper is a virtual network device that allows you to limit the rate of outgoing data flow over some other network device. The traffic that you want to slow down can then be routed through - these virtual devices. See Documentation/networking/shaper.txt for - more information. + these virtual devices. See + for more information. An alternative to this traffic shaper is the experimental Class-Based Queueing (CBQ) scheduling support which you get if you say Y to "QoS and/or fair queueing" above. To set up and configure shaper devices, you need the shapecfg - program, available from ftp://shadow.cabi.net/pub/Linux in the + program, available from in the shaper package. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called shaper.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. If unsure, - say N. + The module will be called shaper.o. If you want to compile it as a + module, say M here and read . If + unsure, say N. FDDI driver support CONFIG_FDDI @@ -10076,8 +11192,8 @@ - Netelligent 100 FDDI DAS UTP - Netelligent 100 FDDI SAS UTP - Netelligent 100 FDDI SAS Fibre MIC - - Read Documentation/networking/skfp.txt for information about + + Read for information about the driver. Questions concerning this driver can be addressed to: @@ -10085,10 +11201,10 @@ If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. This is recommended. - The module will be called skfp.o. + say M here and read . This is + recommended. The module will be called skfp.o. -HIgh Performance Parallel Interface support (EXPERIMENTAL) +HIgh Performance Parallel Interface (HIPPI) support CONFIG_HIPPI HIgh Performance Parallel Interface (HIPPI) is a 800Mbit/sec and 1600Mbit/sec dual-simplex switched or point-to-point network. HIPPI @@ -10098,16 +11214,16 @@ and have a HIPPI network card in your computer that you want to use under Linux, say Y here (you must also remember to enable the driver for your HIPPI card below). Most people will say N here. - + Essential RoadRunner HIPPI PCI adapter support CONFIG_ROADRUNNER Say Y here if this is your PCI HIPPI network card. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called rrunner.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. If unsure, - say N. + The module will be called rrunner.o. If you want to compile it as a + module, say M here and read . If + unsure, say N. Use large TX/RX rings CONFIG_ROADRUNNER_LARGE_RINGS @@ -10117,80 +11233,92 @@ kernel code or by user space programs. Say Y here only if you have the memory. -Acorn Ether1 card +Acorn Ether1 support CONFIG_ARM_ETHER1 If you have an Acorn system with one of these (AKA25) network cards, you should say Y to this option if you wish to use it with Linux. -Acorn/ANT Ether3 card +Acorn/ANT Ether3 support CONFIG_ARM_ETHER3 If you have an Acorn system with one of these network cards, you should say Y to this option if you wish to use it with Linux. -I Cubed EtherH card +I-Cubed EtherH support CONFIG_ARM_ETHERH If you have an Acorn system with one of these network cards, you should say Y to this option if you wish to use it with Linux. -EBSA-110 Ethernet interface +EBSA-110 Ethernet interface (AM79C961A) CONFIG_ARM_AM79C961A If you wish to compile a kernel for the EBSA-110, then you should always answer Y to this. -Support CDROM drives that are not SCSI or IDE/ATAPI +Support Thumb instructions +CONFIG_ARM_THUMB + Say Y if you want to have kernel support for ARM Thumb instructions, + fault handlers, and system calls. + + The Thumb instruction set is a compressed form of the standard ARM + instruction set resulting in smaller binaries at the expense of + slightly less efficient code. + + If you don't know what this all is, saying Y is a safe choice. + +Support CD-ROM drives that are not SCSI or IDE/ATAPI CONFIG_CD_NO_IDESCSI - If you have a CDROM drive that is neither SCSI nor IDE/ATAPI, say Y - here, otherwise N. Read the CDROM-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + If you have a CD-ROM drive that is neither SCSI nor IDE/ATAPI, say Y + here, otherwise N. Read the CD-ROM-HOWTO, available from + . Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause this configure script to skip all - the questions about these CDROM drives. If you are unsure what you + kernel: saying N will just cause the configurator to skip all + the questions about these CD-ROM drives. If you are unsure what you have, say Y and find out whether you have one of the following - drives. + drives. - For each of these drivers, a file Documentation/cdrom/ + For each of these drivers, a file Documentation/cdrom/{driver_name} exists. Especially in cases where you do not know exactly which kind of drive you have you should read there. Most of these drivers use a - file drivers/cdrom/.h where you can define your - interface parameters and switch some internal goodies. + file drivers/cdrom/{driver_name}.h where you can define your + interface parameters and switch some internal goodies. - All these CDROM drivers are also usable as a module ( = code which + All these CD-ROM drivers are also usable as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile them as module, say M instead of Y and - read Documentation/modules.txt. + read . - If you want to use any of these CDROM drivers, you also have to - answer Y or M to "ISO 9660 CDROM file system support" below (this + If you want to use any of these CD-ROM drivers, you also have to + answer Y or M to "ISO 9660 CD-ROM file system support" below (this answer will get "defaulted" for you if you enable any of the Linux - CDROM drivers). + CD-ROM drivers). -Sony CDU31A/CDU33A CDROM support +Sony CDU31A/CDU33A CD-ROM support CONFIG_CDU31A - These CDROM drives have a spring-pop-out caddyless drawer, and a - rectangular green LED centered beneath it. NOTE: these CDROM drives - will not be auto detected by the kernel at boot time; you have to - provide the interface address as an option to the kernel at boot - time as described in Documentation/cdrom/cdu31a or fill in your - parameters into drivers/cdrom/cdu31a.c. Try "man bootparam" or - see the documentation of your boot loader (lilo or loadlin) about - how to pass options to the kernel. + These CD-ROM drives have a spring-pop-out caddyless drawer, and a + rectangular green LED centered beneath it. NOTE: these CD-ROM + drives will not be auto detected by the kernel at boot time; you + have to provide the interface address as an option to the kernel at + boot time as described in or fill + in your parameters into . Try "man + bootparam" or see the documentation of your boot loader (lilo or + loadlin) about how to pass options to the kernel. - If you say Y here, you should also say Y or M to "ISO 9660 CDROM + If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM file system support" below, because that's the file system used on - CDROMs. + CD-ROMs. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called cdu31a.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called cdu31a.o. If you want to compile it as a + module, say M here and read . -Standard Mitsumi [no XA/Multisession] CDROM support +Standard Mitsumi [no XA/Multisession] CD-ROM support CONFIG_MCD This is the older of the two drivers for the older Mitsumi models LU-005, FX-001 and FX-001D. This is not the right driver for the FX-001DE and the triple or quad speed models (all these are - IDE/ATAPI models). Please also the file Documentation/cdrom/mcd. + IDE/ATAPI models). Please also the file + . With the old LU-005 model, the whole drive chassis slides out for cd insertion. The FX-xxx models use a motorized tray type mechanism. @@ -10198,184 +11326,201 @@ (PhotoCDs). There is a new driver (next question) which can do this. If you want that one, say N here. - If you say Y here, you should also say Y or M to "ISO 9660 CDROM + If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM file system support" below, because that's the file system used on - CDROMs. + CD-ROMs. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called mcd.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . IRQ channel for Mitsumi CD-ROM CONFIG_MCD_IRQ This allows you to specify the default value of the IRQ used by the driver. This setting can be overridden by passing the "mcd=" parameter to the kernel at boot time (or at module load time if you - said M to "Standard Mitsumi CDROM support"). + said M to "Standard Mitsumi CD-ROM support"). I/O base address for Mitsumi CD-ROM CONFIG_MCD_BASE This allows you to specify the default value of the I/O base address used by the driver. This setting can be overridden by passing the "mcd=" parameter to the kernel at boot time (or at module load time - if you said M to "Standard Mitsumi CDROM support"). + if you said M to "Standard Mitsumi CD-ROM support"). -Mitsumi [XA/MultiSession] support +Mitsumi [XA/MultiSession] CD-ROM support CONFIG_MCDX Use this driver if you want to be able to read XA or MultiSession CDs (PhotoCDs) as well as ordinary CDs with your Mitsumi LU-005, - FX-001 or FX-001D CDROM drive. In addition, this driver uses much + FX-001 or FX-001D CD-ROM drive. In addition, this driver uses much less kernel memory than the old one, if that is a concern. This driver is able to support more than one drive, but each drive needs a separate interface card. Please read the file - Documentation/cdrom/mcdx. + . - If you say Y here, you should also say Y or M to "ISO 9660 CDROM + If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM file system support" below, because that's the file system used on - CDROMs. + CD-ROMs. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called mcdx.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . -Matsushita/Panasonic/Creative, Longshine, TEAC CDROM support +Matsushita/Panasonic/Creative, Longshine, TEAC CD-ROM support CONFIG_SBPCD This driver supports most of the drives which use the Panasonic or - Sound Blaster interface. Please read the file - Documentation/cdrom/sbpcd. + Sound Blaster interface. Please read the file + . The Matsushita CR-521, CR-522, CR-523, CR-562, CR-563 drives (sometimes labeled "Creative"), the Creative Labs CD200, the - Longshine LCS-7260, the "IBM External ISA CDROM" (in fact a CR-56x - model), the TEAC CD-55A fall under this category. Some other + Longshine LCS-7260, the "IBM External ISA CD-ROM" (in fact a CR-56x + model), the TEAC CD-55A fall under this category. Some other "electrically compatible" drives (Vertos, Genoa, some Funai models) are currently not supported; for the Sanyo H94A drive currently a - separate driver (asked later) is responsible. Most drives have a + separate driver (asked later) is responsible. Most drives have a uniquely shaped faceplate, with a caddyless motorized drawer, but - without external brand markings. The older CR-52x drives have a - caddy and manual loading/eject, but still no external markings. The + without external brand markings. The older CR-52x drives have a + caddy and manual loading/eject, but still no external markings. The driver is able to do an extended auto-probing for interface addresses and drive types; this can help to find facts in cases you are not sure, but can consume some time during the boot process if - none of the supported drives gets found. Once your drive got found, - you should enter the reported parameters into drivers/cdrom/sbpcd.h - and set "DISTRIBUTION 0" there. - - This driver can support up to four CDROM controller cards, and each - card can support up to four CDROM drives; if you say Y here, you - will be asked how many controller cards you have. If compiled as a + none of the supported drives gets found. Once your drive got found, + you should enter the reported parameters into + and set "DISTRIBUTION 0" there. + + This driver can support up to four CD-ROM controller cards, and each + card can support up to four CD-ROM drives; if you say Y here, you + will be asked how many controller cards you have. If compiled as a module, only one controller card (but with up to four drives) is usable. - If you say Y here, you should also say Y or M to "ISO 9660 CDROM + If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM file system support" below, because that's the file system used on - CDROMs. + CD-ROMs. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called sbpcd.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called sbpcd.o. If you want to compile it as a + module, say M here and read . -Matsushita/Panasonic, ... second CDROM controller support +Matsushita/Panasonic, ... second CD-ROM controller support CONFIG_SBPCD2 - Say Y here only if you have two CDROM controller cards of this type - (usually only if you have more than four drives). You should enter + Say Y here only if you have two CD-ROM controller cards of this type + (usually only if you have more than four drives). You should enter the parameters for the second, third and fourth interface card into - include/linux/sbpcd.h before compiling the new kernel. Read - the file Documentation/cdrom/sbpcd. + before compiling the new kernel. Read + the file . + +Matsushita/Panasonic, ... third CD-ROM controller support +CONFIG_SBPCD3 + Say Y here only if you have three CD-ROM controller cards of this + type (usually only if you have more than six drives). You should + enter the parameters for the second, third and fourth interface card + into before compiling the new kernel. + Read the file . + +Matsushita/Panasonic, ... fourth CD-ROM controller support +CONFIG_SBPCD4 + Say Y here only if you have four CD-ROM controller cards of this + type (usually only if you have more than eight drives). You should + enter the parameters for the second, third and fourth interface card + into before compiling the new kernel. + Read the file . -Aztech/Orchid/Okano/Wearnes/TXC/CyDROM CDROM support +Aztech/Orchid/Okano/Wearnes/TXC/CyDROM CD-ROM support CONFIG_AZTCD This is your driver if you have an Aztech CDA268-01A, Orchid - CD-3110, Okano or Wearnes CDD110, Conrad TXC, or CyCDROM CR520 or - CR540 CDROM drive. This driver -- just like all these CDROM drivers - -- is NOT for CDROM drives with IDE/ATAPI interfaces, such as Aztech - CDA269-031SE. Please read the file Documentation/cdrom/aztcd. + CD-3110, Okano or Wearnes CDD110, Conrad TXC, or CyCD-ROM CR520 or + CR540 CD-ROM drive. This driver -- just like all these CD-ROM + drivers -- is NOT for CD-ROM drives with IDE/ATAPI interfaces, such + as Aztech CDA269-031SE. Please read the file + . - If you say Y here, you should also say Y or M to "ISO 9660 CDROM + If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM file system support" below, because that's the file system used on - CDROMs. + CD-ROMs. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called aztcd.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called aztcd.o. If you want to compile it as a + module, say M here and read . -Sony CDU535 CDROM support +Sony CDU535 CD-ROM support CONFIG_CDU535 - This is the driver for the older Sony CDU-535 and CDU-531 CDROM - drives. Please read the file Documentation/cdrom/sonycd535. + This is the driver for the older Sony CDU-535 and CDU-531 CD-ROM + drives. Please read the file . - If you say Y here, you should also say Y or M to "ISO 9660 CDROM + If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM file system support" below, because that's the file system used on - CDROMs. + CD-ROMs. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called sonycd535.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . -Goldstar R420 CDROM support +Goldstar R420 CD-ROM support CONFIG_GSCD - If this is your CDROM drive, say Y here. As described in the file - Documentation/cdrom/gscd, you might have to change a setting - in the file drivers/cdrom/gscd.h before compiling the - kernel. Please read the file Documentation/cdrom/gscd. + If this is your CD-ROM drive, say Y here. As described in the file + , you might have to change a setting + in the file before compiling the + kernel. Please read the file . - If you say Y here, you should also say Y or M to "ISO 9660 CDROM + If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM file system support" below, because that's the file system used on - CDROMs. + CD-ROMs. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called gscd.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called gscd.o. If you want to compile it as a + module, say M here and read . -Philips/LMS CM206 CDROM support +Philips/LMS CM206 CD-ROM support CONFIG_CM206 - If you have a Philips/LMS CDROM drive cm206 in combination with a + If you have a Philips/LMS CD-ROM drive cm206 in combination with a cm260 host adapter card, say Y here. Please also read the file - Documentation/cdrom/cm206. + . - If you say Y here, you should also say Y or M to "ISO 9660 CDROM + If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM file system support" below, because that's the file system used on - CDROMs. + CD-ROMs. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called cm206.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . -Optics Storage DOLPHIN 8000AT CDROM support +Optics Storage DOLPHIN 8000AT CD-ROM support CONFIG_OPTCD This is the driver for the 'DOLPHIN' drive with a 34-pin Sony compatible interface. It also works with the Lasermate CR328A. If you have one of those, say Y. This driver does not work for the - Optics Storage 8001 drive; use the IDE-ATAPI CDROM driver for that - one. Please read the file Documentation/cdrom/optcd. + Optics Storage 8001 drive; use the IDE-ATAPI CD-ROM driver for that + one. Please read the file . - If you say Y here, you should also say Y or M to "ISO 9660 CDROM + If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM file system support" below, because that's the file system used on - CDROMs. + CD-ROMs. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called optcd.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . -Sanyo CDR-H94A CDROM support +Sanyo CDR-H94A CD-ROM support CONFIG_SJCD - If this is your CDROM drive, say Y here and read the file - Documentation/cdrom/sjcd. You should then also say Y or M to - "ISO 9660 CDROM file system support" below, because that's the - file system used on CDROMs. + If this is your CD-ROM drive, say Y here and read the file + . You should then also say Y or M to + "ISO 9660 CD-ROM file system support" below, because that's the + file system used on CD-ROMs. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called sjcd.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . ISP16/MAD16/Mozart soft configurable cdrom interface support CONFIG_ISP16_CDI @@ -10383,12 +11528,17 @@ 82C928 or 82C929 chips. Say Y here to have them detected and possibly configured at boot time. In addition, You'll have to say Y to a driver for the particular cdrom drive you have attached to the - card. Read Documentation/cdrom/isp16 for details. + card. Read for details. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called isp16.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . + +iSeries Virtual I/O CD Support +CONFIG_VIOCD + If you are running Linux on an IBM iSeries system and you want to + read a CD drive owned by OS/400, say Y here. Quota support CONFIG_QUOTA @@ -10396,13 +11546,13 @@ usage (also called disk quotas). Currently, it works only for the ext2 file system. You need additional software in order to use quota support; for details, read the Quota mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Probably the quota + . Probably the quota support is only useful for multi user systems. If unsure, say N. Memory Technology Device (MTD) support CONFIG_MTD Memory Technology Devices are flash, RAM and similar chips, often - used for solid state filesystems on embedded devices. This option + used for solid state file systems on embedded devices. This option will provide the generic support for MTD drivers to register themselves with the kernel and for potential users of MTD devices to enumerate the devices which are present and obtain a handle on @@ -10428,18 +11578,17 @@ RedBoot partition table parsing CONFIG_MTD_REDBOOT_PARTS RedBoot is a ROM monitor and bootloader which deals with multiple - 'images' in flash devices by putting a table in the last erase - block of the device, similar to a partition table, which gives - the offsets, lengths and names of all the images stored in the - flash. + 'images' in flash devices by putting a table in the last erase block + of the device, similar to a partition table, which gives the + offsets, lengths and names of all the images stored in the flash. If you need code which can detect and parse this table, and register MTD 'partitions' corresponding to each image in the table, enable - this option. + this option. You will still need the parsing functions to be called by the driver - for your particular device. It won't happen automatically. The - SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for + for your particular device. It won't happen automatically. The + SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for example. Compaq bootldr partition table parsing @@ -10447,11 +11596,11 @@ The Compaq bootldr deals with multiple 'images' in flash devices by putting a table in one of the first erase blocks of the device, similar to a partition table, which gives the offsets, lengths and - names of all the images stored in the flash. + names of all the images stored in the flash. If you need code which can detect and parse this table, and register MTD 'partitions' corresponding to each image in the table, enable - this option. + this option. You will still need the parsing functions to be called by the driver for your particular device. It won't happen automatically. The @@ -10462,21 +11611,20 @@ CONFIG_MTD_AFS_PARTS The ARM Firmware Suite allows the user to divide flash devices into multiple 'images'. Each such image has a header containing its name - and offset/size etc. + and offset/size etc. - If you need code which can detect and parse these tables, and register - MTD 'partitions' corresponding to each image detected, enable - this option. + If you need code which can detect and parse these tables, and + register MTD 'partitions' corresponding to each image detected, + enable this option. You will still need the parsing functions to be called by the driver - for your particular device. It won't happen automatically. The + for your particular device. It won't happen automatically. The 'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example. -MTD debugging verbosity +MTD debugging verbosity (0 = quiet, 3 = noisy) CONFIG_MTD_DEBUG_VERBOSE Determines the verbosity level of the MTD debugging messages. - Direct chardevice access to MTD devices CONFIG_MTD_CHAR This provides a character device for each MTD device present in @@ -10488,8 +11636,8 @@ CONFIG_MTD_BLOCK Although most flash chips have an erase size too large to be useful as block devices, it is possible to use MTD devices which are based - on RAM chips in this manner. This block device is a user of MTD devices - performing that function. + on RAM chips in this manner. This block device is a user of MTD + devices performing that function. At the moment, it is also required for the Journalling Flash File System(s) to obtain a handle on the MTD device when it's mounted @@ -10498,7 +11646,7 @@ Later, it may be extended to perform read/erase/modify/write cycles on flash chips to emulate a smaller block size. Needless to say, - this is very unsafe, but could be useful for filesystems which are + this is very unsafe, but could be useful for file systems which are almost never written to. You do not need this option for use with the DiskOnChip devices. For @@ -10506,9 +11654,9 @@ Readonly block device access to MTD devices CONFIG_MTD_BLOCK_RO - This allows you to mount read-only filesystems (such as cramfs) from - an MTD device, without the overhead (and danger) of the caching - driver. + This allows you to mount read-only file systems (such as cramfs) + from an MTD device, without the overhead (and danger) of the caching + driver. You do not need this option for use with the DiskOnChip devices. For those, enable NFTL support (CONFIG_NFTL) instead. @@ -10517,8 +11665,8 @@ CONFIG_FTL This provides support for the original Flash Translation Layer which is part of the PCMCIA specification. It uses a kind of pseudo- - filesystem on a flash device to emulate a block device with 512-byte - sectors, on top of which you put a 'normal' filesystem. + file system on a flash device to emulate a block device with + 512-byte sectors, on top of which you put a 'normal' file system. You may find that the algorithms used in this code are patented unless you live in the Free World where software patents aren't @@ -10531,8 +11679,8 @@ CONFIG_NFTL This provides support for the NAND Flash Translation Layer which is used on M-Systems' DiskOnChip devices. It uses a kind of pseudo- - filesystem on a flash device to emulate a block device with 512-byte - sectors, on top of which you put a 'normal' filesystem. + file system on a flash device to emulate a block device with + 512-byte sectors, on top of which you put a 'normal' file system. You may find that the algorithms used in this code are patented unless you live in the Free World where software patents aren't @@ -10543,9 +11691,10 @@ Write support for NFTL (EXPERIMENTAL) CONFIG_NFTL_RW - If you're lucky, this will actually work. Don't whinge if it doesn't. - Send mail to the MTD mailing list if - you want to help to make it more reliable. + If you're lucky, this will actually work. Don't whinge if it + doesn't. Send mail to the MTD mailing list + if you want to help to make it more + reliable. Common Flash Interface (CFI) support CONFIG_MTD_CFI @@ -10553,7 +11702,7 @@ AMD and other flash manufactures that provides a universal method for probing the capabilities of flash devices. If you wish to support any device that is CFI-compliant, you need to enable this - option. Visit (http://www.amd.com/products/nvd/overview/cfi.html) + option. Visit for more information on CFI. CFI Advanced configuration options @@ -10604,6 +11753,7 @@ If your flash chips are interleaved in fours - i.e. you have four flash chips addressed by each bus cycle, then say 'Y'. +# Choice: mtd_swap_data Flash cmd/query data swapping CONFIG_MTD_CFI_NOSWAP This option defines the way in which the CPU attempts to arrange @@ -10612,7 +11762,7 @@ enabled, means that the CPU will not do any swapping; the chips are expected to be wired to the CPU in 'host-endian' form. Specific arrangements are possible with the BIG_ENDIAN_BYTE and - LITTLE_ENDIAN_BYTE, if the bytes are reversed. + LITTLE_ENDIAN_BYTE, if the bytes are reversed. If you have a LART, on which the data (and address) lines were connected in a fashion which ensured that the nets were as short @@ -10621,14 +11771,14 @@ Yes, there really exists something sicker than PDP-endian :) -CFI support for Intel/Sharp Extended Commands +CFI support for Intel/Sharp Extended Command Set chips CONFIG_MTD_CFI_INTELEXT The Common Flash Interface defines a number of different command sets which a CFI-compliant chip may claim to implement. This code provides support for one of those command sets, used on Intel StrataFlash and other parts. -CFI support for AMD/Fujitsu Standard Commands +CFI support for AMD/Fujitsu Standard Command Set chips CONFIG_MTD_CFI_AMDSTD The Common Flash Interface defines a number of different command sets which a CFI-compliant chip may claim to implement. This code @@ -10665,26 +11815,36 @@ This option enables basic support for ROM chips accessed through a bus mapping driver. +JEDEC device support CONFIG_MTD_JEDEC - Enable older older JEDEC flash interface devices for self programming - flash. It is commonly used in older AMD chips. It is only called - JEDEC because the JEDEC association (http://www.jedec.org/) - distributes the identification codes for the chips. WARNING!!!! This - code does not compile and is incomplete as are the specific JEDEC - devices drivers. + Enable older older JEDEC flash interface devices for self + programming flash. It is commonly used in older AMD chips. It is + only called JEDEC because the JEDEC association + distributes the identification codes for the + chips. WARNING!!!! This code does not compile and is incomplete as + are the specific JEDEC devices drivers. CFI Flash device mapped on StrongARM SA11x0 CONFIG_MTD_SA1100 - This enables access to the flash chips on most platforms based on the - SA1100 and SA1110, including the Assabet and the Compaq iPAQ. If you - have such a board, say 'Y'. + This enables access to the flash chips on most platforms based on + the SA1100 and SA1110, including the Assabet and the Compaq iPAQ. + If you have such a board, say 'Y'. +Support for Compaq bootldr partition tables on SA11x0 CONFIG_MTD_SA1100_REDBOOT_PARTITIONS Enabling this option will cause the kernel to look for a RedBoot FIS (Flash Image System) table in the last erase block of the flash chips detected. If you are using RedBoot on your SA11x0-based board and want Linux to present 'partitions' matching the images which - RedBoot has listed, say 'Y'. + RedBoot has listed, say 'Y'. + +Support for Compaq bootldr partition tables on SA11x0 +CONFIG_MTD_SA1100_BOOTLDR_PARTITIONS + Enabling this option will cause the kernel to look for a Compaq + bootldr partition table on the flash chips detected. If you are + using the Compaq bootldr on your SA11x0-based board and want Linux + to present 'partitions' matching the images which the bootldr has + listed, say 'Y'. Flash chip mapping in physical memory CONFIG_MTD_PHYSMAP @@ -10710,6 +11870,7 @@ map which should hopefully be in the documentation for your board. +Buswidth of flash in bytes CONFIG_MTD_PHYSMAP_BUSWIDTH This is the total width of the data bus of the flash devices in octets. For example, if you have a data bus width of 32 @@ -10727,7 +11888,7 @@ CONFIG_MTD_NORA If you had to ask, you don't have one. Say 'N'. -Flash chip mapping on PNC2000 +Flash chip mapping on Photron PNC-2000 CONFIG_MTD_PNC2000 PNC-2000 is the name of Network Camera product from PHOTRON Ltd. in Japan. It uses CFI-compliant flash. @@ -10738,7 +11899,7 @@ a strange sparse mapping. This 'mapping' driver supports that arrangement, allowing the CFI probe and command set driver code to communicate with the chips on the RPXLite board. More at - (http://www.embeddedplanet.com/rpx_lite_specification_sheet.htm). + . Flash chip mapping on AMD SC520 CDP board CONFIG_MTD_SC520CDP @@ -10746,47 +11907,67 @@ Dual-in-line JEDEC chip. This 'mapping' driver supports that arrangement, implementing three MTD devices. -Flash chip mapping on Arcom Control Systems' SBC-MediaGX -CONFIG_MTD_SBC_MEDIAGX +Flash chip mapping on Arcom Control Systems SBC-MediaGX +CONFIG_MTD_SBC_GXX This provides a driver for the on-board flash of Arcom Control - Systems' SBC-MediaGX development board. By default the flash - is split into 3 partitions which are accessed as separate MTD - devices. This board utilizes Intel StrataFlash. More info at - (http://www.arcomcontrols.com/products/icp/pc104/processors/). + Systems' SBC-GXn family of boards, formerly known as SBC-MediaGX. + By default the flash is split into 3 partitions which are accessed + as separate MTD devices. This board utilizes Intel StrataFlash. + More info at + . + +CFI Flash device mapped on D-Box2 +CONFIG_MTD_DBOX2 + This enables access routines for the flash chips on the Nokia/Sagem + D-Box 2 board. If you have one of these boards and would like to use + the flash chips on it, say 'Y'. + +CFI Flash device mapped on the XScale IQ80310 board +CONFIG_MTD_IQ80310 + This enables access routines for the flash chips on the Intel XScale + IQ80310 evaluation board. If you have one of these boards and would + like to use the flash chips on it, say 'Y'. + +CFI Flash device mapped on AMD NetSc520 +CONFIG_MTD_NETSC520 + This enables access routines for the flash chips on the AMD NetSc520 + demonstration board. If you have one of these boards and would like + to use the flash chips on it, say 'Y'. -Flash chip mapping on Arcom Control Systems' ELAN-104NC +Flash chip mapping on Arcom Control Systems ELAN-104NC CONFIG_MTD_ELAN_104NC This provides a driver for the on-board flash of the Arcom Control System's ELAN-104NC development board. By default the flash is split into 3 partitions which are accessed as separate MTD devices. This board utilizes Intel StrataFlash. More info at - (http://www.arcomcontrols.com/products/icp/pc104/processors/). + . Flash chip mapping on Compaq iPAQ/Bitsy CONFIG_MTD_BITSY This provides a driver for the on-board flash found in Compaq's iPAQ Palm PC and their research prototype the Itsy. iPAQ info at - (http://www5.compaq.com/products/handhelds/pocketpc/) and the - Itsy (http://www.research.digital.com/wrl/projects/Itsy/index.html). + and the + Itsy . Flash chip mapping on Compaq iPAQ/Bitsy CONFIG_MTD_DC21285 This provides a driver for the flash accessed using Intel's 21285 bridge used with Intel's StrongARM processors. More info at - (http://developer.intel.com/design/bridge/quicklist/dsc-21285.htm). + . Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board CONFIG_MTD_CSTM_MIPS_IXX - This provides a mapping driver for the Integrated Tecnology - Express, Inc (ITE) QED-4N-S01B eval board and the Globespan IVR Reference - Board. It provides the necessary addressing, length, buswidth, vpp code - and addition setup of the flash device for these boards. In addition, - this mapping driver can be used for other boards via setting of the - CONFIG_MTD_CSTM_MIPS_IXX_START/LEN/BUSWIDTH parameters. This mapping - will provide one mtd device using one partition. The start address can - be offset from the beginning of flash and the len can be less than the - total flash device size to allow a window into the flash. Both CFI and - JEDEC probes are called. + This provides a mapping driver for the Integrated Tecnology Express, + Inc (ITE) QED-4N-S01B eval board and the Globespan IVR Reference + Board. It provides the necessary addressing, length, buswidth, vpp + code and addition setup of the flash device for these boards. In + addition, this mapping driver can be used for other boards via + setting of the CONFIG_MTD_CSTM_MIPS_IXX_START/LEN/BUSWIDTH + parameters. This mapping will provide one mtd device using one + partition. The start address can be offset from the beginning of + flash and the len can be less than the total flash device size to + allow a window into the flash. Both CFI and JEDEC probes are + called. Physical start location of flash mapping CONFIG_MTD_CSTM_MIPS_IXX_START @@ -10800,7 +11981,7 @@ This is the total length that the MTD driver will use for the flash chips on your particular board. Refer to the memory map which should hopefully be in the documentation for your - board. + board. Physical bus width of flash mapping CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH @@ -10814,26 +11995,26 @@ to get on with their job of driving the flash chips without having to know about the paging. If you have one of these boards, you probably want to enable this mapping driver. More info is at - (http://www.itc.hu/). + . Flash chip mapping on Octagon 5066 SBC CONFIG_MTD_OCTAGON This provides a 'mapping' driver which supports the way in which the flash chips are connected in the Octagon-5066 Single Board Computer. More information on the board is available at - (http://www.octagonsystems.com/Products/5066/5066.html). + . Flash chip mapping on Tempustech VMAX SBC301 CONFIG_MTD_VMAX This provides a 'mapping' driver which supports the way in which the flash chips are connected in the Tempustech VMAX SBC301 Single Board Computer. More information on the board is available at - (http://www.tempustech.com/tt301.htm). + . Support for NAND flash devices CONFIG_MTD_NAND This enables support for accessing all type of NAND flash - devices. + devices. Support for software ECC algorithm CONFIG_MTD_NAND_ECC @@ -10863,96 +12044,102 @@ M-Systems Disk-On-Chip 2000 and Millennium support CONFIG_MTD_DOC2000 This provides an MTD device driver for the M-Systems DiskOnChip - 2000 and Millennium devices. Originally designed for the DiskOnChip - 2000, it also now includes support for the DiskOnChip Millennium. + 2000 and Millennium devices. Originally designed for the DiskOnChip + 2000, it also now includes support for the DiskOnChip Millennium. If you have problems with this driver and the DiskOnChip Millennium, you may wish to try the alternative Millennium driver below. To use the alternative driver, you will need to undefine DOC_SINGLE_DRIVER - in the drivers/mtd/devices/docprobe.c source code. + in the source code. If you use this device, you probably also want to enable the NFTL - 'NAND Flash Translation Layer' option below, which is used to emulate - a block device by using a kind of filesystem on the flash chips. + 'NAND Flash Translation Layer' option below, which is used to + emulate a block device by using a kind of file system on the flash + chips. Alternative Disk-On-Chip Millennium support CONFIG_MTD_DOC2001 This provides an alternative MTD device driver for the M-Systems - DiskOnChip Millennium devices. Use this if you have problems with - the combined DiskOnChip 2000 and Millennium driver above. To get + DiskOnChip Millennium devices. Use this if you have problems with + the combined DiskOnChip 2000 and Millennium driver above. To get the DiskOnChip probe code to load and use this driver instead of the other one, you will need to undefine DOC_SINGLE_DRIVER near - the beginning of drivers/mtd/devices/docprobe.c + the beginning of . If you use this device, you probably also want to enable the NFTL - 'NAND Flash Translation Layer' option below, which is used to emulate - a block device by using a kind of filesystem on the flash chips. + 'NAND Flash Translation Layer' option below, which is used to + emulate a block device by using a kind of file system on the flash + chips. Probe for DiskOnChip devices CONFIG_MTD_DOCPROBE - This isn't a real config option, it's derived. + This isn't a real config option, it's derived. Advanced detection options for DiskOnChip CONFIG_MTD_DOCPROBE_ADVANCED This option allows you to specify nonstandard address at which to - probe for a DiskOnChip, or to change the detection options. You're - unlikely to need any of this unless you're using LinuxBIOS. Say 'N'. + probe for a DiskOnChip, or to change the detection options. You + are unlikely to need any of this unless you are using LinuxBIOS. + Say 'N'. -Probe for 0x55 0xAA BIOS Extension Signature. +Probe for 0x55 0xAA BIOS Extension Signature CONFIG_MTD_DOCPROBE_55AA - Check for the 0x55 0xAA signature of a DiskOnChip, and do not continue - with probing if it is absent. The signature will always be present for - a DiskOnChip 2000 or a normal DiskOnChip Millennium. Only if you have - overwritten the first block of a DiskOnChip Millennium will it be - absent. Enable this option if you are using LinuxBIOS or if you need - to recover a DiskOnChip Millennium on which you have managed to wipe - the first block. + Check for the 0x55 0xAA signature of a DiskOnChip, and do not + continue with probing if it is absent. The signature will always be + present for a DiskOnChip 2000 or a normal DiskOnChip Millennium. + Only if you have overwritten the first block of a DiskOnChip + Millennium will it be absent. Enable this option if you are using + LinuxBIOS or if you need to recover a DiskOnChip Millennium on which + you have managed to wipe the first block. Physical address of DiskOnChip CONFIG_MTD_DOCPROBE_ADDRESS - By default, the probe for DiskOnChip devices will look for a DiskOnChip - at every multiple of 0x2000 between 0xC8000 and 0xEE000. This option - allows you to specify a single address at which to probe for the device, - which is useful if you have other devices in that range which get upset - when they're probed. + By default, the probe for DiskOnChip devices will look for a + DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. + This option allows you to specify a single address at which to probe + for the device, which is useful if you have other devices in that + range which get upset when they are probed. - (Note that on PowerPC, the normal probe will only check at 0xE4000000.) + (Note that on PowerPC, the normal probe will only check at + 0xE4000000.) - Normally, you should leave this set to zero, to allow the probe at the - normal addresses. + Normally, you should leave this set to zero, to allow the probe at + the normal addresses. Probe high addresses CONFIG_MTD_DOCPROBE_HIGH - By default, the probe for DiskOnChip devices will look for a DiskOnChip - at every multiple of 0x2000 between 0xC8000 and 0xEE000. This option - changes to make it probe between 0xFFFC8000 and 0xFFFEE000. Unless - you're using LinuxBIOS, this is unlikely to be useful to you. Say 'N'. + By default, the probe for DiskOnChip devices will look for a + DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. + This option changes to make it probe between 0xFFFC8000 and + 0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be + useful to you. Say 'N'. Ramix PMC551 PCI Mezzanine ram card support CONFIG_MTD_PMC551 This provides a MTD device driver for the Ramix PMC551 RAM PCI card - from Ramix Inc. (http://www.ramix.com/products/memory/pmc551.html). + from Ramix Inc. . These devices come in memory configurations from 32M - 1G. If you have one, you probably want to enable this. - If this driver is compiled as a module you get the ability to select the - size of the aperture window pointing into the devices memory. What this - means is that if you have a 1G card, normally the kernel will use a 1G - memory map as it's view of the device. As a module, you can select a - 1M window into the memory and the driver will "slide" the window around - the PMC551's memory. This was particularly useful on the 2.2 kernels - on PPC architectures as there was limited kernel space to deal with. + If this driver is compiled as a module you get the ability to select + the size of the aperture window pointing into the devices memory. + What this means is that if you have a 1G card, normally the kernel + will use a 1G memory map as its view of the device. As a module, + you can select a 1M window into the memory and the driver will + "slide" the window around the PMC551's memory. This was + particularly useful on the 2.2 kernels on PPC architectures as there + was limited kernel space to deal with. PMC551 256M DRAM Bugfix CONFIG_MTD_PMC551_BUGFIX - Some of Ramix's PMC551 boards with 256M configurations have invalid column - and row mux values. This option will fix them, but will break other memory - configurations. If unsure say N. + Some of Ramix's PMC551 boards with 256M configurations have invalid + column and row mux values. This option will fix them, but will + break other memory configurations. If unsure say N. PMC551 Debugging CONFIG_MTD_PMC551_DEBUG - This option makes the PMC551 more verbose during it's operation and is only - really usefull if you are developing on this driver or suspect a possible - hardware or driver bug. If unsure say N. + This option makes the PMC551 more verbose during its operation and + is only really useful if you are developing on this driver or + suspect a possible hardware or driver bug. If unsure say N. Use extra onboard system memory as MTD device CONFIG_MTD_SLRAM @@ -10963,24 +12150,24 @@ Debugging RAM test driver CONFIG_MTD_MTDRAM This enables a test MTD device driver which uses vmalloc() to - provide storage. You probably want to say 'N' unless you're + provide storage. You probably want to say 'N' unless you're testing stuff. -MTDRAM erase block size in KiB +MTDRAM erase block size in KB CONFIG_MTDRAM_ERASE_SIZE This allows you to configure the size of the erase blocks in the - device emulated by the MTDRAM driver. If the MTDRAM driver is built + device emulated by the MTDRAM driver. If the MTDRAM driver is built as a module, it is also possible to specify this as a parameter when loading the module. -MTDRAM device size in KiB +MTDRAM device size in KB CONFIG_MTDRAM_TOTAL_SIZE This allows you to configure the total size of the MTD device - emulated by the MTDRAM driver. If the MTDRAM driver is built + emulated by the MTDRAM driver. If the MTDRAM driver is built as a module, it is also possible to specify this as a parameter when loading the module. -SRAM absolute position +SRAM Hexadecimal Absolute position or 0 CONFIG_MTDRAM_ABS_POS If you have system RAM accessible by the CPU but not used by Linux in normal operation, you can give the physical address at which the @@ -10990,34 +12177,40 @@ Flash chip mapping on the Flaga Digital Module CONFIG_MTD_CFI_FLAGADM - Mapping for the Flaga digital module. If you don´t have one, ignore this - setting. + Mapping for the Flaga digital module. If you don´t have one, ignore + this setting. + +Momenco Ocelot boot flash device +CONFIG_MTD_OCELOT + This enables access routines for the boot flash device and for the + NVRAM on the Momenco Ocelot board. If you have one of these boards + and would like access to either of these, say 'Y'. -Support for USB +USB (Universal Serial Bus) support CONFIG_USB Universal Serial Bus (USB) is a specification for a serial bus subsystem which offers higher speeds and more features than the - traditional PC serial port. The bus supplies power to peripherals - and allows for hot swapping. Up to 127 USB peripherals can be - connected to a single USB port in a tree structure. The USB port is + traditional PC serial port. The bus supplies power to peripherals + and allows for hot swapping. Up to 127 USB peripherals can be + connected to a single USB port in a tree structure. The USB port is the root of the tree, the peripherals are the leaves and the inner - nodes are special USB devices called hubs. Many newer PC's have USB + nodes are special USB devices called hubs. Many newer PC's have USB ports and newer peripherals such as scanners, keyboards, mice, modems, and printers support the USB protocol and can be connected to the PC via those ports. Say Y here if your computer has a USB port and you want to use USB - devices. You then need to say Y to at least one of "UHCI support" or - "OHCI support" below (the type of interface that the USB hardware in - your computer provides to the operating system) and then choose from - among the drivers for USB peripherals. You may want to check out the - information provided in Documentation/usb/ and especially the links - given in Documentation/usb/usb-help.txt. + devices. You then need to say Y to at least one of "UHCI support" + or "OHCI support" below (the type of interface that the USB hardware + in your computer provides to the operating system) and then choose + from among the drivers for USB peripherals. You may want to check + out the information provided in and + especially the links given in . This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called usbcore.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called usbcore.o. If you want to compile it as a + module, say M here and read . USB verbose debug messages CONFIG_USB_DEBUG @@ -11025,7 +12218,29 @@ of debug messages to the system log. Select this if you are having a problem with USB support and want to see more of what is going on. -UHCI (intel PIIX4, VIA, ...) support? +USB fetch large config +CONFIG_USB_LARGE_CONFIG + This option changes the initial request for a config descriptor so + that some poorly designed devices will still work. Some APC UPSes + need it. Basically, the usb subsystem sends a request for a short + (8 byte) config, just to find out how large the real config is. + Incorrectly implemented devices may choke on this small config + request. This option make the initial request for a quite large + config (1009 bytes), and things just work. + + If you have an APC UPS, say Y; otherwise say N. + +USB long timeout +CONFIG_USB_LONG_TIMEOUT + This option makes the standard time out a bit longer. Basically, + some devices are just slow to respond, so this makes usb more + patient. There should be no harm in selecting this, but it is + needed for some MGE Ellipse UPSes. + + If you have an MGE Ellipse UPS, or you see timeouts in HID + transactions, say Y; otherwise say N. + +UHCI (Intel PIIX4, VIA, ...) support CONFIG_USB_UHCI The Universal Host Controller Interface is a standard by Intel for accessing the USB hardware in the PC (which is also called the USB @@ -11037,15 +12252,15 @@ 133). Currently there exist two drivers for UHCI host controllers: this - one and the so-called JE driver, which you can get from + one and the so-called JE driver, which you can get from "UHCI alternate (JE) support", below. You need only one. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called usb-uhci.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . -UHCI (intel PIIX4, VIA, ...) alternate (JE) support? +UHCI (Intel PIIX4, VIA, ...) alternate (JE) support CONFIG_USB_UHCI_ALT The Universal Host Controller Interface is a standard by Intel for accessing the USB hardware in the PC (which is also called the USB @@ -11063,11 +12278,7 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called uhci.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. - -UHCI unlink optimizations (EXPERIMENTAL) -CONFIG_USB_UHCI_ALT_UNLINK_OPTIMIZE - This option currently does nothing. You may say Y or N. + module, say M here and read . OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support CONFIG_USB_OHCI @@ -11079,12 +12290,12 @@ -- like SiS (aktual 610, 610 and so on) or ALi (ALi IV, ALi V, Aladdin Pro..) -- conform to this standard. - You may want to read the file Documentation/usb/ohci.txt. + You may want to read . This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called usb-ohci.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + as a module, say M here and read . USB Human Interface Device (full HID) support CONFIG_USB_HID @@ -11092,14 +12303,28 @@ mice, joysticks, graphic tablets, or any other HID based devices to your computer via USB. You can't use this driver and the HIDBP (Boot Protocol) keyboard and mouse drivers at the same time. - More information is available: Documentation/usb/input.txt. + More information is available: . If unsure, say Y. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called hid.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . + +/dev/usb/hiddev raw HID device support +CONFIG_USB_HIDDEV + Say Y here if you want to support HID devices (from the USB + specification standpoint) that aren't strictly user interface + devices, like monitor controls and Uninterruptable Power Supplies. + It is also used for "consumer keys" on multimedia keyboards and + USB speakers. + + This module supports these devices separately using a separate + event interface on /dev/usb/hiddevX (char 180:96 to 180:111). + This driver requires CONFIG_USB_HID. + + If unsure, say N. USB HIDBP Keyboard (basic) support CONFIG_USB_KBD @@ -11110,7 +12335,7 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called usbkbd.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . If unsure, say N. @@ -11123,41 +12348,30 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called usbmouse.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . If unsure, say N. Wacom Intuos/Graphire tablet support CONFIG_USB_WACOM Say Y here if you want to use the USB version of the Wacom Intuos - or Graphire tablet. Make sure to say Y to "Mouse support" + or Graphire tablet. Make sure to say Y to "Mouse support" (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" (CONFIG_INPUT_EVDEV) as well. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called wacom.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. - -Logitech WingMan Force joystick support -CONFIG_USB_WMFORCE - Say Y here if you want to use the Logitech WingMan Force with Linux - on the USB port. No force-feedback support yet, but other than that - it should work like a normal joystick. - - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called wmforce.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called wacom.o. If you want to compile it as a + module, say M here and read . Use input layer for ADB devices CONFIG_INPUT_ADBHID Say Y here if you want to have ADB (Apple Desktop Bus) HID devices - such as keyboards, mice, joysticks, or graphic tablets handled by the - input layer. If you say Y here, make sure to say Y to the + such as keyboards, mice, joysticks, or graphic tablets handled by + the input layer. If you say Y here, make sure to say Y to the corresponding drivers "Keyboard support" (CONFIG_INPUT_KEYBDEV), - "Mouse Support" (CONFIG_INPUT_MOUSEDEV) and "Event interface support" - (CONFIG_INPUT_EVDEV) as well. + "Mouse Support" (CONFIG_INPUT_MOUSEDEV) and "Event interface + support" (CONFIG_INPUT_EVDEV) as well. If you say N here, you still have the option of using the old ADB keyboard and mouse drivers. @@ -11177,12 +12391,13 @@ Keyboard support CONFIG_INPUT_KEYBDEV Say Y here if you want your USB HID keyboard (or an ADB keyboard - handled by the input layer) to be able to serve as a system keyboard. + handled by the input layer) to be able to serve as a system + keyboard. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called keybdev.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called keybdev.o. If you want to compile it as a + module, say M here and read . Mouse support CONFIG_INPUT_MOUSEDEV @@ -11191,13 +12406,13 @@ /dev/input/mouseX and 13:63 - /dev/input/mice as an emulated ImPS/2 mouse. That way, all user space programs will be able to use your mouse. - + If unsure, say Y. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called mousedev.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . Horizontal screen resolution CONFIG_INPUT_MOUSEDEV_SCREEN_X @@ -11216,71 +12431,81 @@ Joystick support CONFIG_INPUT_JOYDEV Say Y here if you want your USB HID joystick or gamepad to be - accessible as char device 13:0+ - /dev/input/jsX device. + accessible as char device 13:0+ - /dev/input/jsX device. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called joydev.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . Event interface support CONFIG_INPUT_EVDEV - Say Y here if you want your USB or ADB HID device events be accessible - under char device 13:64+ - /dev/input/eventX in a generic way. - This is the future ... + Say Y here if you want your USB or ADB HID device events be + accessible under char device 13:64+ - /dev/input/eventX in a generic + way. This is the future ... USB Scanner support CONFIG_USB_SCANNER Say Y here if you want to connect a USB scanner to your computer's - USB port. Please read Documentation/usb/scanner.txt and - Documentation/usb/scanner-hp-sane.txt for more information. + USB port. Please read and + for more information. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called scanner.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . + +HP 5300C scanner support +CONFIG_USB_HP5300 + Say Y here if you want to connect a HP5300C scanner to your + computer's USB port. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called hp5300.o. If you want to compile it as + a module, say M here and read . USB Audio support CONFIG_USB_AUDIO - Say Y here if you want to connect UAB audio equipment such as + Say Y here if you want to connect USB audio equipment such as speakers to your computer's USB port. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called audio.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . USB Modem (CDC ACM) support CONFIG_USB_ACM This driver supports USB modems and ISDN adapters which support the Communication Device Class Abstract Control Model interface. - Please read Documentation/usb/acm.txt for details. + Please read for details. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called acm.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . -USB Serial converter support +USB serial converter support CONFIG_USB_SERIAL Say Y here if you have a USB device that provides normal serial ports, or acts like a serial device, and you want to connect it to your USB bus. - Please read Documentation/usb/usb-serial.txt for more information - on the specifics of the different devices that are supported, and - on how to use them. - + Please read for more + information on the specifics of the different devices that are + supported, and on how to use them. + This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called usbserial.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + The module will be called usbserial.o. If you want to compile it + as a module, say M here and read . USB Generic Serial Driver CONFIG_USB_SERIAL_GENERIC - Say Y here if you want to use the generic USB serial driver. Please - read Documentation/usb/usb-serial.txt for more information on using - this driver. It is recommended that the "USB Serial converter + Say Y here if you want to use the generic USB serial driver. Please + read for more information on + using this driver. It is recommended that the "USB Serial converter support" be compiled as a module for this driver to be used properly. @@ -11291,20 +12516,20 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called whiteheat.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called whiteheat.o. If you want to compile it as + a module, say M here and read . USB Handspring Visor Driver CONFIG_USB_SERIAL_VISOR - Say Y here if you want to connect to your HandSpring Visor, Palm m500 - or m505 through its USB docking station. - See http://usbvisor.sourceforge.net for more information on using this + Say Y here if you want to connect to your HandSpring Visor, Palm + m500 or m505 through its USB docking station. See + for more information on using this driver. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called visor.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . USB Belkin and Paracom Single Port Serial Driver CONFIG_USB_SERIAL_BELKIN @@ -11315,20 +12540,20 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called belkin_sa.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . USB FTDI Single Port Serial Driver CONFIG_USB_SERIAL_FTDI_SIO Say Y here if you want to use a FTDI SIO single port USB to serial converter device. The implementation I have is called the USC-1000. - See http://reality.sgi.com/bryder_wellington/ftdi_sio for more + See for more information on this driver and the device. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called ftdi_sio.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called ftdi_sio.o. If you want to compile it as + a module, say M here and read . USB Keyspan PDA Single Port Serial Driver CONFIG_USB_SERIAL_KEYSPAN_PDA @@ -11338,8 +12563,8 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called keyspan_pda.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + The module will be called keyspan_pda.o. If you want to compile it + as a module, say M here and read . USB Xircom / Entregra Single Port Serial Driver CONFIG_USB_SERIAL_XIRCOM @@ -11357,15 +12582,15 @@ Say Y here if you want to use Keyspan USB to serial converter devices. This driver makes use of Keyspan's official firmware and was developed with their support. You must also include - firmware to support your particular device(s). + firmware to support your particular device(s). - See http://www.linuxcare.com.au/hugh/keyspan.html for + See for more information. - + This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called keyspan.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . USB Keyspan USA-28 Firmware CONFIG_USB_SERIAL_KEYSPAN_USA28 @@ -11397,8 +12622,8 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called omninet.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called omninet.o. If you want to compile it as a + module, say M here and read . USB Digi International AccelePort USB Serial Driver CONFIG_USB_SERIAL_DIGI_ACCELEPORT @@ -11412,33 +12637,34 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called digi_acceleport.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called digi_acceleport.o. If you want to compile + it as a module, say M here and read + . USB Empeg empeg-car Mark I/II Driver CONFIG_USB_SERIAL_EMPEG Say Y here if you want to connect to your Empeg empeg-car Mark I/II mp3 player via USB. The driver uses a single ttyUSB{0,1,2,...} - device node. See Documentation/usb/usb-serial.txt for more + device node. See for more tidbits of information. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called empeg.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . USB MCT Single Port Serial Driver CONFIG_USB_SERIAL_MCT_U232 Say Y here if you want to use a USB Serial single port adapter from Magic Control Technology Corp. (U232 is one of the model numbers). - + This driver also works with Sitecom U232-P25 and D-Link DU-H3SP USB BAY devices. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called mct_u232.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . USB Prolific 2303 Single Port Serial Driver CONFIG_USB_SERIAL_PL2303 @@ -11448,19 +12674,19 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called pl2303.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . USB REINER SCT cyberJack pinpad/e-com chipcard reader CONFIG_USB_SERIAL_CYBERJACK Say Y here if you want to use a cyberJack pinpad/e-com USB chipcard reader. This is an interface to ISO 7816 compatible contactbased chipcards, e.g. GSM SIMs. - + This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called cyberjack.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. - + a module, say M here and read . + If unsure, say N. USB Edgeport Serial Driver @@ -11486,8 +12712,8 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called io_edgeport.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + The module will be called io_edgeport.o. If you want to compile it + as a module, say M here and read . USB Serial Converter verbose debug CONFIG_USB_SERIAL_DEBUG @@ -11502,57 +12728,85 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called printer.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . USB IBM (Xirlink) C-It Camera support CONFIG_USB_IBMCAM Say Y here if you want to connect a IBM "C-It" camera, also known as - "Xirlink PC Camera" to your computer's USB port. For more - information, read Documentation/usb/ibmcam.txt. + "Xirlink PC Camera" to your computer's USB port. For more + information, read . This driver uses the Video For Linux API. You must enable (Y or M in config) Video For Linux (under Character Devices) to use this driver. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called ibmcam.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. This camera - has several configuration options which can be specified when you - load the module. Read Documentation/usb/ibmcam.txt to learn more. + The module will be called ibmcam.o. If you want to compile it as a + module, say M here and read . This + camera has several configuration options which can be specified when + you load the module. Read to + learn more. USB OV511 Camera support CONFIG_USB_OV511 Say Y here if you want to connect this type of camera to your - computer's USB port. See Documentation/usb/ov511.txt for more + computer's USB port. See for more information and for a list of supported cameras. - + This driver uses the Video For Linux API. You must say Y or M to "Video For Linux" (under Character Devices) to use this driver. Information on this API and pointers to "v4l" programs may be found - on the WWW at http://roadrunner.swansea.uk.linux.org/v4l.shtml . + on the WWW at . This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called ov511.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . + +USB Communication Class Ethernet driver +CONFIG_USB_CDCETHER + This driver supports devices conforming to the Communication Device + Class Ethernet Control Model. This is used in some cable modems. + For more details on the specification, get the Communication Device + Class specification from . + + This driver should work with the following devices: + * Ericsson PipeRider (all variants) + * Motorola (DM100 and SB4100) + * Broadcom Cable Modem (reference design) + * Toshiba PCX1100U and possibly other cable modems + + The device creates a network device (ethX, where X depends on what + other networking devices you have in use), as for a normal PCI + or ISA based ethernet network card. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called CDCEther.o. If you want to compile it as + a module, say M here and read . + +NetChip 1080-based USB Host-to-Host Link +CONFIG_USB_NET1080 + The NetChip 1080 is a USB 1.1 host controller. NetChip has a web + site with technical information at . Philips webcam support CONFIG_USB_PWC Say Y or M here if you want to use one of these Philips USB webcams: - PCA645, PCA646, PCVC675, PCVC680, PCVC690, PCVC730, PCVC740, or - the Askey VC010. The PCA635, PCVC665 and PCVC720 are not - supported by this driver and never will be. - - This driver has an optional plugin, which is distributed as a - binary module only. It contains code that allow you to use - higher resolutions and framerates but may not be distributed - as source. But even without this plugin you can these cams - for most applications. + PCA645, PCA646, PCVC675, PCVC680, PCVC690, PCVC730, PCVC740, or + the Askey VC010. The PCA635, PCVC665 and PCVC720 are not supported + by this driver and never will be. + + This driver has an optional plugin, which is distributed as a binary + module only. It contains code that allow you to use higher + resolutions and framerates but may not be distributed as source. + But even without this plugin you can these cams for most + applications. - See Documentation/usb/philips.txt for more information and + See for more information and installation instructions. The built-in microphone is enabled by selecting USB Audio support. @@ -11560,33 +12814,16 @@ This driver uses the Video For Linux API. You must say Y or M to "Video For Linux" (under Character Devices) to use this driver. Information on this API and pointers to "v4l" programs may be found - on the WWW at http://roadrunner.swansea.uk.linux.org/v4l.shtml . - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called pwc.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. - - -USB SE401 Camera support -CONFIG_USB_SE401 - Say Y here if you want to connect this type of camera to your - computer's USB port. See Documentation/usb/se401.txt for more - information and for a list of supported cameras. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" (under Multimedia Devices) to use this driver. - Information on this API and pointers to "v4l" programs may be found - on the WWW at http://roadrunner.swansea.uk.linux.org/v4l.shtml . + on the WWW at . This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called se401.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called pwc.o. If you want to compile it as a + module, say M here and read . -USB ADMtek Pegasus-based ethernet device support +USB ADMtek Pegasus-based Ethernet device support CONFIG_USB_PEGASUS - Say Y if you want to use your USB ethernet device. Supported + Say Y if you want to use your USB Ethernet device. Supported cards until now are: ADMtek AN986 Pegasus (eval. board) ADMtek ADM8511 Pegasus II (eval. board) @@ -11599,7 +12836,7 @@ LANEED Ethernet LD-USB/TX SMC 202 SOHOware NUB Ethernet - + Any Pegasus II based board also are supported. If you have devices with vendor IDs other than noted above you should add them in the driver code and send a message @@ -11608,8 +12845,9 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called pegasus.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . +USB KLSI KL5USB101-based Ethernet device support ' CONFIG_USB_KAWETH Say Y here if you want to use one of the following 10Mbps only USB Ethernet adapters based on the KLSI KL5KUSB101B chipset: @@ -11626,7 +12864,7 @@ Kingston Technology USB Ethernet Adapter Linksys USB10T Mobility USB-Ethernet Adapter - NetGear EA-101 + NetGear EA-101 Peracom Enet and Enet2 Portsmith Express Ethernet Adapter Shark Pocket Adapter @@ -11658,58 +12896,39 @@ CATC NetMate II smartBridges smartNIC + This driver makes the adapter appear as a normal Ethernet interface, + typically on eth0, if it is the only ethernet device, or perhaps on + eth1, if you have a PCI or ISA ethernet card installed. + This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called catc.o. If you want to compile it as a module, say M here and read . -USB Communication Class Ethernet driver -CONFIG_USB_CDCETHER - This driver supports devices conforming to the Communication Device - Class Ethernet Control Model. This is used in some cable modems. - For more details on the specification, get the Communication Device - Class specification from . - - This driver should work with the following devices: - * Ericsson PipeRider (all variants) - * Motorola (DM100 and SB4100) - * Broadcom Cable Modem (reference design) - * Toshiba PCX1100U and possibly other cable modems - - The device creates a network device (ethX, where X depends on what - other networking devices you have in use), as for a normal PCI - or ISA based ethernet network card. - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called CDCEther.o. If you want to compile it as - a module, say M here and read . - - USB Kodak DC-2xx Camera support CONFIG_USB_DC2XX - Say Y here if you want to connect this type of still camera to - your computer's USB port. See Documentation/usb/dc2xx.txt for more - information; some non-Kodak cameras may also work with this - driver, given application support (such as www.gPhoto.org). + Say Y here if you want to connect this type of still camera to your + computer's USB port. See for + more information; some non-Kodak cameras may also work with this + driver, given application support (such as ). This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called dc2xx.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . -USB Mustek MDC800 Digital Camera Support +USB Mustek MDC800 Digital Camera support CONFIG_USB_MDC800 Say Y here if you want to connect this type of still camera to your computer's USB port. This driver can be used with gphoto 0.4.3 - and higher (look at http://www.gphoto.org ). + and higher (look at ). To use it create a device node with "mknod /dev/mustek c 180 32" and configure it in your software. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called mdc800.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . USB Mass Storage support CONFIG_USB_STORAGE @@ -11719,7 +12938,7 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called usb-storage.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + as a module, say M here and read . USB Mass Storage verbose debug CONFIG_USB_STORAGE_DEBUG @@ -11746,7 +12965,7 @@ Technologies USS-720 chip. These cables are plugged into your USB port and provide USB compatibility to peripherals designed with parallel port interfaces. - + The chip has two modes: automatic mode and manual mode. In automatic mode, it looks to the computer like a standard USB printer. Only printers may be connected to the USS-720 in this mode. The generic @@ -11766,29 +12985,33 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called uss720.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. - + module, say M here and read . + USB device file system CONFIG_USB_DEVICEFS - If you say Y here (and to "/proc file system support" below), you - will get a file /proc/bus/usb/devices which lists the devices - currently connected to your USB busses, a file /proc/bus/usb/drivers - which lists the USB kernel client drivers currently loaded, and for - every connected device a file named "/proc/bus/usb/xxx/yyy", where - xxx is the bus number and yyy the device number; the latter files - can be used by user space programs to talk directly to the device. - These files are "virtual", meaning they are generated on the fly - and not stored on the hard drive. - - For the format of the /proc/bus/usb/ files, please read - Documentation/usb/proc_usb_info.txt. + If you say Y here (and to "/proc file system support" in the "File + systems section, above), you will get a file /proc/bus/usb/devices + which lists the devices currently connected to your USB bus or + busses, a file /proc/bus/usb/drivers which lists the USB kernel + client drivers currently loaded, and for every connected device a + file named "/proc/bus/usb/xxx/yyy", where xxx is the bus number and + yyy the device number; the latter files can be used by user space + programs to talk directly to the device. These files are "virtual", + meaning they are generated on the fly and not stored on the hard + drive. + + You may need to mount the usbdevfs file system to see the files, use + mount -t usbdevfs none /proc/bus/usb + + For the format of the various /proc/bus/usb/ files, please read + . Please note that this code is completely unrelated to devfs, the "/dev file system support". Most users want to say Y here. -USB Bandwidth allocation +Enforce USB bandwidth allocation CONFIG_USB_BANDWIDTH If you say Y here, the USB subsystem enforces USB bandwidth allocation and will prevent some device opens from succeeding @@ -11802,15 +13025,15 @@ DABUSB driver CONFIG_USB_DABUSB A Digital Audio Broadcasting (DAB) Receiver for USB and Linux - brought to you by the DAB-Team (http://dab.in.tum.de). This driver - can be taken as an example for URB-based bulk, control, and + brought to you by the DAB-Team (). This + driver can be taken as an example for URB-based bulk, control, and isochronous transactions. URB's are explained in - Documentation/usb/URB.txt. + . This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called dabusb.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called dabusb.o. If you want to compile it as a + module, say M here and read . Host-to-Host USB networking CONFIG_USB_USBNET @@ -11829,16 +13052,32 @@ The module will be called usbnet.o. If you want to compile it as a module, say M here and read . +Freecom USB/ATAPI Bridge support +CONFIG_USB_STORAGE_FREECOM + Support for the Freecom USB to IDE/ATAPI adaptor. + Freecom has a web page at . + +Microtech CompactFlash/SmartMedia reader +CONFIG_USB_STORAGE_DPCM + Say Y here to support the Microtech ZiO! CompactFlash/SmartMedia + reader, details at . + This driver treats the flash card as a removable storage device. + +Sandisk SDDR-09 SmartMedia reader support +CONFIG_USB_STORAGE_SDDR09 + Say Y here to include additional code to support the Sandisk SDDR-09 + SmartMedia reader in the USB Mass Storage driver. + USB Diamond Rio500 support CONFIG_USB_RIO500 Say Y here if you want to connect a USB Rio500 mp3 player to your - computer's USB port. Please read Documentation/usb/rio.txt + computer's USB port. Please read for more information. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called rio500.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . D-Link DSB-R100 FM radio support CONFIG_USB_DSBR @@ -11851,36 +13090,280 @@ (Y or M in config) Video For Linux (under Character Devices) to use this driver. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called dsbr100.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . + +Always do synchronous disk IO for UBD +CONFIG_BLK_DEV_UBD_SYNC + The User-Mode Linux port includes a driver called UBD which will let + you access arbitrary files on the host computer as block devices. + Writes to such a block device are not immediately written to the + host's disk; this may cause problems if, for example, the User-Mode + Linux 'Virtual Machine' uses a journalling file system and the host + computer crashes. + + Synchronous operation (i.e. always writing data to the host's disk + immediately) is configurable on a per-UBD basis by using a special + kernel command line option. Alternatively, you can say Y here to + turn on synchronous operation by default for all block. + + If you're running a journalling file system (like reiserfs, for + example) in your virtual machine, you will want to say Y here. If + you care for the safety of the data in your virtual machine, Y is a + wise choice too. In all other cases (for example, if you're just + playing around with User-Mode Linux) you can choose N. + +Enable ptrace proxy +CONFIG_PT_PROXY + This option enables a debugging interface which allows gdb to debug + the kernel without needing to actually attach to kernel threads. + If you want to do kernel debugging, say Y here; otherwise say N. + +Management console +CONFIG_MCONSOLE + The user mode linux management console is a low-level interface to + the kernel, somewhat like the i386 SysRq interface. Since there is + a full-blown operating system running under every user mode linux + instance, there is much greater flexibility possible than with the + SysRq mechanism. + + If you answer 'Y' to this option, to use this feature, you need the + mconsole client (called uml_mconsole) which is present in CVS in + 2.4.5-9um and later (path /tools/mconsole), and is also in the + distribution RPM package in 2.4.6 and later. + + It is safe to say 'Y' here. + +Enable kernel debugging symbols +CONFIG_DEBUGSYM + When this is enabled, the User-Mode Linux binary will include + debugging symbols. This enlarges the binary by a few megabytes, + but aids in tracking down kernel problems in UML. It is required + if you intend to do any kernel development. + + If you're truly short on disk space or don't expect to report any + bugs back to the UML developers, say N, otherwise say Y. + +Enable gcov support +CONFIG_GCOV + This option allows developers to retrieve coverage data from a UML + session. + + See for more + details. + + If you're involved in UML kernel development and want to use gcov, + say Y. If you're unsure, say N. + +Enable gprof support +CONFIG_GPROF + This allows profiling of a User-Mode Linux kernel with the gprof + utility. + + See for more + details. + + If you're involved in UML kernel development and want to use gprof, + say Y. If you're unsure, say N. + +Host filesystem +CONFIG_HOSTFS + While the User-Mode Linux port uses its own root file system for + booting and normal file access, this module lets the UML user + access files stored on the host. It does not require any + network connection between the Host and UML. An example use of + this might be: + + mount none /tmp/fromhost -t hostfs -o /tmp/umlshare + + where /tmp/fromhost is an empty directory inside UML and + /tmp/umlshare is a directory on the host with files the UML user + wishes to access. + + For more information, see + . + + If you'd like to be able to work with files stored on the host, + say Y or M here; otherwise say N. + +Example IO Memory driver +CONFIG_MMAPPER + The User-Mode Linux port can provide support for IO Memory + emulation with this option. This allows a host file to be + specified as an I/O region on the kernel command line. That file + will be mapped into UML's kernel address space where a driver can + locate it and do whatever it wants with the memory, including + providing an interface to it for UML processes to use. + + For more information, see + . + + If you'd like to be able to provide a simulated IO port space for + User-Mode Linux processes, say Y. If unsure, say N. + +Virtual Serial Line +CONFIG_SSL + The User-Mode Linux environment allows you to create virtual serial + lines on the UML that are usually made to show up on the host as + ttys or ptys. + + See for more + information and command line examples of how to use this facility. + + Unless you have a specific reason for disabling this, say Y. + +Virtual network device +CONFIG_UML_NET + While the User-Mode port cannot directly talk to any physical + hardware devices, this choice and the following transport options + provide one or more virtual network devices through which the UML + kernels can talk to each other, the host, and with the host's help, + machines on the outside world. + + For more information, including explations of the networking and + sample configurations, see + . + + If you'd like to be able to enable networking in the User-Mode + linux environment, say Y; otherwise say N. Note that you must + enable at least one of the following transport options to actually + make use of UML networking. + +Daemon transport +CONFIG_UML_NET_DAEMON + This User-Mode Linux network transport allows one or more running + UMLs on a single host to communicate with each other, but not to + the host. + + To use this form of networking, you'll need to run the UML + networking daemon on the host. + + For more information, see + That site + has examples of the UML command line to use to enable Daemon + networking. + + If you'd like to set up a network with other UMLs on a single host, + say Y. If you need a network between UMLs on multiple physical + hosts, choose the Multicast Transport. To set up a network with + the host and/or other IP machines, say Y to the Ethertap or Slip + transports. You'll need at least one of them, but may choose + more than one without conflict. If you don't need UML networking, + say N. + +Ethertap transport +CONFIG_UML_NET_ETHERTAP + The Ethertap User-Mode Linux network transport allows a single + running UML to exchange packets with its host over one of the + host's Ethertap devices, such as /dev/tap0. Additional running + UMLs can use additional Ethertap devices, one per running UML. + While the UML believes its on a (multi-device, broadcast) virtual + Ethernet network, it's in fact communicating over a point-to-point + link with the host. + + To use this, your host kernel must have support for Ethertap + devices. Also, if your host kernel is 2.4.x, it must have + CONFIG_NETLINK_DEV configured as Y or M. + + For more information, see + That site + has examples of the UML command line to use to enable Ethertap + networking. + + If you'd like to set up an IP network with the host and/or the + outside world, say Y to this, the Daemon Transport and/or the + Slip Transport. You'll need at least one of them, but may choose + more than one without conflict. If you don't need UML networking, + say N. + +TUN/TAP transport +CONFIG_UML_NET_TUNTAP + The UML TUN/TAP network transport allows a UML instance to exchange + packets with the host over a TUN/TAP device. This option will only + work with a 2.4 host, unless you've applied the TUN/TAP patch to + your 2.2 host kernel. + + To use this transport, your host kernel must have support for TUN/TAP + devices, either built-in or as a module. + +Multicast transport +CONFIG_UML_NET_MCAST + This Multicast User-Mode Linux network transport allows multiple + UMLs (even ones running on different host machines!) to talk to + each other over a virtual ethernet network. However, it requires + at least one UML with one of the other transports to act as a + bridge if any of them need to be able to talk to their hosts or any + other IP machines. + + To use this, your host kernel(s) must support IP Multicasting. + + For more information, see + That site + has examples of the UML command line to use to enable Multicast + networking, and notes about the security of this approach. + + If you need UMLs on multiple physical hosts to communicate as if + they shared an Ethernet network, say Y. If you need to communicate + with other IP machines, make sure you select one of the other + transports (possibly in addition to Multicast; they're not + exclusive). If you don't need to network UMLs say N to each of + the transports. + +SLIP transport +CONFIG_UML_NET_SLIP + The Slip User-Mode Linux network transport allows a running UML to + network with its host over a point-to-point link. Unlike Ethertap, + which can carry any Ethernet frame (and hence even non-IP packets), + the Slip transport can only carry IP packets. + + To use this, your host must support Slip devices. + + For more information, see + . That site + has examples of the UML command line to use to enable Slip + networking, and details of a few quirks with it. + + The Ethertap Transport is preferred over Slip because of its + limitation. If you prefer Slip, however, say Y here. Otherwise + choose the Multicast transport (to network multiple UMLs on + multiple hosts), Ethertap (to network with the host and the + outside world), and/or the Daemon transport (to network multiple + UMLs on a single host). You may choose more than one without + conflict. If you don't need UML networking, say N. Microtek USB scanner support CONFIG_USB_MICROTEK Say Y here if you want support for the Microtek X6USB and possibly the Phantom 336CX, Phantom C6 and ScanMaker V6U(S)L. - Support for anything but the X6 is experimetal. + Support for anything but the X6 is experimental. Please report failures and successes. The scanner will appear as a scsi generic device to the rest of the system. Scsi support is required for this driver to compile and work. SANE 1.0.4 or newer is needed to make use of your scanner. This driver can be compiled as a module. +HP 53xx and Minolta Dual Scanner support +CONFIG_USB_HPUSBSCSI + Say Y here if you want support for the HP 53xx series of scanners + and the Minolta Scan Dual. This driver is experimental. + The scanner will be accessible as a SCSI device. + USB Bluetooth support CONFIG_USB_BLUETOOTH Say Y here if you want to connect a USB Bluetooth device to your computer's USB port. You will need the Bluetooth stack (available - at http://developer.axis.com/software/index.shtml) to fully use + at to fully use the device. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called bluetooth.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. - + The module will be called bluetooth.o. If you want to compile it as + a module, say M here and read . + Minix fs support CONFIG_MINIX_FS Minix is a simple operating system used in many classes about OS's. @@ -11889,58 +13372,66 @@ but has been superseded by the second extended file system ext2fs. You don't want to use the minix file system on your hard disk because of certain built-in restrictions, but it is sometimes found - on older Linux floppy disks. This option will enlarge your kernel by - about 28 KB. If unsure, say N. + on older Linux floppy disks. This option will enlarge your kernel + by about 28 KB. If unsure, say N. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called minix.o. Note that the file system of your root partition - (the one containing the directory /) cannot be compiled as a module. + say M here and read . The module + will be called minix.o. Note that the file system of your root + partition (the one containing the directory /) cannot be compiled as + a module. Reiserfs support CONFIG_REISERFS_FS - Stores not just filenames but the files themselves in a balanced tree. Uses journaling. - Balanced trees are more efficient than traditional - filesystem architectural foundations. + Balanced trees are more efficient than traditional file system + architectural foundations. - In general, ReiserFS is as fast as ext2, but is very efficient - with large directories and small files. Additional patches are - needed for NFS and quotas, please see www.reiserfs.org for - links. + In general, ReiserFS is as fast as ext2, but is very efficient with + large directories and small files. Additional patches are needed + for NFS and quotas, please see for links. It is more easily extended to have features currently found in - database and keyword search systems than block allocation based - filesystems are. The next version will be so extended, and will - support plugins consistent with our motto ``It takes more than a - license to make source code open.'' + database and keyword search systems than block allocation based file + systems are. The next version will be so extended, and will support + plugins consistent with our motto ``It takes more than a license to + make source code open.'' - Read www.reiserfs.org to learn more about reiserfs. + Read to learn more about reiserfs. Sponsored by Threshold Networks, Emusic.com, and Bigstorage.com. If you like it, you can pay us to add new features to it that you need, buy a support contract, or pay us to port it to another OS. -Enable Reiserfs consistency checks +Enable extra Reiserfs consistency checks CONFIG_REISERFS_CHECK - If you set this to yes, then ReiserFS will perform every check it - can possibly imagine of its internal consistency throughout its + If you set this to Y, then ReiserFS will perform every check it can + possibly imagine of its internal consistency throughout its operation. It will also go substantially slower. More than once we have forgotten that this was on, and then gone despondent over the latest benchmarks.:-) Use of this option allows our team to go all out in checking for consistency when debugging without fear of its effect on end users. If you are on the verge of sending in a bug - report, say yes and you might get a useful error message. Almost - everyone should say no. + report, say Y and you might get a useful error message. Almost + everyone should say N. + +Publish some reiserfs-specific info under /proc/fs/reiserfs +CONFIG_REISERFS_PROC_INFO + Create under /proc/fs/reiserfs hierarchy of files, displaying + various ReiserFS statistics and internal data on the expense of + making your kernel or module slightly larger (+8 KB). This also + increases amount of kernel memory required for each mount. Almost + everyone but ReiserFS developers and people fine-tuning reiserfs or + tracing problems should say N. Second extended fs support CONFIG_EXT2_FS This is the de facto standard Linux file system (method to organize - files on a storage device) for hard disks. + files on a storage device) for hard disks. You want to say Y here, unless you intend to use Linux exclusively from inside a DOS partition using the UMSDOS file system. The @@ -11958,70 +13449,151 @@ by about 44 KB. The Ext2fs-Undeletion mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , gives information about + , gives information about how to retrieve deleted files on ext2fs file systems. To change the behavior of ext2 file systems, you can use the tune2fs utility ("man tune2fs"). To modify attributes of files and directories on ext2 file systems, use chattr ("man chattr"). - + Ext2fs partitions can be read from within DOS using the ext2tool command line tool package (available via FTP (user: anonymous) from - ftp://metalab.unc.edu/pub/Linux/system/filesystems/ext2 ) and from + ) and from within Windows NT using the ext2nt command line tool package from - ftp://metalab.unc.edu/pub/Linux/utils/dos . Explore2fs is a + . Explore2fs is a graphical explorer for ext2fs partitions which runs on Windows 95 and Windows NT and includes experimental write support; it is available from - http://jnewbigin-pc.it.swin.edu.au/Linux/Explore2fs.htm . + . If you want to compile this file system as a module ( = code which can be inserted in and removed from the running kernel whenever you - want), say M here and read Documentation/modules.txt. The module - will be called ext2.o. Be aware however that the file system of your - root partition (the one containing the directory /) cannot be - compiled as a module, and so this could be dangerous. Most everyone - wants to say Y here. + want), say M here and read . The + module will be called ext2.o. Be aware however that the file system + of your root partition (the one containing the directory /) cannot + be compiled as a module, and so this could be dangerous. Most + everyone wants to say Y here. + +Ext3 journaling file system support (EXPERIMENTAL) +CONFIG_EXT3_FS + This is the journaling version of the Second extended file system + (often called ext3), the de facto standard Linux file system + (method to organize files on a storage device) for hard disks. + + The journaling code included in this driver means you do not have + to run e2fsck (file system checker) on your file systems after a + crash. The journal keeps track of any changes that were being made + at the time the system crashed, and can ensure that your file system + is consistent without the need for a lengthy check. + + Other than adding the journal to the file system, the on-disk format + of ext3 is identical to ext2. It is possible to freely switch + between using the ext3 driver and the ext2 driver, as long as the + file system has been cleanly unmounted, or e2fsck is run on the file + system. + + To add a journal on an existing ext2 file system or change the + behavior of ext3 file systems, you can use the tune2fs utility ("man + tune2fs"). To modify attributes of files and directories on ext3 + file systems, use chattr ("man chattr"). You need to be using + e2fsprogs version 1.20 or later in order to create ext3 journals + (available at ). + + If you want to compile this file system as a module ( = code which + can be inserted in and removed from the running kernel whenever you + want), say M here and read . The + module will be called ext3.o. Be aware however that the file system + of your root partition (the one containing the directory /) cannot + be compiled as a module, and so this may be dangerous. + +Journal Block Device support (JBD for ext3) (EXPERIMENTAL) +CONFIG_JBD + This is a generic journaling layer for block devices. It is + currently used by the ext3 file system, but it could also be used to + add journal support to other file systems or block devices such as + RAID or LVM. + + If you are using the ext3 file system, you need to say Y here. If + you are not using ext3 then you will probably want to say N. + + If you want to compile this device as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The module + will be called jbd.o. If you are compiling ext3 into the kernel, + you cannot compile this code as a module. + +JBD (ext3) debugging support +CONFIG_JBD_DEBUG + If you are using the ext3 journaled file system (or potentially any + other file system/device using JBD), this option allows you to + enable debugging output while the system is running, in order to + help track down any problems you are having. By default the + debugging output will be turned off. + + If you select Y here, then you will be able to turn on debugging + with "echo N > /proc/sys/fs/jbd-debug", where N is a number between + 1 and 5, the higher the number, the more debugging output is + generated. To turn debugging off again, do + "echo 0 > /proc/sys/fs/jbd-debug". + +Buffer Head tracing (DEBUG) +CONFIG_BUFFER_DEBUG + If you are a kernel developer working with file systems or in the + block device layer, this buffer head tracing may help you to track + down bugs in your code. This enables some debugging macros + (BUFFER_TRACE, etc.) which allow you to track the state of a buffer + through various layers of code. The debugging code is used + primarily by ext3 and JBD code. + + Because this option adds considerably to the size of each buffer, + most people will want to say N here. -BFS file system support (EXPERIMENTAL) +BFS file system support CONFIG_BFS_FS Boot File System (BFS) is a file system used under SCO UnixWare to allow the bootloader access to the kernel image and other important - files during the boot process. It is usually mounted under /stand + files during the boot process. It is usually mounted under /stand and corresponds to the slice marked as "STAND" in the UnixWare - partition. You should say Y if you want to read or write - the files on your /stand slice from within Linux. You then also - need to say Y to "UnixWare slices support", below. More information - about the BFS file system is contained in the file - Documentation/filesystems/bfs.txt. + partition. You should say Y if you want to read or write the files + on your /stand slice from within Linux. You then also need to say Y + to "UnixWare slices support", below. More information about the BFS + file system is contained in the file + . If you don't know what this is about, say N. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called bfs.o. Note that the file system of your root partition (the - one containing the directory /) cannot be compiled as a module. + say M here and read . The module + will be called bfs.o. Note that the file system of your root + partition (the one containing the directory /) cannot be compiled as + a module. Compressed ROM file system support CONFIG_CRAMFS Saying Y here includes support for CramFs (Compressed ROM File - System). Cramfs is designed to be a simple, small, and compressed - file system for ROM based embedded systems. CramFs is read-only, + System). CramFs is designed to be a simple, small, and compressed + file system for ROM based embedded systems. CramFs is read-only, limited to 256MB file systems (with 16MB files), and doesn't support 16/32 bits uid/gid, hard links and timestamps. - - See Documentation/filesystems/cramfs.txt and fs/cramfs/README - for further information. + + See and + for further information. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called cramfs.o. Note that the root file system (the one containing - the directory /) cannot be compiled as a module. + say M here and read . The module + will be called cramfs.o. Note that the root file system (the one + containing the directory /) cannot be compiled as a module. If unsure, say N. - + +CMS file system support +CONFIG_CMS_FS + Read only support for CMS minidisk file systems found on IBM + mainframe systems. Only the basic format is supported so far. If + you don't know what CMS is you probably don't want to know any more. + Virtual memory file system support CONFIG_TMPFS Tmpfs is a file system which keeps all files in virtual memory. @@ -12033,8 +13605,8 @@ Everything is "virtual" in the sense that no files will be created on your hard drive; if you reboot, everything in tmpfs will be lost. - - You should mount the filesystem somewhere to be able to use + + You should mount the file system somewhere to be able to use POSIX shared memory. Adding the following line to /etc/fstab should take care of things: @@ -12044,10 +13616,10 @@ if necessary (/dev/shm is automagically created if you use devfs). You can set limits for the number of blocks and inodes used by the - filesystem with the mount options "size", "nr_blocks" and + file system with the mount options "size", "nr_blocks" and "nr_inodes". These parameters accept a suffix k, m or g for kilo, mega and giga and can be changed on remount. - + The initial permissions of the root directory can be set with the mount option "mode". @@ -12056,56 +13628,65 @@ Ramfs is a file system which keeps all files in RAM. It allows read and write access. - It is more of an programming example than a useable filesystem. If + It is more of an programming example than a useable file system. If you need a file system which lives in RAM with limit checking use tmpfs. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called ramfs.o. + say M here and read . The module + will be called ramfs.o. -ISO 9660 CDROM file system support +ISO 9660 CD-ROM file system support CONFIG_ISO9660_FS - This is the standard file system used on CDROMs. It was previously + This is the standard file system used on CD-ROMs. It was previously known as "High Sierra File System" and is called "hsfs" on other - Unix systems. The so-called Rock-Ridge extensions which allow for + Unix systems. The so-called Rock-Ridge extensions which allow for long Unix filenames and symbolic links are also supported by this - driver. If you have a CDROM drive and want to do more with it than + driver. If you have a CD-ROM drive and want to do more with it than just listen to audio CDs and watch its LEDs, say Y (and read - Documentation/filesystems/isofs.txt and the CDROM-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto ), thereby enlarging - your kernel by about 27 KB; otherwise say N. + and the CD-ROM-HOWTO, + available from ), thereby + enlarging your kernel by about 27 KB; otherwise say N. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called isofs.o. + say M here and read . The module + will be called isofs.o. -Microsoft Joliet CDROM extensions +Microsoft Joliet CD-ROM extensions CONFIG_JOLIET - Joliet is a Microsoft extension for the ISO 9660 CDROM file system + Joliet is a Microsoft extension for the ISO 9660 CD-ROM file system which allows for long filenames in unicode format (unicode is the new 16 bit character code, successor to ASCII, which encodes the characters of almost all languages of the world; see - http://www.unicode.org for more information). Say Y here if you want - to be able to read Joliet CDROMs under Linux. + for more information). Say Y here if you + want to be able to read Joliet CD-ROMs under Linux. -UDF File System support (read only) +Transparent decompression extension +CONFIG_ZISOFS + This is a Linux-specific extension to RockRidge which lets you store + data in compressed form on a CD-ROM and have it transparently + decompressed when the CD-ROM is accessed. See + for the tools + necessary to create such a filesystem. Say Y here if you want to be + able to read such compressed CD-ROMs. + +UDF file system support (read-only) CONFIG_UDF_FS - This is the new file system used on some CDROMs and DVDs. Say Y if + This is the new file system used on some CD-ROMs and DVDs. Say Y if you intend to mount DVD discs or CDRW's written in packet mode, or if written to by other UDF utilities, such as DirectCD. This UDF file system support is read-only. If you want to write to UDF file systems on some media, you need to say Y to "UDF read-write support" below in addition. Please read - Documentation/filesystems/udf.txt. + . This file system support is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called udf.o. If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . If unsure, say N. @@ -12139,23 +13720,23 @@ Linux box, say Y here, mount the floppy under Linux with an MSDOS file system and use GNU tar's M option. GNU tar is a program available for Unix and DOS ("man tar" or "info tar"). - + It is now also becoming possible to read and write compressed FAT - file systems; read Documentation/filesystems/fat_cvf.txt for + file systems; read for details. - + The FAT support will enlarge your kernel by about 37 KB. If unsure, say Y. If you want to compile this as a module however ( = code which can be inserted in and removed from the running kernel whenever you - want), say M here and read Documentation/modules.txt. The module - will be called fat.o. Note that if you compile the FAT support as a - module, you cannot compile any of the FAT-based file systems into - the kernel -- they will have to be modules as well. The file system - of your root partition (the one containing the directory /) cannot - be a module, so don't say M here if you intend to use UMSDOS as your - root file system. + want), say M here and read . The + module will be called fat.o. Note that if you compile the FAT + support as a module, you cannot compile any of the FAT-based file + systems into the kernel -- they will have to be modules as well. + The file system of your root partition (the one containing the + directory /) cannot be a module, so don't say M here if you intend + to use UMSDOS as your root file system. MSDOS fs support CONFIG_MSDOS_FS @@ -12163,8 +13744,8 @@ they are compressed; to access compressed MSDOS partitions under Linux, you can either use the DOS emulator DOSEMU, described in the DOSEMU-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , or try dmsdosfs in - ftp://metalab.unc.edu/pub/Linux/system/filesystems/dosfs . If you + , or try dmsdosfs in + . If you intend to use dosemu with a non-compressed MSDOS partition, say Y here) and MSDOS floppies. This means that file access becomes transparent, i.e. the MSDOS files look and behave just like all @@ -12183,32 +13764,33 @@ answer Y. This will only work if you said Y to "DOS FAT fs support" as well. If you want to compile this as a module however ( = code which can be inserted in and removed from the running kernel - whenever you want), say M here and read Documentation/modules.txt. + whenever you want), say M here and read + . The module will be called msdos.o. VFAT (Windows-95) fs support CONFIG_VFAT_FS This option provides support for normal Windows file systems with - long filenames. That includes non-compressed FAT-based file systems + long filenames. That includes non-compressed FAT-based file systems used by Windows 95, Windows 98, Windows NT 4.0, and the Unix programs from the mtools package. You cannot use the VFAT file system for your Linux root partition (the one containing the directory /); use UMSDOS instead if you want to run Linux from within a DOS partition (i.e. say Y to - "UMSDOS: Unix like fs on top of std MSDOS fs", below). + "Unix like fs on top of std MSDOS fs", below). The VFAT support enlarges your kernel by about 10 KB and it only - works if you said Y to the "DOS FAT fs support" above. Please read - the file Documentation/filesystems/vfat.txt for details. If unsure, - say Y. + works if you said Y to the "DOS FAT fs support" above. Please read + the file for details. If + unsure, say Y. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called vfat.o. + say M here and read . The module + will be called vfat.o. -UMSDOS: Unix-like file system on top of standard MSDOS fs +Unix-like file system on top of standard MSDOS fs CONFIG_UMSDOS_FS Say Y here if you want to run Linux from within an existing DOS partition of your hard drive. The advantage of this is that you can @@ -12216,34 +13798,25 @@ backing everything up and restoring afterwards) and hence you're able to quickly try out Linux or show it to your friends; the disadvantage is that Linux becomes susceptible to DOS viruses and - that UMSDOS is somewhat slower than ext2fs. Another use of UMSDOS + that UMSDOS is somewhat slower than ext2fs. Another use of UMSDOS is to write files with long unix filenames to MSDOS floppies; it also allows Unix-style soft-links and owner/permissions of files on - MSDOS floppies. You will need a program called umssync in order to - make use of UMSDOS; read Documentation/filesystems/umsdos.txt. + MSDOS floppies. You will need a program called umssync in order to + make use of UMSDOS; read + . To get utilities for initializing/checking UMSDOS file system, or latest patches and/or information, visit the UMSDOS home page at - http://www.voyager.hr/~mnalis/umsdos/ . + . This option enlarges your kernel by about 28 KB and it only works if you said Y to both "DOS FAT fs support" and "MSDOS fs support" - above. If you want to compile this as a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called umsdos.o. Note that the file system of your root partition - (the one containing the directory /) cannot be a module, so saying M - could be dangerous. If unsure, say N. - -PReP residual data support -CONFIG_PREP_RESIDUAL - Some PReP systems have residual data passed to the kernel by the - firmware. This allows detection of memory size, devices present and - other useful pieces of information. Sometimes this information is not - present or incorrect. - - Unless you expect to boot on a PReP system, there is not need to select - yes. + above. If you want to compile this as a module ( = code which can + be inserted in and removed from the running kernel whenever you + want), say M here and read . The + module will be called umsdos.o. Note that the file system of your + root partition (the one containing the directory /) cannot be a + module, so saying M could be dangerous. If unsure, say N. /proc file system support CONFIG_PROC_FS @@ -12251,7 +13824,7 @@ of the system. "Virtual" means that it doesn't take up any space on your hard disk: the files are created on the fly by the kernel when you try to access them. Also, you cannot read the files with older - version of the program less: you need to use more or cat. + version of the program less: you need to use more or cat. It's totally cool; for example, "cat /proc/interrupts" gives information about what the different IRQs are used for at the moment @@ -12267,13 +13840,23 @@ /proc" or the equivalent line in /etc/fstab does the job. The /proc file system is explained in the file - Documentation/filesystems/proc.txt and on the proc(5) manpage ("man - 5 proc"). + and on the proc(5) manpage + ("man 5 proc"). This option will enlarge your kernel by about 67 KB. Several programs depend on this, so everyone should say Y here. -/dev file system support (EXPERIMENTAL) +Support for PReP Residual Data +CONFIG_PREP_RESIDUAL + Some PReP systems have residual data passed to the kernel by the + firmware. This allows detection of memory size, devices present and + other useful pieces of information. Sometimes this information is + not present or incorrect. + + Unless you expect to boot on a PReP system, there is not need to + select Y. + +/dev file system support CONFIG_DEVFS_FS This is support for devfs, a virtual file system (like /proc) which provides the file system interface to device drivers, normally found @@ -12284,12 +13867,12 @@ /dev directory using the mknod command (or MAKEDEV script) anymore. This is work in progress. If you want to use this, you *must* read - the material in Documentation/filesystems/devfs/, especially the - file README there. + the material in , especially + the file README there. If unsure, say N. -Enable automatic mounting at boot +Automatically mount devfs at boot time CONFIG_DEVFS_MOUNT This option appears if you have CONFIG_DEVFS_FS enabled. Setting this to 'Y' will make the kernel automatically mount devfs onto /dev @@ -12302,8 +13885,9 @@ CONFIG_DEVFS_DEBUG If you say Y here, then the /dev file system code will generate debugging messages. See the file - Documentation/filesystems/devfs/boot-options for more details. - + for more + details. + If unsure, say N. NFS file system support @@ -12317,35 +13901,35 @@ programs nfsd and mountd (but does not need to have NFS file system support enabled in its kernel). NFS is explained in the Network Administrator's Guide, available from - http://www.linuxdoc.org/docs.html#guide , on its man page: "man + , on its man page: "man nfs", and in the NFS-HOWTO. - + A superior but less widely used alternative to NFS is provided by the Coda file system; see "Coda file system support" below. If you say Y here, you should have said Y to TCP/IP networking also. - This option would enlarge your kernel by about 27 KB. + This option would enlarge your kernel by about 27 KB. This file system is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called nfs.o. If you want to compile it as a module, - say M here and read Documentation/modules.txt. + say M here and read . If you are configuring a diskless machine which will mount its root - file system over NFS at boot time, say Y here and to "IP: kernel - level autoconfiguration" above and to "Root file system on NFS" + file system over NFS at boot time, say Y here and to "Kernel + level IP autoconfiguration" above and to "Root file system on NFS" below. You cannot compile this driver as a module in this case. There are two packages designed for booting diskless machines over the net: netboot and etherboot, both available via FTP from - ftp://metalab.unc.edu/pub/Linux/system/boot/ethernet/ . + . If you don't know what all this is about, say N. -Provide NFSv3 client support (EXPERIMENTAL) +Provide NFSv3 client support CONFIG_NFS_V3 Say Y here if you want your NFS client to be able to speak the newer - version 3 of the NFS protocol. - + version 3 of the NFS protocol. + If unsure, say N. Root file system on NFS @@ -12353,12 +13937,12 @@ If you want your Linux box to mount its whole root file system (the one containing the directory /) from some other computer over the net via NFS (presumably because your box doesn't have a hard disk), - say Y. Read Documentation/nfsroot.txt for details. It is likely that - in this case, you also want to say Y to "IP: kernel level + say Y. Read for details. It is + likely that in this case, you also want to say Y to "Kernel level IP autoconfiguration" so that your box can discover its network address at boot time. - - Most people say N here. + + Most people say N here. NFS server support CONFIG_NFSD @@ -12371,20 +13955,21 @@ faster. In either case, you will need support software; the respective - locations are given in the file Documentation/Changes in the NFS - section. + locations are given in the file in the + NFS section. If you say Y here, you will get support for version 2 of the NFS protocol (NFSv2). If you also want NFSv3, say Y to the next question as well. Please read the NFS-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . The NFS server is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module is called nfsd.o. If you want to compile it as a module, - say M here and read Documentation/modules.txt. If unsure, say N. + The module is called nfsd.o. If you want to compile it as a module, + say M here and read . If unsure, + say N. Provide NFSv3 server support CONFIG_NFSD_V3 @@ -12396,7 +13981,8 @@ If you are a developer and want to work on fixing problems with NFS server over TCP support, say Y here. If unsure, say N. - Some problems can be found by looking for FIXME in net/sunrpc/svcsock.c + Some problems can be found by looking for FIXME in + . OS/2 HPFS file system support CONFIG_HPFS_FS @@ -12406,29 +13992,13 @@ write files to an OS/2 HPFS partition on your hard drive. OS/2 floppies however are in regular MSDOS format, so you don't need this option in order to be able to read them. Read - Documentation/filesystems/hpfs.txt. - - This file system is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module is called hpfs.o. If you want to compile it as a module, - say M here and read Documentation/modules.txt. If unsure, say N. - -FreeVxFS file system support (VERITAS VxFS(TM) compatible) -CONFIG_VXFS_FS - FreeVxFS is a filesystem driver that support the VERITAS VxFS(TM) - filesystem format. VERITAS VxFS(TM) is the standard filesystem - of SCO UnixWare (and possibly others) and optionally available - for Sunsoft Solaris, HP-UX and many other operating systems. - Currently only readonly access is supported. - - NOTE: the filesystem type as used by mount(1), mount(2) and fstab(5) - is 'vxfs' as it describes the filesystem format, not the - actual driver. + . This file system is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module is called freevxfs.o. If you want to compile it as a module, - say M here and read Documentation/modules.txt. If unsure, say N. + The module is called hpfs.o. If you want to compile it as a module, + say M here and read . If unsure, + say N. NTFS file system support (read-only) CONFIG_NTFS_FS @@ -12461,94 +14031,98 @@ If unsure, say N. -System V, Version 7, Xenix and Coherent filesystem support +System V/Xenix/V7/Coherent file system support CONFIG_SYSV_FS SCO, Xenix and Coherent are commercial Unix systems for Intel machines, and Version 7 was used on the DEC PDP-11. Saying Y here would allow you to read from their floppies and hard disk - partitions. If you also want to write to these media, say Y to - "SYSV file system write support" below. + partitions. If you have floppies or hard disk partitions like that, it is likely that they contain binaries from those other Unix systems; in order to run these binaries, you will want to install linux-abi which is a - a set of kernel modules that lets you run SCO, Xenix, Wyse, UnixWare, - Dell Unix and System V programs under Linux. It's available via FTP - (user: ftp) from ftp://ftp.openlinux.org/pub/people/hch/linux-abi). + a set of kernel modules that lets you run SCO, Xenix, Wyse, + UnixWare, Dell Unix and System V programs under Linux. It is + available via FTP (user: ftp) from + ). NOTE: that will work only for binaries from Intel-based systems; PDP ones will have to wait until somebody ports Linux to -11 ;-) If you only intend to mount files from some other Unix over the network using NFS, you don't need the System V file system support - (but you need NFS file system support obviously). + (but you need NFS file system support obviously). Note that this option is generally not needed for floppies, since a good portable way to transport files and directories between unixes (and even other operating systems) is given by the tar program ("man - tar" or preferably "info tar"). Note also that this option has + tar" or preferably "info tar"). Note also that this option has nothing whatsoever to do with the option "System V IPC". Read about - the System V file system in Documentation/filesystems/sysv-fs.txt. + the System V file system in + . Saying Y here will enlarge your kernel by about 27 KB. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called sysv.o. + say M here and read . The module + will be called sysv.o. If you haven't heard about all of this before, it's safe to say N. Amiga FFS file system support CONFIG_AFFS_FS The Fast File System (FFS) is the common file system used on hard - disks by Amiga(tm) systems since AmigaOS Version 1.3 (34.20). Say Y + disks by Amiga(tm) systems since AmigaOS Version 1.3 (34.20). Say Y if you want to be able to read and write files from and to an Amiga - FFS partition on your hard drive. Amiga floppies however cannot be + FFS partition on your hard drive. Amiga floppies however cannot be read with this driver due to an incompatibility of the floppy controller used in an Amiga and the standard floppy controller in - PCs and workstations. Read Documentation/filesystems/affs.txt and - fs/affs/Changes. + PCs and workstations. Read + and . With this driver you can also mount disk files used by Bernd - Schmidt's Un*X Amiga Emulator (http://www.freiburg.linux.de/~uae/ ). + Schmidt's Un*X Amiga Emulator + (). If you want to do this, you will also need to say Y or M to "Loop device support", above. This file system is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module is called affs.o. If you want to compile it as a module, - say M here and read Documentation/modules.txt. If unsure, say N. + The module is called affs.o. If you want to compile it as a module, + say M here and read . If unsure, + say N. -Apple Macintosh file system support (EXPERIMENTAL) +Apple Macintosh file system support CONFIG_HFS_FS If you say Y here, you will be able to mount Macintosh-formatted floppy disks and hard drive partitions with full read-write access. - Please read fs/hfs/HFS.txt to learn about the available mount - options. + Please read to learn about the available mount + options. This file system support is also available as a module ( = code which can be inserted in and removed from the running kernel - whenever you want). The module is called hfs.o. If you want to + whenever you want). The module is called hfs.o. If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . ROM file system support CONFIG_ROMFS_FS This is a very small read-only file system mainly intended for initial ram disks of installation disks, but it could be used for - other read-only media as well. Read - Documentation/filesystems/romfs.txt for details. + other read-only media as well. Read + for details. This file system support is also available as a module ( = code which can be inserted in and removed from the running kernel - whenever you want). The module is called romfs.o. If you want to + whenever you want). The module is called romfs.o. If you want to compile it as a module, say M here and read - Documentation/modules.txt. Note that the file system of your root - partition (the one containing the directory /) cannot be a module. + . Note that the file system of your + root partition (the one containing the directory /) cannot be a + module. If you don't know whether you need it, then you don't need it: answer N. -QNX4 file system support (read only) (EXPERIMENTAL) +QNX4 file system support (read only) CONFIG_QNX4FS_FS This is the file system used by the operating system QNX 4. Say Y if you intend to mount QNX hard disks or floppies. Unless you say Y to @@ -12559,7 +14133,7 @@ which can be inserted in and removed from the running kernel whenever you want). The module is called qnx4.o. If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . If you don't know whether you need it, then you don't need it: answer N. @@ -12576,8 +14150,8 @@ automounter (amd), which is a pure user space daemon. To use the automounter you need the user-space tools from the autofs - package; you can find the location in Documentation/Changes. You - also want to answer Y to "NFS file system support", below. + package; you can find the location in . + You also want to answer Y to "NFS file system support", below. If you want to use the newer version of the automounter with more features, say N here and say Y to "Kernel automounter v4 support", @@ -12585,13 +14159,13 @@ If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called autofs.o. + say M here and read . The module + will be called autofs.o. If you are not a part of a fairly large, distributed network, you probably do not need an automounter, and can say N here. -Kernel automounter v4 support +Kernel automounter version 4 support (also supports v3) CONFIG_AUTOFS4_FS The automounter is a tool to automatically mount remote file systems on demand. This implementation is partially kernel-based to reduce @@ -12599,46 +14173,70 @@ automounter (amd), which is a pure user space daemon. To use the automounter you need the user-space tools from - ftp://ftp.kernel.org/pub/linux/daemons/autofs/testing-v4 ; you also + ; you also want to answer Y to "NFS file system support", below. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called autofs4.o. You will need to add "alias autofs autofs4" to - your modules configuration file. + say M here and read . The module + will be called autofs4.o. You will need to add "alias autofs + autofs4" to your modules configuration file. If you are not a part of a fairly large, distributed network or don't have a laptop which needs to dynamically reconfigure to the local network, you probably do not need an automounter, and can say N here. -EFS file system support (read-only) (EXPERIMENTAL) +EFS file system support (read-only) CONFIG_EFS_FS - EFS is an older file system used for non-ISO9660 CDROMs and hard + EFS is an older file system used for non-ISO9660 CD-ROMs and hard disk partitions by SGI's IRIX operating system (IRIX 6.0 and newer uses the XFS file system for hard disk partitions however). This implementation only offers read-only access. If you don't know what all this is about, it's safe to say N. For more information - about EFS see its home page at http://aeschi.ch.eu.org/efs/ . + about EFS see its home page at . If you want to compile the EFS file system support as a module ( = code which can be inserted in and removed from the running kernel - whenever you want), say M here and read Documentation/modules.txt. - The module will be called efs.o. + whenever you want), say M here and read + . The module will be called efs.o. -Support for the Journalling Flash Filesystem +Journalling Flash File System (JFFS) support CONFIG_JFFS_FS JFFS is the Journaling Flash File System developed by Axis Communications in Sweden, aimed at providing a crash/powerdown-safe - filesystem for disk-less embedded devices. Further information is - available at (http://developer.axis.com/software/jffs/). + file system for disk-less embedded devices. Further information is + available at (). -JFFS debugging verbosity +JFFS debugging verbosity (0 = quiet, 3 = noisy) CONFIG_JFFS_FS_VERBOSE Determines the verbosity level of the JFFS debugging messages. +Journalling Flash File System v2 (JFFS2) support +CONFIG_JFFS2_FS + JFFS2 is the second generation of the Journalling Flash File System + for use on diskless embedded devices. It provides improved wear + levelling, compression and support for hard links. You cannot use + this on normal block devices, only on 'MTD' devices. + + Further information should be made available soon at + . + +JFFS2 debugging verbosity (0 = quiet, 2 = noisy) +CONFIG_JFFS2_FS_DEBUG + This controls the amount of debugging messages produced by the JFFS2 + code. Set it to zero for use in production systems. For evaluation, + testing and debugging, it's advisable to set it to one. This will + enable a few assertions and will print debugging messages at the + KERN_DEBUG loglevel, where they won't normally be visible. Level 2 + is unlikely to be useful - it enables extra debugging in certain + areas which at one point needed debugging, but when the bugs were + located and fixed, the detailed messages were relegated to level 2. + + If reporting bugs, please try to have available a full dump of the + messages at debug level 1 while the misbehaviour was occurring. + UFS file system support (read-only) CONFIG_UFS_FS BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD, @@ -12647,11 +14245,11 @@ this file system as well. Saying Y here will allow you to read from these partitions; if you also want to write to them, say Y to the experimental "UFS file system write support", below. Please read the - file Documentation/filesystems/ufs.txt for more information. + file for more information. If you only intend to mount files from some other Unix over the network using NFS, you don't need the UFS file system support (but - you need NFS file system support obviously). + you need NFS file system support obviously). Note that this option is generally not needed for floppies, since a good portable way to transport files and directories between unixes @@ -12660,12 +14258,12 @@ When accessing NeXTstep files, you may need to convert them from the NeXT character set to the Latin1 character set; use the program - recode ("info recode") for this purpose. + recode ("info recode") for this purpose. If you want to compile the UFS file system support as a module ( = code which can be inserted in and removed from the running kernel - whenever you want), say M here and read Documentation/modules.txt. - The module will be called ufs.o. + whenever you want), say M here and read + . The module will be called ufs.o. If you haven't heard about all of this before, it's safe to say N. @@ -12681,11 +14279,38 @@ architecture than your Linux system. Note that the answer to this question won't directly affect the - kernel: saying N will just cause this configure script to skip all - the questions about foreign partitioning schemes. + kernel: saying N will just cause the configurator to skip all + the questions about foreign partitioning schemes. If unsure, say N. +Acorn partition support +CONFIG_ACORN_PARTITION + Support hard disks partitioned under Acorn operating systems. + +Native filecore partition support +CONFIG_ACORN_PARTITION_ADFS + The Acorn Disc Filing System is the standard file system of the + RiscOS operating system which runs on Acorn's ARM-based Risc PC + systems and the Acorn Archimedes range of machines. If you say + `Y' here, Linux will support disk partitions created under ADFS. + +PowerTec partition support +CONFIG_ACORN_PARTITION_POWERTEC + Support reading partition tables created on Acorn machines using + the PowerTec SCSI drive. + +RISCiX partition support +CONFIG_ACORN_PARTITION_RISCIX + Once upon a time, there was a native Unix port for the Acorn series + of machines called RISCiX. If you say 'Y' here, Linux will be able + to read disks partitioned under RISCiX. + +ICS partition support +CONFIG_ACORN_PARTITION_ICS + Say Y here if you would like to use hard disks under Linux which + were partitioned using the ICS interface on Acorn machines. + Alpha OSF partition support CONFIG_OSF_PARTITION Say Y here if you would like to use hard disks under Linux which @@ -12708,10 +14333,11 @@ mirrored, striped or RAID volumes, all without the need for rebooting. - Normal partitions are now called Basic Disks under Windows 2000 and XP. + Normal partitions are now called Basic Disks under Windows 2000 and + XP. Technical documentation to accompany this driver is available from: - + . If unsure, say N. @@ -12728,6 +14354,16 @@ Say Y here if you would like to use hard disks under Linux which were partitioned on an x86 PC (not necessarily by DOS). +Amiga partition table support +CONFIG_AMIGA_PARTITION + Say Y here if you would like to use hard disks under Linux which + were partitioned under AmigaOS. + +Atari partition table support +CONFIG_ATARI_PARTITION + Say Y here if you would like to use hard disks under Linux which + were partitioned under the Atari OS. + BSD disklabel (FreeBSD partition tables) support CONFIG_BSD_DISKLABEL FreeBSD uses its own hard disk partition scheme on your PC. It @@ -12745,7 +14381,7 @@ Say Y here if you want to mount and use Minix 2.0.0/2.0.2 subpartitions. -Sun partition tables support +Sun partition table support CONFIG_SUN_PARTITION Like most systems, SunOS uses its own hard disk partition table format, incompatible with all others. Saying Y here allows you to @@ -12771,13 +14407,19 @@ Say Y here if you would like to be able to read the hard disk partition table format used by SGI machines. -Ultrix partition support +Ultrix partition table support CONFIG_ULTRIX_PARTITION Say Y here if you would like to be able to read the hard disk partition table format used by DEC (now Compaq) Ultrix machines. Otherwise, say N. -ADFS file system support (EXPERIMENTAL) +IBM disk label and partition support +CONFIG_IBM_PARTITION + Say Y here if you would like to be able to read the hard disk + partition table format used by IBM DASD disks operating under CMS. + Otherwise, say N. + +ADFS file system support CONFIG_ADFS_FS The Acorn Disc Filing System is the standard file system of the RiscOS operating system which runs on Acorn's ARM-based Risc PC @@ -12788,12 +14430,12 @@ The ADFS partition should be the first partition (i.e., /dev/[hs]d?1) on each of your drives. Please read the file - Documentation/filesystems/adfs.txt for further details. + for further details. This code is also available as a module called adfs.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . If unsure, say N. @@ -12813,17 +14455,55 @@ to acquire a pseudo terminal, a process opens /dev/ptmx; the number of the pseudo terminal is then made available to the process and the pseudo terminal slave can be accessed as /dev/pts/. What was - traditionally /dev/ttyp2 will then be /dev/pts/2, for example. + traditionally /dev/ttyp2 will then be /dev/pts/2, for example. The GNU C library glibc 2.1 contains the requisite support for this mode of operation; you also need client programs that use the Unix98 - API. Please read Documentation/Changes for more information about - the Unix98 pty devices. + API. Please read for more information + about the Unix98 pty devices. Note that the experimental "/dev file system support" (CONFIG_DEVFS_FS) is a more general facility. -UnixWare slices support (EXPERIMENTAL) +# This is for Linus's tree +FreeVxFS file system support (VERITAS VxFS(TM) compatible) +CONFIG_VXFS_FS + FreeVxFS is a file system driver that support the VERITAS VxFS(TM) + file system format. VERITAS VxFS(TM) is the standard file system + of SCO UnixWare (and possibly others) and optionally available + for Sunsoft Solaris, HP-UX and many other operating systems. + Currently only readonly access is supported. + + NOTE: the file system type as used by mount(1), mount(2) and + fstab(5) is 'vxfs' as it describes the file system format, not + the actual driver. + + This file system is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module is called freevxfs.o. If you want to compile it as a + module, say M here and read . If + unsure, say N. + +# This is for Alan's tree. Note the name difference. +FreeVxFS file system support (VERITAS VxFS(TM) compatible) +CONFIG_FREEVXFS_FS + FreeVxFS is a file system driver that support the VERITAS VxFS(TM) + file system format. VERITAS VxFS(TM) is the standard file system + of SCO UnixWare (and possibly others) and optionally available + for Sunsoft Solaris, HP-UX and many other operating systems. + Currently only readonly access is supported. + + NOTE: the file system type as used by mount(1), mount(2) and + fstab(5) is 'vxfs' as it describes the filesystem format, not + the actual driver. + + This file system is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module is called freevxfs.o. If you want to compile it as a + module, say M here and read . If + unsure, say N. + +UnixWare slices support CONFIG_UNIXWARE_DISKLABEL Like some systems, UnixWare uses its own slice table inside a partition (VTOC - Virtual Table of Contents). Its format is @@ -12837,7 +14517,7 @@ removable IDE drives. Note, however, that a good portable way to transport files and directories between unixes (and even other operating systems) is given by the tar program ("man tar" or - preferably "info tar"). + preferably "info tar"). If you don't know what all this is about, say N. @@ -12845,29 +14525,29 @@ CONFIG_SMB_FS SMB (Server Message Block) is the protocol Windows for Workgroups (WfW), Windows 95/98, Windows NT and OS/2 Lan Manager use to share - files and printers over local networks. Saying Y here allows you to + files and printers over local networks. Saying Y here allows you to mount their file systems (often called "shares" in this context) and - access them just like any other Unix directory. Currently, this + access them just like any other Unix directory. Currently, this works only if the Windows machines use TCP/IP as the underlying - transport protocol, and not NetBEUI. For details, read - Documentation/filesystems/smbfs.txt and the SMB-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + transport protocol, and not NetBEUI. For details, read + and the SMB-HOWTO, + available from . Note: if you just want your box to act as an SMB *server* and make files and printing services available to Windows clients (which need to have a TCP/IP stack), you don't need to say Y here; you can use the program samba (available via FTP (user: anonymous) in - ftp://metalab.unc.edu/pub/Linux/system/network/samba ) for that. + ) for that. General information about how to connect Linux, Windows machines and - Macs is on the WWW at http://www.eats.com/linux_mac_win.html . + Macs is on the WWW at . If you want to compile the SMB support as a module ( = code which can be inserted in and removed from the running kernel whenever you - want), say M here and read Documentation/modules.txt. The module - will be called smbfs.o. Most people say N, however. + want), say M here and read . The + module will be called smbfs.o. Most people say N, however. -use nls by default +Use a default NLS CONFIG_SMB_NLS_DEFAULT Enabling this will make smbfs use nls translations by default. You need to specify the local charset (CONFIG_NLS_DEFAULT) in the nls @@ -12879,7 +14559,7 @@ smbmount from samba 2.2.0 or later supports this. -nls support setting +Default Remote NLS Option CONFIG_SMB_NLS_REMOTE This setting allows you to specify a default value for which codepage the server uses. If this field is left blank no @@ -12896,72 +14576,85 @@ Coda is an advanced network file system, similar to NFS in that it enables you to mount file systems of a remote server and access them with regular Unix commands as if they were sitting on your hard - disk. Coda has several advantages over NFS: support for disconnected - operation (e.g. for laptops), read/write server replication, - security model for authentication and encryption, persistent client - caches and write back caching. + disk. Coda has several advantages over NFS: support for + disconnected operation (e.g. for laptops), read/write server + replication, security model for authentication and encryption, + persistent client caches and write back caching. If you say Y here, your Linux box will be able to act as a Coda - *client*. You will need user level code as well, both for the client - and server. Servers are currently user level, i.e. they need no - kernel support. Please read Documentation/filesystems/coda.txt and - check out the Coda home page http://www.coda.cs.cmu.edu . + *client*. You will need user level code as well, both for the + client and server. Servers are currently user level, i.e. they need + no kernel support. Please read + and check out the Coda + home page . If you want to compile the coda client support as a module ( = code which can be inserted in and removed from the running kernel - whenever you want), say M here and read Documentation/modules.txt. - The module will be called coda.o. + whenever you want), say M here and read + . The module will be called coda.o. + +InterMezzo file system support (experimental, replicating fs) +CONFIG_INTERMEZZO_FS + InterMezzo is a networked file system with disconnected operation + and kernel level write back caching. It is most often used for + replicating potentially large trees or keeping laptop/desktop copies + in sync. + + If you say Y or M your kernel or module will provide InterMezzo + support. You will also need a file server daemon, which you can get + from . NCP file system support (to mount NetWare volumes) CONFIG_NCP_FS NCP (NetWare Core Protocol) is a protocol that runs over IPX and is - used by Novell NetWare clients to talk to file servers. It is to IPX - what NFS is to TCP/IP, if that helps. Saying Y here allows you to - mount NetWare file server volumes and to access them just like any - other Unix directory. For details, please read the file - Documentation/filesystems/ncpfs.txt in the kernel source and the - IPX-HOWTO from http://www.linuxdoc.org/docs.html#howto . + used by Novell NetWare clients to talk to file servers. It is to + IPX what NFS is to TCP/IP, if that helps. Saying Y here allows you + to mount NetWare file server volumes and to access them just like + any other Unix directory. For details, please read the file + in the kernel source and + the IPX-HOWTO from . You do not have to say Y here if you want your Linux box to act as a file *server* for Novell NetWare clients. General information about how to connect Linux, Windows machines and - Macs is on the WWW at http://www.eats.com/linux_mac_win.html . + Macs is on the WWW at . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called ncpfs.o. Say N unless you are connected to a Novell network. + say M here and read . The module + will be called ncpfs.o. Say N unless you are connected to a Novell + network. Packet signatures CONFIG_NCPFS_PACKET_SIGNING NCP allows packets to be signed for stronger security. If you want - security, say Y. Normal users can leave it off. To be able to use + security, say Y. Normal users can leave it off. To be able to use packet signing you must use ncpfs > 2.0.12. Proprietary file locking CONFIG_NCPFS_IOCTL_LOCKING - Allows locking of records on remote volumes. Say N unless you have + Allows locking of records on remote volumes. Say N unless you have special applications which are able to utilize this locking scheme. Clear remove/delete inhibit when needed CONFIG_NCPFS_STRONG - Allows manipulation of files flagged as Delete or Rename Inhibit. To - use this feature you must mount volumes with the ncpmount parameter - "-s" (ncpfs-2.0.12 and newer). Say Y unless you are not mounting - volumes with -f 444. + Allows manipulation of files flagged as Delete or Rename Inhibit. + To use this feature you must mount volumes with the ncpmount + parameter "-s" (ncpfs-2.0.12 and newer). Say Y unless you are not + mounting volumes with -f 444. -Use NFS namespace when available +Use NFS namespace if available CONFIG_NCPFS_NFS_NS - Allows you to utilize NFS namespace on NetWare servers. It brings - you case sensitive filenames. Say Y. You can disable it at + Allows you to utilize NFS namespace on NetWare servers. It brings + you case sensitive filenames. Say Y. You can disable it at mount-time with the `-N nfs' parameter of ncpmount. -Use OS2/LONG namespace when available +Use LONG (OS/2) namespace if available CONFIG_NCPFS_OS2_NS Allows you to utilize OS2/LONG namespace on NetWare servers. Filenames in this namespace are limited to 255 characters, they are - case insensitive, and case in names is preserved. Say Y. You can + case insensitive, and case in names is preserved. Say Y. You can disable it at mount time with the -N os2 parameter of ncpmount. Lowercase DOS filenames on LONG namespace volume @@ -12970,7 +14663,7 @@ the OS2/LONG namespace and created under DOS or on a volume using DOS namespace will be converted to lowercase characters. Saying N here will give you these filenames in uppercase. - + This is only a cosmetic option since the OS2/LONG namespace is case insensitive. The only major reason for this option is backward compatibility when moving from DOS to OS2/LONG namespace support. @@ -12982,25 +14675,7 @@ effects by saying Y to "Allow using of Native Language Support" below. -Allow mounting of volume subdirectories -CONFIG_NCPFS_MOUNT_SUBDIR - Allows you to mount not only whole servers or whole volumes, but - also subdirectories from a volume. It can be used to reexport data - and so on. There is no reason to say N, so Y is recommended unless - you count every byte. - - To utilize this feature you must use ncpfs-2.0.12 or newer. - -NDS authentication support -CONFIG_NCPFS_NDS_DOMAINS - This allows storing NDS private keys in kernel space where they - can be used to authenticate another server as interserver NDS - accesses need it. You must use ncpfs-2.0.12.1 or newer to utilize - this feature. Say Y if you are using NDS connections to NetWare - servers. Do not say Y if security is primary for you because root - can read your session key (from /proc/kcore). - -Allow using of Native Language Support +Use Native Language Support CONFIG_NCPFS_NLS Allows you to use codepages and I/O charsets for file name translation between the server file system and input/output. This @@ -13018,21 +14693,24 @@ To use the new attributes, it is recommended to use the flags '-f 600 -d 755' on the ncpmount command line. -nls default codepage +Default NLS Option CONFIG_NLS_DEFAULT - The default NLS used when mounting file system. Currently, the valid - values are: + The default NLS used when mounting file system. Note, that this is + the NLS used by your console, not the NLS used by a specific file + system (if different) to store data (filenames) on a disk. + Currently, the valid values are: big5, cp437, cp737, cp775, cp850, cp852, cp855, cp857, cp860, cp861, cp862, cp863, cp864, cp865, cp866, cp869, cp874, cp932, cp936, - cp949, cp950, euc-jp, euc-kr, gb2312, iso8859-1, iso8859-2, iso8859-3, - iso8859-4, iso8859-5, iso8859-6, iso8859-7, iso8859-8, iso8859-9, - iso8859-14, iso8859-15, koi8-r, sjis - If you specify a wrong value, it will use the built-in NLS; compatible - with iso8859-1. + cp949, cp950, cp1251, cp1255, euc-jp, euc-kr, gb2312, iso8859-1, + iso8859-2, iso8859-3, iso8859-4, iso8859-5, iso8859-6, iso8859-7, + iso8859-8, iso8859-9, iso8859-13, iso8859-14, iso8859-15, + koi8-r, koi8-ru, koi8-u, sjis, tis-620, utf8. + If you specify a wrong value, it will use the built-in NLS; + compatible with iso8859-1. If unsure, specify it as "iso8859-1". -nls codepage 437 +Codepage 437 (United States, Canada) CONFIG_NLS_CODEPAGE_437 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored @@ -13043,7 +14721,7 @@ say Y here if you want to include the DOS codepage that is used in the United States and parts of Canada. This is recommended. -nls codepage 737 +Codepage 737 (Greek) CONFIG_NLS_CODEPAGE_737 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored @@ -13054,7 +14732,7 @@ say Y here if you want to include the DOS codepage that is used for Greek. If unsure, say N. -nls codepage 775 +Codepage 775 (Baltic Rim) CONFIG_NLS_CODEPAGE_775 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored @@ -13063,9 +14741,10 @@ DOS/Windows partitions correctly. This does apply to the filenames only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage that is used - for the Baltic Rim Languages. If unsure, say N. + for the Baltic Rim Languages (Latvian and Lithuanian). If unsure, + say N. -nls codepage 850 +Codepage 850 (Europe) CONFIG_NLS_CODEPAGE_850 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13076,11 +14755,11 @@ say Y here if you want to include the DOS codepage that is used for much of Europe -- United Kingdom, Germany, Spain, Italy, and [add more countries here]. It has some characters useful to many European - languages that are not part of the US codepage 437. + languages that are not part of the US codepage 437. If unsure, say Y. -nls codepage 852 +Codepage 852 (Central/Eastern Europe) CONFIG_NLS_CODEPAGE_852 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13094,7 +14773,7 @@ Finnish, Hungarian, Irish, German, Polish, Romanian, Serbian (Latin transcription), Slovak, Slovenian, and Sorbian. -nls codepage 855 +Codepage 855 (Cyrillic) CONFIG_NLS_CODEPAGE_855 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13104,7 +14783,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Cyrillic. -nls codepage 857 +Codepage 857 (Turkish) CONFIG_NLS_CODEPAGE_857 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13114,7 +14793,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Turkish. -nls codepage 860 +Codepage 860 (Portuguese) CONFIG_NLS_CODEPAGE_860 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13124,7 +14803,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Portuguese. -nls codepage 861 +Codepage 861 (Icelandic) CONFIG_NLS_CODEPAGE_861 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13134,7 +14813,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Icelandic. -nls codepage 862 +Codepage 862 (Hebrew) CONFIG_NLS_CODEPAGE_862 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13144,7 +14823,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Hebrew. -nls codepage 863 +Codepage 863 (Canadian French) CONFIG_NLS_CODEPAGE_863 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13155,7 +14834,7 @@ say Y here if you want to include the DOS codepage for Canadian French. -nls codepage 864 +Codepage 864 (Arabic) CONFIG_NLS_CODEPAGE_864 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13165,7 +14844,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Arabic. -nls codepage 865 +Codepage 865 (Norwegian, Danish) CONFIG_NLS_CODEPAGE_865 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13176,7 +14855,7 @@ say Y here if you want to include the DOS codepage for the Nordic European countries. -nls codepage 866 +Codepage 866 (Cyrillic/Russian) CONFIG_NLS_CODEPAGE_866 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13187,7 +14866,7 @@ say Y here if you want to include the DOS codepage for Cyrillic/Russian. -nls codepage 869 +Codepage 869 (Greek) CONFIG_NLS_CODEPAGE_869 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13197,7 +14876,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Greek. -nls codepage 874 +Thai charset (CP874, TIS-620) CONFIG_NLS_CODEPAGE_874 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13207,7 +14886,18 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Thai. -nls codepage 932 +Windows CP1251 (Bulgarian, Belarusian) +CONFIG_NLS_CODEPAGE_1251 + The Microsoft FAT file system family can deal with filenames in + native language character sets. These character sets are stored in + so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage for Russian and + Bulgarian and Belarusian. + +Japanese charsets (Shift-JIS, EUC-JP) CONFIG_NLS_CODEPAGE_932 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13217,9 +14907,9 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Shift-JIS or EUC-JP. To use EUC-JP, you can use 'euc-jp' as mount option or - NLS Default value during kernel configuration , instead of 'cp932' + NLS Default value during kernel configuration, instead of 'cp932'. -nls codepage 936 +Simplified Chinese charset (CP936, GB2312) CONFIG_NLS_CODEPAGE_936 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13230,7 +14920,7 @@ say Y here if you want to include the DOS codepage for Simplified Chinese(GBK). -nls codepage 949 +Korean charset (CP949, EUC-KR) CONFIG_NLS_CODEPAGE_949 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13240,7 +14930,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for UHC. -nls codepage 950 +Traditional Chinese charset (Big5) CONFIG_NLS_CODEPAGE_950 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -13251,10 +14941,10 @@ say Y here if you want to include the DOS codepage for Traditional Chinese(Big5). -nls iso8859-1 +NLS ISO 8859-1 (Latin 1; Western European Languages) CONFIG_NLS_ISO8859_1 If you want to display filenames with native language characters - from the Microsoft FAT file system family or from JOLIET CDROMs + from the Microsoft FAT file system family or from JOLIET CD-ROMs correctly on the screen, you need to include the appropriate input/output character sets. Say Y here for the Latin 1 character set, which covers most West European languages such as Albanian, @@ -13262,101 +14952,110 @@ Galician, Irish, Icelandic, Italian, Norwegian, Portuguese, Spanish, and Swedish. It is also the default for the US. If unsure, say Y. -nls iso8859-2 +NLS ISO 8859-2 (Latin 2; Slavic/Central European Languages) CONFIG_NLS_ISO8859_2 If you want to display filenames with native language characters - from the Microsoft FAT file system family or from JOLIET CDROMs + from the Microsoft FAT file system family or from JOLIET CD-ROMs correctly on the screen, you need to include the appropriate input/output character sets. Say Y here for the Latin 2 character set, which works for most Latin-written Slavic and Central European languages: Czech, German, Hungarian, Polish, Rumanian, Croatian, Slovak, Slovene. -nls iso8859-3 +NLS ISO 8859-3 (Latin 3; Esperanto, Galician, Maltese, Turkish) CONFIG_NLS_ISO8859_3 If you want to display filenames with native language characters - from the Microsoft FAT file system family or from JOLIET CDROMs + from the Microsoft FAT file system family or from JOLIET CD-ROMs correctly on the screen, you need to include the appropriate input/output character sets. Say Y here for the Latin 3 character set, which is popular with authors of Esperanto, Galician, Maltese, and Turkish. -nls iso8859-4 +NLS ISO 8859-4 (Latin 4; old Baltic charset) CONFIG_NLS_ISO8859_4 If you want to display filenames with native language characters - from the Microsoft FAT file system family or from JOLIET CDROMs + from the Microsoft FAT file system family or from JOLIET CD-ROMs correctly on the screen, you need to include the appropriate input/output character sets. Say Y here for the Latin 4 character set which introduces letters for Estonian, Latvian, and - Lithuanian. It is an incomplete predecessor of Latin 6. + Lithuanian. It is an incomplete predecessor of Latin 7. -nls iso8859-5 +NLS ISO 8859-5 (Cyrillic) CONFIG_NLS_ISO8859_5 If you want to display filenames with native language characters - from the Microsoft FAT file system family or from JOLIET CDROMs + from the Microsoft FAT file system family or from JOLIET CD-ROMs correctly on the screen, you need to include the appropriate input/output character sets. Say Y here for ISO8859-5, a Cyrillic - character set with which you can type Bulgarian, Byelorussian, + character set with which you can type Bulgarian, Belarusian, Macedonian, Russian, Serbian, and Ukrainian. Note that the charset KOI8-R is preferred in Russia. -nls iso8859-6 +NLS ISO 8859-6 (Arabic) CONFIG_NLS_ISO8859_6 If you want to display filenames with native language characters - from the Microsoft FAT file system family or from JOLIET CDROMs + from the Microsoft FAT file system family or from JOLIET CD-ROMs correctly on the screen, you need to include the appropriate input/output character sets. Say Y here for ISO8859-6, the Arabic character set. -nls iso8859-7 +NLS ISO 8859-7 (Modern Greek) CONFIG_NLS_ISO8859_7 If you want to display filenames with native language characters - from the Microsoft FAT file system family or from JOLIET CDROMs + from the Microsoft FAT file system family or from JOLIET CD-ROMs correctly on the screen, you need to include the appropriate input/output character sets. Say Y here for ISO8859-7, the Modern Greek character set. -nls iso8859-8 +Hebrew charsets (ISO-8859-8, CP1255) CONFIG_NLS_ISO8859_8 If you want to display filenames with native language characters - from the Microsoft FAT file system family or from JOLIET CDROMs + from the Microsoft FAT file system family or from JOLIET CD-ROMs correctly on the screen, you need to include the appropriate input/output character sets. Say Y here for ISO8859-8, the Hebrew character set. -nls iso8859-9 +NLS ISO 8859-9 (Latin 5; Turkish) CONFIG_NLS_ISO8859_9 If you want to display filenames with native language characters - from the Microsoft FAT file system family or from JOLIET CDROMs + from the Microsoft FAT file system family or from JOLIET CD-ROMs correctly on the screen, you need to include the appropriate input/output character sets. Say Y here for the Latin 5 character set, and it replaces the rarely needed Icelandic letters in Latin 1 with the Turkish ones. Useful in Turkey. -nls iso8859-10 +NLS ISO 8859-10 (Latin 6; Nordic) CONFIG_NLS_ISO8859_10 If you want to display filenames with native language characters - from the Microsoft FAT file system family or from JOLIET CDROMs + from the Microsoft FAT file system family or from JOLIET CD-ROMs correctly on the screen, you need to include the appropriate input/output character sets. Say Y here for the Latin 6 character set, which adds the last Inuit (Greenlandic) and Sami (Lappish) letters that were missing in Latin 4 to cover the entire Nordic area. +NLS ISO 8859-13 (Latin 7; Baltic) +CONFIG_NLS_ISO8859_13 + If you want to display filenames with native language characters + from the Microsoft FAT file system family or from JOLIET CD-ROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for the Latin 7 character + set, which supports modern Baltic languages including Latvian + and Lithuanian. + NLS ISO 8859-14 (Latin 8; Celtic) CONFIG_NLS_ISO8859_14 If you want to display filenames with native language characters - from the Microsoft FAT file system family or from JOLIET CDROMs + from the Microsoft FAT file system family or from JOLIET CD-ROMs correctly on the screen, you need to include the appropriate input/output character sets. Say Y here for the Latin 8 character set, which adds the last accented vowels for Welsh (aka Cymraeg) - (and Manx Gaelic) hat were missing in Latin 1. - http://linux.speech.cymru.org/ has further information. + (and Manx Gaelic) that were missing in Latin 1. + has further information. -nls iso8859-15 +NLS ISO 8859-15 (Latin 9; Western European languages with Euro) CONFIG_NLS_ISO8859_15 If you want to display filenames with native language characters - from the Microsoft FAT file system family or from JOLIET CDROMs + from the Microsoft FAT file system family or from JOLIET CD-ROMs correctly on the screen, you need to include the appropriate input/output character sets. Say Y here for the Latin 9 character set, which covers most West European languages such as Albanian, @@ -13365,17 +15064,33 @@ Portuguese, Spanish, and Swedish. Latin 9 is an update to Latin 1 (ISO 8859-1) that removes a handful of rarely used characters and instead adds support for Estonian, corrects the - support for French and Finnish, and adds the new Euro character. If - unsure, say Y. + support for French and Finnish, and adds the new Euro character. + If unsure, say Y. -nls koi8-r +NLS KOI8-R (Russian) CONFIG_NLS_KOI8_R If you want to display filenames with native language characters - from the Microsoft FAT file system family or from JOLIET CDROMs + from the Microsoft FAT file system family or from JOLIET CD-ROMs correctly on the screen, you need to include the appropriate input/output character sets. Say Y here for the preferred Russian character set. +NLS KOI8-U/RU (Ukrainian, Belarusian) +CONFIG_NLS_KOI8_U + If you want to display filenames with native language characters + from the Microsoft FAT file system family or from JOLIET CD-ROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for the preferred Ukrainian + (koi8-u) and Belarusian (koi8-ru) character sets. + +NLS UTF8 +CONFIG_NLS_UTF8 + If you want to display filenames with native language characters + from the Microsoft FAT file system family or from JOLIET CD-ROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for the UTF-8 encoding of + the Unicode/ISO9646 universal character set. + Virtual terminal CONFIG_VT If you say Y here, you will get support for terminal devices with @@ -13423,38 +15138,236 @@ If unsure, say Y. -Support for PowerMac keyboard -CONFIG_MAC_KEYBOARD +STI console +CONFIG_STI_CONSOLE + The STI console is the builtin display/keyboard on HP-PARISC + machines. Say Y here to build support for it into your kernel. + The alternative is to use your primary serial port as a console. + +Use MDIO for PHY configuration +CONFIG_USE_MDIO + On some boards the hardware configuration of the ethernet PHY can be + used without any software interaction over the MDIO interface, so + all MII code can be omitted. Say N here if unsure or if you don't + need link status reports. + +860T FEC Ethernet +CONFIG_FEC_ENET + Enable Ethernet support via the Fast Ethernet Controller (FCC) on + the Motorola MPC8260. + +Ethernet on FCC1 +CONFIG_FCC1_ENET + Use MPC8260 fast Ethernet controller 1 to drive Ethernet (default). + +Ethernet on FCC2 +CONFIG_FCC2_ENET + Use MPC8260 fast Ethernet controller 2 to drive Ethernet. + +Ethernet on FCC3 +CONFIG_FCC3_ENET + Use MPC8260 fast Ethernet controller 3 to drive Ethernet. + +CPM SCC Ethernet +CONFIG_SCC_ENET + Enable Ethernet support via the Motorola MPC8xx serial + commmunications controller. + +Ethernet on SCC1 +CONFIG_SCC1_ENET + Use MPC8xx serial communications controller 1 to drive Ethernet + (default). + +Ethernet on SCC2 +CONFIG_SCC2_ENET + Use MPC8xx serial communications controller 2 to drive Ethernet. + +Ethernet on SCC3 +CONFIG_SCC3_ENET + Use MPC8xx serial communications controller 3 to drive Ethernet. + +Use Big CPM Ethernet Buffers +CONFIG_ENET_BIG_BUFFERS + Allocate large buffers for MPC8xx Etherenet. Increases throughput + and decreases the likelihood of dropped packets, but costs memory. + +Apple Desktop Bus (ADB) support +CONFIG_ADB + Apple Desktop Bus (ADB) support is for support of devices which + are connected to an ADB port. ADB devices tend to have 4 pins. + If you have an Apple Macintosh prior to the iMac, or a + "Blue and White G3", you probably want to say Y here. Otherwise + say N. + +Support for CUDA based PowerMacs +CONFIG_ADB_CUDA + This provides support for CUDA based Power Macintosh systems. This + includes most OldWorld PowerMacs, the first generation iMacs, the + Blue&White G3 and the Yikes G4 (PCI Graphics). All later models + should use CONFIG_ADB_PMU instead. + + If unsure say Y. + +Support for PMU-based PowerMacs +CONFIG_ADB_PMU + On the PowerBook 3400 and 2400, the PMU is a 6805 microprocessor + core whose primary function is to control battery charging and + system power. The PMU also controls the ADB (Apple Desktop Bus) + which connects to the keyboard and mouse, as well as the + non-volatile RAM and the RTC (real time clock) chip. Say Y to + enable support for this device; you should do so if your machine + is one of these PowerBooks. + +Include MacIO ADB driver +CONFIG_ADB_MACIO + Say Y here to include direct support for the ADB controller in the + Hydra chip used on PowerPC Macintoshes of the CHRP type. (The Hydra + also includes a MESH II SCSI controller, DBDMA controller, VIA chip, + OpenPIC controller and two RS422/Geoports.) + +Support for ADB keyboard (old driver) +CONFIG_ADB_KEYBOARD This option allows you to use an ADB keyboard attached to your machine. Note that this disables any other (ie. PS/2) keyboard support, even if your machine is physically capable of using both at the same time. - + If you use an ADB keyboard (4 pin connector), say Y here. If you use a PS/2 keyboard (6 pin connector), say N here. -Standard/generic serial support +HIL keyboard support +CONFIG_HIL + The "Human Interface Loop" is a older, 8-channel USB-like controller + used in Hewlette Packard PA-RISC based machines. There are a few + cases where it is seen on PC/MAC architectures as well, usually also + manufactured by HP. This driver is based off MACH and BSD drivers, + and implements support for a keyboard attached to the HIL port. + Full support for the USB-like functions and non-keyboard channels of + the HIL is not provided for in this driver. There are vestiges of + mouse support in the driver, but it is probably not working. The + necessary hardware documentation to fully support the HIL controller + and interface it to the linux-input API is lacking. + + Enable this option if you intend to use a HIL keyboard. + +Include IOP (IIfx/Quadra 9x0) ADB driver +CONFIG_ADB_IOP + The I/O Processor (IOP) is an Apple custom IC designed to provide + intelligent support for I/O controllers. It is described at + to enable direct + support for it, say 'Y' here. + +Mac II style Apple Desktop Bus support +CONFIG_ADB_MACII + Say Y here if want your kernel to support Macintosh systems that use + the Mac II style ADB. This includes the II, IIx, IIcx, SE/30, IIci, + Quadra 610, Quadra 650, Quadra 700, Quadra 800, Centris 610 and + Centris 650. + +Mac IIsi style Apple Desktop Bus support +CONFIG_ADB_MACIISI + Say Y here if want your kernel to support Macintosh systems that use + the Mac IIsi style ADB. This includes the IIsi, IIvi, IIvx, Classic + II, LC, LC II, LC III, Performa 460, and the Performa 600. + +Apple 68K PowerBook Power Management and Desktop Bus support +CONFIG_ADB_PMU68K + Say Y here if want your kernel to support the m68k based Powerbooks. + This includes the PowerBook 140, PowerBook 145, PowerBook 150, + PowerBook 160, PowerBook 165, PowerBook 165c, PowerBook 170, + PowerBook 180, PowerBook, 180c, PowerBook 190cs, PowerBook 520, + PowerBook Duo 210, PowerBook Duo 230, PowerBook Duo 250, + PowerBook Duo 270c, PowerBook Duo 280 and PowerBook Duo 280c. + +Macintosh IIfx/Quadra 900/Quadra 950 floppy support +CONFIG_BLK_DEV_SWIM_IOP + Say Y here to support the SWIM (Super Woz Integrated Machine) IOP + floppy controller on the Macintosh IIfx and Quadra 900/950. + +Macintosh NS8390 based Ethernet support +CONFIG_MAC8390 + If you want to include a driver to support Nubus or LC-PDS + Ethernet cards using an NS8390 chipset or its equivalent, say Y + and read the Ethernet-HOWTO, available from + . + +Macintosh CS89x0 based Ethernet support +CONFIG_MAC89x0 + Support for CS89x0 chipset based Ethernet cards. If you have a + Nubus or LC-PDS network (Ethernet) card of this type, say Y and + read the Ethernet-HOWTO, available from + . + + If you want to compile this as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read as well as + . This module will + be called mac89x0.o. + +Macintosh onboard AMD 79C940 MACE based Ethernet support +CONFIG_MACMACE + Support for the onboard AMD 79C940 MACE Ethernet controller used in + the 660AV and 840AV Macintosh. If you have one of these Macintoshes + say Y and read the Ethernet-HOWTO, available from + . + +Macintosh SONIC based Ethernet support (onboard, NuBus, LC, CS) +CONFIG_MACSONIC + Support for NatSemi SONIC based Ethernet devices. This includes + the onboard Ethernet in many Quadras as well as some LC-PDS, + a few Nubus and all known Comm Slot Ethernet cards. If you have + one of these say Y and read the Ethernet-HOWTO, available from + . + + If you want to compile this as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read as well as + . This module will + be called macsonic.o. + +Macintosh NCR5380 SCSI support +CONFIG_MAC_SCSI + This is the NCR 5380 SCSI controller included on most of the 68030 + based Macintoshes. If you have one of these say Y and read the + SCSI-HOWTO, available from + . + +Macintosh NCR53c9[46] SCSI support +CONFIG_SCSI_MAC_ESP + This is the NCR 53c9x SCSI controller found on most of the 68040 + based Macintoshes. If you have one of these say Y and read the + SCSI-HOWTO, available from + . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called mac_esp.o. If you want to compile it as + a module, say M here and read . + +Standard/generic (8250/16550 and compatible UARTs) serial support CONFIG_SERIAL This selects whether you want to include the driver for the standard - serial ports. The standard answer is Y. People who might say N here - are those that are setting up dedicated Ethernet WWW/FTP servers, or - users that have one of the various bus mice instead of a serial - mouse and don't intend to use their machine's standard serial port - for anything. (Note that the Cyclades and Stallion multi serial port - drivers do not need this driver built in for them to work.) + serial ports. The standard answer is Y. People who might say N + here are those that are setting up dedicated Ethernet WWW/FTP + servers, or users that have one of the various bus mice instead of a + serial mouse and don't intend to use their machine's standard serial + port for anything. (Note that the Cyclades and Stallion multi + serial port drivers do not need this driver built in for them to + work.) If you want to compile this driver as a module, say M here and read - Documentation/modules.txt. The module will be called serial.o. + . The module will be called + serial.o. [WARNING: Do not compile this driver as a module if you are using non-standard serial ports, since the configuration information will - be lost when the driver is unloaded. This limitation may be lifted + be lost when the driver is unloaded. This limitation may be lifted in the future.] BTW1: If you have a mouseman serial mouse which is not recognized by - the X window system, try running gpm first. - + the X window system, try running gpm first. + BTW2: If you intend to use a software modem (also called Winmodem) - under Linux, forget it. These modems are crippled and require + under Linux, forget it. These modems are crippled and require proprietary drivers which are only available under Windows. Most people will say Y or M here, so that they can use serial mice, @@ -13495,7 +15408,8 @@ become a dial-in server. If you want to compile this driver as a module, say M here and read - Documentation/modules.txt. The module will be called rocket.o. + . The module will be called + rocket.o. Digiboard Intelligent async support CONFIG_DIGIEPCA @@ -13505,14 +15419,14 @@ box, for instance in order to become a dial-in server. This driver supports the original PC (ISA) boards as well as PCI, and EISA. If you have a card like this, say Y here and read the file - Documentation/digiepca.txt. + . NOTE: There is another, separate driver for the Digiboard PC boards: "Digiboard PC/Xx Support" below. You should (and can) only select - one of the two drivers. + one of the two drivers. If you want to compile this driver as a module, say M here and read - Documentation/modules.txt. The module will be called epca.o. + . The module will be called epca.o. Digiboard PC/Xx Support CONFIG_DIGI @@ -13520,10 +15434,10 @@ that give you many serial ports. You would need something like this to connect more than two modems to your Linux box, for instance in order to become a dial-in server. If you have a card like that, say - Y here and read the file Documentation/digiboard.txt. + Y here and read the file . If you want to compile this driver as a module, say M here and read - Documentation/modules.txt. The module will be called pcxx.o. + . The module will be called pcxx.o. SDL RISCom/8 card support CONFIG_RISCOM8 @@ -13531,7 +15445,7 @@ which gives you many serial ports. You would need something like this to connect more than two modems to your Linux box, for instance in order to become a dial-in server. If you have a card like that, - say Y here and read the file Documentation/riscom8.txt. + say Y here and read the file . Also it's possible to say M here and compile this driver as kernel loadable module; the module will be called riscom8.o. @@ -13539,37 +15453,50 @@ Computone IntelliPort Plus serial support CONFIG_COMPUTONE This driver supports the entire family of Intelliport II/Plus - controllers with the exception of the MicroChannel controllers and + controllers with the exception of the MicroChannel controllers and products previous to the Intelliport II. These are multiport cards, - which give you many serial ports. You would need something like - this to connect more than two modems to your Linux box, for - instance in order to become a dial-in server. If you have a - card like that, say Y here and read Documentation/computone.txt. + which give you many serial ports. You would need something like this + to connect more than two modems to your Linux box, for instance in + order to become a dial-in server. If you have a card like that, say + Y here and read . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. You will get two - modules called ip2.o and ip2main.o. + say M here and read . You will get + two modules called ip2.o and ip2main.o. Specialix IO8+ card support CONFIG_SPECIALIX This is a driver for the Specialix IO8+ multiport card (both the - ISA and the PCI version) which gives you many serial ports. You - would need something like this to connect more than two modems to + ISA and the PCI version) which gives you many serial ports. You + would need something like this to connect more than two modems to your Linux box, for instance in order to become a dial-in server. If you have a card like that, say Y here and read the file - Documentation/specialix.txt. Also it's possible to say M here and - compile this driver as kernel loadable module which will be called - specialix.o. + . Also it's possible to say M here + and compile this driver as kernel loadable module which will be + called specialix.o. Specialix DTR/RTS pin is RTS CONFIG_SPECIALIX_RTSCTS - The Specialix card can only support either RTS or DTR. If you say N - here, the driver will use the pin as "DTR" when the tty is in - software handshake mode. If you say Y here or hardware handshake is - on, it will always be RTS. Read the file Documentation/specialix.txt - for more information. + The Specialix IO8+ card can only support either RTS or DTR. If you + say N here, the driver will use the pin as "DTR" when the tty is in + software handshake mode. If you say Y here or hardware handshake is + on, it will always be RTS. Read the file + for more information. + +Specialix RIO system support +CONFIG_RIO + This is a driver for the Specialix RIO, a smart serial card which + drives an outboard box that can support up to 128 ports. Product + information is at . + There are both ISA and PCI versions. + +Support really old RIO/PCI cards +CONFIG_RIO_OLDPCI + Older RIO PCI cards need some initialization-time configuration to + determine the IRQ and some control addresses. If you have a RIO and + this doesn't seem to work, try setting this to Y. Cyclades async mux support CONFIG_CYCLADES @@ -13577,19 +15504,19 @@ would need something like this to connect more than two modems to your Linux box, for instance in order to become a dial-in server. For information about the Cyclades-Z card, read - drivers/char/README.cycladesZ. + . As of 1.3.9x kernels, this driver's minor numbers start at 0 instead of 32. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called cyclades.o. + say M here and read . The module + will be called cyclades.o. If you haven't heard about it, it's safe to say N. -Cyclades-Z interrupt mode operation (EXPERIMENTAL) +Cyclades-Z interrupt mode operation CONFIG_CYZ_INTR The Cyclades-Z family of multiport cards allows 2 (two) driver op modes: polling and interrupt. In polling mode, the driver will check @@ -13599,47 +15526,47 @@ status of the Cyclades-Z ports. The default op mode is polling. If unsure, say N. -Stallion multiport serial support +Stallion multiport serial support CONFIG_STALDRV - Stallion cards give you many serial ports. You would need something + Stallion cards give you many serial ports. You would need something like this to connect more than two modems to your Linux box, for - instance in order to become a dial-in server. If you say Y here, you - will be asked for your specific card model in the next questions. - Make sure to read drivers/char/README.stallion in this case. If you - have never heard about all this, it's safe to say N. + instance in order to become a dial-in server. If you say Y here, + you will be asked for your specific card model in the next + questions. Make sure to read in + this case. If you have never heard about all this, it's safe to + say N. -Stallion EasyIO or EC8/32 support +Stallion EasyIO or EC8/32 support CONFIG_STALLION If you have an EasyIO or EasyConnection 8/32 multiport Stallion - card, then this is for you; say Y. Make sure to read - Documentation/stallion.txt. + card, then this is for you; say Y. Make sure to read + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called stallion.o. + say M here and read . The module + will be called stallion.o. Stallion EC8/64, ONboard, Brumby support CONFIG_ISTALLION If you have an EasyConnection 8/64, ONboard, Brumby or Stallion serial multiport card, say Y here. Make sure to read - Documentation/stallion.txt. + . To compile it as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and - read Documentation/modules.txt. The module will be called + read . The module will be called istallion.o. Microgate SyncLink adapter support CONFIG_SYNCLINK - Provides support for the SyncLink ISA and PCI - multiprotocol serial adapters. These adapters - support asynchronous and HDLC bit synchronous - communication up to 10Mbps (PCI adapter). + Provides support for the SyncLink ISA and PCI multiprotocol serial + adapters. These adapters support asynchronous and HDLC bit + synchronous communication up to 10Mbps (PCI adapter). This driver can only be built as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called synclink.o. If you want to do that, say M + The module will be called synclink.o. If you want to do that, say M here. Synchronous HDLC line discipline support @@ -13655,7 +15582,7 @@ Specialix SX (and SI) card support CONFIG_SX This is a driver for the SX and SI multiport serial cards. - Please read the file Documentation/sx.txt for details. + Please read the file for details. This driver can only be built as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -13663,14 +15590,14 @@ Hayes ESP serial port support CONFIG_ESPSERIAL - This is a driver which supports Hayes ESP serial ports. Both single - port cards and multiport cards are supported. Make sure to read - Documentation/hayes-esp.txt. + This is a driver which supports Hayes ESP serial ports. Both single + port cards and multiport cards are supported. Make sure to read + . To compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here - and read Documentation/modules.txt. The module will be called esp.o. - If unsure, say N. + and read . The module will be + called esp.o. If unsure, say N. Moxa Intellio support CONFIG_MOXA_INTELLIO @@ -13690,13 +15617,14 @@ The module will be called mxser.o. If you want to do that, say M here. -Multi-Tech multiport card support (EXPERIMENTAL) +Multi-Tech multiport card support CONFIG_ISI This is a driver for the Multi-Tech cards which provide several - serial ports. The driver is experimental and can currently only be + serial ports. The driver is experimental and can currently only be built as a module ( = code which can be inserted in and removed from - the running kernel whenever you want). Please read - Documentation/modules.txt. The module will be called isicom.o + the running kernel whenever you want). Please read + . The module will be called + isicom.o. Unix98 PTY support CONFIG_UNIX98_PTYS @@ -13706,7 +15634,7 @@ read data from and write data to the slave, thereby emulating a terminal. Typical programs for the master side are telnet servers and xterms. - + Linux has traditionally used the BSD-like names /dev/ptyxx for masters and /dev/ttyxx for slaves of pseudo terminals. This scheme has a number of problems. The GNU C library glibc 2.1 and later, @@ -13722,8 +15650,8 @@ If you want to say Y here, you need to have the C library glibc 2.1 or later (equal to libc-6.1, check with "ls -l /lib/libc.so.*"). - Read the instructions in Documentation/Changes pertaining to pseudo - terminals. It's safe to say N. + Read the instructions in pertaining to + pseudo terminals. It's safe to say N. Maximum number of Unix98 PTYs in use (0-2048) CONFIG_UNIX98_PTY_COUNT @@ -13740,23 +15668,23 @@ CONFIG_PRINTER If you intend to attach a printer to the parallel port of your Linux box (as opposed to using a serial printer; if the connector at the - printer has 9 or 25 holes ["female"], then it's serial), say Y. Also - read the Printing-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + printer has 9 or 25 holes ["female"], then it's serial), say Y. + Also read the Printing-HOWTO, available from + . It is possible to share one parallel port among several devices (e.g. printer and ZIP drive) and it is safe to compile the - corresponding drivers into the kernel. If you want to compile this + corresponding drivers into the kernel. If you want to compile this driver as a module however ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and - read Documentation/modules.txt and Documentation/parport.txt. The - module will be called lp.o. + read and + . The module will be called lp.o. If you have several parallel ports, you can specify which ports to - use with the "lp" kernel command line option. (Try "man bootparam" + use with the "lp" kernel command line option. (Try "man bootparam" or see the documentation of your boot loader (lilo or loadlin) about - how to pass options to the kernel at boot time.) The syntax of the - "lp" command line option can be found in drivers/char/lp.c. + how to pass options to the kernel at boot time.) The syntax of the + "lp" command line option can be found in . If you have more than 8 printers, you need to increase the LP_NO macro in lp.c and the PARPORT_MAX macro in parport.h. @@ -13773,7 +15701,7 @@ By defining CONSOLE_LP_STRICT to 0 (at your own risk) you can make the kernel continue when this happens, but it'll lose the kernel messages. - + If unsure, say N. Support for user-space parallel port device drivers @@ -13787,74 +15715,206 @@ It is safe to say N to this -- it is not needed for normal printing or parallel port CD-ROM/disk support. - This support is also available as a module. If you want to compile - it as a module, say M here and read Documentation/modules.txt. The - module will be called ppdev.o. + This support is also available as a module. If you want to compile + it as a module, say M here and read + . The module will be called + ppdev.o. If unsure, say N. +Cobalt Networks support +CONFIG_COBALT + Support for Cobalt Networks x86-based servers. + +Gen III (3000 series) system support +CONFIG_COBALT_GEN_III + This option enables support for the 3000 series of Cobalt Networks + systems. This includes the RaQ 3, RaQ 4, and Qube 3 product lines. + + This platform uses an AMD K6-2 processor, an ALI M1541/1533 chipset, + an optional NCR 53c875 SCSI controller, and two Intel 82559ER or + National Semiconductor DP83815 NICs. + + Getting this option wrong will likely result in a kernel that does + not boot. Selecting support for more than 1 system series will add + bloat to your kernel, but will not cause anything bad to happen. + + If you have a Cobalt Networks System, but aren't sure what kind, + say Y here. + +Gen V (5000 series) system support +CONFIG_COBALT_GEN_V + This option enables support for the 5000 series of Cobalt Networks + systems. This includes the RaQ XTR product line. + + This platform uses Intel Pentium III Coppermine FCPGA CPUs, the + ServerWorks LE chipset (with registered ECC DIMMs only!), two + HighPoint HPT370 IDE controllers, and two National Semiconductor + DP83815 NICs. + + Getting this option wrong will likely result in a kernel that does + not boot. Selecting support for more than 1 system series will add + bloat to your kernel, but will not cause anything bad to happen. + + If you have a Cobalt Networks System, but aren't sure what kind, + say Y here. + +Create legacy /proc files +CONFIG_COBALT_OLDPROC + This option forces some Cobalt Networks drivers to support legacy + files in /proc. Older versions of these drivers exported files + directly in /proc, as opposed to the newer /proc/cobalt. If you say + N to this option, the old filenames will no longer be exported. + Regardless of your selection here, files in /proc/cobalt will be + exported. Of course, you have to include support for /proc fs, too. + + It is safe to say Y here. + +Front panel LCD support +CONFIG_COBALT_LCD + This enables support for the Cobalt Networks front panel. This is + for the LCD panel and buttons. The primary method for connection is + via the parallel port (IO base 0x370), but newer systems use an + I2C bus. + + If you have a Cobalt Networks system, you should say Y here. + +Software controlled LED support +CONFIG_COBALT_LED + This enables support for the software-controlled LEDs on Cobalt + Networks systems. This includes the fault light and front panel + LEDs on the RaQ XTR, the lightbar on the Qube 3, and others. + + If you have a Cobalt Networks system, you should say Y here. + +Silicon serial number support +CONFIG_COBALT_SERNUM + This enables support for the on-board serial number on Cobalt + Networks systems. This is a universally-unique 64-bit serial + number. Some systems use a Dallas DS2401 chip, others have an I2C + based EEPROM. + + If you select Y here, the files /proc/cobalt/hostid and + /proc/cobalt/serialnumber will be created. The hostid file contains + a 32 bit integer generated from the serial number, in binary form. + The serialnumber file contains the hexadecimal representation of the + serial number, in ASCII. + + If you have a Cobalt Networks system, you should say Y here. + +Chipset watchdog timer support +CONFIG_COBALT_WDT + This enables support for the watchdog timer built into Cobalt + chipsets. The timer wakes up periodically, to make find out if + system has hung, or disabled interrupts too long. The result of + detecting a hang is a hard reboot. + + If you have a Cobalt Networks system, you should say Y here. + +Thermal sensor support +CONFIG_COBALT_THERMAL + This enables support for the thermal sensor(s) built into Cobalt + Networks systems. This driver exports /proc/cobalt/thermal_sensors. + + If you have a Cobalt Networks system, you should say Y here. + +Fan tachometer support +CONFIG_COBALT_FANS + This enables support for the fan tachometers built into some Cobalt + Networks systems. This driver exports /proc/cobalt/faninfo. Some + Cobalt software depends on this feature, and enabling it does not + cause any risks. + + If you have a Cobalt Networks system, you should say Y here, unless + you are absolutely sure. + +Disk drive ruler support +CONFIG_COBALT_RULER + This enables support for the cobalt hard drive ruler, found on some + Cobalt systems, including the RaQ XTR. This is the device that + enables swapping of drives. It is not needed for basic disk + operation. Enabling this on a system with no ruler will have no + adverse effects. + + If you have a Cobalt Networks system, you should say Y here, + unless you are absolutely sure. + I2C support CONFIG_I2C I2C (pronounce: I-square-C) is a slow serial bus protocol used in - many micro controller applications and developed by Philips. SMBus, - or System Management Bus is a subset of the I2C protocol. More - information is contained in the directory Documentation/i2c/, + many micro controller applications and developed by Philips. SMBus, + or System Management Bus is a subset of the I2C protocol. More + information is contained in the directory , especially in the file called "summary" there. Both I2C and SMBus are supported here. You will need this for - hardware sensors support, and also for Video for Linux support. + hardware sensors support, and also for Video For Linux support. Specifically, if you want to use a BT848 based frame grabber/overlay boards under Linux, say Y here and also to "I2C bit-banging interfaces", below. If you want I2C support, you should say Y here and also to the - specific driver for your bus adapter(s) below. If you say Y to + specific driver for your bus adapter(s) below. If you say Y to "/proc file system" below, you will then get a /proc interface which - is documented in Documentation/i2c/proc-interface. + is documented in . - This I2C support is also available as a module. If you want to + This I2C support is also available as a module. If you want to compile it as a module, say M here and read - Documentation/modules.txt. The module will be called i2c-core.o. + . + The module will be called i2c-core.o. + +UltraSPARC-III bootbus i2c controller driver +CONFIG_BBC_I2C + The BBC devices on the UltraSPARC III have two I2C controllers. The + first I2C controller connects mainly to configuration PROMs (NVRAM, + CPU configuration, DIMM types, etc.). The second I2C controller + connects to environmental control devices such as fans and + temperature sensors. The second controller also connects to the + smartcard reader, if present. Say Y to enable support for these. I2C bit-banging interfaces CONFIG_I2C_ALGOBIT This allows you to use a range of I2C adapters called bit-banging - adapters. Say Y if you own an I2C adapter belonging to this class + adapters. Say Y if you own an I2C adapter belonging to this class and then say Y to the specific driver for you adapter below. - This support is also available as a module. If you want to compile - it as a module, say M here and read Documentation/modules.txt. The - module will be called i2c-algo-bit.o. + This support is also available as a module. If you want to compile + it as a module, say M here and read + . + The module will be called i2c-algo-bit.o. Philips style parallel port adapter CONFIG_I2C_PHILIPSPAR - This supports parallel-port I2C adapters made by Philips. Say Y if + This supports parallel-port I2C adapters made by Philips. Say Y if you own such an adapter. - This driver is also available as a module. If you want to compile - it as a module, say M here and read Documentation/modules.txt. The - module will be called i2c-philips-par.o. + This driver is also available as a module. If you want to compile + it as a module, say M here and read + . + The module will be called i2c-philips-par.o. Note that if you want support for different parallel port devices, life will be much easier if you compile them all as modules. ELV adapter CONFIG_I2C_ELV - This supports parallel-port I2C adapters called ELV. Say Y if you + This supports parallel-port I2C adapters called ELV. Say Y if you own such an adapter. - This driver is also available as a module. If you want to compile - it as a module, say M here and read Documentation/modules.txt. The - module will be called i2c-elv.o. + This driver is also available as a module. If you want to compile + it as a module, say M here and read + . + The module will be called i2c-elv.o. Velleman K9000 adapter CONFIG_I2C_VELLEMAN - This supports the Velleman K9000 parallel-port I2C adapter. Say Y if - you own such an adapter. + This supports the Velleman K9000 parallel-port I2C adapter. Say Y + if you own such an adapter. - This driver is also available as a module. If you want to compile - it as a module, say M here and read Documentation/modules.txt. The - module will be called i2c-velleman.o. + This driver is also available as a module. If you want to compile + it as a module, say M here and read + . + The module will be called i2c-velleman.o. I2C PCF 8584 interfaces CONFIG_I2C_ALGOPCF @@ -13862,29 +15922,41 @@ Say Y if you own an I2C adapter belonging to this class and then say Y to the specific driver for you adapter below. - This support is also available as a module. If you want to compile - it as a module, say M here and read Documentation/modules.txt. The - module will be called i2c-algo-pcf.o. + This support is also available as a module. If you want to compile + it as a module, say M here and read + . + The module will be called i2c-algo-pcf.o. Elektor ISA card CONFIG_I2C_ELEKTOR - This supports the PCF8584 ISA bus I2C adapter. Say Y if you own such - an adapter. + This supports the PCF8584 ISA bus I2C adapter. Say Y if you own + such an adapter. - This driver is also available as a module. If you want to compile - it as a module, say M here and read Documentation/modules.txt. The - module will be called i2c-elektor.o. + This driver is also available as a module. If you want to compile + it as a module, say M here and read + . + The module will be called i2c-elektor.o. I2C device interface CONFIG_I2C_CHARDEV Say Y here to use i2c-* device files, usually found in the /dev - directory on your system. They make it possible to have user-space - programs use the I2C bus. Information on how to do this is contained - in the file Documentation/i2c/dev-interface. + directory on your system. They make it possible to have user-space + programs use the I2C bus. Information on how to do this is + contained in the file . + + This code is also available as a module. If you want to compile + it as a module, say M here and read + . + The module will be called i2c-dev.o. + +I2C /proc support +CONFIG_I2C_PROC + This provides support for i2c device entries in the /proc filesystem. + The entries will be found in /proc/sys/dev/sensors. This code is also available as a module. If you want to compile - it as a module, say M here and read Documentation/modules.txt. The - module will be called i2c-dev.o. + it as a module, say M here and read . + The module will be called i2c-proc.o. Bus Mouse Support CONFIG_BUSMOUSE @@ -13893,11 +15965,11 @@ Microsoft mouse (made by Logitech) that plugs into a COM port (rectangular with 9 or 25 pins). These people say N here. If you have something else, read the Busmouse-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , and say Y here. + , and say Y here. If you have a laptop, you either have to check the documentation or experiment a bit to find out whether the trackball is a serial mouse - or not; it's best to say Y here for you. + or not; it's best to say Y here for you. This is the generic bus mouse driver code. If you have a bus mouse, you will have to say Y here and also to the specific driver for your @@ -13906,7 +15978,7 @@ This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called busmouse.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . Mouse Support (not serial and bus mice) CONFIG_MOUSE @@ -13916,30 +15988,30 @@ MouseSystem or Microsoft mouse (made by Logitech) that plugs into a COM port (rectangular with 9 or 25 pins). These people say N here. If you have something else, read the Busmouse-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . This HOWTO contains + . This HOWTO contains information about all non-serial mice, not just bus mice. If you have a laptop, you either have to check the documentation or experiment a bit to find out whether the trackball is a serial mouse - or not; it's best to say Y here for you. + or not; it's best to say Y here for you. Note that the answer to this question won't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions about non-serial mice. If unsure, say Y. Logitech busmouse support CONFIG_LOGIBUSMOUSE - Logitech mouse connected to a proprietary interface card. It's + Logitech mouse connected to a proprietary interface card. It's generally a round connector with 9 pins. Note that the newer mice made by Logitech don't use the Logitech protocol anymore; for those, - you don't need this option. You want to read the Busmouse-HOWTO , - available from http://www.linuxdoc.org/docs.html#howto . + you don't need this option. You want to read the Busmouse-HOWTO, + available from . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called busmouse.o. If you are unsure, say N and read the HOWTO - nevertheless: it will tell you what you have. + say M here and read . The module + will be called busmouse.o. If you are unsure, say N and read the + HOWTO nevertheless: it will tell you what you have. PS/2 mouse (aka "auxiliary device") support CONFIG_PSMOUSE @@ -13953,12 +16025,12 @@ Although PS/2 mice are not technically bus mice, they are explained in detail in the Busmouse-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . When using a PS/2 mouse, you can get problems if you want to use the mouse both on the Linux console and under X. Using the "-R" option of the Linux mouse managing program gpm (available from - ftp://metalab.unc.edu/pub/Linux/system/mouse ) solves this + ) solves this problem, or you can get the "mconv2" utility from the same location. C&T 82C710 mouse port support (as on TI Travelmate) @@ -13966,25 +16038,25 @@ This is a certain kind of PS/2 mouse used on the TI Travelmate. If you are unsure, try first to say N here and come back if the mouse doesn't work. Read the Busmouse-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . PC110 digitizer pad support CONFIG_PC110_PAD - This drives the digitizer pad on the IBM PC110 palmtop. It can turn + This drives the digitizer pad on the IBM PC110 palmtop. It can turn the digitizer pad into a PS/2 mouse emulation with tap gestures or into an absolute pad. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called pc110pad.o. + say M here and read . The module + will be called pc110pad.o. Microsoft busmouse support CONFIG_MS_BUSMOUSE These animals (also called Inport mice) are connected to an expansion board using a round connector with 9 pins. If this is what you have, say Y and read the Busmouse-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you are unsure, say N and read the HOWTO nevertheless: it will tell you what you have. Also be aware that several vendors talk @@ -13993,41 +16065,33 @@ If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called msbusmouse.o. - -Apple Desktop Bus support -CONFIG_ADB - Apple Desktop Bus (ADB) support is for support of devices which - are connected to an ADB port. ADB devices tend to have 4 pins. - If you have an Apple Macintosh prior to the iMac, or a - "Blue and White G3", you probably want to say Y here. Otherwise - say N. + say M here and read . The module + will be called msbusmouse.o. Apple Desktop Bus mouse support CONFIG_ADBMOUSE Say Y here if you have this type of bus mouse (4 pin connector) as - is common on Macintoshes. You may want to read the Busmouse-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + is common on Macintoshes. You may want to read the Busmouse-HOWTO, + available from . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called adbmouse.o. + say M here and read . The module + will be called adbmouse.o. ATIXL busmouse support CONFIG_ATIXL_BUSMOUSE This is a rare type of busmouse that is connected to the back of an - ATI video card. Say Y if you have one of those. Note however that + ATI video card. Say Y if you have one of those. Note however that most mice by ATI are actually Microsoft busmice; you should say Y to - "Microsoft busmouse support" above if you have one of those. Read + "Microsoft busmouse support" above if you have one of those. Read the Busmouse-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called atixlmouse.o. + say M here and read . The module + will be called atixlmouse.o. If you are unsure, say N and read the HOWTO nevertheless: it will tell you what you have. @@ -14037,29 +16101,34 @@ If you have a non-SCSI tape drive like that, say Y. Or, if you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here - and read Documentation/modules.txt. The module will be called + and read . The module will be called tpqic02.o. +iSeries Virtual Tape Support +CONFIG_VIOTAPE + If you are running Linux on an iSeries system and you want Linux + to read and/or write a tape drive owned by OS/400, say Y here. + Do you want runtime configuration for QIC-02 CONFIG_QIC02_DYNCONF You can either configure this driver once and for all by editing a - header file (include/linux/tpqic02.h), in which case you should - say N, or you can fetch a program via anonymous FTP which is able - to configure this driver during runtime. The program to do this is - called 'qic02conf' and it is part of the tpqic02-support-X.Y.tar.gz - support package. + header file (), in which case you + should say N, or you can fetch a program via anonymous FTP which is + able to configure this driver during runtime. The program to do + this is called 'qic02conf' and it is part of the + tpqic02-support-X.Y.tar.gz support package. If you want to use the qic02conf program, say Y. Floppy tape drive (QIC-80/40/3010/3020/TR-1/TR-2/TR-3) support CONFIG_FTAPE If you have a tape drive that is connected to your floppy - controller, say Y here. + controller, say Y here. Some tape drives (like the Seagate "Tape Store 3200" or the Iomega "Ditto 3200" or the Exabyte "Eagle TR-3") come with a "high speed" controller of their own. These drives (and their companion - controllers) are also supported if you say Y here. + controllers) are also supported if you say Y here. If you have a special controller (such as the CMS FC-10, FC-20, Mountain Mach-II, or any controller that is based on the Intel 82078 @@ -14067,56 +16136,57 @@ Iomega's "Ditto Dash") you must configure it by selecting the appropriate entries from the "Floppy tape controllers" sub-menu below and possibly modify the default values for the IRQ and DMA - channel and the IO base in ftape's configuration menu. + channel and the IO base in ftape's configuration menu. If you want to use your floppy tape drive on a PCI-bus based system, - please read the file drivers/char/ftape/README.PCI. + please read the file . The ftape kernel driver is also available as a runtime loadable module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a - module, say M here and read Documentation/modules.txt. The module - will be called ftape.o. + module, say M here and read . The + module will be called ftape.o. Note that the Ftape-HOWTO is out of date (sorry) and documents the older version 2.08 of this software but still contains useful - information. There is a web page with more recent documentation at - http://www.math1.rwth-aachen.de/~heine/ftape/ . This page + information. There is a web page with more recent documentation at + . This page always contains the latest release of the ftape driver and useful information (backup software, ftape related patches and - documentation, FAQ). Note that the file system interface has changed - quite a bit compared to previous versions of ftape. Please read - Documentation/ftape.txt. + documentation, FAQ). Note that the file system interface has + changed quite a bit compared to previous versions of ftape. Please + read . -The file system interface for ftape +VFS interface for ftape CONFIG_ZFTAPE Normally, you want to say Y or M. DON'T say N here or you WON'T BE ABLE TO USE YOUR FLOPPY TAPE DRIVE. The ftape module itself no longer contains the routines necessary to interface with the kernel VFS layer (i.e. to actually write data - to and read data from the tape drive). Instead the file system + to and read data from the tape drive). Instead the file system interface (i.e. the hardware independent part of the driver) has been moved to a separate module. If you say M zftape will be compiled as a runtime loadable module ( = code which can be inserted in and removed from the - running kernel whenever you want). In this case you should read - Documentation/modules.txt. The module will be called zftape.o. + running kernel whenever you want). In this case you should read + . The module will be called + zftape.o. Regardless of whether you say Y or M here, an additional runtime loadable module called `zft-compressor.o' which contains code to support user transparent on-the-fly compression based on Ross - William's lzrw3 algorithm will be produced. If you have enabled the + William's lzrw3 algorithm will be produced. If you have enabled the kernel module loader (i.e. have said Y to "Kernel module loader support", above) then `zft-compressor.o' will be loaded automatically by zftape when needed. - Despite its name, zftape does NOT use compression by default. The - file Documentation/ftape.txt contains a short description of the - most important changes in the file system interface compared to - previous versions of ftape. The ftape home page - http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/ contains + Despite its name, zftape does NOT use compression by default. The + file contains a short description of + the most important changes in the file system interface compared to + previous versions of ftape. The ftape home page + contains further information. IMPORTANT NOTE: zftape can read archives created by previous @@ -14155,7 +16225,7 @@ wastes 32 KB of memory. Please note that this memory cannot be swapped out. -Procfs entry for ftape +Enable procfs status report (+2kb) CONFIG_FT_PROC_FS Optional. Saying Y will result in creation of a directory `/proc/ftape' under the /proc file system. The files can be viewed @@ -14172,6 +16242,7 @@ interface. Accessing `/proc/ftape' while the module is unloaded will result in a kernel Oops. This cannot be fixed from inside ftape. +# Choice: ftdebug Controlling the amount of debugging output of ftape CONFIG_FT_NORMAL_DEBUG This option controls the amount of debugging output the ftape driver @@ -14193,9 +16264,22 @@ printed to the console but only makes it possible to produce "Excessive" debugging output. - Please read Documentation/ftape.txt for a short description + Please read for a short description how to control the amount of debugging output. +Excessive +CONFIG_FT_FULL_DEBUG + Extremely verbose output for driver debugging purposes. + +Reduced +CONFIG_FT_NO_TRACE + Reduced tape driver debugging output. + +None +CONFIG_FT_NO_TRACE_AT_ALL + Suppress all debugging output from the tape drive. + +# Choice: ftcontroller The floppy drive controller for ftape CONFIG_FT_STD_FDC Only change this setting if you have a special controller. If you @@ -14231,13 +16315,13 @@ have said Y to "Floppy tape drive") or module load time (i.e. if you have said M to "Floppy tape drive"). - Please read also the file Documentation/ftape.txt which + Please read also the file which contains a short description of the parameters that can be set at boot or load time. If you want to use your floppy tape drive on a PCI-bus based system, please read the file - drivers/char/ftape/README.PCI. + . -IO base of the floppy disk controller used with Ftape +IO base for the floppy disk controller used with Ftape CONFIG_FT_FDC_BASE You don't need to specify a value if the following default settings for the base IO address are correct: @@ -14259,9 +16343,9 @@ "Floppy tape drive") or module load time (i.e. if you have said M to "Floppy tape drive"). - Please read also the file Documentation/ftape.txt which contains a - short description of the parameters that can be set at boot or load - time. + Please read also the file which + contains a short description of the parameters that can be set at + boot or load time. IRQ channel for the floppy disk controller used with Ftape CONFIG_FT_FDC_IRQ @@ -14285,9 +16369,9 @@ "Floppy tape drive") or module load time (i.e. if you said M to "Floppy tape drive"). - Please read also the file Documentation/ftape.txt which contains a - short description of the parameters that can be set at boot or load - time. + Please read also the file which + contains a short description of the parameters that can be set at + boot or load time. DMA channel for the floppy disk controller used with Ftape CONFIG_FT_FDC_DMA @@ -14311,9 +16395,9 @@ "Floppy tape drive") or module load time (i.e. if you said M to "Floppy tape drive"). - Please read also the file Documentation/ftape.txt which contains a - short description of the parameters that can be set at boot or load - time. + Please read also the file which + contains a short description of the parameters that can be set at + boot or load time. FDC FIFO Threshold before requesting DMA service CONFIG_FT_FDC_THR @@ -14345,10 +16429,16 @@ introduced in XFree86 4.0. If you say Y here, you need to select the module that's right for your graphics card from the list below. These modules provide support for synchronization, security, and - DMA transfers. Please see http://dri.sourceforge.net for more + DMA transfers. Please see for more details. You should also select and configure AGP (/dev/agpgart) support. +Build drivers for new (XFree 4.1) DRM +CONFIG_DRM_NEW + If you set this option, the new DRM version needed by XFree86 4.1 + will be used. Otherwise, the old DRM version will be used, + appropriate for XFree86 4.0. + 3dfx Banshee/Voodoo3+ CONFIG_DRM_TDFX Choose this option if you have a 3dfx Banshee or Voodoo3 (or later), @@ -14365,19 +16455,69 @@ is selected, the module will be called r128.o. AGP support for this card is strongly suggested (unless you have a PCI version). +ATI Radeon +CONFIG_DRM_RADEON + Choose this option if you have an ATI Radeon graphics card. There + are both PCI and AGP versions. You don't need to choose this to + run the Radeon in plain VGA mode. There is a product page at + . + If M is selected, the module will be called radeon.o. + Intel I810 CONFIG_DRM_I810 Choose this option if you have an Intel I810 graphics card. If M is selected, the module will be called i810.o. AGP support is required for this driver to work. -Matrox g200/g400 +Matrox G200/G400/G450 CONFIG_DRM_MGA - Choose this option if you have a Matrox g200 or g400 graphics card. If M - is selected, the module will be called mga.o. AGP support is required + Choose this option if you have a Matrox G200, G400 or G450 graphics + card. If M is selected, the module will be called mga.o. AGP + support is required for this driver to work. + +3dfx Banshee/Voodoo3+ +CONFIG_DRM40_TDFX + Choose this option if you have a 3dfx Banshee or Voodoo3 (or later), + graphics card. If M is selected, the module will be called tdfx.o. + +3dlabs GMX 2000 +CONFIG_DRM40_GAMMA + Choose this option if you have a 3dlabs GMX 2000 graphics card. + If M is selected, the module will be called gamma.o. + +ATI Rage 128 +CONFIG_DRM40_R128 + Choose this option if you have an ATI Rage 128 graphics card. If M + is selected, the module will be called r128.o. AGP support for + this card is strongly suggested (unless you have a PCI version). + +ATI Radeon +CONFIG_DRM40_RADEON + Choose this option if you have an ATI Radeon graphics card. There + are both PCI and AGP versions. You don't need to choose this to + run the Radeon in plain VGA mode. There is a product page at + . + If M is selected, the module will be called radeon.o. + +Intel I810 +CONFIG_DRM40_I810 + Choose this option if you have an Intel I810 graphics card. If M is + selected, the module will be called i810.o. AGP support is required for this driver to work. -MTRR control and configuration +Matrox G200/G400/G450 +CONFIG_DRM40_MGA + Choose this option if you have a Matrox G200, G400 or G450 graphics + card. If M is selected, the module will be called mga.o. AGP + support is required for this driver to work. + +Creator/Creator3D/Elite3D +CONFIG_DRM_FFB + Choose this option if you have one of Sun's Creator3D-based graphics + and frame buffer cards. Product page at + . + +MTRR (Memory Type Range Register) support CONFIG_MTRR On Intel P6 family processors (Pentium Pro, Pentium II and later) the Memory Type Range Registers (MTRRs) may be used to control @@ -14387,10 +16527,10 @@ before bursting over the PCI/AGP bus. This can increase performance of image write operations 2.5 times or more. Saying Y here creates a /proc/mtrr file which may be used to manipulate your processor's - MTRRs. Typically the X server should use this. + MTRRs. Typically the X server should use this. - This code has a reasonably generic interface so that similar - control registers on other processors can be easily supported + This code has a reasonably generic interface so that similar + control registers on other processors can be easily supported as well: The Cyrix 6x86, 6x86MX and M II processors have Address Range @@ -14408,9 +16548,9 @@ You can safely say Y even if your machine doesn't have MTRRs, you'll just add about 9 KB to your kernel. - See Documentation/mtrr.txt for more information. + See for more information. -Main CPU frequency, only for DEC alpha machine +CPU clock frequency of your DEC Alpha CONFIG_FT_ALPHA_CLOCK On some DEC Alpha machines the CPU clock frequency cannot be determined automatically, so you need to specify it here ONLY if @@ -14419,11 +16559,11 @@ Double Talk PC internal speech card support CONFIG_DTLK This driver is for the DoubleTalk PC, a speech synthesizer - manufactured by RC Systems (http://www.rcsys.com/ ). It is also + manufactured by RC Systems (). It is also called the `internal DoubleTalk'. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read - Documentation/modules.txt. The module will be called dtlk.o. + . The module will be called dtlk.o. Siemens R3964 serial protocol support CONFIG_R3964 @@ -14433,7 +16573,7 @@ To compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here - and read Documentation/modules.txt. The module will be called + and read . The module will be called n_r3964.o. If unsure, say N. @@ -14443,28 +16583,28 @@ This driver provides the kernel-side support for the intelligent fieldbus cards made by Applicom International. More information about these cards can be found on the WWW at the address - http://www.applicom-int.com/ , or by email from David Woodhouse + , or by email from David Woodhouse . To compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here - and read Documentation/modules.txt. The module will be called + and read . The module will be called applicom.o. If unsure, say N. Sony Vaio Programmable I/O Control Device support CONFIG_SONYPI - This driver enables access to the Sony Programmable I/O Control Device - which can be found in many (all ?) Sony Vaio laptops. + This driver enables access to the Sony Programmable I/O Control + Device which can be found in many (all ?) Sony Vaio laptops. - If you have one of those laptops, read Documentation/sonypi.txt, - and say Y or M here. + If you have one of those laptops, read + , and say Y or M here. If you want to compile the driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read . The module will be - called sonypi.o. + say M here and read . The module + will be called sonypi.o. Intel Random Number Generator support CONFIG_INTEL_RNG @@ -14477,7 +16617,7 @@ To compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here - and read Documentation/modules.txt. The module will be called + and read . The module will be called i810_rng.o. If unsure, say N. @@ -14486,50 +16626,76 @@ CONFIG_PM "Power Management" means that parts of your computer are shut off or put into a power conserving "sleep" mode if they are not - being used. There are two competing standards for doing this: APM - and ACPI. If you want to use either one, say Y here and then also to - the requisite support below. + being used. There are two competing standards for doing this: APM + and ACPI. If you want to use either one, say Y here and then also + to the requisite support below. Power Management is most important for battery powered laptop computers; if you have a laptop, check out the Linux Laptop home page on the WWW at - http://www.cs.utexas.edu/users/kharker/linux-laptop/ and the Battery - Powered Linux mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + and the + Battery Powered Linux mini-HOWTO, available from + . Note that, even if you say N here, Linux on the x86 architecture will issue the hlt instruction if nothing is to be done, thereby sending the processor to sleep and saving power. +USB SE401 Camera support +CONFIG_USB_SE401 + Say Y here if you want to connect this type of camera to your + computer's USB port. See for more + information and for a list of supported cameras. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" (under Multimedia Devices) to use this driver. + Information on this API and pointers to "v4l" programs may be found + on the WWW at . + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called se401.o. If you want to compile it as a + module, say M here and read . + ACPI support CONFIG_ACPI - ACPI/OSPM support for Linux is currently under development. As such, - this support is preliminary and EXPERIMENTAL. Configuring ACPI support - enables kernel interfaces that allow higher level software (OSPM) to - manipulate ACPI defined hardware and software interfaces, including - the evaluation of ACPI control methods. If unsure, choose N here. - Note, this option will enlarge your kernel by about 120K. + ACPI/OSPM support for Linux is currently under development. As such, + this support is preliminary and EXPERIMENTAL. Configuring ACPI + support enables kernel interfaces that allow higher level software + (OSPM) to manipulate ACPI defined hardware and software interfaces, + including the evaluation of ACPI control methods. If unsure, choose + N here. Note, this option will enlarge your kernel by about 120K. This support requires an ACPI compliant platform (hardware/firmware). If both ACPI and Advanced Power Management (APM) support are configured, whichever is loaded first shall be used. - This code DOES NOT currently provide a complete OSPM implementation -- - it has not yet reached APM's level of functionality. When fully + This code DOES NOT currently provide a complete OSPM implementation + -- it has not yet reached APM's level of functionality. When fully implemented, Linux ACPI/OSPM will provide a more robust functional - replacement for legacy configuration and power management interfaces, - including the Plug-and-Play BIOS specification (PNP BIOS), the Multi- - Processor Specification (MPS), and the Advanced Power Management - specification (APM). + replacement for legacy configuration and power management + interfaces, including the Plug-and-Play BIOS specification (PnP + BIOS), the Multi-Processor Specification (MPS), and the Advanced + Power Management specification (APM). Linux support for ACPI/OSPM is based on Intel Corporation's ACPI Component Architecture (ACPI CA). The latest ACPI CA source code, documentation, debug builds, and implementation status information can be downloaded from: - http://developer.intel.com/technology/iapc/acpi/downloads.htm - + . + The ACPI mailing list may also be of interest: - http://phobos.fs.tum.de/acpi/index.html + . + +Enable ACPI 2.0 with errata 1.3 +CONFIG_ACPI20 + Enable support for the 2.0 version of the ACPI interpreter. See the + help for ACPI for caveats and discussion. + +ACPI kernel configuration manager +CONFIG_ACPI_KERNEL_CONFIG + If you say `Y' here, Linux's ACPI support will use the + hardware-level system descriptions found on IA64 machines. ACPI Debug Statements CONFIG_ACPI_DEBUG @@ -14540,37 +16706,39 @@ ACPI Bus Manager CONFIG_ACPI_BUSMGR The ACPI Bus Manager enumerates devices in the ACPI namespace, and - handles PnP messages. All ACPI devices use its services, so using them - requires saying Y here. + handles PnP messages. All ACPI devices use its services, so using + them requires saying Y here. ACPI System Driver CONFIG_ACPI_SYS - This driver will enable your system to shut down using ACPI, and dump - your ACPI DSDT table using /proc/acpi/dsdt. + This driver will enable your system to shut down using ACPI, and + dump your ACPI DSDT table using /proc/acpi/dsdt. ACPI Processor Driver CONFIG_ACPI_CPU - This driver installs ACPI as the idle handler for Linux, and uses ACPI - C2 and C3 processor states to save power, on systems that support it. + This driver installs ACPI as the idle handler for Linux, and uses + ACPI C2 and C3 processor states to save power, on systems that + support it. ACPI Button CONFIG_ACPI_BUTTON - This driver registers for events based on buttons, such as the power, - sleep, and lid switch. In the future, a daemon will read + This driver registers for events based on buttons, such as the + power, sleep, and lid switch. In the future, a daemon will read /proc/acpi/event and perform user-defined actions such as shutting - down the system. Until then, you can cat it, and see output when + down the system. Until then, you can cat it, and see output when a button is pressed. ACPI AC Adapter CONFIG_ACPI_AC This driver adds support for the AC Adapter object, which indicates - whether a system is on AC, or not. Typically, only laptops have this - object, since desktops are always on AC. + whether a system is on AC, or not. Typically, only laptops have + this object, since desktops are always on AC. ACPI Embedded Controller CONFIG_ACPI_EC - This driver is required on some systems for the proper operation of the - battery and thermal drivers. If you are compiling for a laptop, say Y. + This driver is required on some systems for the proper operation of + the battery and thermal drivers. If you are compiling for a laptop, + say Y. ACPI Control Method Battery CONFIG_ACPI_CMBATT @@ -14598,13 +16766,13 @@ machines with more than one CPU. In order to use APM, you will need supporting software. For location - and more information, read Documentation/pm.txt and the Battery - Powered Linux mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + and more information, read and the + Battery Powered Linux mini-HOWTO, available from + . This driver does not spin down disk drives (see the hdparm(8) manpage ("man 8 hdparm") for that), and it doesn't turn off - VESA-compliant "green" monitors. + VESA-compliant "green" monitors. This driver does not support the TI 4000M TravelMate and the ACER 486/DX4/75 because they don't have compliant BIOSes. Many "green" @@ -14615,30 +16783,30 @@ much point in using this driver and you should say N. If you get random kernel OOPSes or reboots that don't seem to be related to anything, try disabling/enabling this option (or disabling/enabling - APM in your BIOS). + APM in your BIOS). Some other things you should try when experiencing seemingly random, "weird" problems: 1) make sure that you have enough swap space and that it is - enabled. - 2) pass the "no-hlt" option to the kernel + enabled. + 2) pass the "no-hlt" option to the kernel 3) switch on floating point emulation in the kernel and pass the "no387" option to the kernel 4) pass the "floppy=nodma" option to the kernel - 5) pass the "mem=4M" option to the kernel (thereby disabling + 5) pass the "mem=4M" option to the kernel (thereby disabling all but the first 4 MB of RAM) 6) make sure that the CPU is not over clocked. - 7) read the sig11 FAQ at http://www.bitwizard.nl/sig11/ + 7) read the sig11 FAQ at 8) disable the cache from your BIOS settings 9) install a fan for the video card or exchange video RAM 10) install a better fan for the CPU - 11) exchange RAM chips + 11) exchange RAM chips 12) exchange the motherboard. To compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here - and read Documentation/modules.txt. The module will be called + and read . The module will be called apm.o. Ignore USER SUSPEND @@ -14663,7 +16831,7 @@ T400CDT. This is off by default since most machines do fine without this feature. -Do CPU IDLE calls +Make CPU Idle calls when idle CONFIG_APM_CPU_IDLE Enable calls to APM CPU Idle/CPU Busy inside the kernel's idle loop. On some machines, this can activate improved power savings, such as @@ -14711,7 +16879,7 @@ a work-around for a number of buggy BIOSes. Switch this option on if your computer crashes instead of powering off properly. -Watchdog Timer Support +Watchdog Timer Support CONFIG_WATCHDOG If you say Y here (and to one of the following options) and create a character special file /dev/watchdog with major number 10 and minor @@ -14723,12 +16891,12 @@ implementation entirely in software (which can sometimes fail to reboot the machine) and a driver for hardware watchdog boards, which are more robust and can also keep track of the temperature inside - your computer. For details, read Documentation/watchdog.txt in the - kernel source. + your computer. For details, read + in the kernel source. - The watchdog is usually used together with the watchdog daemon + The watchdog is usually used together with the watchdog daemon which is available via FTP (user: anonymous) from - ftp://tsx-11.mit.edu/pub/linux/sources/sbin/ . This daemon can also + . This daemon can also monitor NFS connections and can reboot the machine when the process table is full. @@ -14753,22 +16921,22 @@ If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called wdt.o. + say M here and read . The module + will be called wdt.o. WDT PCI Watchdog timer CONFIG_WDTPCI - If you have a PCI WDT500/501 watchdog board, say Y here, - otherwise N. It is not possible to probe for this board, which means - that you have to inform the kernel about the IO port and IRQ using - the "wdt=" kernel option (try "man bootparam" or see the - documentation of your boot loader (lilo or loadlin) about how to - pass options to the kernel at boot time). + If you have a PCI WDT500/501 watchdog board, say Y here, otherwise + N. It is not possible to probe for this board, which means that you + have to inform the kernel about the IO port and IRQ using the "wdt=" + kernel option (try "man bootparam" or see the documentation of your + boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time). If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called wdt_pci.o. + say M here and read . The module + will be called wdt_pci.o. WDT501 features CONFIG_WDT_501 @@ -14788,12 +16956,13 @@ CONFIG_SOFT_WATCHDOG A software monitoring watchdog. This will fail to reboot your system from some situations that the hardware watchdog will recover - from. Equally it's a lot cheaper to install. + from. Equally it's a lot cheaper to install. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. The module will be called softdog.o. + . The module will be called + softdog.o. Berkshire Products PC Watchdog CONFIG_PCWATCHDOG @@ -14801,65 +16970,101 @@ This card simply watches your kernel to make sure it doesn't freeze, and if it does, it reboots your computer after a certain amount of time. This driver is like the WDT501 driver but for different - hardware. Please read Documentation/pcwd-watchdog.txt. The PC - watchdog cards can be ordered from http://www.berkprod.com . + hardware. Please read . The PC + watchdog cards can be ordered from . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called pcwd.o. If you want to compile it as a module, - say M here and read Documentation/modules.txt. + say M here and read . Most people will say N. Acquire SBC Watchdog Timer CONFIG_ACQUIRE_WDT This is the driver for the hardware watchdog on the PSC-6x86 Single - Board Computer produced by Acquire Inc (and others). This watchdog + Board Computer produced by Acquire Inc (and others). This watchdog simply watches your kernel to make sure it doesn't freeze, and if it does, it reboots your computer after a certain amount of time. This driver is like the WDT501 driver but for different hardware. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module is called pscwdt.o. If you want to compile it as a + The module is called pscwdt.o. If you want to compile it as a + module, say M here and read . Most + people will say N. + +Advantech SBC Watchdog Timer +CONFIG_ADVANTECH_WDT + If you are configuring a Linux kernel for the Advantech single-board + computer, say `Y' here to support its built-in watchdog timer + feature. See the help for CONFIG_WATCHDOG for discussion. + +W83877F Watchdog Timer +CONFIG_W83877F_WDT + This is the driver for the hardware watchdog on the W83877F chipset + as used in EMACS PC-104 motherboards (and likely others). This + watchdog simply watches your kernel to make sure it doesn't freeze, + and if it does, it reboots your computer after a certain amount of + time. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module is called mixcomwd.o. If you want to compile it as a + module, say M here and read . Most + people will say N. + +IB700 SBC Watchdog Timer +CONFIG_IB700_WDT + This is the driver for the hardware watchdog on the IB700 Single + Board Computer produced by TMC Technology (www.tmc-uk.com). This watchdog + simply watches your kernel to make sure it doesn't freeze, and if + it does, it reboots your computer after a certain amount of time. + + This driver is like the WDT501 driver but for slightly different hardware. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module is called ib700wdt.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. Most people will say N. Mixcom Watchdog -CONFIG_MIXCOMWD - This is a driver for the Mixcom hardware watchdog cards. This +CONFIG_MIXCOMWD + This is a driver for the Mixcom hardware watchdog cards. This watchdog simply watches your kernel to make sure it doesn't freeze, and if it does, it reboots your computer after a certain amount of time. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module is called mixcomwd.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. Most people - will say N. + The module is called mixcomwd.o. If you want to compile it as a + module, say M here and read . Most + people will say N. ZF MachZ Watchdog CONFIG_MACHZ_WDT - If you are using a ZF Micro MachZ processor, say Y here, otherwise N. - This is the driver for the watchdog timer builtin on that processor - using ZF-Logic interface. This watchdog simply watches your kernel to - make sure it doesn't freeze, and if it does, it reboots your computer - after a certain amount of time. + If you are using a ZF Micro MachZ processor, say Y here, otherwise + N. This is the driver for the watchdog timer builtin on that + processor using ZF-Logic interface. This watchdog simply watches + your kernel to make sure it doesn't freeze, and if it does, it + reboots your computer after a certain amount of time. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module is called machzwd.o. If you want to compile it as a module, - say M here and read Documentation/modules.txt. + The module is called machzwd.o. If you want to compile it as a + module, say M here and read . Toshiba Laptop support CONFIG_TOSHIBA - This adds a driver to safely access the System Management Mode - of the CPU on Toshiba portables. The System Management Mode + This adds a driver to safely access the System Management Mode of + the CPU on Toshiba portables with a genuine Toshiba BIOS. It does + not work on models with a Pheonix BIOS. The System Management Mode is used to set the BIOS and power saving options on Toshiba portables. For information on utilities to make use of this driver see the Toshiba Linux utilities web site at: - http://www.buzzard.org.uk/toshiba/ + . Say Y if you intend to run this kernel on a Toshiba portable. Say N otherwise. @@ -14869,18 +17074,19 @@ If you say Y here and also to "/dev file system support" in the 'File systems' section, you will be able to update the microcode on Intel processors in the IA32 family, e.g. Pentium Pro, Pentium II, - Pentium III, Pentium 4, Xeon etc. You will obviously need the actual - microcode binary data itself which is not shipped with the Linux kernel. + Pentium III, Pentium 4, Xeon etc. You will obviously need the + actual microcode binary data itself which is not shipped with the + Linux kernel. For latest news and information on obtaining all the required ingredients for this driver, check: - http://www.urbanmyth.org/microcode/ + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called microcode.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. If you use - modprobe or kmod you may also want to add the line + The module will be called microcode.o. If you want to compile it as + a module, say M here and read . If + you use modprobe or kmod you may also want to add the line 'alias char-major-10-184 microcode' to your /etc/modules.conf file. /dev/cpu/*/msr - Model-specific register support @@ -14914,7 +17120,7 @@ CONFIG_RTC If you say Y here and create a character special file /dev/rtc with major number 10 and minor number 135 using mknod ("man mknod"), you - will get access to the real time clock (or hardware clock) built + will get access to the real time clock (or hardware clock) built into your computer. Every PC has such a clock built in. It can be used to generate @@ -14928,23 +17134,19 @@ and set the RTC in an SMP compatible fashion. If you think you have a use for such a device (such as periodic data - sampling), then say Y here, and read Documentation/rtc.txt for - details. + sampling), then say Y here, and read + for details. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called rtc.o. If you want to compile it as a module, - say M here and read Documentation/modules.txt. - -### Add -#EFI Real Time Clock Services -#CONFIG_EFI_RTC + say M here and read . Tadpole ANA H8 Support CONFIG_H8 The Hitachi H8/337 is a microcontroller used to deal with the power and thermal environment. If you say Y here, you will be able to - communicate with it via a character special device. + communicate with it via a character special device. If unsure, say N. @@ -14954,7 +17156,7 @@ with major number 10 and minor number 144 using mknod ("man mknod"), you get read and write access to the 50 bytes of non-volatile memory in the real time clock (RTC), which is contained in every PC and - most Ataris. + most Ataris. This memory is conventionally called "CMOS RAM" on PCs and "NVRAM" on Ataris. /dev/nvram may be used to view settings there, or to @@ -14971,8 +17173,9 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called nvram.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . +# Linus tree only Joystick support CONFIG_JOYSTICK If you have a joystick, 6dof controller, gamepad, steering wheel, @@ -14980,232 +17183,299 @@ enable generic support for these controllers. You will also need to say Y or M to at least one of the hardware specific drivers. This will make the controllers available as /dev/input/jsX devices. - Please read the file Documentation/joystick.txt which contains more - information and the location of the joystick package that you'll - need. + Please read the file which + contains more information and the location of the joystick package + that you'll need. + +# AC tree only +Game port support +CONFIG_INPUT_GAMEPORT + Gameport support is for the standard 15-pin PC gameport. If you + have a joystick, gamepad, gameport card, a soundcard with a gameport + or anything else that uses the gameport, say Y or M here and also to + at least one of the hardware specific drivers. + Please read the file which + contains more information and the location of the joystick package + that you'll need if you use the gameport with a joystick. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called gameport.o. If you want to compile it as + a module, say M here and read . -ns558 gameports +Classic ISA/PnP gameports CONFIG_INPUT_NS558 - Say Y here if you have an ISA, ISAPnP or PCI standard gameport. + Say Y here if you have an ISA or PnP gameport. For more information on how to use the driver please read - Documentation/joystick.txt + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called ns558.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called ns558.o. If you want to compile it as a + module, say M here and read . PDPI Lightning 4 gamecard CONFIG_INPUT_LIGHTNING Say Y here if you have a PDPI Lightning 4 gamecard. For more information on how to use the driver please read - Documentation/joystick.txt + . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called lightning.o. If you want to compile it as + a module, say M here and read . + +Crystal SoundFusion gameports +CONFIG_INPUT_CS461X + Say Y here if you have a Cirrus CS461x aka "Crystal SoundFusion" + PCI audio accelerator. A product page for the CS4614 is at + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called lightning.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called cs461x.o. If you want to compile it as a + module, say M here and read . Aureal Vortex and Trident 4DWave gameports CONFIG_INPUT_PCIGAME Say Y here if you have a Trident 4DWave DX/NX or Aureal Vortex 1/2 card. For more information on how to use the driver please read - Documentation/joystick.txt + . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called pcigame.o. If you want to compile it as a + module, say M here and read . + +SoundBlaster Live! gameports +CONFIG_INPUT_EMU10K1 + Say Y here if you have a SoundBlaster Live! card and want to use + its gameport. For more information on how to use the driver + please read . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called pcigame.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called emu10k1-gp.o. If you want to compile it as + a module, say M here and read . Classic PC analog joysticks and gamepads CONFIG_INPUT_ANALOG Say Y here if you have a controller that connects to the PC - gameport. This supports many different types, including joysticks + gameport. This supports many different types, including joysticks with throttle control, with rudders, or with extensions like additional hats and buttons compatible with CH Flightstick Pro, ThrustMaster FCS, 6 and 8 button gamepads, or Saitek Cyborg - joysticks. For more information on how to use the driver please read - Documentation/joystick.txt + joysticks. For more information on how to use the driver please + read . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called analog.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called analog.o. If you want to compile it as a + module, say M here and read . -Assasin 3D and MadCatz Panther devices +Assassin 3D and MadCatz Panther devices CONFIG_INPUT_A3D Say Y here if you have an FPGaming or MadCatz controller using the - A3D protocol over the PC gameport. For more information on how to - use the driver please read Documentation/joystick.txt + A3D protocol over the PC gameport. For more information on how to + use the driver please read . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called a3d.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called a3d.o. If you want to compile it as a + module, say M here and read . Logitech ADI digital joysticks and gamepads CONFIG_INPUT_ADI Say Y here if you have a Logitech controller using the ADI protocol over the PC gameport. For more information on how to use - the driver please read Documentation/joystick.txt + the driver please read . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called adi.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called adi.o. If you want to compile it as a + module, say M here and read . Creative Labs Blaster Cobra gamepad CONFIG_INPUT_COBRA Say Y here if you have a Creative Labs Blaster Cobra gamepad. For more information on how to use the driver please read - Documentation/joystick.txt + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called cobra.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called cobra.o. If you want to compile it as a + module, say M here and read . Genius Flight2000 Digital joysticks and gamepads CONFIG_INPUT_GF2K - Say Y here if you have a Genius Flight2000 or MaxFighter - digitally communicating joystick or gamepad. For more information - on how to use the driver please read Documentation/joystick.txt + Say Y here if you have a Genius Flight2000 or MaxFighter digitally + communicating joystick or gamepad. For more information on how to + use the driver please read . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called gf2k.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called gf2k.o. If you want to compile it as a + module, say M here and read . Gravis GrIP joysticks and gamepads CONFIG_INPUT_GRIP Say Y here if you have a Gravis controller using the GrIP protocol - over the PC gameport. For more information on how to use the driver - please read Documentation/joystick.txt + over the PC gameport. For more information on how to use the driver + please read . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called grip.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called grip.o. If you want to compile it as a + module, say M here and read . InterAct digital joysticks and gamepads CONFIG_INPUT_INTERACT Say Y hereif you have an InterAct gameport or joystick - communicating digitally over the gameport. For more information on - how to use the driver please read Documentation/joystick.txt + communicating digitally over the gameport. For more information on + how to use the driver please read . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called interact.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called interact.o. If you want to compile it as + a module, say M here and read . ThrustMaster DirectConnect joysticks and gamepads CONFIG_INPUT_TMDC Say Y here if you have a ThrustMaster controller using the - DirectConnect (BSP) protocol over the PC gameport. For more + DirectConnect (BSP) protocol over the PC gameport. For more information on how to use the driver please read - Documentation/joystick.txt + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called tmdc.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called tmdc.o. If you want to compile it as a + module, say M here and read . Microsoft SideWinder digital joysticks and gamepads CONFIG_INPUT_SIDEWINDER Say Y here if you have a Microsoft controller using the Digital - Overdrive protocol over PC gameport. For more information on how to - use the driver please read Documentation/joystick.txt + Overdrive protocol over PC gameport. For more information on how to + use the driver please read . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called sidewinder.o. If you want to compile it + as a module, say M here and read . + +Serial port device support +CONFIG_INPUT_SERIO + Say Y here and to the Serial port input line discipline option if + you plan to use a joystick that communicates over the serial (COM) + port. For more information on how to use the driver please read + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called sidewinder.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called sidewinder.o. If you want to compile it + as a module, say M here and read . Serial port input line discipline CONFIG_INPUT_SERPORT - Say Y hereif you plan to use a joystick that communicates over the - serial (COM) port. For more information on how to use the driver - please read Documentation/joystick.txt + Say Y here if you plan to use a joystick that communicates over the + serial (COM) port. For more information on how to use the driver + please read . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called serport.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called serport.o. If you want to compile it as a + module, say M here and read . Logitech WingMan Warrior joystick CONFIG_INPUT_WARRIOR - Say Y here if you have a Logitech WingMan Warrior joystick - connected to your computer's serial port. For more information on - how to use the driver please read Documentation/joystick.txt + Say Y here if you have a Logitech WingMan Warrior joystick connected + to your computer's serial port. For more information on how to use + the driver please read . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called warrior.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called warrior.o. If you want to compile it as a + module, say M here and read . LogiCad3d Magellan/SpaceMouse 6dof controller CONFIG_INPUT_MAGELLAN Say Y here if you have a Magellan or Space Mouse 6DOF controller connected to your computer's serial port. For more information on - how to use the driver please read Documentation/joystick.txt + how to use the driver please read . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called magellan.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called magellan.o. If you want to compile it as + a module, say M here and read . SpaceTec SpaceOrb/Avenger 6dof controller CONFIG_INPUT_SPACEORB Say Y here if you have a SpaceOrb 360 or SpaceBall Avenger 6DOF controller connected to your computer's serial port. For more information on how to use the driver please read - Documentation/joystick.txt + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called spaceorb.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. - + The module will be called spaceorb.o. If you want to compile it as + a module, say M here and read . + SpaceTec SpaceBall 4000 FLX 6dof controller CONFIG_INPUT_SPACEBALL Say Y here if you have a SpaceTec SpaceBall 4000 FLX controller - connected to your computer's serial port. For more information on - how to use the driver please read Documentation/joystick.txt + connected to your computer's serial port. For more information on + how to use the driver please read . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called spaceball.o. If you want to compile it as + a module, say M here and read . + +Gravis Stinger gamepad +CONFIG_INPUT_STINGER + Say Y here if you have a Gravis Stinger connected to one of your + serial ports. For more information on how to use the driver please + read . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called stinger.o. If you want to compile it as a + module, say M here and read . I-Force joysticks/wheels CONFIG_INPUT_IFORCE_232 Say Y here if you have an I-Force joystick or steering wheel - connected to your serial (COM) port. For more information on - how to use the driver please read Documentation/joystick.txt + connected to your serial (COM) port. For more information on how + to use the driver please read . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called iforce.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called iforce.o. If you want to compile it as a + module, say M here and read . I-Force joysticks/wheels CONFIG_INPUT_IFORCE_USB Say Y here if you have an I-Force joystick or steering wheel - connected to your USB port. For more information on how to use the - driver please read Documentation/joystick.txt + connected to your USB port. For more information on how to use the + driver please read . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called iforce.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. - + The module will be called iforce.o. If you want to compile it as a + module, say M here and read . + Multisystem, Sega Genesis, Saturn joysticks and gamepads CONFIG_INPUT_DB9 Say Y here if you have a Sega Master System gamepad, Sega Genesis gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga, - Commodore, Amstrad CPC joystick connected to your parallel port. - For more information on how to use the driver please read - Documentation/joystick.txt and Documentation/joystick-parport.txt. + Commodore, Amstrad CPC joystick connected to your parallel port. + For more information on how to use the driver please read + and + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called db9.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called db9.o. If you want to compile it as a + module, say M here and read . Multisystem, NES, SNES, N64, PSX joysticks and gamepads CONFIG_INPUT_GAMECON @@ -15214,47 +17484,48 @@ Sony PlayStation gamepad or a Multisystem -- Atari, Amiga, Commodore, Amstrad CPC joystick connected to your parallel port. For more information on how to use the driver please read - Documentation/joystick.txt and Documentation/joystick-parport.txt + and + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called gamecon.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called gamecon.o. If you want to compile it as a + module, say M here and read . Multisystem joysticks via TurboGraFX device CONFIG_INPUT_TURBOGRAFX - Say Y here if you have the TurboGraFX interface by Steffen - Schwenke, and want to use it with Multiststem -- Atari, Amiga, - Commodore, Amstrad CPC joystick. For more information on how to use - the driver please read Documentation/joystick.txt and - Documentation/joystick-parport.txt + Say Y here if you have the TurboGraFX interface by Steffen Schwenke, + and want to use it with Multisystem -- Atari, Amiga, Commodore, + Amstrad CPC joystick. For more information on how to use the driver + please read and + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called turbografx.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called turbografx.o. If you want to compile it + as a module, say M here and read . Amiga joysticks CONFIG_INPUT_AMIJOY Say Y here if you have an Amiga with a digital joystick connected - to it. For more information on how to use the driver please read - Documentation/joystick.txt + to it. For more information on how to use the driver please read + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-amiga.o. If you want to compile - it as a module, say M here and read Documentation/modules.txt. + The module will be called joy-amiga.o. If you want to compile it as + a module, say M here and read . -Atomwide Serial Support +Atomwide serial port support CONFIG_ATOMWIDE_SERIAL If you have an Atomwide Serial card for an Acorn system, say Y to - this option. The driver can handle 1, 2, or 3 port cards. - If unsure, say N + this option. The driver can handle 1, 2, or 3 port cards. + If unsure, say N. -The Serial Port Dual Serial Port +Dual serial port support CONFIG_DUALSP_SERIAL If you have the Serial Port's dual serial card for an Acorn system, - say Y to this option. If unsure, say N + say Y to this option. If unsure, say N. NetWinder Button CONFIG_NWBUTTON @@ -15275,7 +17546,8 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. The module will be called nwbutton.o. + . The module will be called + nwbutton.o. Most people will answer Y to this question and "Reboot Using Button" below to be able to initiate a system shutdown from the button. @@ -15293,37 +17565,37 @@ Sound card support CONFIG_SOUND If you have a sound card in your computer, i.e. if it can say more - than an occasional beep, say Y. Be sure to have all the information + than an occasional beep, say Y. Be sure to have all the information about your sound card and its configuration down (I/O port, - interrupt and DMA channel), because you will be asked for it. + interrupt and DMA channel), because you will be asked for it. You want to read the Sound-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . General information - about the modular sound system is contained in the files - Documentation/sound/Introduction. The file - Documentation/sound/README.OSS contains some slightly outdated but - still useful information as well. + . General information about + the modular sound system is contained in the files + . The file + contains some slightly + outdated but still useful information as well. If you have a PnP sound card and you want to configure it at boot time using the ISA PnP tools (read - http://www.roestock.demon.co.uk/isapnptools/ ), then you need to + ), then you need to compile the sound card support as a module ( = code which can be inserted in and removed from the running kernel whenever you want) - and load that module after the PnP configuration is finished. To do - this, say M here and read Documentation/modules.txt as well as - Documentation/sound/README.modules; the module will be called - soundcore.o. + and load that module after the PnP configuration is finished. To do + this, say M here and read as well + as ; the module will be + called soundcore.o. I'm told that even without a sound card, you can make your computer say more than an occasional beep, by programming the PC speaker. Kernel patches and supporting utilities to do that are in the pcsp - package, available at ftp://ftp.infradead.org/pub/pcsp/ . + package, available at . OSS sound modules CONFIG_SOUND_OSS - OSS is the Open Sound System suite of sound card drivers. They make - sound programming easier since they provide a common API. Say Y or M - here (the module will be called sound.o) if you haven't found a + OSS is the Open Sound System suite of sound card drivers. They make + sound programming easier since they provide a common API. Say Y or + M here (the module will be called sound.o) if you haven't found a driver for your sound card above, then pick your driver from the list below. @@ -15340,7 +17612,7 @@ then you can get the persistent DMA buffer functionality by passing the command-line argument "dmabuf=1" to the sound.o module. - Say Y unless you have 16MB or less RAM or a PCI sound card. + Say Y unless you have 16MB or more RAM or a PCI sound card. Support for Aztech Sound Galaxy (non-PnP) cards CONFIG_SOUND_SGALAXY @@ -15352,9 +17624,9 @@ "sgalaxy=,,,," to the kernel command line. -Support for AD1816(A) based cards (EXPERIMENTAL) +Support for AD1816(A) based cards CONFIG_SOUND_AD1816 - Say M here if you have a sound card based on the Analog Devices + Say M here if you have a sound card based on the Analog Devices AD1816(A) chip. If you compile the driver into the kernel, you have to add @@ -15363,8 +17635,8 @@ Yamaha OPL3-SA1 audio controller CONFIG_SOUND_OPL3SA1 Say Y or M if you have a Yamaha OPL3-SA1 sound chip, which is - usually built into motherboards. Read Documentation/sound/OPL3-SA - for details. + usually built into motherboards. Read + for details. If you compile the driver into the kernel, you have to add "opl3sa=,,,,," to the kernel @@ -15375,7 +17647,7 @@ Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio 16 or Logitech SoundMan 16 sound card. Answer N if you have some other card made by Media Vision or Logitech since those are not - PAS16 compatible. Please read Documentation/sound/PAS16. + PAS16 compatible. Please read . It is not necessary to add Sound Blaster support separately; it is included in PAS support. @@ -15383,6 +17655,11 @@ "pas2=,,,,,,, to the kernel command line. +Enable PAS16 joystick port +CONFIG_PAS_JOYSTICK + Say Y here to enable the Pro Audio Spectrum 16's auxiliary joystick + port. + 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support CONFIG_SOUND_SB Answer Y if you have an original Sound Blaster card made by Creative @@ -15390,48 +17667,43 @@ SM Games). For an unknown card you may answer Y if the card claims to be Sound Blaster-compatible. - Please read the file Documentation/sound/Soundblaster. + Please read the file . You should also say Y here for cards based on the Avance Logic - ALS-007 and ALS-1X0 chips (read Documentation/sound/ALS) and for cards - based on ESS chips (read Documentation/sound/ESS1868 and - Documentation/sound/ESS). If you have an SB AWE 32 or SB AWE 64, say - Y here and also to "AWE32 synth" below and read - Documentation/sound/INSTALL.awe. If you have an IBM Mwave card, say - Y here and read Documentation/sound/mwave. + ALS-007 and ALS-1X0 chips (read ) and + for cards based on ESS chips (read + and + ). If you have an SB AWE 32 or SB AWE + 64, say Y here and also to "AWE32 synth" below and read + . If you have an IBM Mwave + card, say Y here and read . If you compile the driver into the kernel and don't want to use isapnp, you have to add "sb=,,," to the kernel command line. - + You can say M here to compile this driver as a module; the module is called sb.o. -#Loopback MIDI device support -#CONFIG_SOUND_VMIDI -### -### somebody please fill this in. -### -# Gravis Ultrasound support CONFIG_SOUND_GUS - Say Y here for any type of Gravis Ultrasound card, including - the GUS or GUS MAX. See also Documentation/sound/ultrasound for - more information on configuring this card with modules. + Say Y here for any type of Gravis Ultrasound card, including the GUS + or GUS MAX. See also for more + information on configuring this card with modules. If you compile the driver into the kernel, you have to add "gus=,,," to the kernel command line. MPU-401 support (NOT for SB16) CONFIG_SOUND_MPU401 - Be careful with this question. The MPU401 interface is supported by - all sound cards. However, some natively supported cards have their - own driver for MPU401. Enabling this MPU401 option with these cards - will cause a conflict. Also, enabling MPU401 on a system that - doesn't really have a MPU401 could cause some trouble. If your card + Be careful with this question. The MPU401 interface is supported by + all sound cards. However, some natively supported cards have their + own driver for MPU401. Enabling this MPU401 option with these cards + will cause a conflict. Also, enabling MPU401 on a system that + doesn't really have a MPU401 could cause some trouble. If your card was in the list of supported cards, look at the card specific - instructions in the drivers/sound/Readme.cards file. It's safe to - answer Y if you have a true MPU401 MIDI interface card. + instructions in the file. It + is safe to answer Y if you have a true MPU401 MIDI interface card. If you compile the driver into the kernel, you have to add "mpu401=," to the kernel command line. @@ -15451,7 +17723,7 @@ ADSP-16 or some other card based on the PSS chipset (AD1848 codec + ADSP-2115 DSP chip + Echo ESC614 ASIC CHIP). For more information on how to compile it into the kernel or as a module see the file - Documentation/sound/PSS. + . If you compile the driver into the kernel, you have to add "pss=,,,,," to the kernel @@ -15466,7 +17738,7 @@ If you said M to "PSS support" above, you may enable or disable this PSS mixer with the module parameter pss_mixer. For more information - see the file Documentation/sound/PSS. + see the file . Have DSPxxx.LD firmware file CONFIG_PSS_HAVE_BOOT @@ -15481,10 +17753,10 @@ Microsoft Sound System support CONFIG_SOUND_MSS - Again think carefully before answering Y to this question. It's safe - to answer Y if you have the original Windows Sound System card made - by Microsoft or Aztech SG 16 Pro (or NX16 Pro). Also you may say Y - in case your card is NOT among these: + Again think carefully before answering Y to this question. It's + safe to answer Y if you have the original Windows Sound System card + made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). Also you may + say Y in case your card is NOT among these: ATI Stereo F/X, AdLib, Audio Excell DSP16, Cardinal DSP16, Ensoniq SoundScape (and compatibles made by Reveal and Spea), @@ -15501,9 +17773,9 @@ synthesizers (OPL2, OPL3 and OPL4), 6850 UART MIDI Interface. For cards having native support in VoxWare, consult the card - specific instructions in drivers/sound/Readme.cards. Some drivers - have their own MSS support and saying Y to this option will cause a - conflict. + specific instructions in . + Some drivers have their own MSS support and saying Y to this option + will cause a conflict. If you compile the driver into the kernel, you have to add "ad1848=,,,[,]" to the kernel command @@ -15511,11 +17783,12 @@ SGI Visual Workstation on-board audio CONFIG_SOUND_VWSND - Say Y or M if you have an SGI Visual Workstation and you want to - be able to use its on-board audio. Read Documentation/sound/vwsnd - for more info on this driver's capabilities. + Say Y or M if you have an SGI Visual Workstation and you want to be + able to use its on-board audio. Read + for more info on this driver's + capabilities. -Ensoniq Soundscape support +Ensoniq SoundScape support CONFIG_SOUND_SSCAPE Answer Y if you have a sound card based on the Ensoniq SoundScape chipset. Such cards are being manufactured at least by Ensoniq, Spea @@ -15545,27 +17818,28 @@ Support for OPTi MAD16 and/or Mozart based cards CONFIG_SOUND_MAD16 Answer Y if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi - 82C928 or 82C929 or 82C931) audio interface chip. These chips are + 82C928 or 82C929 or 82C931) audio interface chip. These chips are quite common so it's possible that many no-name cards have one of them. In addition the MAD16 chip is used in some cards made by known manufacturers such as Turtle Beach (Tropez), Reveal (some models) and Diamond (latest ones). Note however that the Tropez sound cards have their own driver; if you have one of those, say N here and Y or - M to "Full support for Turtle Beach WaveFront", below. + M to "Full support for Turtle Beach WaveFront", below. If you compile the driver into the kernel, you have to add "mad16=,,,,," to the kernel command line. - See also Documentation/sound/Opti and Documentation/sound/MAD16 for - more information on setting these cards up as modules. + See also and + for more information on setting + these cards up as modules. -Full support for Turtle Beach WaveFront synth/sound cards +Full support for Turtle Beach WaveFront (Tropez Plus, Tropez, Maui) synth/sound cards CONFIG_SOUND_WAVEFRONT Answer Y or M if you have a Tropez Plus, Tropez or Maui sound card - and read the files Documentation/sound/Wavefront and - Documentation/sound/Tropez+. - + and read the files and + . + Support MIDI in older MAD16 based cards (requires SB) CONFIG_MAD16_OLDCARD Answer Y (or M) if you have an older card based on the C928 or @@ -15581,15 +17855,15 @@ "cs4232=,,,,," to the kernel command line. - See Documentation/sound/CS4232 for more information on configuring - this card. + See for more information on + configuring this card. Support for Yamaha OPL3-SA2 and SA3 based PnP cards CONFIG_SOUND_OPL3SA2 - Say Y or M if you have a card based on one of these Yamaha - sound chipsets or the "SAx", which is actually a SA3. Read - Documentation/sound/OPL3-SA2 for more information on configuring - these cards. + Say Y or M if you have a card based on one of these Yamaha sound + chipsets or the "SAx", which is actually a SA3. Read + for more information on + configuring these cards. If you compile the driver into the kernel and do not also configure in the optional ISA PnP support, you will have to add @@ -15618,52 +17892,118 @@ Support for Turtle Beach MultiSound Classic, Tahiti, Monterey CONFIG_SOUND_MSNDCLAS Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or - Monterey (not for the Pinnacle or Fiji). + Monterey (not for the Pinnacle or Fiji). - See Documentation/sound/MultiSound for important information about - this driver. + See for important information + about this driver. Note that it has been discontinued, but the + Voyetra Turtle Beach knowledge base entry for it is still available + at . + +MSND Classic I/O +CONFIG_MSNDCLAS_IO + I/O port address for the MultiSound Classic and related cards. + +MSND Classic IRQ +CONFIG_MSNDCLAS_IRQ + Interrupt Request line for the MultiSound Classic and related cards. + +MSND Classic memory address +CONFIG_MSNDCLAS_MEM + Memory-mapped I/O base address for the MultiSound Classic and + related cards. Full pathname of MSNDINIT.BIN firmware file CONFIG_MSNDCLAS_INIT_FILE The MultiSound cards have two firmware files which are required for operation, and are not currently included. These files can be - obtained from Turtle Beach. See Documentation/sound/MultiSound for - information on how to obtain this. + obtained from Turtle Beach. See + for information on how to + obtain this. Full pathname of MSNDPERM.BIN firmware file CONFIG_MSNDCLAS_PERM_FILE The MultiSound cards have two firmware files which are required for operation, and are not currently included. These files can be - obtained from Turtle Beach. See Documentation/sound/MultiSound for - information on how to obtain this. + obtained from Turtle Beach. See + for information on how to + obtain this. Support for Turtle Beach MultiSound Pinnacle, Fiji CONFIG_SOUND_MSNDPIN Say M here if you have a Turtle Beach MultiSound Pinnacle or Fiji. - See Documentation/sound/MultiSound for important information about - this driver. + See for important information + about this driver. Note that it has been discontinued, but the + Voyetra Turtle Beach knowledge base entry for it is still available + at . + +MSND Pinnacle IDE I/O 0 +CONFIG_MSNDPIN_IDE_IO0 + CD-ROM drive 0 memory-mapped I/O base address for the MultiSound + Pinnacle and Fiji sound cards. + +MSND Pinnacle IDE I/O 1 +CONFIG_MSNDPIN_IDE_IO1 + CD-ROM drive 1 memory-mapped I/O base address for the MultiSound + Pinnacle and Fiji sound cards. + +MSND Pinnacle IDE IRQ +CONFIG_MSNDPIN_IDE_IRQ + Interrupt request number for the IDE CD-ROM interface on the + MultiSound Pinnacle and Fiji sound cards. + +MSND Pinnacle I/O +CONFIG_MSNDPIN_IO + Memory-mapped I/O base address for the primary synthesizer on + MultiSound Pinnacle and Fiji sound cards. + +MSND Pinnacle MPU I/O +CONFIG_MSNDPIN_MPU_IO + Memory-mapped I/O base address for the Kurzweil daughterboard + synthesizer on MultiSound Pinnacle and Fiji sound cards. + +MSND Pinnacle MPU IRQ +CONFIG_MSNDPIN_MPU_IRQ + Iinterrupt request number for the Kurzweil daughterboard + synthesizer on MultiSound Pinnacle and Fiji sound cards. + +MSND Pinnacle IRQ +CONFIG_MSNDPIN_IRQ + Interrupt request line for the primary synthesizer on MultiSound + Pinnacle and Fiji sound cards. + +MSND Pinnacle joystick I/O +CONFIG_MSNDPIN_JOYSTICK_IO + Memory-mapped I/O base address for the joystick port on MultiSound + Pinnacle and Fiji sound cards. + +MSND Pinnacle memory +CONFIG_MSNDPIN_MEM + Memory-mapped I/O base address for the primary synthesizer on + MultiSound Pinnacle and Fiji sound cards. Full pathname of PNDSPINI.BIN firmware file CONFIG_MSNDPIN_INIT_FILE - The MultiSound cards have two firmware files which are required for - operation, and are not currently included. These files can be - obtained from Turtle Beach. See Documentation/sound/MultiSound for - information on how to obtain this. + The MultiSound cards have two firmware files which are required + for operation, and are not currently included. These files can be + obtained from Turtle Beach. See + for information on how to + obtain this. Full pathname of PNDSPERM.BIN firmware file CONFIG_MSNDPIN_PERM_FILE The MultiSound cards have two firmware files which are required for operation, and are not currently included. These files can be - obtained from Turtle Beach. See Documentation/sound/MultiSound for - information on how to obtain this. + obtained from Turtle Beach. See + for information on how to + obtain this. -MSND Pinnacle have S/PDIF I/O +MSND Pinnacle has S/PDIF I/O CONFIG_MSNDPIN_DIGITAL If you have the S/PDIF daughter board for the Pinnacle or Fiji, answer Y here; otherwise, say N. If you have this, you will be able to play and record from the S/PDIF port (digital signal). See - Documentation/sound/MultiSound for information on how to make use of - this capability. + for information on how to make + use of this capability. MSND Pinnacle non-PnP Mode CONFIG_MSNDPIN_NONPNP @@ -15690,15 +18030,14 @@ and Pinnacle). Larger values reduce the chance of data overruns at the expense of overall latency. If unsure, use the default. -FM synthesizer (YM3812/OPL-3) support +Yamaha FM synthesizer (YM3812/OPL-3) support CONFIG_SOUND_YM3812 Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). Answering Y is usually a safe and recommended choice, however some cards may have software (TSR) FM emulation. Enabling FM support with these cards may cause trouble (I don't currently know of any such - cards, however). - Please read the file Documentation/sound/OPL3 if your card has an - OPL3 chip. + cards, however). Please read the file + if your card has an OPL3 chip. If you compile the driver into the kernel, you have to add "opl3=" to the kernel command line. @@ -15708,27 +18047,27 @@ ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20 radio) CONFIG_SOUND_ACI_MIXER ACI (Audio Command Interface) is a protocol used to communicate with - the microcontroller on some sound cards produced by miro and Cardinal - Technologies. The main function of the ACI is to control the mixer - and to get a product identification. + the microcontroller on some sound cards produced by miro and + Cardinal Technologies. The main function of the ACI is to control + the mixer and to get a product identification. - This Voxware ACI driver currently supports the ACI functions on the + This VoxWare ACI driver currently supports the ACI functions on the miroSOUND PCM1-pro, PCM12 and PCM20 radio. On the PCM20 radio, ACI also controls the radio tuner. This is supported in the video4linux - miropcm20 driver (say M or Y here and go back to "Multimedia devices" - -> "Radio Adapters"). + miropcm20 driver (say M or Y here and go back to "Multimedia + devices" -> "Radio Adapters"). This driver is also available as a module and will be called aci.o. SB32/AWE support CONFIG_SOUND_AWE32_SYNTH Say Y here if you have a Sound Blaster SB32, AWE32-PnP, SB AWE64 or - similar sound card. See Documentation/sound/README.awe, - Documentation/sound/AWE32 and the Soundblaster-AWE mini-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto for more - info. + similar sound card. See , + and the Soundblaster-AWE + mini-HOWTO, available from + for more info. -Gallant's Audio Excel DSP 16 support (SC-6000 and SC-6600) +Gallant Audio Cards (SC-6000 and SC-6600 based) CONFIG_SOUND_AEDSP16 Answer Y if you have a Gallant's Audio Excel DSP 16 card. This driver supports Audio Excel DSP 16 but not the III nor PnP versions @@ -15742,10 +18081,10 @@ accordingly. You should say Y to one and only one of these two questions. - Read the drivers/sound/lowlevel/README.aedsp16 file and the head of - drivers/sound/lowlevel/aedsp16.c as well as - Documentation/sound/AudioExcelDSP16 to get more information about - this driver and its configuration. + Read the file and the head of + as well as + to get more information + about this driver and its configuration. Audio Excel DSP 16 (SBPro emulation) CONFIG_AEDSP16_SBPRO @@ -15775,17 +18114,22 @@ Say Y here in order to use the joystick interface of the Audio Excel DSP 16 card. -SC-6600 CDROM Interface -CONFIG_SC6600_CDROM - This is used to activate the CDROM interface of the Audio Excel +SC-6600 CD-ROM Interface +CONFIG_SC6600_CDROM (4=None, 3=IDE, 1=Panasonic, 0=Sony) + This is used to activate the CD-ROM interface of the Audio Excel DSP 16 card. Enter: 0 for Sony, 1 for Panasonic, 2 for IDE, 4 for no - CDROM present. + CD-ROM present. + +SC-6600 CD-ROM Interface I/O Address +CONFIG_SC6600_CDROMBASE + Base I/O port address for the CD-ROM interface of the Audio Excel + DSP 16 card. Audio Excel DSP 16 (MPU401 emulation) CONFIG_AEDSP16_MPU401 Answer Y if you want your audio card to emulate the MPU-401 midi interface. You should then also say Y to "MPU-401 support". - + Note that the I/O base for MPU-401 support of aedsp16 is the same you have selected for "MPU-401 support". If you are using this driver as a module you have to specify the MPU I/O base address with @@ -15794,46 +18138,72 @@ C-Media PCI (CMI8338/8378) CONFIG_SOUND_CMPCI Say Y or M if you have a PCI sound card using the CMI8338 - or the CMI8378 chip.set. + or the CMI8378 chipset. Data on these chips are available at + . -Creative EMU10K1 based PCI sound cards +Support CMI8738 based audio cards +CONFIG_SOUND_CMPCI_CM8738 + Say Y or M if you have a PCI sound card using the CMI8338 + or the CMI8378 chipset. Data on this chip is available at + . + +Enable joystick +CONFIG_SOUND_CMPCI_JOYSTICK + Say here in order to enable the joystick port on a sound crd using + the CMI8338 or the CMI8738 chipset. Data on these chips are + available at . + +Number of speakers (2, 4, 5, 6) +CONFIG_SOUND_CMPCI_SPEAKERS + Specify the number of speaker channels you want the card to drive, + as an integer. + +Enable S/PDIF loop for CMI8738 +CONFIG_SOUND_CMPCI_SPDIFLOOP + Enable loopback from SPDIF in to SPDIF out. For discussion, see + "The 8738 Audio SPDIF In/Out Technical Data" on the technical + support page at . + +Creative SBLive! (EMU10K1) based PCI sound cards CONFIG_SOUND_EMU10K1 Say Y or M if you have a PCI sound card using the EMU10K1 chipset, - such as the various Creative SBLive!, SB PCI512 or Emu-APS. + such as the Creative SBLive!, SB PCI512 or Emu-APS. + + For more information about the degree of support for the different + card models please check: - For more information on this driver and the degree of support for the - different card models please check . + It is now possible to load dsp microcode patches into the EMU10K1 - chip. These patches are used to implement real time sound processing - effects which include for example: signal routing, bass/treble - control, AC3 passthrough, ... + chip. These patches are used to implement real time sound + processing effects which include for example: signal routing, + bass/treble control, AC3 passthrough, ... Userspace tools to create new patches and load/unload them can be found at . - + Creative EMU10K1 MIDI CONFIG_MIDI_EMU10K1 - Say Y if you want to be able to use the OSS /dev/sequencer interface. - This code is still experimental. + Say Y if you want to be able to use the OSS /dev/sequencer + interface. This code is still experimental. Crystal SoundFusion (CS4280/461x) CONFIG_SOUND_FUSION - This module drives the Crystal SoundFusion devices (CS4280/46xx series) - when wired as native sound drivers with AC97 codecs. If this driver - does not work try the CS4232 driver. + This module drives the Crystal SoundFusion devices (CS4280/46xx + series) when wired as native sound drivers with AC97 codecs. If + this driver does not work try the CS4232 driver. -Ensoniq ES1370 based PCI sound cards +Ensoniq AudioPCI (ES1370) based PCI sound cards CONFIG_SOUND_ES1370 Say Y or M if you have a PCI sound card utilizing the Ensoniq ES1370 chipset, such as Ensoniq's AudioPCI (non-97). To find out if your sound card uses an ES1370 without removing your - computer's cover, use lspci -n and look for the PCI ID + computer's cover, use lspci -n and look for the PCI ID 1274:5000. Since Ensoniq was bought by Creative Labs, Sound Blaster 64/PCI models are either ES1370 or ES1371 based. This driver differs slightly from OSS/Free, so PLEASE READ - Documentation/sound/es1370. + . -Ensoniq ES1371 based PCI sound cards +Ensoniq AudioPCI 97 (ES1371) based sound cards CONFIG_SOUND_ES1371 Say Y or M if you have a PCI sound card utilizing the Ensoniq ES1371 chipset, such as Ensoniq's AudioPCI97. To find out if @@ -15841,25 +18211,26 @@ cover, use lspci -n and look for the PCI ID 1274:1371. Since Ensoniq was bought by Creative Labs, Sound Blaster 64/PCI models are either ES1370 or ES1371 based. This driver differs - slightly from OSS/Free, so PLEASE READ Documentation/sound/es1371. + slightly from OSS/Free, so PLEASE READ + . ESS Solo1 based PCI sound cards (eg. SC1938) CONFIG_SOUND_ESSSOLO1 Say Y or M if you have a PCI sound card utilizing the ESS Technology Solo1 chip. To find out if your sound card uses a Solo1 chip without removing your computer's cover, use - lspci -n and look for the PCI ID 125D:1969. This driver + lspci -n and look for the PCI ID 125D:1969. This driver differs slightly from OSS/Free, so PLEASE READ - Documentation/sound/solo1. + . S3 SonicVibes based PCI sound cards CONFIG_SOUND_SONICVIBES Say Y or M if you have a PCI sound card utilizing the S3 SonicVibes chipset. To find out if your sound card uses a SonicVibes chip without removing your computer's cover, use - lspci -n and look for the PCI ID 5333:CA00. This driver + lspci -n and look for the PCI ID 5333:CA00. This driver differs slightly from OSS/Free, so PLEASE READ - Documentation/sound/sonicvibes. + . Trident 4DWave DX/NX, SiS 7018 or ALi 5451 PCI Audio Core CONFIG_SOUND_TRIDENT @@ -15876,26 +18247,29 @@ 10B9:5451 stands for ALi5451. This driver supports S/PDIF in/out (record/playback) for ALi 5451 - embedded in ALi M1535+ and M1535D+. Note that they aren't all + embedded in ALi M1535+ and M1535D+. Note that they aren't all enabled by default; you can enable them by saying Y to "/proc file - system support" and "Sysctl support", and after the /proc file + system support" and "Sysctl support", and after the /proc file system has been mounted, executing the command command what is enabled - + echo 0>/proc/ALi5451 pcm out is also set to S/PDIF out. (Default). - + echo 1>/proc/ALi5451 use S/PDIF out to output pcm data. - - echo 2>/proc/ALi5451 use S/PDIF out to output non-pcm data.(AC3...). - echo 3>/proc/ALi5451 record from Ac97 in(MIC, Line in...). (Default). - - echo 4>/proc/ALi5451 no matter Ac97 settings, record from S/PDIF in. - - + echo 2>/proc/ALi5451 use S/PDIF out to output non-pcm data. + (AC3...). + + echo 3>/proc/ALi5451 record from Ac97 in(MIC, Line in...). + (Default). + + echo 4>/proc/ALi5451 no matter Ac97 settings, record from S/PDIF + in. + + This driver differs slightly from OSS/Free, so PLEASE READ the - comments at the top of driver/sound/trident.c + comments at the top of . Rockwell WaveArtist CONFIG_SOUND_WAVEARTIST @@ -15913,10 +18287,10 @@ VIA 82C686 MIDI CONFIG_MIDI_VIA82CXXX - Answer Y to use the MIDI interface of the Via686. You may need to - enable this in the BIOS before it will work. This is for connection - to external MIDI hardware, and is not required for software playback - of MIDI files. + Answer Y to use the MIDI interface of the Via686. You may need to + enable this in the BIOS before it will work. This is for connection + to external MIDI hardware, and is not required for software playback + of MIDI files. NeoMagic 256AV/256ZX sound chipsets CONFIG_SOUND_NM256 @@ -15926,13 +18300,76 @@ laptops. It includes support for an AC97-compatible mixer and an apparently proprietary sound engine. - See Documentation/sound/NM256 for further information. + See for further information. -ESS Maestro sound chipsets +ESS Maestro, Maestro2, Maestro2E driver CONFIG_SOUND_MAESTRO Say Y or M if you have a sound system driven by ESS's Maestro line of PCI sound chips. These include the Maestro 1, Maestro 2, and - Maestro 2E. See Documentation/sound/Maestro for more details. + Maestro 2E. See for more + details. + +ESS Maestro3/Allegro driver +CONFIG_SOUND_MAESTRO3 + Say Y or M if you have a sound system driven by ESS's Maestro 3 + PCI sound chip. + +Adlib Cards +CONFIG_SOUND_ADLIB + Includes ASB 64 4D. Information on programming AdLib cards is + available at . + +Crystal Sound CS4281 +CONFIG_SOUND_CS4281 + Picture and feature list at + . + +16 bit sampling option of GUS (_NOT_ GUS MAX) +CONFIG_SOUND_GUS16 + Support for Gravis Ulstrasound (GUS) cards (other than the GUS), + sampling at 16-bit width. + +GUS MAX support +CONFIG_SOUND_GUSMAX + Support for Gravis Ulstrasound MAX. + +Intel ICH audio support +CONFIG_SOUND_ICH + Support for integral audio in Intel's I/O Controller Hub (ICH) + chipset, as used on the 810/820/840 motherboards. + +Verbose initialization +CONFIG_SOUND_TRACEINIT + Verbose soundcard initialization -- affects the format of autoprobe + and initialization messages at boot time. + +TV card (bt848) mixer support +CONFIG_SOUND_TVMIXER + Support for audio mixer facilities on the BT848 TV frame-grabber + card. + +VIDC 16-bit sound +CONFIG_SOUND_VIDC + 16-bit support for the VIDC onboard sound hardware found on Acorn + machines. + +Loopback MIDI device support +CONFIG_SOUND_VMIDI + Support for MIDI loopback on port 1 or 2. + +Yamaha YMF7xx PCI audio (native mode) +CONFIG_SOUND_YMFPCI + Support for Yamaha cards including the YMF711, YMF715, YMF718, + YMF719, YMF724, Waveforce 192XG, and Waveforce 192 Digital. + +Yamaha PCI legacy ports support +CONFIG_SOUND_YMFPCI_LEGACY + Support for YMF7xx PCI cards emulating an MP401. + +RME Hammerfall (RME96XX) support +CONFIG_SOUND_RME96XX + Say Y or M if you have a Hammerfall, Hammerfall light or Hammerfall + DSP card from RME. Are you using a crosscompiler CONFIG_CROSSCOMPILE @@ -15951,6 +18388,73 @@ only useful for people working on the floating point exception handler. If you don't, say N. +Galileo EV64120 Evaluation board +CONFIG_MIPS_EV64120 + This is an evaluation board based on the Galileo GT-64120 + single-chip system controller that contains a MIPS R5000 compatible + core running at 75/100MHz. Their website is located at + . Say Y here if you wish to build a + kernel for this platform. + +Galileo EV96100 Evaluation board +CONFIG_MIPS_EV96100 + This is an evaluation board based on the Galielo GT-96100 LAN/WAN + communications controllers containing a MIPS R5000 compatible core + running at 83MHz. Their website is . Say Y + here if you wish to build a kernel for this platform. + +Support for ITE 8172G board +CONFIG_MIPS_ITE8172 + Ths is an evaluation board made by ITE (http://www.ite.com.tw/) + with ATX form factor that utilizes a MIPS R5000 to work with its + ITE8172G companion internet appliance chip. The MIPS core can be + either a NEC Vr5432 or QED RM5231. Say Y here if you wish to build + a kernel for this platform. + +Support for Globespan IVR board +CONFIG_MIPS_IVR + This is an evaluation board built by Globespan to showcase thir + iVR (Internet Video Recorder) design. It utilizes a QED RM5231 + R5000 MIPS core. More information can be found out their website + located at P. Say Y + here if you wish to build a kernel for this platform. + +Support for Alchemy Semi PB1000 board +CONFIG_MIPS_PB1000 + This is an evaluation board built by Alchemy Semiconducttor to + showcase their Au1000 Internet Edge Processor. It is SOC design + containing a MIPS32 core running at 266/400/500MHz with many + integrated peripherals. Further information can be found at their + website, . Say Y here if you wish to + build a kernel for this platform. + +Support for Philips Nino +CONFIG_NINO + Say Y here to select a kernel for the Philips Nino Palm PC. The + website at + will have more information. + +Model-500/510 +CONFIG_NINO_16MB + Say Y here to build a kernel specifically for Nino 500/501 color + Palm PCs from Philips (INCOMPLETE). + +Model-200/210/312/320/325/350/390 +CONFIG_NINO_8MB + Say Y here to build a kernel specifically for Nino Palm PCs with + 8MB of memory. These include models 200/210/312/320/325/350/390. + +Model-300/301/302/319 +CONFIG_NINO_4MB + Say Y here to build a kernel specifically for Nino Palm PCs with + 4MB of memory. These include models 300/301/302/319. + +Low-level debugging +CONFIG_LL_DEBUG + Enable low-level debugging assertion macros in the kernel code. + Currently used only by the time services code in the MIPS port. + Don't turn this on unless you know what you are doing. + Remote GDB kernel debugging CONFIG_REMOTE_DEBUG If you say Y here, it will be possible to remotely debug the MIPS @@ -15976,34 +18480,34 @@ by pressing various keys while holding SysRq (Alt+PrintScreen). It also works on a serial console (on PC hardware at least), if you send a BREAK and then within 5 seconds a command keypress. The - keys are documented in Documentation/sysrq.txt. Don't say Y unless - you really know what this hack does. + keys are documented in . Don't say Y + unless you really know what this hack does. -ISDN subsystem +ISDN support CONFIG_ISDN ISDN ("Integrated Services Digital Networks", called RNIS in France) is a special type of fully digital telephone service; it's mostly used to connect to your Internet service provider (with SLIP or - PPP). The main advantage is that the speed is higher than ordinary + PPP). The main advantage is that the speed is higher than ordinary modem/telephone connections, and that you can have voice - conversations while downloading stuff. It only works if your + conversations while downloading stuff. It only works if your computer is equipped with an ISDN card and both you and your service - provider purchased an ISDN line from the phone company. For details, - read http://alumni.caltech.edu/~dank/isdn/ on the WWW. + provider purchased an ISDN line from the phone company. For + details, read on the WWW. This driver allows you to use an ISDN-card for networking - connections and as dialin/out device. The isdn-tty's have a built in - AT-compatible modem emulator. Network devices support autodial, + connections and as dialin/out device. The isdn-tty's have a built + in AT-compatible modem emulator. Network devices support autodial, channel-bundling, callback and caller-authentication without having - a daemon running. A reduced T.70 protocol is supported with tty's - suitable for German BTX. On D-Channel, the protocols EDSS1 - (Euro-ISDN) and 1TR6 (German style) are supported. See - Documentation/isdn/README for more information. + a daemon running. A reduced T.70 protocol is supported with tty's + suitable for German BTX. On D-Channel, the protocols EDSS1 + (Euro-ISDN) and 1TR6 (German style) are supported. See + for more information. If you want to compile the ISDN code as a module ( = code which can be inserted in and removed from the running kernel whenever you - want), say M here and read Documentation/modules.txt. The module - will be called isdn.o. If unsure, say N. + want), say M here and read . The + module will be called isdn.o. If unsure, say N. Support synchronous PPP CONFIG_ISDN_PPP @@ -16014,20 +18518,21 @@ protocol is used by Cisco and Sun for example. So you want to say Y here if the other end of your ISDN connection supports it. You will need a special version of pppd (called ipppd) for using this - feature. See Documentation/isdn/README.syncppp and - Documentation/isdn/syncPPP.FAQ for more information. + feature. See and + for more information. Support generic MP (RFC 1717) CONFIG_ISDN_MPP With synchronous PPP enabled, it is possible to increase throughput by bundling several ISDN-connections, using this protocol. See - Documentation/isdn/README.syncppp for more information. + for more information. Use VJ-compression with synchronous PPP CONFIG_ISDN_PPP_VJ This enables Van Jacobson header compression for synchronous PPP. Say Y if the other end of the connection supports it. +Support BSD compression CONFIG_ISDN_PPP_BSDCOMP Support for the BSD-Compress compression method for PPP, which uses the LZW compression method to compress each PPP packet before it is @@ -16045,19 +18550,19 @@ your Linux box as an ISDN-answering machine. Of course, this must be supported by the lowlevel driver also. Currently, the HiSax driver is the only voice-supporting driver. See - Documentation/isdn/README.audio for more information. + for more information. X.25 PLP on top of ISDN CONFIG_ISDN_X25 - This feature provides the X.25 protocol over ISDN connections. - See Documentation/isdn/README.x25 for more information + This feature provides the X.25 protocol over ISDN connections. + See for more information if you are thinking about using this. ISDN diversion services support CONFIG_ISDN_DIVERSION This option allows you to use some supplementary diversion services in conjunction with the HiSax driver on an EURO/DSS1 - line. + line. Supported options are CD (call deflection), CFU (Call forward unconditional), CFB (Call forward when busy) and CFNR (call forward @@ -16068,22 +18573,23 @@ countries. The keypad protocol is still not implemented. CD should work in all countries if the service has been subscribed to. - Please read the file Documentation/isdn/README.diversion. + Please read the file . ICN 2B and 4B support CONFIG_ISDN_DRV_ICN This enables support for two kinds of ISDN-cards made by a German - company called ICN. 2B is the standard version for a single ISDN - line with two B-channels, 4B supports two ISDN lines. For running + company called ICN. 2B is the standard version for a single ISDN + line with two B-channels, 4B supports two ISDN lines. For running this card, additional firmware is necessary, which has to be downloaded into the card using a utility which is distributed - separately. See Documentation/isdn/README and README.icn for more - information. + separately. See and + for more + information. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called icn.o. + say M here and read . The module + will be called icn.o. isdnloop support CONFIG_ISDN_DRV_LOOP @@ -16098,19 +18604,19 @@ This is a driver supporting the Siemens chipset on various ISDN-cards (like AVM A1, Elsa ISDN cards, Teles S0-16.0, Teles S0-16.3, Teles S0-8, Teles/Creatix PnP, ITK micro ix1 and many - compatibles). + compatibles). HiSax is just the name of this driver, not the name of any hardware. - + If you have a card with such a chipset, you should say Y here and also to the configuration option of the driver for your particular card, below. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called hisax.o. See Documentation/isdn/README.HiSax for more - information on using this driver. + say M here and read . The module + will be called hisax.o. See + for more information on using this driver. HiSax Support for EURO/DSS1 CONFIG_HISAX_EURO @@ -16118,9 +18624,9 @@ telephone service company provides. The call control protocol E-DSS1 is used in most European countries. - If unsure, say yes. + If unsure, say Y. -Support for german charge info +Support for German chargeinfo CONFIG_DE_AOC If you want that the HiSax hardware driver sends messages to the upper level of the isdn code on each AOCD (Advice Of Charge, During @@ -16155,230 +18661,265 @@ HiSax Support for US NI1 CONFIG_HISAX_NI1 - Enable this if you like to use ISDN in US on a NI1 basic rate interface. + Enable this if you like to use ISDN in US on a NI1 basic rate + interface. Teles 16.0/8.0 CONFIG_HISAX_16_0 This enables HiSax support for the Teles ISDN-cards S0-16.0, S0-8 - and many compatibles. + and many compatibles. - See Documentation/isdn/README.HiSax on how to configure it using the - different cards, a different D-channel protocol, or non-standard - IRQ/port/shmem settings. + See on how to configure it + using the different cards, a different D-channel protocol, or + non-standard IRQ/port/shmem settings. Teles 16.3 or PNP or PCMCIA CONFIG_HISAX_16_3 This enables HiSax support for the Teles ISDN-cards S0-16.3 the Teles/Creatix PnP and the Teles PCMCIA. - - See Documentation/isdn/README.HiSax on how to configure it using the - different cards, a different D-channel protocol, or non-standard - IRQ/port settings. + + See on how to configure it + using the different cards, a different D-channel protocol, or + non-standard IRQ/port settings. Teles PCI CONFIG_HISAX_TELESPCI This enables HiSax support for the Teles PCI. - See Documentation/isdn/README.HiSax on how to configure it. - + See on how to configure it. + Teles S0Box CONFIG_HISAX_S0BOX This enables HiSax support for the Teles/Creatix parallel port - S0BOX. See Documentation/isdn/README.HiSax on how to configure it. + S0BOX. See on how to + configure it. AVM A1 (Fritz) CONFIG_HISAX_AVM_A1 This enables HiSax support for the AVM A1 (aka "Fritz"). - - See Documentation/isdn/README.HiSax on how to configure it using the - different cards, a different D-channel protocol, or non-standard - IRQ/port settings. -AVM PnP/PCI (Fritz!PNP/PCI) + See on how to configure it + using the different cards, a different D-channel protocol, or + non-standard IRQ/port settings. + +AVM PnP/PCI (Fritz!PnP/PCI) CONFIG_HISAX_FRITZPCI This enables HiSax support for the AVM "Fritz!PnP" and "Fritz!PCI". - See Documentation/isdn/README.HiSax on how to configure it. + See on how to configure it. AVM A1 PCMCIA (Fritz) CONFIG_HISAX_AVM_A1_PCMCIA This enables HiSax support for the AVM A1 "Fritz!PCMCIA"). - See Documentation/isdn/README.HiSax on how to configure it. + See on how to configure it. Elsa cards CONFIG_HISAX_ELSA This enables HiSax support for the Elsa Mircolink ISA cards, for the Elsa Quickstep series cards and Elsa PCMCIA. - See Documentation/isdn/README.HiSax on how to configure it using the - different cards, a different D-channel protocol, or non-standard - IRQ/port settings. + See on how to configure it + using the different cards, a different D-channel protocol, or + non-standard IRQ/port settings. ITK ix1-micro Revision 2 CONFIG_HISAX_IX1MICROR2 This enables HiSax support for the ITK ix1-micro Revision 2 card. - See Documentation/isdn/README.HiSax on how to configure it using the - different cards, a different D-channel protocol, or non-standard - IRQ/port settings. + See on how to configure it + using the different cards, a different D-channel protocol, or + non-standard IRQ/port settings. Eicon.Diehl Diva cards CONFIG_HISAX_DIEHLDIVA This enables HiSax support for the Eicon.Diehl Diva none PRO versions passive ISDN cards. - See Documentation/isdn/README.HiSax on how to configure it using the - different cards, a different D-channel protocol, or non-standard - IRQ/port settings. + See on how to configure it + using the different cards, a different D-channel protocol, or + non-standard IRQ/port settings. ASUSCOM ISA cards CONFIG_HISAX_ASUSCOM This enables HiSax support for the AsusCom and their OEM versions passive ISDN ISA cards. - See Documentation/isdn/README.HiSax on how to configure it using the - different cards, a different D-channel protocol, or non-standard - IRQ/port settings. + See on how to configure it + using the different cards, a different D-channel protocol, or + non-standard IRQ/port settings. TELEINT cards CONFIG_HISAX_TELEINT This enables HiSax support for the TELEINT SA1 semiactiv ISDN card. - See Documentation/isdn/README.HiSax on how to configure it using the - different cards, a different D-channel protocol, or non-standard - IRQ/port settings. + See on how to configure it + using the different cards, a different D-channel protocol, or + non-standard IRQ/port settings. HFC-S based cards CONFIG_HISAX_HFCS This enables HiSax support for the HFC-S 2BDS0 based cards, like teles 16.3c. - - See Documentation/isdn/README.HiSax on how to configure it using the - different cards, a different D-channel protocol, or non-standard - IRQ/port settings. + + See on how to configure it + using the different cards, a different D-channel protocol, or + non-standard IRQ/port settings. Sedlbauer cards CONFIG_HISAX_SEDLBAUER This enables HiSax support for the Sedlbauer passive ISDN cards. - See Documentation/isdn/README.HiSax on how to configure it using the - different cards, a different D-channel protocol, or non-standard - IRQ/port settings. + See on how to configure it + using the different cards, a different D-channel protocol, or + non-standard IRQ/port settings. USR Sportster internal TA CONFIG_HISAX_SPORTSTER This enables HiSax support for the USR Sportster internal TA card. - See Documentation/isdn/README.HiSax on how to configure it using a - different D-channel protocol, or non-standard IRQ/port settings. + See on how to configure it + using a different D-channel protocol, or non-standard IRQ/port + settings. MIC card CONFIG_HISAX_MIC - This enables HiSax support for the ITH MIC card. + This enables HiSax support for the ITH MIC card. - See Documentation/isdn/README.HiSax on how to configure it using a - different D-channel protocol, or non-standard IRQ/port settings. + See on how to configure it + using a different D-channel protocol, or non-standard IRQ/port + settings. NETjet card CONFIG_HISAX_NETJET This enables HiSax support for the NetJet from Traverse Technologies. - See Documentation/isdn/README.HiSax on how to configure it using a - different D-channel protocol, or non-standard IRQ/port settings. + See on how to configure it + using a different D-channel protocol, or non-standard IRQ/port + settings. NETspider U card CONFIG_HISAX_NETJET_U - This enables HiSax support for the Netspider U interface ISDN card from - Traverse Technologies. - See Documentation/isdn/README.HiSax on how to configure it using a - different D-channel protocol, or non-standard IRQ/port settings. + This enables HiSax support for the Netspider U interface ISDN card + from Traverse Technologies. + See on how to configure it + using a different D-channel protocol, or non-standard IRQ/port + settings. Niccy PnP/PCI card CONFIG_HISAX_NICCY - This enables HiSax support for the Dr. Neuhaus Niccy PnP or PCI. + This enables HiSax support for the Dr. Neuhaus Niccy PnP or PCI. - See Documentation/isdn/README.HiSax on how to configure it using a - different D-channel protocol, or non-standard IRQ/port settings. + See on how to configure it + using a different D-channel protocol, or non-standard IRQ/port + settings. Siemens I-Surf card CONFIG_HISAX_ISURF This enables HiSax support for the Siemens I-Talk/I-Surf card with ISAR chip. - See Documentation/isdn/README.HiSax on how to configure it using a - different D-channel protocol, or non-standard IRQ/port settings. + See on how to configure it + using a different D-channel protocol, or non-standard IRQ/port + settings. HST Saphir card CONFIG_HISAX_HSTSAPHIR This enables HiSax support for the HST Saphir card. - - See Documentation/isdn/README.HiSax on how to configure it using a - different D-channel protocol, or non-standard IRQ/port settings. + + See on how to configure it + using a different D-channel protocol, or non-standard IRQ/port + settings. Telekom A4T card CONFIG_HISAX_BKM_A4T This enables HiSax support for the Telekom A4T card. - - See Documentation/isdn/README.HiSax on how to configure it using a - different D-channel protocol, or non-standard IRQ/port settings. + + See on how to configure it + using a different D-channel protocol, or non-standard IRQ/port + settings. Scitel Quadro card CONFIG_HISAX_SCT_QUADRO This enables HiSax support for the Scitel Quadro card. - - See Documentation/isdn/README.HiSax on how to configure it using a - different D-channel protocol, or non-standard IRQ/port settings. + + See on how to configure it + using a different D-channel protocol, or non-standard IRQ/port + settings. Gazel cards CONFIG_HISAX_GAZEL This enables HiSax support for the Gazel cards. - See Documentation/isdn/README.HiSax on how to configure it using a - different D-channel protocol, or non-standard IRQ/port settings. + See on how to configure it + using a different D-channel protocol, or non-standard IRQ/port + settings. HFC PCI-Bus cards CONFIG_HISAX_HFC_PCI This enables HiSax support for the HFC-S PCI 2BDS0 based cards. - - For more informations see under Documentation/isdn/README.hfc-pci. + + For more informations see under + . Winbond W6692 based cards CONFIG_HISAX_W6692 This enables HiSax support for Winbond W6692 based PCI ISDN cards. - - See Documentation/isdn/README.HiSax on how to configure it using a - different D-channel protocol, or non-standard IRQ/port settings. -HFC-S+, HFC-SP, HFC-PCMCIA cards (EXPERIMENTAL) + See on how to configure it + using a different D-channel protocol, or non-standard IRQ/port + settings. + +HFC-S+, HFC-SP, HFC-PCMCIA cards CONFIG_HISAX_HFC_SX This enables HiSax support for the HFC-S+, HFC-SP and HFC-PCMCIA cards. This code is not finished yet. -Am7930 (EXPERIMENTAL) +Am7930 CONFIG_HISAX_AMD7930 This enables HiSax support for the AMD7930 chips on some SPARCs. This code is not finished yet. +HiSax debugging +CONFIG_HISAX_DEBUG + This enables debugging code in the new-style HiSax drivers, i.e. + the ST5481 USB driver currently. + If in doubt, say yes. + +ELSA PCMCIA MicroLink cards +CONFIG_HISAX_ELSA_CS + This enables the PCMCIA client driver for the Elsa PCMCIA MicroLink + card. + +Sedlbauer PCMCIA cards +CONFIG_HISAX_SEDLBAUER_CS + This enables the PCMCIA client driver for the Sedlbauer Speed Star + and Speed Star II cards. + +ST5481 USB ISDN adapter +CONFIG_HISAX_ST5481 + This enables the driver for ST5481 based USB ISDN adapters, + e.g. the BeWan Gazel 128 USB + PCBIT-D support CONFIG_ISDN_DRV_PCBIT - This enables support for the PCBIT ISDN-card. This card is - manufactured in Portugal by Octal. For running this card, additional - firmware is necessary, which has to be downloaded into the card - using a utility which is distributed separately. See - Documentation/isdn/README and Documentation/isdn/README.pcbit for - more information. + This enables support for the PCBIT ISDN-card. This card is + manufactured in Portugal by Octal. For running this card, + additional firmware is necessary, which has to be downloaded into + the card using a utility which is distributed separately. See + and + for more information. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called pcbit.o. + say M here and read . The module + will be called pcbit.o. -Spellcaster support (EXPERIMENTAL) +Spellcaster support CONFIG_ISDN_DRV_SC - This enables support for the Spellcaster BRI ISDN boards. This + This enables support for the Spellcaster BRI ISDN boards. This driver currently builds only in a modularized version ( = code which can be inserted in and removed from the running kernel whenever you - want, details in Documentation/modules.txt); the module will be - called sc.o. See Documentation/isdn/README.sc and - http://www.spellcast.com for more information. + want, details in ); the module will + be called sc.o. See and + for more information. Eicon active card support CONFIG_ISDN_DRV_EICON @@ -16386,23 +18927,29 @@ this card, additional firmware is necessary, which has to be loaded into the card using the eiconctrl utility which is part of the latest isdn4k-utils package. Please read the file - Documentation/isdn/README.eicon for more information. - -Eicon Diva Server card support + for more information. + +Legacy Eicon driver +CONFIG_ISDN_DRV_EICON_OLD + Say Y here to use your Eicon active ISDN card with ISDN4Linux + isdn module. + +Eicon PCI DIVA Server BRI/PRI/4BRI support CONFIG_ISDN_DRV_EICON_PCI - Say Y here if you have an Eicon Diva Server (BRI/PRI/4BRI) ISDN card. - Please read Documentation/isdn/README.eicon for more information. - -Eicon old-type card support + Say Y here if you have an Eicon Diva Server (BRI/PRI/4BRI) ISDN + card. Please read for more + information. + +Eicon old-type (S,SX,SCOM,Quadro,S2M) card support CONFIG_ISDN_DRV_EICON_ISA Say Y here if you have an old-type Eicon active ISDN card. In order to use this card, additional firmware is necessary, which has to be loaded into the card using the eiconctrl utility which is part of the latest isdn4k-utils package. Please read the file - Documentation/isdn/README.eicon for more information. + for more information. Eicon driver type standalone -CONFIG_ISDN_DRV_EICON_STANDALONE +CONFIG_ISDN_DRV_EICON_DIVAS Enable this option if you want the eicon driver as standalone version with no interface to the ISDN4Linux isdn module. If you say Y here, the eicon module only supports the Diva Server PCI @@ -16415,20 +18962,49 @@ Fax Class 1 and 2 commands. Using a getty with fax-support (mgetty+sendfax, hylafax), you will be able to use your Linux box as an ISDN-fax-machine. This must be supported by the lowlevel driver - also. See Documentation/isdn/README.fax for more information. + also. See for more information. CAPI2.0 support CONFIG_ISDN_CAPI This provides the CAPI (Common ISDN Application Programming Interface, a standard making it easy for programs to access ISDN - hardware, see http://www.capi.org/ . This is needed for AVM's set of - active ISDN controllers like B1, T1, M1. + hardware, see . This is needed for AVM's set + of active ISDN controllers like B1, T1, M1. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The modules will be called capi.o and kernelcapi.o. If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . + +CAPI2.0 /dev/capi20 support +CONFIG_ISDN_CAPI_CAPI20 + This option will provide the CAPI 2.0 interface to userspace + applications via /dev/capi20. Applications should use the + standardized libcapi20 to access this functionality. You should say + Y/M here. + +CAPI2.0 Middleware support +CONFIG_ISDN_CAPI_MIDDLEWARE + This option will enhance the capabilities of the /dev/capi20 + interface. It will provide a means of moving a data connection, + established via the usual /dev/capi20 interface to a special tty + device. If you want to use pppd with pppdcapiplugin to dial up to + your ISP, say Y here. + +CAPI2.0 filesystem support +CONFIG_ISDN_CAPI_CAPIFS + This option provides a special file system, similar to /dev/pts with + device nodes for the special ttys established by using the + middleware extension above. If you want to use pppd with + pppdcapiplugin to dial up to your ISP, say Y here. + +CAPI2.0 capidrv interface support +CONFIG_ISDN_CAPI_CAPIDRV + This option provides the glue code to hook up CAPI driven cards to + the legacy isdn4linux link layer. If you have a card which is + supported by a CAPI driver, but still want to use old features like + ippp interfaces or ttyI emulation, say Y/M here. AVM B1 ISA support CONFIG_ISDN_DRV_AVMB1_B1ISA @@ -16451,6 +19027,11 @@ CONFIG_ISDN_DRV_AVMB1_B1PCMCIA Enable support for the PCMCIA version of the AVM B1 card. +AVM B1/M1/M2 PCMCIA cs module +CONFIG_ISDN_DRV_AVMB1_AVM_CS + Enable the PCMCIA client driver for the AVM B1/M1/M2 + PCMCIA cards. + AVM T1/T1-B PCI support CONFIG_ISDN_DRV_AVMB1_T1PCI Enable support for the AVM T1 T1B card. @@ -16467,40 +19048,40 @@ disconnecting. This will increase the size of the kernel by 7 KB. If unsure, say Y. -IBM Active 2000 support (EXPERIMENTAL) +IBM Active 2000 support CONFIG_ISDN_DRV_ACT2000 Say Y here if you have an IBM Active 2000 ISDN card. In order to use this card, additional firmware is necessary, which has to be loaded into the card using a utility which is part of the latest isdn4k-utils package. Please read the file - Documentation/isdn/README.act2000 for more information. + for more information. Auvertech TurboPAM support CONFIG_ISDN_DRV_TPAM This enables support for the Auvertech TurboPAM ISDN-card. For running this card, additional firmware is necessary, which has to be downloaded into the card using a utility which is distributed - separately from the Auvertech's web site: http://www.auvertech.fr. + separately from the Auvertech's web site: . Please redirect all support questions to support@auvertech.fr. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called tpam.o. + say M here and read . The module + will be called tpam.o. Hypercope HYSDN cards (Champ, Ergo, Metro) support (module) CONFIG_HYSDN Say Y here if you have one of Hypercope's active PCI ISDN cards Champ, Ergo and Metro. You will then get a module called hysdn.o. - Please read the file Documentation/isdn/README.hysdn for more + Please read the file for more information. HYSDN CAPI 2.0 support CONFIG_HYSDN_CAPI - Say Y here if you like to use Hypercope's CAPI 2.0 interface + Say Y here if you like to use Hypercope's CAPI 2.0 interface. -Support for Sun4 architecture +Support for SUN4 machines (disables SUN4[CDM] support) CONFIG_SUN4 Say Y here if, and only if, your machine is a Sun4. Note that a kernel compiled with this option will run only on Sun4. @@ -16514,7 +19095,7 @@ This support is also available as a module called esp.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . PTI Qlogic, ISP Driver CONFIG_SCSI_QLOGICPTI @@ -16526,17 +19107,22 @@ This support is also available as a module called qlogicpti.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . + +Sun PROM console +CONFIG_PROM_CONSOLE + Say Y to build a console driver for Sun machines that uses the + terminal emulation built into their console PROMS. -SPARC /dev/openprom compatibility driver (EXPERIMENTAL) +/dev/openprom device support CONFIG_SUN_OPENPROMIO This driver provides user programs with an interface to the SPARC PROM device tree. The driver implements a SunOS-compatible - interface and a NetBSD-compatible interface. + interface and a NetBSD-compatible interface. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M and read Documentation/modules.txt. If unsure, say Y. + say M and read . If unsure, say Y. Openprom tree appears in /proc/openprom CONFIG_SUN_OPENPROMFS @@ -16546,8 +19132,9 @@ If you want to compile the /proc/openprom support as a module ( = code which can be inserted in and removed from the running kernel - whenever you want), say M here and read Documentation/modules.txt. - The module will be called openpromfs.o. If unsure, say M. + whenever you want), say M here and read + . + The module will be called openpromfs.o. If unsure, say M. Kernel support for Linux/Sparc 32bit binary compatibility CONFIG_SPARC32_COMPAT @@ -16567,11 +19154,11 @@ SunOS binary emulation CONFIG_SUNOS_EMUL - This allows you to run most SunOS binaries. If you want to do this, + This allows you to run most SunOS binaries. If you want to do this, say Y here and place appropriate files in /usr/gnemul/sunos. See - http://www.ultralinux.org/faq.html for more information. If you want - to run SunOS binaries on an Ultra you must also say Y to "Kernel - support for 32-bit a.out binaries" above. + for more information. If you + want to run SunOS binaries on an Ultra you must also say Y to + "Kernel support for 32-bit a.out binaries" above. Mostek real time clock support CONFIG_SUN_MOSTEK_RTC @@ -16596,17 +19183,37 @@ This driver supports the serial ports on newer (PCI) Ultra systems. Say Y if you want to be able to use your serial ports. -Aurora Multiboard 1600se (EXPERIMENTAL) +Videopix Frame Grabber +CONFIG_SUN_VIDEOPIX + Say Y here to support the Videopix Frame Grabber from Sun + Microsystems, commonly found on SPARCstations. This card, which is + based on the Phillips SAA9051, can handle NTSC and PAL/SECAM and + SVIDEO signals. + +Sun bidirectional parallel port support +CONFIG_SUN_BPP + Say Y here to support Sun's obsolete variant of IEEE1284 + bidirectional parallel port protocol as /dev/bppX. Can be built on + x86 machines. + +Aurora Multiboard 1600se CONFIG_SUN_AURORA The Aurora Multiboard is a multi-port high-speed serial controller. If you have one of these, say Y. -Audio support (EXPERIMENTAL) +Tadpole TS102 Microcontroller support +CONFIG_TADPOLE_TS102_UCTRL + Say Y here to directly support the TS102 Microcontroller interface + on the Tadpole Sparcbook 3. This device handles power-management + events, and can also notice the attachment/detachment of external + monitors and mice. + +Audio support CONFIG_SPARCAUDIO This driver provides support for the build-in sound devices on most Sun machines. If you want to be able to use this, select this option and one or more of the lowlevel drivers below. See - http://www.dementia.org/~shadow/sparcaudio.html for more + for more information. AMD7930 Lowlevel Driver @@ -16624,34 +19231,124 @@ This driver supports the DBRI audio interface found on the SS10, SS20, Sparcbook 3, and Voyager systems. -Dummy lowlevel Driver +Dummy Lowlevel Driver CONFIG_SPARCAUDIO_DUMMY This is a pseudo-driver used for debugging and testing the sparcaudio subsystem. Say N unless you want to work on this subsystem. -Sparc hardware (EXPERIMENTAL) +Sparc hardware CONFIG_PARPORT_SUNBPP This driver provides support for the bidirectional parallel port found on many Sun machines. Note that many of the newer Ultras actually have pc style hardware instead. +/proc/hardware support +CONFIG_PROC_HARDWARE + Say Y here to support the /proc/hardware file, which gives you + access to information about the machine you're running on, + including the model, CPU, MMU, clock speed, BogoMIPS rating, + and memory size. + +Bluetooth subsystem support +CONFIG_BLUEZ + Bluetooth is low-cost, low-power, short-range wireless technology. + It was designed as a replacement for cables and other short-range + technologies like IrDA. Bluetooth operates in personal area range + that typically extends up to 10 meters. More information about + Bluetooth can be found at . + + Linux Bluetooth subsystem consist of several layers: + HCI Core (device and connection manager, scheduler) + HCI Device drivers (interface to the hardware) + L2CAP Module (L2CAP protocol) + + Say Y here to enable Linux Bluetooth support and to build HCI Core + layer. + + To use Linux Bluetooth subsystem, you will need several user-space + utilities like hciconfig and hcid. These utilities and updates to + Bluetooth kernel modules are provided in the BlueZ package. + For more information, see . + + If you want to compile HCI Core as module (hci.o) say M here. + +L2CAP protocol support +CONFIG_BLUEZ_L2CAP + L2CAP (Logical Link Control and Adaptation Protocol) provides + connection oriented and connection-less data transport. L2CAP + support is required for most Bluetooth applications. + + Say Y here to compile L2CAP support into the kernel or say M to + compile it as module (l2cap.o). + +HCI UART driver +CONFIG_BLUEZ_HCIUART + Bluetooth HCI UART driver. + This driver is required if you want to use Bluetooth devices with + serial port interface. + + Say Y here to compile support for Bluetooth UART devices into the + kernel or say M to compile it as module (hci_uart.o). + +HCI USB driver +CONFIG_BLUEZ_HCIUSB + Bluetooth HCI USB driver. + This driver is required if you want to use Bluetooth devices with + USB interface. + + Say Y here to compile support for Bluetooth USB devices into the + kernel or say M to compile it as module (hci_usb.o). + +HCI VHCI virtual HCI device driver +CONFIG_BLUEZ_HCIVHCI + Bluetooth Virtual HCI device driver. + This driver is required if you want to use HCI Emulation software. + + Say Y here to compile support for virtual HCI devices into the + kernel or say M to compile it as module (hci_vhci.o). + # # m68k-specific kernel options -# Documented by Chris Lawrence et al. +# Documented by Chris Lawrence et al. # Amiga support CONFIG_AMIGA This option enables support for the Amiga series of computers. If you plan to use this kernel on an Amiga, say Y here and browse the - material available in Documentation/m68k; otherwise say N. + material available in ; otherwise say N. + +Commodore A2232 serial support +CONFIG_A2232 + This option supports the 2232 7-port serial card shipped with the + Amiga 2000 and other Zorro-bus machines, dating from 1989. At + a max of 19,200 bps, the ports are served by a 6551 ACIA UART chip + each, plus a 8520 CIA, and a master 6502 CPU and buffer as well. The + ports were connected with 8 pin DIN connectors on the card bracket, + for which 8 pin to DB25 adapters were supplied. The card also had + jumpers internally to toggle various pinning configurations. + + This driver can be built as a module; but then "generic_serial.o" + will also be built as a module. This has to be loaded before + "ser_a2232.o". If you want to do this, answer M here and read + "". + +A4000T SCSI support +CONFIG_A4000T_SCSI + Support for the NCR53C710 SCSI controller on the Amiga 4000T. + +A4091 SCSI support +CONFIG_A4091_SCSI + Support for the NCR53C710 chip on the Amiga 4091 Z3 SCSI2 controller + (1993). Very obscure -- the 4091 was part of an Amiga 4000 upgrade + plan at the time the Amiga business was sold to DKB. Atari support CONFIG_ATARI This option enables support for the 68000-based Atari series of computers (including the TT, Falcon and Medusa). If you plan to use this kernel on an Atari, say Y here and browse the material - available in Documentation/m68k; otherwise say N. + available in ; otherwise say N. Hades support CONFIG_HADES @@ -16663,7 +19360,7 @@ This option enables support for the Apple Macintosh series of computers (yes, there is experimental support now, at least for part of the series). - + Say N unless you're willing to code the remaining necessary support. ;) @@ -16674,14 +19371,51 @@ If you plan to try to use the kernel on such a machine say Y here. Everybody else says N. +Q40/Q60 support +CONFIG_Q40 + The Q40 is a Motorola 68040-based successor to the Sinclair QL + manufactured in Germany. There is an official Q40 home page at + . This option enables support for the Q40 and + Q60. Select your CPU below. For 68LC060 don't forget to enable FPU + emulation. + +Sun 3 support +CONFIG_SUN3 + This option enables support for the Sun 3 series of workstations. + Currently, only the Sun 3/80 is supported within the Sun 3x family. + You will also want to enable 68030 support. General Linux + information on the Sun 3x series (now discontinued) is at + . + + If you don't want to compile a kernel for a Sun 3, say N. + Sun 3X support CONFIG_SUN3X This option enables support for the Sun 3x series of workstations. Be warned that this support is very experimental. You will also want to say Y to 68020 support and N to the other processors below. + General Linux information on the Sun 3x series (now discontinued) + is at . If you don't want to compile a kernel for a Sun 3x, say N. +Sun3x builtin serial support +CONFIG_SUN3X_ZS + ZS refers to a type of asynchronous serial port built in to the Sun3 + and Sun3x workstations; if you have a Sun 3, you probably have + these. Say 'Y' to support ZS ports directly. This option must be + enabled in order to support the + keyboard and mouse ports. + +Sun keyboard support +CONFIG_SUN_KEYBOARD + Say Y here to support the keyboard found on Sun 3 and 3x + workstations. It can also be used support Sun Type-5 keyboards + through an adaptor. See + and + for details on the + latter. + 68020 support CONFIG_M68020 If you anticipate running this kernel on a computer with a MC68020 @@ -16707,7 +19441,7 @@ If you anticipate running this kernel on a computer with a MC68060 processor, say Y. Otherwise, say N. -Math emulation support (EXPERIMENTAL) +Math emulation support CONFIG_M68KFPU_EMU At some point in the future, this will cause floating-point math instructions to be emulated by the kernel on machines that lack a @@ -16739,19 +19473,25 @@ This gives you access to some advanced options for the CPU. The defaults should be fine for most users, but these options may make it possible for you to improve performance somewhat if you know what - you are doing. + you are doing. Note that the answer to this question won't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions about these options. Most users should say N to this question. +Use one physical chunk of memory only +CONFIG_SINGLE_MEMORY_CHUNK + Ignore all but the first contiguous chunk of physical memory for VM + purposes. This will save a few bytes kernel size and may speed up + some operations. Say N if not sure. + Use read-modify-write instructions CONFIG_RMW_INSNS This allows to use certain instructions that work with indivisible read-modify-write bus cycles. While this is faster than the - workaround of disabling interrupts, it can conflict with DMA + workaround of disabling interrupts, it can conflict with DMA ( = direct memory access) on many Amiga systems, and it is also said to destabilize other machines. It is very likely that this will cause serious problems on any Amiga or Atari Medusa if set. The only @@ -16760,7 +19500,7 @@ really know what you are doing, say N. Try Y only if you're quite adventurous. -Zorro support +Amiga Zorro (AutoConfig) bus support CONFIG_ZORRO This enables support for the Zorro bus in the Amiga. If you have expansion cards in your Amiga that conform to the Amiga @@ -16782,23 +19522,34 @@ When in doubt, say Y. -Amiga 1200/600 PCMCIA support (EXPERIMENTAL) +Amiga 1200/600 PCMCIA support CONFIG_AMIGA_PCMCIA Include support in the kernel for pcmcia on Amiga 1200 and Amiga 600. If you intend to use pcmcia cards say Y; otherwise say N. +Hisoft Whippet PCMCIA serial support +CONFIG_WHIPPET_SERIAL + HiSoft has a web page at , but there + is no listing for the Whippet in their Amiga section. + Amiga Zorro II ramdisk support CONFIG_AMIGA_Z2RAM This enables support for using Chip RAM and Zorro II RAM as a ramdisk or as a swap partition. Say Y if you want to include this - driver in the kernel. This driver is also available as a module + driver in the kernel. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called z2ram.o. If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . -Atari ST-RAM swap support +Support for ST-RAM as swap space CONFIG_STRAM_SWAP + Some Atari 68k macines (including the 520STF and 1020STE) divide + their addressible memory into ST and TT sections. The TT section + (up to 512MB) is the main memory; the ST section (up to 4MB) is + accessible to the built-in graphics board, runs slower, and is + present mainly for backward compatibility with older machines. + This enables support for using (parts of) ST-RAM as swap space, instead of as normal system memory. This can first enhance system performance if you have lots of alternate RAM (compared to the size @@ -16809,6 +19560,12 @@ sound). The probability that such allocations at module load time fail is drastically reduced. +ST-RAM statistics in /proc +CONFIG_STRAM_PROC + Say Y here to report ST-RAM usage statistics in /proc/stram. See + the help for CONFIG_STRAM_SWAP for discussion of ST-RAM and its + uses. + Atari ACSI support CONFIG_ATARI_ACSI This enables support for the Atari ACSI interface. The driver @@ -16819,7 +19576,7 @@ driver is also the basis for certain other drivers for devices attached to the ACSI bus: Atari SLM laser printer, BioNet-100 Ethernet, and PAMsNet Ethernet. If you want to use one of these - devices, you need ACSI support, too. + devices, you need ACSI support, too. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -16850,7 +19607,7 @@ also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called wd33c93.o. If you want to compile it as a module, say M here - and read Documentation/modules.txt. + and read . A2091 WD33C93A support CONFIG_A2091_SCSI @@ -16858,7 +19615,7 @@ say N. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called wd33c93.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . GVP Series II WD33C93A support CONFIG_GVP11_SCSI @@ -16871,21 +19628,21 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called gvp11.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + as a module, say M here and read . -Cyberstorm SCSI support +CyberStorm SCSI support CONFIG_CYBERSTORM_SCSI If you have an Amiga with an original (MkI) Phase5 Cyberstorm accelerator board and the optional Cyberstorm SCSI controller, answer Y. Otherwise, say N. -Cyberstorm II SCSI support +CyberStorm II SCSI support CONFIG_CYBERSTORMII_SCSI If you have an Amiga with a Phase5 Cyberstorm MkII accelerator board and the optional Cyberstorm SCSI controller, say Y. Otherwise, answer N. -Blizzard 2060 SCSI support (EXPERIMENTAL) +Blizzard 2060 SCSI support CONFIG_BLZ2060_SCSI If you have an Amiga with a Phase5 Blizzard 2060 accelerator board and want to use the onboard SCSI controller, say Y. Otherwise, @@ -16907,18 +19664,25 @@ If you have the Phase5 Fastlane Z3 SCSI controller, or plan to use one in the near future, say Y to this question. Otherwise, say N. +BSC Oktagon SCSI support +CONFIG_OKTAGON_SCSI + If you have the BSC Oktagon SCSI disk controller for the Amiga, say + Y to this question. If you're in doubt about whether you have one, + see the picture at + . + Atari native SCSI support CONFIG_ATARI_SCSI If you have an Atari with built-in NCR5380 SCSI controller (TT, Falcon, ...) say Y to get it supported. Of course also, if you have - a compatible SCSI controller (e.g. for Medusa). This driver is also + a compatible SCSI controller (e.g. for Medusa). This driver is also available as a module ( = code which can be inserted in and removed - from the running kernel whenever you want). The module is called - atari_scsi.o. If you want to compile it as a module, say M here and - read Documentation/modules.txt. This driver supports both styles of - NCR integration into the system: the TT style (separate DMA), and - the Falcon style (via ST-DMA, replacing ACSI). It does NOT support - other schemes, like in the Hades (without DMA). + from the running kernel whenever you want). The module is called + atari_scsi.o. If you want to compile it as a module, say M here and + read . This driver supports both + styles of NCR integration into the system: the TT style (separate + DMA), and the Falcon style (via ST-DMA, replacing ACSI). It does + NOT support other schemes, like in the Hades (without DMA). Long delays for Toshiba CD-ROMs CONFIG_ATARI_SCSI_TOSHIBA_DELAY @@ -16927,6 +19691,12 @@ use a Toshiba CD-ROM drive; otherwise, the option is not needed and would impact performance a bit, so say N. +Reset SCSI-devices at boottime +CONFIG_ATARI_SCSI_RESET_BOOT + Reset the devices on your Atari whenever it boots. This makes the + boot process fractionally longer but may assist recovery from errors + that leave the devices with SCSI operations partway completed. + Hades SCSI DMA emulator CONFIG_TT_DMA_EMUL This option enables code which emulates the TT SCSI DMA chip on the @@ -16941,7 +19711,7 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called ariadne.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . Ariadne II and X-Surf support CONFIG_ARIADNE2 @@ -16952,7 +19722,7 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called ariadne2.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . A2065 support CONFIG_A2065 @@ -16962,7 +19732,7 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called a2065.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . Hydra support CONFIG_HYDRA @@ -16971,17 +19741,30 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called hydra.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . -Pcmcia NE2000 compatible support +Sun3 NCR5380 SCSI +CONFIG_SUN3_SCSI + This option will enable support for the OBIO (onboard io) NCR5380 + SCSI controller found in the Sun 3/50 and 3/60. Note that this + driver does not provide support for VME SCSI boards. + General Linux information on the Sun 3 series (now discontinued) + is at . + +Sun3x ESP SCSI driver +CONFIG_SUN3X_ESP + The ESP was an on-board SCSI controller used on Sun 3/80 + machines. Say Y here to compile in support for it. + +PCMCIA NE2000 and compatibles support CONFIG_APNE - If you have a pcmcia ne2000 compatible adapter, say Y. Otherwise, + If you have a PCMCIA NE2000 compatible adapter, say Y. Otherwise, say N. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called apne.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . Atari Lance support CONFIG_ATARILANCE @@ -17003,32 +19786,32 @@ Amiga mouse support CONFIG_AMIGAMOUSE - If you want to be able to use an Amiga mouse in Linux, say Y. + If you want to be able to use an Amiga mouse in Linux, say Y. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called amigamouse.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . Atari mouse support CONFIG_ATARIMOUSE - If you want to be able to use an Atari mouse in Linux, say Y. + If you want to be able to use an Atari mouse in Linux, say Y. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called atarimouse.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . Atari MFP serial support CONFIG_ATARI_MFPSER If you like to use the MFP serial ports ("Modem1", "Serial1") under Linux, say Y. The driver equally supports all kinds of MFP serial - ports and automatically detects whether Serial1 is available. + ports and automatically detects whether Serial1 is available. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . Note for Falcon users: You also have an MFP port, it's just not wired to the outside... But you could use the port under Linux. @@ -17044,7 +19827,7 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . Atari SCC serial DMA support CONFIG_ATARI_SCC_DMA @@ -17061,9 +19844,9 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . -Atari DSP56k Digital Signal Processor support (EXPERIMENTAL) +Atari DSP56k Digital Signal Processor support CONFIG_ATARI_DSP56K If you want to be able to use the DSP56001 in Falcons, say Y. This driver is still experimental, and if you don't know what it is, or @@ -17072,7 +19855,12 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . + +Support for early boot text console +CONFIG_BOOTX_TEXT + Say Y here to see progress messages from the boot firmware in text + mode. Requires either BootX or Open Firmware. Amiga builtin serial support CONFIG_AMIGA_BUILTIN_SERIAL @@ -17082,13 +19870,23 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . GVP IO-Extender support CONFIG_GVPIOEXT If you want to use a GVP IO-Extender serial card in Linux, say Y. Otherwise, say N. +GVP IO-Extender parallel printer support +CONFIG_GVPIOEXT_LP + Say Y to enable driving a printer from the parallel port on your + GVP IO-Extender card, N otherwise. + +GVP IO-Extender PLIP support +CONFIG_GVPIOEXT_PLIP + Say Y to enable doing IP over the parallel port on your GVP + IO-Extender card, N otherwise. + Multiface Card III serial support CONFIG_MULTIFACE_III_TTY If you want to use a Multiface III card's serial port in Linux, @@ -17097,7 +19895,15 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . + +Amiga/Atari/PowerMac DMA sound support +CONFIG_DMASOUND + Support built-in audio chips accessible by DMA on various machines + that have them. Note that this symbol does not affect the kernel + directly; rather, it controls whether configuration questions + enabling DMA sound drivers for various specific machine + architectures will be used. Atari DMA sound support CONFIG_DMASOUND_ATARI @@ -17108,7 +19914,7 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . PowerMac DMA sound support CONFIG_DMASOUND_AWACS @@ -17119,7 +19925,7 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . Amiga DMA sound support CONFIG_DMASOUND_PAULA @@ -17130,7 +19936,7 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . Q40 sound support CONFIG_DMASOUND_Q40 @@ -17141,7 +19947,7 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . HP DCA serial support CONFIG_HPDCA @@ -17159,39 +19965,122 @@ HP300 machines. If you are using such a system you almost certainly want this. +# Choice: ppctype Processor Type CONFIG_6xx - There are four types of PowerPC chips supported. The more common - types (601, 603, 604, 740, 750, 7400), the Motorola embedded versions - (821, 823, 850, 855, 860, 8260), the IBM embedded versions (403 and - 405) and the high end 64 bit Power processors (Power 3, Power 4). - Unless you are building a kernel for one of the embedded processor - systems, or a 64 bit IBM RS/6000, choose 6xx. Note that the kernel - runs in 32-bit mode even on 64-bit chips. Also note that because - the 82xx family has a 603e core, specific support for that chipset - is asked later on. + There are four types of PowerPC chips supported. The more common + types (601, 603, 604, 740, 750, 7400), the Motorola embedded + versions (821, 823, 850, 855, 860, 8260), the IBM embedded versions + (403 and 405) and the high end 64 bit Power processors (Power 3, + Power 4). Unless you are building a kernel for one of the embedded + processor systems, or a 64 bit IBM RS/6000, choose 6xx. Note that + the kernel runs in 32-bit mode even on 64-bit chips. Also note that + because the 82xx family has a 603e core, specific support for that + chipset is asked later on. Motorola MPC8260 CPM support CONFIG_8260 The MPC8260 CPM (Communications Processor Module) is a typical - embedded CPU made by Motorola. Selecting this option means that you - wish to build a kernel for a machine with specifically an 8260 for - a CPU. + embedded CPU made by Motorola. Selecting this option means that + you wish to build a kernel for a machine with specifically an 8260 + for a CPU. If in doubt, say N. +# Choice: Machine type +Oak +CONFIG_OAK + Select Oak if you have an IBM 403GCX "Oak" Evaluation Board. + + Select Walnut if you have an IBM 405GP "Walnut" Evaluation Board. + + More information on these boards is available at: + . + +Walnut +CONFIG_WALNUT + Select Walnut if you have an IBM 405GP "Walnut" Evaluation Board. + Workarounds for PPC601 bugs CONFIG_PPC601_SYNC_FIX Some versions of the PPC601 (the first PowerPC chip) have bugs which - mean that extra synchronization instructions are required near certain - instructions, typically those that make major changes to the CPU state. - These extra instructions reduce performance slightly. If you say N - here, these extra instructions will not be included, resulting in a - kernel which will run faster but may not run at all on some systems - with the PPC601 chip. + mean that extra synchronization instructions are required near + certain instructions, typically those that make major changes to the + CPU state. These extra instructions reduce performance slightly. + If you say N here, these extra instructions will not be included, + resulting in a kernel which will run faster but may not run at all + on some systems with the PPC601 chip. + + If in doubt, say Y here. + +8xx Cache (Copy-Back or Writethrough) +CONFIG_8xx_COPYBACK + Saying Y here will cause the cache on an MPC8xx processor to be used + in Copy-Back mode. If you say N here, it is used in Writethrough + mode. + + If in doubt, say Y here. + +MPC860 (Pre Rev. C) CPU6 Silicon Errata +CONFIG_8xx_CPU6 + MPC860 CPUs, prior to Rev C have some bugs in the silicon, which + require workarounds for Linux (and most other OSes to work). If you + get a BUG() very early in boot, this might fix the problem. For + more details read the document entitled "MPC860 Family Device Errata + Reference" on Motorola's website. This option also incurs a + performance hit. + + If in doubt, say N here. + +MPC8xx IDE support +CONFIG_BLK_DEV_MPC8xx_IDE + This option provides support for IDE on Motorola MPC8xx Systems. + Please see 'Type of MPC8xx IDE interface' for details. + + If unsure, say N. + +Type of MPC8xx IDE interface +CONFIG_IDE_8xx_PCCARD + Select how the IDE devices are connected to the MPC8xx system: + + 8xx_PCCARD uses the 8xx internal PCMCIA interface in combination + with a PC Card (e.g. ARGOSY portable Hard Disk Adapter), + ATA PC Card HDDs or ATA PC Flash Cards (example: TQM8xxL + systems) + + 8xx_DIRECT is used for directly connected IDE devices using the 8xx + internal PCMCIA interface (example: IVMS8 systems) + + EXT_DIRECT is used for IDE devices directly connected to the 8xx + bus using some glue logic, but _not_ the 8xx internal + PCMCIA interface (example: IDIF860 systems) + +Use SMC2 for UART +CONFIG_SMC2_UART + If you would like to use SMC2 as a serial port, say Y here. If in doubt, say Y here. +Use SMC2 for Console +CONFIG_CONS_SMC2 + If you are going to have a serial console on your device and are + using SMC2 for your serial port, say Y here, else say N. + +Use the alternate SMC2 I/O +CONFIG_ALTSMC2 + If you have an MPC823 or MPC850 and would like to use the alternate + SMC2 for I/O, say Y here. + + If in doubt, say N here. + +Enable SCC2 and SCC3 for UART +CONFIG_USE_SCC_IO + If your MPC8xx board has other SCC ports that you would like to use + for for a serial port, say Y here. + + If in doubt, say N here. + +# Choice: ppc6xxtype Machine Type CONFIG_ALL_PPC Linux currently supports several different kinds of PowerPC-based @@ -17201,7 +20090,28 @@ and some IBM RS/6000 systems), CHRP (Common Hardware Reference Platform), and several embedded PowerPC systems containing 4xx, 6xx, 7xx, 8xx, 74xx, and 82xx processors. Currently, the default option - is to build a kernel which works on the first three. + is to build a kernel which works on the first three. + + 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: + . + + Select APUS if configuring for a PowerUP Amiga. More information is + available 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: + . Embedded 8xx Board Type CONFIG_RPXLITE @@ -17325,7 +20235,7 @@ altivec registers, and turning on the 'altivec enable' bit so user processes can execute altivec instructions. - This option is only usefully if you have a processor that supports + This option is only usefully if you have a processor that supports altivec (G4, otherwise known as 74xx series), but does not have any affect on a non-altivec cpu (it does, however add code to the kernel). @@ -17336,7 +20246,7 @@ CONFIG_TAU G3 and G4 processors have an on-chip temperature sensor called the 'Thermal Assist Unit (TAU)', which, in theory, can measure the on-die - temperature within 2-4 degrees celcius. This option shows the current + temperature within 2-4 degrees Celsius. This option shows the current on-die temperature in /proc/cpuinfo if the cpu supports it. Unfortunately, on some chip revisions, this sensor is very inaccurate @@ -17360,27 +20270,11 @@ Average high and low temp CONFIG_TAU_AVERAGE The TAU hardware can compare the temperature to an upper and lower bound. - The default behavior is to show both the upper and lower bound in + The default behavior is to show both the upper and lower bound in /proc/cpuinfo. If the range is large, the temperature is either changing a lot, or the TAU hardware is broken (likely on some G4's). If the range is small (around 4 degrees), the temperature is relatively stable. -Support for CUDA based PowerMacs -CONFIG_ADB_CUDA - This provides support for CUDA based Power Macintosh systems. This - includes most OldWorld PowerMacs, the first generation iMacs, the - Blue&White G3 and the Yikes G4 (PCI Graphics). All later models - should use CONFIG_ADB_PMU instead. - - If unsure say Y. - -Support for PMU based PowerMacs -CONFIG_ADB_PMU - This provides support for PMU based Power Macintosh systems. This - includes all PowerBooks and all AGP-based machines. - - If unsure say Y. - Power management support for PowerBooks CONFIG_PMAC_PBOOK This provides support for putting a PowerBook to sleep; it also @@ -17389,7 +20283,7 @@ must get the power management daemon, pmud, to make it work and you must have the /dev/pmu device (see the pmud README). - Get pmud from ftp://linuxcare.com.au/pub/ppclinux/pmud/ + Get pmud from . If you have a PowerBook, you should say Y. @@ -17397,33 +20291,218 @@ have it autoloaded. The act of removing the module shuts down the sound hardware for more power savings. +Backlight control for LCD screens +CONFIG_PMAC_BACKLIGHT + Say Y here to build in code to manage the LCD backlight on a + Macintosh PowerBook. With this code, the backlight will be turned + on and off appropriately on power-management and lid-open/lid-closed + events; also, the PowerBook button device will be enabled so you can + change the screen brightness. + +# Choice: ppc8xxtype +RPX-Lite +CONFIG_RPXLITE + Single-board computers based around the PowerPC MPC8xx chips and + intended for embedded applications. The following types are + supported: + + RPX-Lite -- PC104 form-factor SBC based on the MPC823 + RPX-Classic -- Credit-card-size SBC based on the MPC 860 + BSE-IP -- Bright Star Engineering BSE-IP SBC + TQM823L -- TQM823L SBC from TQ Components + TQM850L -- TQM850L SBC from TQ Components + TQM855L -- TQM855L SBC from TQ Components + TQM860L -- TQM860L SBC from TQ Components + FPS850L -- FingerPrint Sensor from TQ Components + TQM860 -- TQM860 SBC from IKENDI AG + SPD823TS -- Speech Design TeleServer from Speech Design + IVMS8 -- Integrated VoiceMail SBC from Speech Design + SM850 -- Service Module 850 from Dependable Computer Systems + MBX -- MBX821 and MBX860 SBCs + Wincept -- Wincept SBCs for thin-client machines + +RPX-Classic +CONFIG_RPXCLASSIC + The RPX-Classic is a single-board computer based on the Motorola + MPC860. It features 16MB of DRAM and a variable amount of flash, + I2C EEPROM, thermal monitoring, a PCMCIA slot, a DIP switch and two + LEDs. Variants with Ethernet ports exist. Say Y here to support it + directly. + +BSE-IP +CONFIG_BSEIP + Say Y here to support the Bright Star Engineering ipEngine SBC. + This is a credit-card-sized device featuring a MPC823 processor, + 26MB DRAM, 4MB flash, Ethernet, a 16K-gate FPGA, USB, an LCD/video + controller, and two RS232 ports. + +TQM823L +CONFIG_TQM823L + Say Y here to support the TQM823L, one of an MPC8xx-based family of + mini SBCs (half credit-card size) from TQ Components first released + in late 1999. Technical references are at + , and + , and an image at + . + +TQM850L +CONFIG_TQM850L + Say Y here to support the TQM850L, one of an MPC8xx-based family of + mini SBCs (half credit-card size) from TQ Components first released + in late 1999. Technical references are at + , and + , and an image at + . + +TQM855L +CONFIG_TQM855L + Say Y here to support the TQM855L, one of an MPC8xx-based family of + mini SBCs (half credit-card size) from TQ Components first released + in late 1999. Technical references are at + , and + , and an image at + . + +TQM860L +CONFIG_TQM860L + Say Y here to support the TQM860L, one of an MPC8xx-based family of + mini SBCs (half credit-card size) from TQ Components first released + in late 1999. Technical references are at + , and + , and an image at + . + +FPS850 +CONFIG_FPS850 + Say Y here to support the FingerPrint Sensor from AKENDI IG, based + on the TQ Components TQM850L module, released November 1999 and + discontinued a year later. + +TQM860 +CONFIG_TQM860 + Say Y here to support the TQM860, one of an MPC8xx-based family of + SBCs (credit-card size) from TQ Components first released in + mid-1999 and discontinued mid-2000. + +SM850 +CONFIG_SM850 + Say Y here to support the Service Module 850 from Dependable + Computer Systems, an SBC based on the TQM850L module by TQ + Components. This board is no longer in production. The + manufacturer's website is at . + +SPD823TS +CONFIG_SPD823TS + Say Y here to support the Speech Design 823 Tele-Server from Speech + Design, released in 2000. The manufacturer's website is at + . + +IVMS8 +CONFIG_IVMS8 + Say Y here to support the Integrated Voice-Mail Small 8-channel SBC + from Speech Design, released March 2001. The manufacturer's website + is at . + +# IVML24 is not yet active +IVML24 +CONFIG_IVML24 + Say Y here to support the Integrated Voice-Mail Large 24-channel SBC + from Speech Design, released March 2001. The manufacturer's website + is at . + +MBX +CONFIG_MBX + MBX is a line of Motorola single-board computer based around the + MPC821 and MPC860 processors, and intended for embedded-controller + applications. Say Y here to support these boards directly. + +WinCept +CONFIG_WINCEPT + The Wincept 100/110 is a Motorola single-board computer based on the + MPC821 PowerPC, introduced in 1998 and designed to be used in + thin-client machines. Say Y to support it directly. + +# More systems that will be supported soon, according to +# Wolfgang Denk : +# +# TQM8260: +# MPC8260 based module +# +# Manufacturer: TQ Components, www.tq-group.de +# Date of Release: June 2001 +# End of Life: not yet :-) +# URL: +# +# IP860: +# VMEBus IP (Industry Pack) carrier board with MPC860 +# +# Manufacturer: MicroSys GmbH, +# Date of Release: ? +# End of life: - +# URL: +# +# CU824: +# VMEBus Board with PCI extension with MPC8240 CPU +# +# Manufacturer: MicroSys GmbH, +# Date of Release: early 2001 (?) +# End of life: - +# URL: +# Date of Release: mid 2001 +# End of life: - +# URL: +# +# PCU_E: +# PCU = Peripheral Controller Unit; E = extended (?) +# +# Mfr: Siemens AG, ICN (Information and Communication Networks) +# +# Date of Release: April 2001 +# End of life: - +# URL: n. a.o + +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 + and has probably been discontinued or rebadged. + 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, you - can dynamically switch via the + 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 argument. - + sysctl and with the "keyboard_sends_linux_keycodes=" kernel + argument. + If unsure, say Y here. 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 entries: + 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 /proc/sys/dev/mac_hid/mouse_button3_keycode -Enhanced Real Time Clock Support +Enhanced Real Time Clock Support (/dev/rtc) CONFIG_PPC_RTC If you say Y here and create a character special file /dev/rtc with major number 10 and minor number 135 using mknod ("man mknod"), you - will get access to the real time clock (or hardware clock) built + will get access to the real time clock (or hardware clock) built into your computer. - + If unsure, say Y here. Support for Open Firmware device tree in /proc @@ -17432,27 +20511,27 @@ an image of the device tree that the kernel copies from Open Firmware. If unsure, say Y here. -RTAS proc interface +RTAS (RunTime Abstraction Services) in /proc CONFIG_PPC_RTAS When you use this option, you will be able to use RTAS from - userspace. - + userspace. + RTAS stands for RunTime Abstraction Services and should provide a portable way to access and set system information. This is - commonly used on RS/6000 (pSeries) computers. - - You can access RTAS via the special proc filesystem entry rtas. + commonly used on RS/6000 (pSeries) computers. + + You can access RTAS via the special proc file system entry rtas. Don't confuse this rtas entry with the one in /proc/device-tree/rtas which is readonly. - + If you don't know if you can use RTAS look into /proc/device-tree/rtas. If there are some entries, it is very likely that you will be able to use RTAS. You can do cool things with rtas. To print out information about - various sensors in the system, just do a + various sensors in the system, just do a - $ cat /proc/rtas/sensors + $ cat /proc/rtas/sensors or if you power off your machine at night but want it running when you enter your office at 7:45 am, do a @@ -17461,7 +20540,7 @@ and shutdown. - If unsure, say Y + If unsure, say Y. MESH (Power Mac internal SCSI) support CONFIG_SCSI_MESH @@ -17471,9 +20550,9 @@ adaptor. This driver is also available as a module called mesh.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, - say M here and read Documentation/modules.txt. + say M here and read . -Maximum synchronous transfer rate +Maximum synchronous transfer rate (MB/s) (0 = async) CONFIG_SCSI_MESH_SYNC_RATE On Power Macintoshes (and clones) where the MESH SCSI bus adaptor drives a bus which is entirely internal to the machine (such as the @@ -17489,12 +20568,12 @@ On Power Macintoshes (and clones) with two SCSI buses, the external SCSI bus is usually controlled by a 53C94 SCSI bus adaptor. Older machines which only have one SCSI bus, such as the 7200, also use - the 53C94. Say Y to include support for the 53C94. + the 53C94. Say Y to include support for the 53C94. This driver is also available as a module called mac53c94.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . MACE (Power Mac Ethernet) support CONFIG_MACE @@ -17505,7 +20584,7 @@ This driver is also available as a module called mace.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . Use AAUI port instead of TP by default CONFIG_MACE_AAUI_PORT @@ -17514,53 +20593,52 @@ instead of an 8-pin RJ45 connector for twisted-pair ethernet. Say Y here if you have such a machine. If unsure, say N. The driver will default to AAUI on ANS anyway, and if you use it as - a module, you can provide the port_aaui=0|1 to force the driver - setting. + a module, you can provide the port_aaui=0|1 to force the driver. -BMAC (G3 ethernet) support +BMAC (G3 Ethernet) support CONFIG_BMAC Say Y for support of BMAC Ethernet interfaces. These are used on G3 - computers. + computers. This driver is also available as a module called bmac.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . -GMAC (G4/iBook ethernet) support +GMAC (G4/iBook Ethernet) support CONFIG_GMAC Say Y for support of GMAC Ethernet interfaces. These are used on G4 - and iBook computers. + and iBook computers. This driver is also available as a module called gmac.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . -National DP83902AV (Oak ethernet) support +National DP83902AV (Oak Ethernet) support CONFIG_OAKNET Say Y if your machine has this type of Ethernet network card. This driver is also available as a module called oaknet.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . Video For Linux CONFIG_VIDEO_DEV Support for audio/video capture and overlay devices and FM radio cards. The exact capabilities of each device vary. User tools for this are available from - ftp://ftp.uk.linux.org/pub/linux/video4linux . + . If you are interested in writing a driver for such an audio/video device or user software interacting with such a driver, please read - the file Documentation/video4linux/API.html. + the file . This driver is also available as a module called videodev.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . Video For Linux /proc file system information CONFIG_VIDEO_PROC_FS @@ -17576,122 +20654,121 @@ in the port address below. Note that newer AIMSlab RadioTrack cards have a different chipset - and are not supported by this driver. For these cards, use the + and are not supported by this driver. For these cards, use the RadioTrack II driver below. If you have a GemTeks combined (PnP) sound- and radio card you must - use this driver as a module and setup the card with isapnptools. You - must also pass the module a suitable io parameter, 0x248 has been - reported to be used by these cards. + use this driver as a module and setup the card with isapnptools. + You must also pass the module a suitable io parameter, 0x248 has + been reported to be used by these cards. In order to control your radio card, you will need to use programs - that are compatible with the Video for Linux API. Information on + that are compatible with the Video For Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . More information - is contained in the file Documentation/video4linux/radiotrack.txt. + . More + information is contained in the file + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called radio-aimslab.o. + say M here and read . The module + will be called radio-aimslab.o. -RadioTrack i/o port +RadioTrack I/O port CONFIG_RADIO_RTRACK_PORT - Enter either 0x30f or 0x20f here. The card default is 0x30f, if you + Enter either 0x30f or 0x20f here. The card default is 0x30f, if you haven't changed the jumper setting on the card. AIMSlab RadioTrack II support CONFIG_RADIO_RTRACK2 - Choose Y here if you have this FM radio card, and then fill in the + Choose Y here if you have this FM radio card, and then fill in the port address below. In order to control your radio card, you will need to use programs - that are compatible with the Video for Linux API. Information on + that are compatible with the Video For Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called radio-rtrack2.o. + say M here and read . The module + will be called radio-rtrack2.o. -RadioTrack II i/o port +RadioTrack II I/O port CONFIG_RADIO_RTRACK2_PORT - Enter either 0x30c or 0x20c here. The card default is 0x30c, if you + Enter either 0x30c or 0x20c here. The card default is 0x30c, if you haven't changed the jumper setting on the card. Aztech/Packard Bell Radio CONFIG_RADIO_AZTECH Choose Y here if you have one of these FM radio cards, and then fill in the port address below. - + In order to control your radio card, you will need to use programs - that are compatible with the Video for Linux API. Information on + that are compatible with the Video For Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called radio-aztech.o. + say M here and read . The module + will be called radio-aztech.o. -Aztech/Packard Bell radio card i/o port +Aztech/Packard Bell radio card I/O port CONFIG_RADIO_AZTECH_PORT - Enter either 0x350 or 0x358 here. The card default is 0x350, if you - haven't changed the setting of jumper JP3 on the card. Removing the + Enter either 0x350 or 0x358 here. The card default is 0x350, if you + haven't changed the setting of jumper JP3 on the card. Removing the jumper sets the card to 0x358. ADS Cadet AM/FM Radio Tuner Card CONFIG_RADIO_CADET Choose Y here if you have one of these AM/FM radio cards, and then fill in the port address below. - + In order to control your radio card, you will need to use programs - that are compatible with the Video for Linux API. Information on + that are compatible with the Video For Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . Further documentation on this driver can be found on the WWW at - http://linux.blackhawke.net/cadet.html . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called radio-cadet.o. + say M here and read . The module + will be called radio-cadet.o. SF16FMI Radio CONFIG_RADIO_SF16FMI - Choose Y here if you have one of these FM radio cards, and then fill - in the port address below. + Choose Y here if you have one of these FM radio cards. If you + compile the driver into the kernel and your card is not PnP one, you + have to add "sf16fm=" to the kernel command line (I/O address is + 0x284 or 0x384). In order to control your radio card, you will need to use programs - that are compatible with the Video for Linux API. Information on + that are compatible with the Video For Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called radio-sf16fmi.o + say M here and read . The module + will be called radio-sf16fmi.o. -SF16FMI I/O port (0x284 or 0x384) -CONFIG_RADIO_SF16FMI_PORT - Enter the I/O port of your SF16FMI radio card. - -Typhoon Radio +Typhoon Radio (a.k.a. EcoRadio) CONFIG_RADIO_TYPHOON Choose Y here if you have one of these FM radio cards, and then fill in the port address and the frequency used for muting below. In order to control your radio card, you will need to use programs - that are compatible with the Video for Linux API. Information on + that are compatible with the Video For Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called radio-typhoon.o + say M here and read . The module + will be called radio-typhoon.o. Support for /proc/radio-typhoon CONFIG_RADIO_TYPHOON_PROC_FS @@ -17720,88 +20797,132 @@ in the port address below. In order to control your radio card, you will need to use programs - that are compatible with the Video for Linux API. Information on + that are compatible with the Video For Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called radio-zoltrix.o + say M here and read . The module + will be called radio-zoltrix.o. ZOLTRIX I/O port (0x20c or 0x30c) CONFIG_RADIO_ZOLTRIX_PORT Enter the I/O port of your Zoltrix radio card. -IIC on parallel port +I2C on parallel port CONFIG_I2C_PARPORT I2C is a simple serial bus system used in many micro controller - applications. Saying Y here will allow you to use your parallel port - as an I2C interface. + applications. Saying Y here will allow you to use your parallel + port as an I2C interface. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called i2c-parport.o. + say M here and read . The module + will be called i2c-parport.o. miroSOUND PCM20 radio CONFIG_RADIO_MIROPCM20 - Choose Y here if you have this sound card. You also need to say Y + Choose Y here if you have this FM radio card. You also need to say Y to "ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20 radio)" (in "Sound") for this to work. In order to control your radio card, you will need to use programs - that are compatible with the Video for Linux API. Information on + that are compatible with the Video For Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called miropcm20.o + say M here and read . The module + will be called miropcm20.o. miroSOUND PCM20 radio RDS user interface (EXPERIMENTAL) CONFIG_RADIO_MIROPCM20_RDS - Choose Y here if you want to see RDS/RBDS information like RadioText, - Programme Service name, Clock Time and date, Programme TYpe and - Traffic Announcement/Programme identification. You also need to say - Y to "miroSOUND PCM20 radio" and devfs! + Choose Y here if you want to see RDS/RBDS information like + RadioText, Programme Service name, Clock Time and date, Programme + TYpe and Traffic Announcement/Programme identification. You also + need to say Y to "miroSOUND PCM20 radio" and devfs! It's not possible to read the raw RDS packets from the device, so - the driver cant provide an V4L interface for this. But the - availability of RDS is reported over V4L by the basic driver already. - Here RDS can be read from files in /dev/v4l/rds. + the driver cant provide an V4L interface for this. But the + availability of RDS is reported over V4L by the basic driver + already. Here RDS can be read from files in /dev/v4l/rds. As module the driver will be called miropcm20-rds.o. -GemTek Radio Card +Maestro on board radio +CONFIG_RADIO_MAESTRO + Say Y here to directly support the on-board radio tuner on the + Maestro 2 or 2E sound card. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found on the WWW at + . + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The module + will be called radio-maestro.o. + +Guillemot MAXI Radio FM 2000 Radio Card +CONFIG_RADIO_MAXIRADIO + Choose Y here if you have this radio card. This card may also be + found as Gemtek PCI FM. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found on the WWW at + . + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The module + will be called radio-maxiradio.o. + +GemTek Radio Card support CONFIG_RADIO_GEMTEK - Choose Y here if you have this FM radio card, and then fill in the + Choose Y here if you have this FM radio card, and then fill in the port address below. In order to control your radio card, you will need to use programs - that are compatible with the Video for Linux API. Information on + that are compatible with the Video For Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called radio-gemtek.o. + say M here and read . The module + will be called radio-gemtek.o. -GemTek i/o port +GemTek I/O port CONFIG_RADIO_GEMTEK_PORT Enter either 0x20c, 0x30c, 0x24c or 0x34c here. The card default is 0x34c, if you haven't changed the jumper setting on the card. On - Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the i/o + Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O port is 0x28c. +Gemtek PCI Radio +CONFIG_RADIO_GEMTEK_PCI + Choose Y here if you have this PCI FM radio card. + + In order to control your radio card, you will need to use programs + that are compatible with the Video for Linux API. Information on + this API and pointers to "v4l" programs may be found on the WWW at + . + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The module + will be called radio-gemtek-pci.o. + PlanB Video-In for PowerMacs CONFIG_VIDEO_PLANB PlanB is the V4L driver for the PowerMac 7x00/8x00 series video input hardware. If you want to experiment with this, say Y. Otherwise, or if you don't understand a word, say N. - See http://www.cpu.lu/~mlan/planb.html for more info. + See for more info. Saying M will compile this driver as a module (planb.o). @@ -17810,34 +20931,27 @@ Choose Y here if you have this FM radio card, and then fill in the port address below. (TODO) - Note: This driver is in its early stages. Right now volume and + Note: This driver is in its early stages. Right now volume and frequency control and muting works at least for me, but - unfortunately i have not found anybody who wants to use this card - with Linux. So if it is this what YOU are trying to do right now, - PLEASE DROP ME A NOTE!! Rolf Offermanns (rolf@offermanns.de) - + unfortunately I have not found anybody who wants to use this card + with Linux. So if it is this what YOU are trying to do right now, + PLEASE DROP ME A NOTE!! Rolf Offermanns (rolf@offermanns.de) + In order to control your radio card, you will need to use programs - that are compatible with the Video for Linux API. Information on + that are compatible with the Video For Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called radio-terratec.o. + say M here and read . The module + will be called radio-terratec.o. -Terratec i/o port (normally 0x590) +Terratec I/O port (normally 0x590) CONFIG_RADIO_TERRATEC_PORT - Fill in the i/o port of your TerraTec FM radio card. If unsure, go + Fill in the I/O port of your TerraTec FM radio card. If unsure, go with the default. -### Add these -# Zoran ZR36057/36060 support -# CONFIG_VIDEO_ZORAN - -# Include support for Iomega Buz -# CONFIG_VIDEO_BUZ - Trust FM radio card CONFIG_RADIO_TRUST This is a driver for the Trust FM radio cards. Say Y if you have @@ -17846,38 +20960,76 @@ This driver is also available as a module called radio-trust.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . -Trust i/o port (usually 0x350 or 0x358) +Trust I/O port (usually 0x350 or 0x358) CONFIG_RADIO_TRUST_PORT - Enter the i/o port of your Trust FM radio card. If unsure, try the + Enter the I/O port of your Trust FM radio card. If unsure, try the values "0x350" or "0x358". BT848 Video For Linux CONFIG_VIDEO_BT848 Support for BT848 based frame grabber/overlay boards. This includes the Miro, Hauppauge and STB boards. Please read the material in - Documentation/video4linux/bttv for more information. + for more information. If you say Y or M here, you need to say Y or M to "I2C support" and "I2C bit-banging interfaces" in the character device section. - + This driver is available as a module called bttv.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . + +BT878 Audio DMA +CONFIG_SOUND_BT878 + Audio DMA support for bt878 based grabber boards. As you might have + already noticed, bt878 is listed with two functions in /proc/pci. + Function 0 does the video stuff (bt848 compatible), function 1 does + the same for audio data. This is a driver for the audio part of + the chip. If you say 'Y' here you get a oss-compatible dsp device + where you can record from. If you want just watch TV you probably + don't need this driver as most TV cards handle sound with a short + cable from the TV card to your sound card's line-in. + + This driver is available as a module called btaudio.o ( = code + which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read . + +SGI Vino Video For Linux +CONFIG_VIDEO_VINO + Say Y here to build in support for the Vino video input system found + on SGI Indy machines. + +Stradis 4:2:2 MPEG-2 video driver +CONFIG_VIDEO_STRADIS + Say Y here to enable support for the Stradis 4:2:2 MPEG-2 video + driver for PCI. There is a product page at + . + +Zoran ZR36057/36060 Video For Linux +CONFIG_VIDEO_ZORAN + Say Y here to include support for video cards based on the Zoran + ZR36057/36060 encoder/decoder chip (including the Iomega Buz and the + Miro DC10 and DC30 video capture cards). + +Include support for Iomega Buz +CONFIG_VIDEO_BUZ + Say Y here to include support for the Iomega Buz video card. There + is a Buz/Linux homepage at . -ZR36120/36125 Video for Linux +Zoran ZR36120/36125 Video For Linux CONFIG_VIDEO_ZR36120 Support for ZR36120/ZR36125 based frame grabber/overlay boards. This includes the Victor II, WaveWatcher, Video Wonder, Maxi-TV, and Buster boards. Please read the material in - Documentation/video4linux/zr36120.txt for more information. + for more information. This driver is also available as a module called zr36120.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . SAA5249 Teletext processor CONFIG_VIDEO_SAA5249 @@ -17887,26 +21039,37 @@ This driver is also available as a module called saa5249.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . -Quickcam BW Video For Linux +QuickCam BW Video For Linux CONFIG_VIDEO_BWQCAM Say Y have if you the black and white version of the QuickCam - camera. See the next option for the color version. + camera. See the next option for the color version. This driver is also available as a module called bw-qcam.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + here and read . -Colour QuickCam Video For Linux +QuickCam Colour Video For Linux CONFIG_VIDEO_CQCAM This is the video4linux driver for the colour version of the - Connectix Quickcam. If you have one of these cameras, say Y here, - otherwise say N. This driver does not work with the original - monochrome Quickcam, Quickcam VC or QuickClip. It is also available - as a module (c-qcam.o). Read Documentation/video4linux/CQcam.txt for - more information. + Connectix QuickCam. If you have one of these cameras, say Y here, + otherwise say N. This driver does not work with the original + monochrome QuickCam, QuickCam VC or QuickClip. It is also available + as a module (c-qcam.o). + Read for more information. + +Winbond W9966CF Webcam Video For Linux +CONFIG_VIDEO_W9966 + Video4linux driver for Winbond's w9966 based Webcams. + Currently tested with the LifeView FlyCam Supra. + If you have one of these cameras, say Y here + otherwise say N. + This driver is also available as a module (w9966.o). + + Check out and + for more information. CPiA Video For Linux CONFIG_VIDEO_CPIA @@ -17915,10 +21078,10 @@ Blaster Webcam II. If you have one of these cameras, say Y here and select parallel port and/or USB lowlevel support below, otherwise say N. This will not work with the Creative Webcam III. - - Please read Documentation/video4linux/README.cpia for more + + Please read for more information. - + This driver is also available as a module (cpia.o). CPiA Parallel Port Lowlevel Support @@ -17939,11 +21102,13 @@ Mediavision Pro Movie Studio Video For Linux CONFIG_VIDEO_PMS - Say Y if you have such a thing. This driver is also available as a + Say Y if you have such a thing. This driver is also available as a module called pms.o ( = code which can be inserted in and removed - from the running kernel whenever you want). If you want to compile - it as a module, say M here and read Documentation/modules.txt. + from the running kernel whenever you want). If you want to compile + it as a module, say M here and read + . +Sony Vaio Picturebook Motion Eye Video for Linux CONFIG_VIDEO_MEYE This is the video4linux driver for the Motion Eye camera found in the Vaio Picturebook laptops. Please read the material in @@ -17964,20 +21129,20 @@ s390-compiler released by IBM (based on gcc-2.95.1) before. Merge some code into the kernel to make the image IPLable -CONFIG_IPLABLE +CONFIG_IPL If you want to use the produced kernel to IPL directly from a device, you have to merge a bootsector specific to the device into the first bytes of the kernel. You will have to select the - IPL device on another question, that pops up, when you select - CONFIG_IPLABE. + IPL device. -IPL from a /390 tape unit +# Choice: s390_ipl +IPL from a S/390 tape unit CONFIG_IPL_TAPE Select this option if you want to IPL the image from a Tape. IPL from a virtual card reader emulated by VM/ESA CONFIG_IPL_RDR_VM - Select this option if you are running under VM/ESA and want + Select this option if you are running under VM/ESA and want to IPL the image from the emulated card reader. IPL from a real card reader @@ -17986,7 +21151,7 @@ card reader. Maybe you still got one and want to try. We didn't test. -IBMs S/390 Harddisks (DASDs) +Support for DASD hard disks CONFIG_DASD Enable this option if you want to access DASDs directly utilizing S/390s channel subsystem commands. This is necessary for running @@ -18019,6 +21184,163 @@ CONFIG_DASD_FBA FBA devices are currently unsupported. +Merge some code into the kernel to make the image IPLable +CONFIG_IPLABLE + If you want to use the produced kernel to IPL directly from a + device, you have to merge a bootsector specific to the device + into the first bytes of the kernel. You will have to select the + IPL device on another question, that pops up, when you select + CONFIG_IPLABE. + +Support for 3215 line mode terminal +CONFIG_3215 + Include support for IBM 3215 line-mode terminals. Can't be used + if 3270 console support is chosen. + +Support for console on 3215 line mode terminal +CONFIG_3215_CONSOLE + Include support for using an IBM 3215 line-mode terminal as the + Linux system console. Can't be used if 3270 console support is + chosen. + +Support for 3270 line mode terminal +CONFIG_3270 + Include support for IBM 3270 line-mode terminals. + +Support for console on 3270 line mode terminal +CONFIG_3270_CONSOLE + Include support for using an IBM 3270 line-mode terminal as the + Linux system console. Excludes using 3215s. Available only if 3270 + support is compiled in statically. + +Support for HWC line mode terminal +CONFIG_HWC + Include support for IBM HWC line-mode terminals. + +Console on HWC line mode terminal +CONFIG_HWC_CONSOLE + Include support for using an IBM HWC line-mode terminal as the Linux + system console. + +S/390 tape device support +CONFIG_S390_TAPE + Select this option if you want to access channel-attached tape + devices on IBM S/390 or zSeries. + If you select this option you will also want to select at + least one of the tape interface options and one of the tape + hardware options in order to access a tape device. + This option is also available as a module. The module will be + called tape390.o and include all selected interfaces and + hardware drivers. + +Support for tape character devices +CONFIG_S390_TAPE_CHAR + Select this option if you want to access your channel-attached + tape devices using the character device interface. + This interface is similar to other Linux tape devices like + SCSI-Tapes (st) and the floppy tape device (ftape). + If unsure, say "Y". + +Support for tape block devices +CONFIG_S390_TAPE_BLOCK + Select this option if you want to access your channel-attached tape + devices using the block device interface. This interface is similar + to CD-ROM devices on other platforms. The tapes can only be + accessed read-only when using this interface. Have a look at + Documentation/s390/TAPE for further information about creating + volumes for and using this interface. It is safe to say "Y" here. + +Support for 3490 tape hardware +CONFIG_S390_TAPE_3490 + Select this option if you want to access IBM 3480 magnetic + tape subsystems and 100% compatibles. + It is safe to say "Y" here. + +Support for 3480 tape hardware +CONFIG_S390_TAPE_3480 + Select this option if you want to access IBM 3490 magnetic + tape subsystems and 100% compatibles. + +CTC device support +CONFIG_CTC + Select this option if you want to use channel-to-channel networking + on IBM S/390 or zSeries. This device driver supports real CTC + coupling using ESCON. It also supports virtual CTCs when running + under VM. It will use the channel device configuration if this is + available. This option is also available as a module which will be + called ctc.o. If you do not know what it is, it's safe to say "Y". + +Support for DIAG access to CMS reserved Disks +CONFIG_DASD_DIAG + Select this option if you want to use CMS reserved Disks under VM + with the Diagnose250 command. If you are not running under VM or + unsure what it is, say "N". + +XPRAM disk support +CONFIG_BLK_DEV_XPRAM + Select this option if you want to use your expanded storage on S/390 + or zSeries as a disk. This is useful as a _fast_ swap device if you + want to access more than 2G of memory when running in 31 bit mode. + This option is also available as a module which will be called + xpram.o. If unsure, say "N". + +Fast IRQ handling +CONFIG_FAST_IRQ + Select this option in order to get the interrupts processed faster + on your S/390 or zSeries machine. If selected, after an interrupt + is processed, the channel subsystem will be asked for other pending + interrupts which will also be processed before leaving the interrupt + context. This speeds up the I/O a lot. Say "Y". + +IUCV device support (VM only) +CONFIG_IUCV + Select this option if you want to use inter-user communication + vehicle networking under VM or VIF. This option is also available + as a module which will be called iucv.o. If unsure, say "Y". + +Kernel support for 31 bit ELF binaries +CONFIG_S390_SUPPORT + Select this option if you want to enable your system kernel to + handle system-calls from ELF binaries for 31 bit ESA. This option + (and some other stuff like libraries and such) is needed for + executing 31 bit applications. It is safe to say "Y". + +Channel Device Configuration +CONFIG_CHANDEV + The channel device layer is a layer to provide a consistent + interface for configuration & default machine check (devices + appearing & disappearing) handling on Linux for s/390 & z/Series + channel devices. + + s/390 & z/Series channel devices include among others + + lcs (the most common ethernet/token ring/fddi standard on + zSeries) + ctc/escon hi speed like serial link standard on zSeries + claw used to talk to cisco routers. + qeth gigabit ethernet. + + These devices use two channels one read & one write for + configuration & communication (& a third channel, the data + channel the case of gigabit ethernet). The motivation + behind developing this layer was that there was a lot of + duplicate code among the channel device drivers for + configuration. + + Also the lcs & ctc drivers tended to fight over + 3088/08's & 3088/1F's which could be either 2216/3172 + channel attached lcs compatible devices or escon/ctc pipes + had to be configured separately as they couldn't autodetect, + this is now simplified by doing the configuration in a single + place (the channel device layer). + + This layer isn't invasive & it is quite okay to use channel + drivers which don't use the channel device layer in + conjunction with drivers which do. + + For more info see the chandev manpage usually distributed in + in the Linux source tree. + SAB3036 tuner support CONFIG_TUNER_3036 Say Y here to include support for Philips SAB3036 compatible tuners. @@ -18026,15 +21348,24 @@ Compaq SMART2 support CONFIG_BLK_CPQ_DA - This is the driver for Compaq Smart Array controllers. - Everyone using these boards should say Y here. - See the file Documentation/cpqarray.txt for the current list of - boards supported by this driver, and for further information - on the use of this driver. + This is the driver for Compaq Smart Array controllers. Everyone + using these boards should say Y here. See the file + for the current list of boards + supported by this driver, and for further information on the use of + this driver. + +Show crashed user process info +CONFIG_PROCESS_DEBUG + Say Y to print all process fault locations to the console. This is + a debugging option; you probably do not want to set it unless you + are an S390 port maintainer. # # ARM options # +# CML2 transition note: CML1 asks ARCH_ARCA5K, then has ARCH_A5K and ARCH_ARK +# as subquestions. CML2 asks the subquestions in the armtype menu and makes +# ARCH_ARCA5K a derived symbol. ARM System type CONFIG_ARCH_ARCA5K This selects what ARM system you wish to build the kernel for. It @@ -18042,110 +21373,224 @@ to set this option to, please consult any information supplied with your system. +# Choice: armtype +A5000 +CONFIG_ARCH_A5K + Say Y here to to support the Acorn A5000. Linux can support the + internal IDE disk and CD-ROM interface, serial and parallel port, + and the floppy drive. Note that on some A5000s the floppy is + plugged into the wrong socket on the motherboard. + +Archimedes +CONFIG_ARCH_ARC + The Acorn Archimedes was an personal computer based on an 8K ARM2 + processor, released in 1987. It supported 512K of RAM and 2 800K + floppy disks. Picture and more detailed specifications at + . + +EBSA-110 +CONFIG_ARCH_EBSA110 + This is an evaluation board for the StrongARM processor available + from Digital. It has limited hardware on-board, including an onboard + Ethernet interface, two PCMCIA sockets, two serial ports and a + parallel port. + +RiscPC +CONFIG_ARCH_RPC + On the Acorn Risc-PC, Linux can support the internal IDE disk and + CD-ROM interface, serial and parallel port, and the floppy drive. + 2MB physical memory CONFIG_PAGESIZE_16 Say Y here if your Archimedes or A5000 system has only 2MB of memory, otherwise say N. The resulting kernel will not run on a machine with 4MB of memory. -Include support for the CATS +CATS CONFIG_ARCH_CATS Say Y here if you intend to run this kernel on the CATS. Saying N will reduce the size of the Footbridge kernel. -Include support for the EBSA285 +EBSA285 (addin mode) +CONFIG_ARCH_EBSA285_ADDIN + Say Y here if you intend to run this kernel on the EBSA285 card + in addin mode. + + Saying N will reduce the size of the Footbridge kernel. + +EBSA285 (host mode) CONFIG_ARCH_EBSA285_HOST Say Y here if you intend to run this kernel on the EBSA285 card in host ("central function") mode. Saying N will reduce the size of the Footbridge kernel. -Include support for the LinkUp Systems L7200 SDB +LinkUp Systems L7200 SDB CONFIG_ARCH_L7200 Say Y here if you intend to run this kernel on a LinkUp Systems L7200 Software Development Board which uses an ARM720T processor. Information on this board can be obtained at: - http://www.linkupsys.com/ + If you have any questions or comments about the Linux kernel port - to this board, send e-mail to sjhill@cotw.com + to this board, send e-mail to sjhill@cotw.com. -Include support for the NetWinder +NetWinder CONFIG_ARCH_NETWINDER Say Y here if you intend to run this kernel on the Rebel.COM NetWinder. Information about this machine can be found at: - http://www.netwinder.org/ + Saying N will reduce the size of the Footbridge kernel. -Include support for the Compaq Personal Server +P720T +CONFIG_ARCH_P720T + Say Y here if you intend to run this kernel on the ARM Prospector + 720T. + +Compaq Personal Server CONFIG_ARCH_PERSONAL_SERVER Say Y here if you intend to run this kernel on the Compaq Personal Server. - + Saying N will reduce the size of the Footbridge kernel. - The Compaq Personal Server is not available for purchase. + The Compaq Personal Server is not available for purchase. There are no product plans beyond the current research prototypes at this time. Information is available at: - - http://crl.research.compaq.com/projects/personalserver - + + + If you have any questions or comments about the Compaq Personal - Server, send e-mail to skiff@crl.dec.com + Server, send e-mail to skiff@crl.dec.com. -Include support for Assabet +Assabet CONFIG_SA1100_ASSABET Say Y here if you are using the Intel(R) StrongARM(R) SA-1110 Microprocessor Development Board (also known as the Assabet). -Include support for Neponset +Neponset CONFIG_ASSABET_NEPONSET Say Y here if you are using the Intel(R) StrongARM(R) SA-1110 Microprocessor Development Board (Assabet) with the SA-1111 Development Board (Nepon). -Include support for the Compaq iPAQ H3600 (Bitsy) -CONFIG_SA1100_BITSY - Say Y here if you intend to run this kernel on the Compaq iPAQ +Compaq iPAQ H3600 +CONFIG_SA1100_H3600 + Say Y here if you intend to run this kernel on the Compaq iPAQ H3600 handheld computer. Information about this machine and the Linux port to this machine can be found at: - http://www.handhelds.org/Compaq/index.html#iPAQ_H3600 - http://www.compaq.com/products/handhelds/pocketpc/ + + -Include support for Brutus +Brutus CONFIG_SA1100_BRUTUS Say Y here if you are using the Intel(R) StrongARM(R) SA-1100 Microprocessor Development Board (also known as the Brutus). -Include support for LART +LART CONFIG_SA1100_LART Say Y here if you are using the Linux Advanced Radio Terminal - (also known as the LART). See http://www.lart.tudelft.nl/ for + (also known as the LART). See for information on the LART. -Include support for GraphicsClient +GraphicsClient CONFIG_SA1100_GRAPHICSCLIENT Say Y here if you are using an Applied Data Systems Intel(R) StrongARM(R) SA-1100 based Graphics Client SBC. See - http://www.flatpanels.com/ for information on this system. + for information on this system. + +CerfBoard +CONFIG_SA1100_CERF + The Intrinsyc CerfBoard is based on the StrongARM 1110. + More information is available at: + . + + Say Y if configuring for an Intrinsyc CerfBoard. + Say N otherwise. + +nanoEngine +CONFIG_SA1100_NANOENGINE + The nanoEngine is a StrongARM 1110-based single board computer + from Bright Star Engineering. More information is available at: + . + + Say Y if configuring for a nanoEngine. + Say N otherwise. + +Pangolin +CONFIG_SA1100_PANGOLIN + Pangolin is a StrongARM 1110-based evaluation platform produced + by Dialogue Technology. It has EISA slots for ease of configuration + with SDRAM/Flash memory card, USB/Serial/Audio card, Compact Flash + card, and TFT-LCD card. -Include support for Victor + Say Y if configuring for a Pangolin. + Say N otherwise. + +Victor CONFIG_SA1100_VICTOR Say Y here if you are using a Visu Aide Intel(R) StrongARM(R) SA-1100 based Victor Digital Talking Book Reader. See - http://www.visuaide.com/pagevictor.en.html for information on + for information on this system. -Load kernel using Angel Debug Monitor -CONFIG_ANGELBOOT - Say Y if you plan to load the kernel using Angel, ARM Ltd's target - debug stub. If you are not using Angel, you must say N. It is - important to get this setting correct. +Support ARM610 processor +CONFIG_CPU_ARM610 + The ARM610 is the successor to the ARM3 processor + and was produced by VLSI Technology Inc. + + Say Y if you want support for the ARM610 processor. + Otherwise, say N. + +Support ARM710 processor +CONFIG_CPU_ARM710 + A 32-bit RISC microprocessor based on the ARM7 processor core + designed by Advanced RISC Machines Ltd. The ARM710 is the + successor to the ARM610 processor. It was released in + July 1994 by VLSI Technology Inc. + + Say Y if you want support for the ARM710 processor. + Otherwise, say N. + +Support ARM720T processor +CONFIG_CPU_ARM720T + A 32-bit RISC processor with 8kByte Cache, Write Buffer and + MMU built around an ARM7TDMI core. + + Say Y if you want support for the ARM720T processor. + Otherwise, say N. + +Support ARM920T processor +CONFIG_CPU_ARM920T + The ARM920T is licensed to be produced by numerous vendors, + and is used in the Maverick EP9312. More information at + . + + Say Y if you want support for the ARM920T processor. + Otherwise, say N. + +Support ARM1020 processor +CONFIG_CPU_ARM1020 + The ARM1020 is the cached version of the ARM10 processor, + with an addition of a floating-point unit. + + Say Y if you want support for the ARM1020 processor. + Otherwise, say N. + +Support StrongARM SA-110 processor +CONFIG_CPU_SA110 + The Intel StrongARM(R) SA-110 is a 32-bit microprocessor and + is available at five speeds ranging from 100 MHz to 233 MHz. + More information is available at + . + + Say Y if you want support for the SA-110 processor. + Otherwise, say N. Math emulation CONFIG_FPE_NWFPE @@ -18162,24 +21607,26 @@ You may say N here if you are going to load the Acorn FPEmulator early in the bootup. +FastFPE math emulation CONFIG_FPE_FASTFPE Say Y here to include the FAST floating point emulator in the kernel. This is an experimental much faster emulator which has only 32 bit - precision for the mantissa. It does not support any exceptions. This - makes it very simple, it is approximately 4-8 times faster than NWFPE. - - It should be sufficient for most programs. It is definitely not - suitable if you do scientific calculations that need double precision - for iteration formulas that sum up lots of very small numbers. If you - do not feel you need a faster FP emulation you should better choose + precision for the mantissa. It does not support any exceptions. + This makes it very simple, it is approximately 4-8 times faster than NWFPE. + It should be sufficient for most programs. It is definitely not + suitable if you do scientific calculations that need double + precision for iteration formulas that sum up lots of very small + numbers. If you do not feel you need a faster FP emulation you + should better choose NWFPE. + It is also possible to say M to build the emulator as a module - (fastfpe.o). But keep in mind that you should only load the FP emulator - early in the bootup. You should never change from NWFPE to FASTFPE or - vice versa in an active system! + (fastfpe.o). But keep in mind that you should only load the FP + emulator early in the bootup. You should never change from NWFPE to + FASTFPE or vice versa in an active system! -DS1620 Thermometer support +DS1620 thermometer support CONFIG_DS1620 Say Y here to include support for the thermal management hardware found in the NetWinder. This driver allows the user to control the @@ -18189,6 +21636,11 @@ It is recommended to be used on a NetWinder, but it is not a necessity. +Debug high memory support +CONFIG_DEBUG_HIGHMEM + This options enables addition error checking for high memory systems. + Disable for production systems. + Verbose kernel error messages CONFIG_DEBUG_ERRORS This option controls verbose debugging information which can be @@ -18205,7 +21657,7 @@ information that is reported is severely limited. Most people should say N here. -User fault debugging +Verbose user fault messages CONFIG_DEBUG_USER When a user program crashes due to an exception, the kernel can print a brief message explaining what the problem was. This is @@ -18229,22 +21681,18 @@ Kernel low-level debugging messages via footbridge serial port CONFIG_DEBUG_DC21285_PORT - Say Y here if you want the low-level print routines to direct their - output to the serial port in the DC21285 (Footbridge). - -Split initialisation functions into discardable section -CONFIG_TEXT_SECTIONS - If you say Y here, kernel code that is only used during - initialisation is collected into a special area of the kernel so - that it can be discarded and the memory reclaimed when - initialisation is complete. In addition, if the kernel you wish to - build is able to run on multiple architectures, it allows the unused - code to be discarded. Some versions of binutils, however, have a bug - that causes the kernel to crash during startup when this option is - enabled. Say Y unless you experience problems that you suspect may - be caused by this. + Say Y here if you want the debug print routines to direct their + output to the serial port in the DC21285 (Footbridge). Saying N + will cause the debug messages to appear on the first 16550 + serial port. + +Kernel low-level debugging messages via UART2 +CONFIG_DEBUG_CLPS711X_UART2 + Say Y here if you want the debug print routines to direct their + output to the second serial port on these devices. Saying N will + cause the debug messages to appear on the first serial port. -Disable pgtable cache (EXPERIMENTAL) +Disable pgtable cache CONFIG_NO_PGT_CACHE Normally the kernel maintains a `quicklist' of preallocated pagetable structures in order to increase performance. On machines @@ -18254,7 +21702,7 @@ RISC OS personality CONFIG_ARTHUR Say Y here to include the kernel code necessary if you want to run - Acorn RISC OS/Arthur binaries under Linux. This code is still very + Acorn RISC OS/Arthur binaries under Linux. This code is still very experimental; if this sounds frightening, say N and sleep in peace. You can also say M here to compile this support as a module (which will be called arthur.o). @@ -18265,9 +21713,9 @@ for the boot loader to pass arguments to the kernel. For these architectures, you should supply some command-line options at build time by entering them here. As a minimum, you should specify the - memory size and the root device (e.g., mem=64M root=/dev/nfs) + memory size and the root device (e.g., mem=64M root=/dev/nfs). -Hardware alignment trap (EXPERIMENTAL) +Kernel-mode alignment trap handler CONFIG_ALIGNMENT_TRAP ARM processors can not fetch/store information which is not naturally aligned on the bus, i.e., a 4 byte fetch must start at an @@ -18277,57 +21725,82 @@ correct operation of some network protocols. With an IP-only configuration it is safe to say N, otherwise say Y. -21285 serial port support +DC21285 serial port support CONFIG_SERIAL_21285 If you have a machine based on a 21285 (Footbridge) StrongARM(R)/ PCI bridge you can enable its onboard serial port by enabling this option. The device has major ID 4, minor 64. -Console on 21285 serial port +Console on DC21285 serial port CONFIG_SERIAL_21285_CONSOLE If you have enabled the serial port on the 21285 footbridge you can make it the console by answering Y to this option. SA1100 serial port support CONFIG_SERIAL_SA1100 + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainers. * If you have a machine based on a SA1100/SA1110 StrongARM CPU you can enable its onboard serial port by enabling this option. - Please read Documentation/arm/SA1100/serial_UART for further info. + Please read for further + info. Console on SA1100 serial port CONFIG_SERIAL_SA1100_CONSOLE + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainers. * If you have enabled the serial port on the SA1100/SA1110 StrongARM CPU you can make it the console by answering Y to this option. L7200 serial port support CONFIG_SERIAL_L7200 + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainers. * If you have a LinkUp Systems L7200 board you can enable its two onboard serial ports by enabling this option. The device numbers are major ID 4 with minor 64 and 65 respectively. Console on L7200 serial port CONFIG_SERIAL_L7200_CONSOLE + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainers. * If you have enabled the serial ports on the L7200 development board you can make the first serial port the console by answering Y to this option. L7200 SDB keyboard support CONFIG_KEYBOARD_L7200 + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainers. * Enable this option if you would like to be able to use a keyboard on a LinkUp Systems L7200 board. L7200 SDB Fujitsu keyboard support CONFIG_KEYBOARD_L7200_NORM + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainers. * Select the Fujitsu keyboard if you want a normal QWERTY style keyboard on the LinkUp SDB. L7200 SDB Prototype keyboard support CONFIG_KEYBOARD_L7200_DEMO + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainers. * Select the prototype keyboard if you want to play with the LCD/keyboard combination on the LinkUp SDB. Footbridge Mode CONFIG_HOST_FOOTBRIDGE + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainers. * The 21285 Footbridge chip can operate in either `host mode' or `add-in' mode. Say Y if your 21285 is in host mode, and therefore is the configuration master, otherwise say N. This must not be @@ -18365,7 +21838,7 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called nwflash.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + module, say M here and read . If you're not sure, say N. @@ -18378,22 +21851,22 @@ This driver is also available as a module and will be called srm_env.o if you build it as a module. - + Footbridge internal watchdog CONFIG_21285_WATCHDOG - The Intel Footbridge chip contains a builtin watchdog circuit. Say Y + The Intel Footbridge chip contains a builtin watchdog circuit. Say Y here if you wish to use this. Alternatively say M to compile the driver as a module, which will be called wdt285.o. - This driver does not work on all machines. In particular, early CATS - boards have hardware problems that will cause the machine to simply + This driver does not work on all machines. In particular, early CATS + boards have hardware problems that will cause the machine to simply lock up if the watchdog fires. "If in doubt, leave it out" - say N. -NetWinder WB977 watchdog +NetWinder WB83C977 watchdog CONFIG_977_WATCHDOG - Say Y here to include support for the WB977 watchdog included in + Say Y here to include support for the WB977 watchdog included in NetWinder machines. Alternatively say M to compile the driver as a module, which will be called wdt977.o. @@ -18406,27 +21879,34 @@ infrared communication and is supported by most laptops and PDA's. To use Linux support for the IrDA (tm) protocols, you will also need - some user-space utilities like irattach. For more information, see the file - Documentation/networking/irda.txt. You also want to read the - IR-HOWTO, available at http://www.linuxdoc.org/docs.html#howto . + some user-space utilities like irattach. For more information, see + the file . You also want to + read the IR-HOWTO, available at + . - This support is also available as a module called irda.o. If you + This support is also available as a module called irda.o. If you want to compile it as a module, say M here and read - Documentation/modules.txt. + . + +Ultra (connectionless) protocol +CONFIG_IRDA_ULTRA + Say Y here to support the connectionless Ultra IRDA protocol, also + called IrOBEX. IrDA protocol options CONFIG_IRDA_OPTIONS - Say Y here if you want to configure any of the following IrDA options. + Say Y here if you want to configure any of the following IrDA + options. -IrDA Cache last LSAP +IrDA cache last LSAP CONFIG_IRDA_CACHE_LAST_LSAP - Say Y here if you want IrLMP to cache the last LSAP used. This makes - sense since most frames will be sent/received on the same - connection. Enabling this option will save a hash-lookup per frame. + Say Y here if you want IrLMP to cache the last LSAP used. This + makes sense since most frames will be sent/received on the same + connection. Enabling this option will save a hash-lookup per frame. If unsure, say Y. -IrDA Fast RR's +IrDA Fast RRs CONFIG_IRDA_FAST_RR Say Y here is you want IrLAP to send fast RR (Receive Ready) frames when acting as a primary station. This will make IrLAP send out a RR @@ -18442,129 +21922,165 @@ If unsure, say N. -IrDA Debug +IrDA debugging information CONFIG_IRDA_DEBUG Say Y here if you want the IrDA subsystem to write debug information to your syslog. You can change the debug level in - /proc/sys/net/irda/debug + /proc/sys/net/irda/debug . If unsure, say Y (since it makes it easier to find the bugs). -IrLAP Compression support +IrLAP compression support CONFIG_IRDA_COMPRESSION Compression is _not_ part of the IrDA(tm) protocol specification, but it's working great! Linux is the first to try out compression support at the IrLAP layer. This means that you will only benefit from compression if you are running a Linux <-> Linux configuration. - + If you say Y here, you also need to say Y or M to a compression protocol below. -IrLAP Deflate Compression Protocol (EXPERIMENTAL) +IrLAP Deflate compression CONFIG_IRDA_DEFLATE Say Y here if you want to build support for the Deflate compression protocol. The deflate compression (GZIP) is exactly - the same as the one used by the PPP protocol. + the same as the one used by the PPP protocol. If you want to compile this compression support as a module, say M - here and read Documentation/modules.txt. The module will be called - irda_deflate.o. + here and read . The module will be + called irda_deflate.o. -IrLAN Protocol +IrLAN protocol CONFIG_IRLAN - Say Y here if you want to build support for the IrLAN protocol. If + Say Y here if you want to build support for the IrLAN protocol. If you want to compile it as a module (irlan.o), say M here and read - Documentation/modules.txt. IrLAN emulates an Ethernet and makes it - possible to put up a wireless LAN using infrared beams. + . IrLAN emulates an Ethernet and + makes it possible to put up a wireless LAN using infrared beams. - The IrLAN protocol can be used to talk with infrared access points - like the HP NetbeamIR, or the ESI JetEye NET. You can also connect - to another Linux machine running the IrLAN protocol for ad-hoc + The IrLAN protocol can be used to talk with infrared access points + like the HP NetbeamIR, or the ESI JetEye NET. You can also connect + to another Linux machine running the IrLAN protocol for ad-hoc networking! -IrNET Protocol +IrNET protocol CONFIG_IRNET - Say Y here if you want to build support for the IrNET protocol. If + Say Y here if you want to build support for the IrNET protocol. If you want to compile it as a module (irnet.o), say M here and read - Documentation/modules.txt. IrNET is a PPP driver, so you will also - need a working PPP subsystem (driver, daemon and config)... + . IrNET is a PPP driver, so you + will also need a working PPP subsystem (driver, daemon and + config)... - IrNET is an alternate way to tranfer TCP/IP traffic over IrDA. It - uses synchronous PPP over a set of point to point IrDA sockets. You + IrNET is an alternate way to tranfer TCP/IP traffic over IrDA. It + uses synchronous PPP over a set of point to point IrDA sockets. You can use it between Linux machine or with W2k. -IrCOMM Protocol +IrCOMM protocol CONFIG_IRCOMM - Say Y here if you want to build support for the IrCOMM protocol. If + Say Y here if you want to build support for the IrCOMM protocol. If you want to compile it as a module (you will get ircomm.o and - ircomm-tty.o), say M here and read Documentation/modules.txt. IrCOMM - implements serial port emulation, and makes it possible to use all - existing applications that understands TTY's with an infrared link. - Thus you should be able to use application like PPP, minicom and - others. Enabling this option will create two modules called ircomm - and ircomm_tty. + ircomm-tty.o), say M here and read . + IrCOMM implements serial port emulation, and makes it possible to + use all existing applications that understands TTY's with an + infrared link. Thus you should be able to use application like PPP, + minicom and others. Enabling this option will create two modules + called ircomm and ircomm_tty. IrTTY IrDA Device Driver CONFIG_IRTTY_SIR Say Y here if you want to build support for the IrTTY line - discipline. If you want to compile it as a module (irtty.o), say M - here and read Documentation/modules.txt. IrTTY makes it possible to - use Linux's own serial driver for all IrDA ports that are 16550 - compatible. Most IrDA chips are 16550 compatible so you should - probably say Y to this option. Using IrTTY will however limit the - speed of the connection to 115200 bps (IrDA SIR mode) + discipline. If you want to compile it as a module (irtty.o), say M + here and read . IrTTY makes it + possible to use Linux's own serial driver for all IrDA ports that + are 16550 compatible. Most IrDA chips are 16550 compatible so you + should probably say Y to this option. Using IrTTY will however + limit the speed of the connection to 115200 bps (IrDA SIR mode). If unsure, say Y. -IrPORT IrDA Device Driver +IrPORT IrDA serial driver CONFIG_IRPORT_SIR Say Y here if you want to build support for the IrPORT IrDA device driver. If you want to compile it as a module (irport.o), say M here - and read Documentation/modules.txt. IrPORT can be used instead of - IrTTY and sometimes this can be better. One example is if your IrDA - port does not have echo-canceling, which will work OK with IrPORT - since this driver is working in half-duplex mode only. You don't - need to use irattach with IrPORT, but you just insert it the same - way as FIR drivers (insmod irport io=0x3e8 irq=11). Notice that - IrPORT is a SIR device driver which means that speed is limited to - 115200 bps. + and read . IrPORT can be used + instead of IrTTY and sometimes this can be better. One example is + if your IrDA port does not have echo-canceling, which will work OK + with IrPORT since this driver is working in half-duplex mode only. + You don't need to use irattach with IrPORT, but you just insert it + the same way as FIR drivers (insmod irport io=0x3e8 irq=11). Notice + that IrPORT is a SIR device driver which means that speed is limited + to 115200 bps. If unsure, say Y. +USB IrDA FIR dongle Device Driver +CONFIG_USB_IRDA + Say Y here if you want to build support for the USB IrDA FIR Dongle + device driver. If you want to compile it as a module (irda-usb.o), + say M here and read . IrDA-USB + support the various IrDA USB dongles available and most of their + pecularities. Those dongles plug in the USB port of your computer, + are plug and play, and support SIR and FIR (4Mbps) speeds. On the + other hand, those dongles tend to be less efficient than a FIR + chipset. + + Please note that the driver is still experimental. And of course, + you will need both USB and IrDA support in your kernel... + Winbond W83977AF IrDA Device Driver CONFIG_WINBOND_FIR Say Y here if you want to build IrDA support for the Winbond - W83977AF super-io chipset. This driver should be used for the IrDA - chipset in the Corel NetWinder. The driver supports SIR, MIR and FIR - (4Mbps) speeds. + W83977AF super-io chipset. This driver should be used for the IrDA + chipset in the Corel NetWinder. The driver supports SIR, MIR and + FIR (4Mbps) speeds. If you want to compile it as a module, say M here and read - Documentation/modules.txt. The module will be called w83977af_ir.o. + . The module will be called + w83977af_ir.o. -NSC PC87108 IrDA Device Driver +NSC PC87108/PC87338 IrDA Device Driver CONFIG_NSC_FIR Say Y here if you want to build support for the NSC PC87108 and - PC87338 IrDA chipsets. This driver supports SIR, - MIR and FIR (4Mbps) speeds. + PC87338 IrDA chipsets. This driver supports SIR, + MIR and FIR (4Mbps) speeds. If you want to compile it as a module, say M here and read - Documentation/modules.txt. The module will be called nsc-ircc.o. + . The module will be called + nsc-ircc.o. + +National Semiconductor DP83820 series driver +CONFIG_NS83820 + This is a driver for the National Semiconductor DP83820 series + of gigabit ethernet MACs. Cards using this chipset include + the D-Link DGE-500T, PureData's PDP8023Z-TG, SMC's SMC9462TX, + SOHO-GA2000T, SOHO-GA2500T. The driver supports the use of + zero copy. -Toshiba Type-O IR Port Device Driver +Toshiba Type-O IR Port device driver CONFIG_TOSHIBA_FIR Say Y here if you want to build support for the Toshiba Type-O IR - chipset. This chipset is used by the Toshiba Libretto 100CT, and - many more laptops. If you want to compile it as a module, say M here - and read Documentation/modules.txt. The module will be called - toshoboe.o. + chipset. This chipset is used by the Toshiba Libretto 100CT, and + many more laptops. If you want to compile it as a module, say M + here and read . The module will be + called toshoboe.o. -SMC IrCC (Experimental) +SMC IrCC CONFIG_SMC_IRCC_FIR Say Y here if you want to build support for the SMC Infrared - Communications Controller. It is used in the Fujitsu Lifebook 635t - and Sony PCG-505TX. If you want to compile it as a module, say M - here and read Documentation/modules.txt. The module will be called - smc-ircc.o. + Communications Controller. It is used in the Fujitsu Lifebook 635t + and Sony PCG-505TX. If you want to compile it as a module, say M + here and read . The module will be + called smc-ircc.o. + +ALi M5123 FIR Controller Driver +CONFIG_ALI_FIR + Say Y here if you want to build support for the ALi M5123 FIR + Controller. The ALi M5123 FIR Controller is embedded in ALi M1543C, + M1535, M1535D, M1535+, M1535D Sourth Bridge. This driver supports + SIR, MIR and FIR (4Mbps) speeds. + + If you want to compile it as a module, say M here and read + . The module will be called + ali-ircc.o. VLSI 82C147 PCI-IrDA Controller Driver CONFIG_VLSI_FIR @@ -18583,69 +22099,76 @@ or M to the driver for your particular dongle below. Note that the answer to this question won't directly affect the - kernel: saying N will just cause this configure script to skip all + kernel: saying N will just cause the configurator to skip all the questions about serial dongles. -ESI JetEye PC Dongle +ESI JetEye PC dongle CONFIG_ESI_DONGLE Say Y here if you want to build support for the Extended Systems - JetEye PC dongle. If you want to compile it as a module, say M here - and read Documentation/modules.txt. The ESI dongle attaches to the - normal 9-pin serial port connector, and can currently only be used - by IrTTY. To activate support for ESI dongles you will have to + JetEye PC dongle. If you want to compile it as a module, say M here + and read . The ESI dongle attaches + to the normal 9-pin serial port connector, and can currently only be + used by IrTTY. To activate support for ESI dongles you will have to start irattach like this: "irattach -d esi". ACTiSYS IR-220L and IR220L+ dongle CONFIG_ACTISYS_DONGLE - Say Y here if you want to build support for the ACTiSYS - IR-220L and IR220L+ dongles. If you want to compile it as a module, - say M here and read Documentation/modules.txt. The ACTiSYS dongles + Say Y here if you want to build support for the ACTiSYS IR-220L and + IR220L+ dongles. If you want to compile it as a module, say M here + and read . The ACTiSYS dongles attaches to the normal 9-pin serial port connector, and can - currently only be used by IrTTY. To activate support for ACTiSYS - dongles you will have to start irattach like this: + currently only be used by IrTTY. To activate support for ACTiSYS + dongles you will have to start irattach like this: "irattach -d actisys" or "irattach -d actisys+". Tekram IrMate 210B dongle CONFIG_TEKRAM_DONGLE - Say Y here if you want to build support for the Tekram IrMate 210B - dongle. If you want to compile it as a module, say M here - and read Documentation/modules.txt. The Tekram dongle attaches to - the normal 9-pin serial port connector, and can currently only be - used by IrTTY. To activate support for Tekram dongles you will have - to start irattach like this: "irattach -d tekram". + Say Y here if you want to build support for the Tekram IrMate 210B + dongle. If you want to compile it as a module, say M here and read + . The Tekram dongle attaches to the + normal 9-pin serial port connector, and can currently only be used + by IrTTY. To activate support for Tekram dongles you will have to + start irattach like this: "irattach -d tekram". Greenwich GIrBIL dongle CONFIG_GIRBIL_DONGLE Say Y here if you want to build support for the Greenwich GIrBIL - dongle. If you want to compile it as a module, say M here and read - Documentation/modules.txt. The Greenwich dongle attaches to the - normal 9-pin serial port connector, and can currently only be used - by IrTTY. To activate support for Greenwich dongles you will have to - insert "irattach -d girbil" in the /etc/irda/drivers script. + dongle. If you want to compile it as a module, say M here and read + . The Greenwich dongle attaches to + the normal 9-pin serial port connector, and can currently only be + used by IrTTY. To activate support for Greenwich dongles you will + have to insert "irattach -d girbil" in the /etc/irda/drivers script. -Parallax Litelink dongle +Parallax LiteLink dongle CONFIG_LITELINK_DONGLE Say Y here if you want to build support for the Parallax Litelink - dongle. If you want to compile it as a module, say M here and read - Documentation/modules.txt. The Parallax dongle attaches to the - normal 9-pin serial port connector, and can currently only be used - by IrTTY. To activate support for Parallax dongles you will have to - start irattach like this "irattach -d litelink". + dongle. If you want to compile it as a module, say M here and read + . The Parallax dongle attaches to + the normal 9-pin serial port connector, and can currently only be + used by IrTTY. To activate support for Parallax dongles you will + have to start irattach like this "irattach -d litelink". Old Belkin dongle CONFIG_OLD_BELKIN_DONGLE Say Y here if you want to build support for the Adaptec Airport 1000 - and 2000 dongles. If you want to compile it as a module, say M here - and read Documentation/modules.txt. The module will be called - old_belkin.o. Some information is contained in the comments at the - top of drivers/net/irda/old_belkin.c. + and 2000 dongles. If you want to compile it as a module, say M here + and read . The module will be + called old_belkin.o. Some information is contained in the comments + at the top of . VME (Motorola and BVM) support CONFIG_VME Say Y here if you want to build a kernel for a 680x0 based VME - board. Boards currently supported include Motorola boards MVME162, - MVME166, MVME167, MVME172, and MVME177. BVME4000 and BVME6000 - boards from BVM Ltd are also supported. + board. Boards currently supported include Motorola boards MVME147, + MVME162, MVME166, MVME167, MVME172, and MVME177. BVME4000 and + BVME6000 boards from BVM Ltd are also supported. + +MVME147 support +CONFIG_MVME147 + Say Y to include support for early Motorola VME boards. This will + build a kernel which can run on MVME147 single-board computers. If + you select this option you will have to select the appropriate + drivers for SCSI, Ethernet and serial ports later on. MVME162, 166 and 167 support CONFIG_MVME16x @@ -18675,6 +22198,16 @@ is hardwired on. The 53c710 SCSI driver is known to suffer from this problem. +WD33C93 SCSI driver for MVME147 +CONFIG_MVME147_SCSI + Support for the on-board SCSI controller on the Motorola MVME147 + single-board computer. + +SCC support for MVME147 serial ports +CONFIG_MVME147_SCC + This is the driver for the serial ports on the Motorola MVME147 + boards. Everyone using one of these boards should say Y here. + NCR53C710 SCSI driver for MVME16x CONFIG_MVME16x_SCSI The Motorola MVME162, 166, 167, 172 and 177 boards use the NCR53C710 @@ -18687,19 +22220,26 @@ SCSI controller chip. Almost everyone using one of these boards will want to say Y to this question. +MVME147 (Lance) Ethernet support +CONFIG_MVME147_NET + Support for the on-board Ethernet interface on the Motorola MVME147 + single-board computer. Say Y here to include the + driver for this chip in your kernel. If you want to compile it as + a module, say M here and read . + MVME16x Ethernet support CONFIG_MVME16x_NET This is the driver for the Ethernet interface on the Motorola MVME162, 166, 167, 172 and 177 boards. Say Y here to include the driver for this chip in your kernel. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read . BVME6000 Ethernet support CONFIG_BVME6000_NET This is the driver for the Ethernet interface on BVME4000 and BVME6000 VME boards. Say Y here to include the driver for this chip in your kernel. If you want to compile it as a module, say M here - and read Documentation/modules.txt. + and read . CD2401 support for MVME166/7 serial ports CONFIG_SERIAL167 @@ -18726,11 +22266,707 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called display7seg.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + as a module, say M here and read . If you do not have a CompactPCI model CP1400 or CP1500, or another UltraSPARC-IIi-cEngine boardset with a 7-segment display, - you should say N to this option. + you should say N to this option. + +# Choice: cristype +Etrax-100-LX-v1 +CONFIG_ETRAX100LX + Support version 1 of the Etrax 100LX. + +Etrax-100-LX-v2 +CONFIG_ETRAX100LX_V2 + Support version 2 of the Etrax 100LX. + +Etrax-100-LX-for-xsim-simulator +CONFIG_SVINTO_SIM + Support the xsim ETRAX Simulator. + +DRAM size (dec, in MB) +CONFIG_ETRAX_DRAM_SIZE + Size of DRAM (decimal in MB) typically 2, 8 or 16. + +ETRAX Flash Memory configuration +CONFIG_ETRAX_FLASH_BUSWIDTH + Width in bytes of the Flash bus (1, 2 or 4). Is usually 2. + +LED configuration on PA +CONFIG_ETRAX_PA_LEDS + The Etrax network driver is responsible for flashing LED's when + packets arrive and are sent. It uses macros defined in + , and those macros are defined after what + YOU choose in this option. The actual bits used are configured + separately. Select this if the LEDs are on port PA. Some products + put the leds on PB or a memory-mapped latch (CSP0) instead. + +LED configuration on PB +CONFIG_ETRAX_PB_LEDS + The Etrax network driver is responsible for flashing LED's when + packets arrive and are sent. It uses macros defined in + , and those macros are defined after what + YOU choose in this option. The actual bits used are configured + separately. Select this if the LEDs are on port PB. Some products + put the leds on PA or a memory-mapped latch (CSP0) instead. + +LED configuration on CSP0 +CONFIG_ETRAX_CSP0_LEDS + The Etrax network driver is responsible for flashing LED's when + packets arrive and are sent. It uses macros defined in + , and those macros are defined after what + YOU choose in this option. The actual bits used are configured + separately. Select this if the LEDs are on a memory-mapped latch + using chip select CSP0, this is mapped at 0x90000000. + Some products put the leds on PA or PB instead. + +No LED at all +CONFIG_ETRAX_NO_LEDS + Select this option if you don't have any LED at all. + +First green LED bit +CONFIG_ETRAX_LED1G + Bit to use for the first green LED. + Most Axis products use bit 2 here. + +First red LED bit +CONFIG_ETRAX_LED1R + Bit to use for the first red LED. + Most Axis products use bit 3 here. + For products with only one controllable LED, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Second green LED bit +CONFIG_ETRAX_LED2G + Bit to use for the second green LED. The "Active" LED. + Most Axis products use bit 4 here. + For products with only one controllable LED, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Second red LED bit +CONFIG_ETRAX_LED2R + Bit to use for the second red LED. + Most Axis products use bit 5 here. + For products with only one controllable LED, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Third green LED bit +CONFIG_ETRAX_LED3G + Bit to use for the third green LED. The "Drive" LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Third red LED bit +CONFIG_ETRAX_LED3R + Bit to use for the third red LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Fourth green LED bit +CONFIG_ETRAX_LED4G + Bit to use for the fourth green LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Fourth red LED bit +CONFIG_ETRAX_LED4R + Bit to use for the fourth red LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Fifth green LED bit +CONFIG_ETRAX_LED5G + Bit to use for the fifth green LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Fifth red LED bit +CONFIG_ETRAX_LED5R + Bit to use for the fifth red LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Sixth green LED bit +CONFIG_ETRAX_LED6G + Bit to use for the sixth green LED. The "Drive" LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Sixth red LED bit +CONFIG_ETRAX_LED6R + Bit to use for the sixth red LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Seventh green LED bit +CONFIG_ETRAX_LED7G + Bit to use for the seventh green LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Seventh red LED bit +CONFIG_ETRAX_LED7R + Bit to use for the seventh red LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Eighth yellow LED bit +CONFIG_ETRAX_LED8Y + Bit to use for the eighth yellow LED. The "Drive" LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Ninth yellow LED bit +CONFIG_ETRAX_LED9Y + Bit to use for the ninth yellow LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Tenth yellow LED bit +CONFIG_ETRAX_LED10Y + Bit to use for the tenth yellow LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Eleventh yellow LED bit +CONFIG_ETRAX_LED11Y + Bit to use for the eleventh yellow LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Twelfth red LED bit +CONFIG_ETRAX_LED12R + Bit to use for the twelfth red LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Flash LED off during activity +CONFIG_ETRAX_LED_OFF_DURING_ACTIVITY + This option allows you to decide whether the network LED (and + Bluetooth LED in case you use Bluetooth) will be on or off when + the network is connected, and whether it should flash off or on + when there is activity. If you say y to this option the network + LED will be lit when there is a connection, and will flash off + when there is activity. + +PA button configuration +CONFIG_ETRAX_PA_BUTTON_BITMASK + This is a bitmask with information about what bits on PA that + are used for buttons. + Most products has a so called TEST button on PA1, if that's true + use 02 here. + Use 00 if there are no buttons on PA. + If the bitmask is <> 00 a button driver will be included in the gpio + driver. Etrax general I/O support must be enabled. + +PA changeable direction bits +CONFIG_ETRAX_PA_CHANGEABLE_DIR + This is a bitmask with information of what bits in PA that a user + can change direction on using ioctl's. + Bit set = changeable. + You probably want 00 here. + +PA changeable data bits +CONFIG_ETRAX_PA_CHANGEABLE_BITS + This is a bitmask with information of what bits in PA that a user + can change change the value on using ioctl's. + Bit set = changeable. + You probably want 00 here. + +PA changeable direction bits +CONFIG_ETRAX_PB_CHANGEABLE_DIR + This is a bitmask with information of what bits in PB that a user + can change direction on using ioctl's. + Bit set = changeable. + You probably want 00 here. + +PB changeable data bits +CONFIG_ETRAX_PB_CHANGEABLE_BITS + This is a bitmask with information of what bits in PB that a user + can change the value on using ioctl's. + Bit set = changeable. + You probably want 00 here. + +Kernel debugger (kgdb) +CONFIG_ETRAX_KGDB + The CRIS version of gdb can be used to remotely debug a running + Linux kernel via the serial debug port. Provided you have gdb-cris + installed, run gdb-cris vmlinux, then type + + (gdb) set remotebaud 115200 <- kgdb uses 115200 as default + (gdb) target remote /dev/ttyS0 <- maybe you use another port + + This should connect you to your booted kernel (or boot it now if you + didn't before). The kernel halts when it boots, waiting for gdb if + this option is turned on! + +Etrax bus waitstates +CONFIG_ETRAX_DEF_R_WAITSTATES + Waitstates for SRAM, Flash and peripherials (not DRAM). 95f8 is a + good choice for most Axis products... + +Etrax bus configuration +CONFIG_ETRAX_DEF_R_BUS_CONFIG + Assorted bits controlling write mode, DMA burst length etc. 104 is + a good choice for most Axis products... + +Etrax SDRAM configuration +CONFIG_ETRAX_SDRAM + Enable this if you use SDRAM chips and configure + R_SDRAM_CONFIG and R_SDRAM_TIMING as well. + +DRAM size (dec, in MB) +CONFIG_ETRAX_DEF_R_DRAM_CONFIG + The R_DRAM_CONFIG register specifies everything on how the DRAM + chips in the system are connected to the Etrax CPU. This is + different depending on the manufacturer, chip type and number of + chips. So this value often needs to be different for each Axis + product. + +Etrax DRAM timing +CONFIG_ETRAX_DEF_R_DRAM_TIMING + Different DRAM chips have different speeds. Current Axis products + use 50ns DRAM chips which can use the timing: 5611. + +Etrax SDRAM configuration +CONFIG_ETRAX_DEF_R_SDRAM_CONFIG + The R_SDRAM_CONFIG register specifies everything on how the SDRAM + chips in the system are connected to the Etrax CPU. This is + different depending on the manufacturer, chip type and number of + chips. So this value often needs to be different for each Axis + product. + +Etrax SDRAM timing +CONFIG_ETRAX_DEF_R_SDRAM_TIMING + Different SDRAM chips have different timing. + +Etrax General port A direction +CONFIG_ETRAX_DEF_R_PORT_PA_DIR + Configures the direction of general port A bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +Etrax General port A data +CONFIG_ETRAX_DEF_R_PORT_PA_DATA + Configures the initial data for the general port A bits. Most + products should use 00 here. + +Etrax General port B config +CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG + Configures the type of the general port B bits. 1 is chip select, + 0 is port. Most products should use 00 here. + +Etrax General port B direction +CONFIG_ETRAX_DEF_R_PORT_PB_DIR + Configures the direction of general port B bits. 1 is out, 0 is in. + This is often totally different depending on the product used. Bits + 0 and 1 on port PB are usually used for I2C communication, but the + kernel I2C driver sets the appropriate directions itself so you + don't need to take that into consideration when setting this option. + If you don't know what to use, it is always safe to put all as + inputs. + +Etrax General port B data +CONFIG_ETRAX_DEF_R_PORT_PB_DATA + Configures the initial data for the general port A bits. Most + products should use FF here. + +Etrax General port device +CONFIG_ETRAX_GPIO + Enables the Etrax general port device (major 120, minors 0 and 1). + You can use this driver to access the general port bits. It supports + these ioctl's: + #include + fd = open("/dev/gpioa", O_RDWR); // or /dev/gpiob + ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS), bits_to_set); + ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS), bits_to_clear); + val = ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_READBITS), NULL); + Remember that you need to setup the port directions appropriately in + the General configuration. + +Etrax parallel data support +CONFIG_ETRAX_PARDATA + Adds support for writing data to the parallel port par0 of the ETRAX + 100. If you create a character special file with major number 126, + you can write to the data bits of par0. + Note: you need to disable Etrax100 parallel port support. + +Etrax parallel LCD (HD44780) Driver +CONFIG_ETRAX_LCD_HD44780 + Adds support for a HD44780 controlled LCD connected to the parallel + port par0 of the Etrax. + +Etrax Serial port ser0 support +CONFIG_ETRAX_SERIAL + Enables the ETRAX 100 serial driver for ser0 (ttyS0) + You probably want this enabled. + +/proc/serial entry +CONFIG_ETRAX_SERIAL_PROC_ENTRY + Enables /proc/serial entry where errors and statistics can be + viewed. CONFIG_PROC_FS must also be set for this to work. + +Etrax Serial port fast flush of DMA using fast timer API +CONFIG_ETRAX_SERIAL_FAST_TIMER + Select this to have the serial DMAs flushed at a higher rate than + normally, possible by using the fast timer API, the timeout is + approx. 4 character times. + If unsure, say N. + +Etrax Serial port fast flush of DMA +CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST + Select this to have the serial DMAs flushed at a higher rate than + normally possible through a fast timer interrupt (currently at + 15360 Hz). + If unsure, say N. + +Etrax Serial port receive flush timeout +CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS + Number of timer ticks between flush of receive fifo (1 tick = 10ms). + Try 0-3 for low latency applications. Approx 5 for high load + applications (e.g. PPP). Maybe this should be more adaptive some + day... + +Etrax Serial port ser0 DTR, RI, DSR and CD support on PB +CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB + Enables the status and control signals DTR, RI, DSR and CD on PB for + ser0. + +Serial port 1 enabled +CONFIG_ETRAX_SERIAL_PORT1 + Enables the ETRAX 100 serial driver for ser1 (ttyS1). + +Etrax Serial port ser1 DTR, RI, DSR and CD support on PB +CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB + Enables the status and control signals DTR, RI, DSR and CD on PB for + ser1. + +Serial port 2 enabled +CONFIG_ETRAX_SERIAL_PORT2 + Enables the ETRAX 100 serial driver for ser2 (ttyS2). + +Etrax Serial port ser2 DTR, RI, DSR and CD support on PA +CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA + Enables the status and control signals DTR, RI, DSR and CD on PA for + ser2. + +Serial port 3 enabled +CONFIG_ETRAX_SERIAL_PORT3 + Enables the ETRAX 100 serial driver for ser3 (ttyS3). + +Etrax100 RS-485 support +CONFIG_ETRAX_RS485 + Enables support for RS-485 serial communication. For a primer on + RS-485, see . + +Etrax100 RS-485 mode on PA +CONFIG_ETRAX_RS485_ON_PA + Control Driver Output Enable on RS485 tranceiver using a pin on PA + port: + Axis 2400/2401 uses PA 3. + +Etrax100 RS-485 mode on PA bit +CONFIG_ETRAX_RS485_ON_PA_BIT + Control Driver Output Enable on RS485 tranceiver using a this bit + on PA port. + +Ser0 DTR on PB bit +CONFIG_ETRAX_SER0_DTR_ON_PB_BIT + Specify the pin of the PB port to carry the DTR signal for serial + port 0. + +Ser0 RI on PB bit +CONFIG_ETRAX_SER0_RI_ON_PB_BIT + Specify the pin of the PB port to carry the RI signal for serial + port 0. + +Ser0 DSR on PB bit +CONFIG_ETRAX_SER0_DSR_ON_PB_BIT + Specify the pin of the PB port to carry the DSR signal for serial + port 0. + +Ser0 CD on PB bit +CONFIG_ETRAX_SER0_CD_ON_PB_BIT + Specify the pin of the PB port to carry the CD signal for serial + port 0. + +Ser1 DTR on PB bit +CONFIG_ETRAX_SER1_DTR_ON_PB_BIT + Specify the pin of the PB port to carry the DTR signal for serial + port 1. + +Ser1 RI on PB bit +CONFIG_ETRAX_SER1_RI_ON_PB_BIT + Specify the pin of the PB port to carry the RI signal for serial + port 1. + +Ser1 DSR on PB bit +CONFIG_ETRAX_SER1_DSR_ON_PB_BIT + Specify the pin of the PB port to carry the DSR signal for serial + port 1. + +Ser1 CD on PB bit +CONFIG_ETRAX_SER1_CD_ON_PB_BIT + Specify the pin of the PB port to carry the CD signal for serial + port 1. + +Ser2 DTR on PA bit +CONFIG_ETRAX_SER2_DTR_ON_PA_BIT + Specify the pin of the PA port to carry the DTR signal for serial + port 2. + +Ser2 RI on PA bit +CONFIG_ETRAX_SER2_RI_ON_PA_BIT + Specify the pin of the PA port to carry the RI signal for serial + port 2. + +Ser2 DSR on PA bit +CONFIG_ETRAX_SER2_DSR_ON_PA_BIT + Specify the pin of the PA port to carry the DTR signal for serial + port 2. + +Ser2 CD on PA bit +CONFIG_ETRAX_SER2_CD_ON_PA_BIT + Specify the pin of the PA port to carry the CD signal for serial + port 2. + +Etrax100 RS-485 disable receiver +CONFIG_ETRAX_RS485_DISABLE_RECEIVER + It's necessary to disable the serial receiver to avoid serial + loopback. Not all products are able to do this in software only. + Axis 2400/2401 must disable receiver. + +Etrax100 I2C Support +CONFIG_ETRAX_I2C + Enables an I2C driver on PB0 and PB1 on ETRAX100. + EXAMPLE usage: + i2c_arg = I2C_WRITEARG(STA013_WRITE_ADDR, reg, val); + ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_WRITEREG), i2c_arg); + i2c_arg = I2C_READARG(STA013_READ_ADDR, reg); + val = ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_READREG), i2c_arg); + +Etrax100 I2C configuration +CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C + Select whether to use the special I2C mode in the PB I/O register or + not. This option needs to be selected in order to use some drivers + that access the I2C I/O pins directly instead of going through the + I2C driver, like the DS1302 realtime-clock driver. If you are + uncertain, choose Y here. + +Etrax100 I2C EEPROM (NVRAM) support +CONFIG_ETRAX_I2C_EEPROM + Enables I2C EEPROM (non-volatile RAM) on PB0 and PB1 using the I2C + driver. Select size option: Probed, 2k, 8k, 16k. + (Probing works for 2k and 8k but not that well for 16k) + +Etrax100 I2C EEPROM (NVRAM) size/16kB +CONFIG_ETRAX_I2C_EEPROM_16KB + Use a 16kB EEPROM. + +Etrax100 I2C EEPROM (NVRAM) size/2kB +CONFIG_ETRAX_I2C_EEPROM_2KB + Use a 2kB EEPROM. + +Etrax100 I2C EEPROM (NVRAM) size/8kB +CONFIG_ETRAX_I2C_EEPROM_8KB + Use a 8kB EEPROM. + +Etrax100 I2C EEPROM (NVRAM) size/probe +CONFIG_ETRAX_I2C_EEPROM_PROBE + Specifies size or auto probe of the EEPROM size. + Options: Probed, 2k, 8k, 16k. + (Probing works for 2k and 8k but not that well for 16k) + +Etrax DS1302 Real-Time Clock driver +CONFIG_ETRAX_DS1302 + Enables the driver for the DS1302 Real-Time Clock battery-backed + chip on some products. The kernel reads the time when booting, and + the date can be set using ioctl(fd, RTC_SET_TIME, &rt) with rt a + rtc_time struct (see ) on the /dev/rtc + device, major 121. You can check the time with cat /proc/rtc, but + normal time reading should be done using libc function time and + friends. + +Etrax DS1302 RST on the Generic Port +CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT + If your product has the RST signal line for the DS1302 RTC on the + Generic Port then say Y here, otherwise leave it as N in which + case the RST signal line is assumed to be connected to Port PB + (just like the SCL and SDA lines). + +Etrax DS1302 RST bit number +CONFIG_ETRAX_DS1302_RSTBIT + This is the bit number for the RST signal line of the DS1302 RTC on + the selected port. If you have selected the generic port then it + should be bit 27, otherwise your best bet is bit 5. + +Etrax DS1302 SCL bit number +CONFIG_ETRAX_DS1302_SCLBIT + This is the bit number for the SCL signal line of the DS1302 RTC on + Port PB. This is probably best left at 3. + +Etrax DS1302 SDA bit number +CONFIG_ETRAX_DS1302_SDABIT + This is the bit number for the SDA signal line of the DS1302 RTC on + Port PB. This is probably best left at 2. + +Etrax 100 IDE Reset +CONFIG_ETRAX_IDE_CSP0_8_RESET + Configures the pin used to reset the IDE bus. + +Etrax 100 IDE Reset +CONFIG_ETRAX_IDE_CSPE1_16_RESET + Configures the pin used to reset the IDE bus. + +Etrax 100 ATA/IDE support +CONFIG_ETRAX_IDE_DELAY + Sets the time to wait for disks to regain consciousness after reset. + +Etrax 100 IDE Reset +CONFIG_ETRAX_IDE_G27_RESET + Configures the pin used to reset the IDE bus. + +IDE reset on PB Bit 7 +CONFIG_ETRAX_IDE_PB7_RESET + Configures the pin used to reset the IDE bus. + +USB 1.1 host +CONFIG_ETRAX_USB_HOST + This option enables the host functionality of the ETRAX 100LX + built-in USB controller. In host mode the controller is designed + for CTRL and BULK traffic only, INTR traffic may work as well + however (depending on the requirements of timeliness). + +USB 1.1 host port 1 enabled +CONFIG_ETRAX_USB_HOST_PORT1 + This option enables port 1 of the ETRAX 100LX USB root hub (RH). + +USB 1.1 host port 2 enabled +CONFIG_ETRAX_USB_HOST_PORT2 + This option enables port 2 of the ETRAX 100LX USB root hub (RH). + +ETRAX 100LX 10/100Mbit Ethernet controller +CONFIG_ETRAX_ETHERNET + This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet + controller. + +ETRAX 100LX Synchronous serial ports +CONFIG_ETRAX_SYNCHRONOUS_SERIAL + This option enables support for the ETRAX 100LX built-in + synchronous serial ports. These ports are used for continuous + streamed data like audio. The default setting is compatible + with the STA 013 MP3 decoder, but can easily be tuned to fit + any other audio encoder/decoder and SPI. + +ETRAX 100LX Synchronous serial port 0 enabled +CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0 + Enables the ETRAX 100LX synchronous serial port 0 (syncser0). + +ETRAX 100LX Synchronous serial port 0 uses DMA +CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA + Makes synchronous serial port 0 use DMA. + +ETRAX 100LX Synchronous serial port 1 enabled +CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1 + Enables the ETRAX 100LX synchronous serial port 1 (syncser1). + +ETRAX 100LX Synchronous serial port 1 uses DMA +CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA + Makes synchronous serial port 1 use DMA. + +Delay for drives to regain consciousness +CONFIG_IDE_DELAY + Number of seconds to wait for IDE drives to spin up after an IDE + reset. + +ARTPEC-1 support +CONFIG_JULIETTE + The ARTPEC-1 is a video-compression chip used in the AXIS 2100 + network camera, which is built around an ETRAX-100 board. With this + option selected, the ETRAX kernel configures a DMA channel at boot + time to talk to the chip. + +Axis flash-map support +CONFIG_ETRAX_AXISFLASHMAP + This option enables MTD mapping of flash devices. Needed to use + flash memories. If unsure, say Y. + +Byte-offset of partition table sector +CONFIG_ETRAX_PTABLE_SECTOR + Byte-offset of the partition table in the first flash chip. + The default value is 64kB and should not be changed unless + you know exactly what you are doing. The only valid reason + for changing this is when the flash block size is bigger + than 64kB (e.g. when using two parallel 16 bit flashes). + +Enable Etrax100 watchdog +CONFIG_ETRAX_WATCHDOG + Enable the built-in watchdog timer support on Etrax100 embedded + network computers. + +# Choice: crisdebug +Serial-0 +CONFIG_ETRAX_DEBUG_PORT0 + Choose a serial port for the ETRAX debug console. Default to + port 0. + +Etrax debug port on ser1 +CONFIG_ETRAX_DEBUG_PORT1 + Use serial port 1 for the console. + +Etrax debug port on ser2 +CONFIG_ETRAX_DEBUG_PORT2 + Use serial port 2 for the console. + +Etrax debug port on ser3 +CONFIG_ETRAX_DEBUG_PORT3 + Use serial port 3 for the console. + +No Etrax debug port +CONFIG_ETRAX_DEBUG_PORT_NULL + Disable serial-port debugging. + +Parallel port support +CONFIG_ETRAX_PARPORT + Say Y here to enable the ETRAX on-board parallel ports. + +Parallel port 0 enabled +CONFIG_ETRAX_PARALLEL_PORT0 + Say Y here to enable parallel port 0. + +Parallel port 1 enabled +CONFIG_ETRAX_PARALLEL_PORT1 + Say Y here to enable parallel port 1. + +# Choice: crisrescue +Select a product rescue port +CONFIG_ETRAX_RESCUE_SER0 + Select one of the four serial ports as a rescue port. The default + is port 0. + +Serial-1 +CONFIG_ETRAX_RESCUE_SER1 + Use serial port 1 as the rescue port. + +Serial-2 +CONFIG_ETRAX_RESCUE_SER2 + Use serial port 2 as the rescue port. + +Serial-3 +CONFIG_ETRAX_RESCUE_SER3 + Use serial port 3 as the rescue port. + +RIO Hardware Watchdog support +CONFIG_WATCHDOG_RIO + Say Y here to support the hardware watchdog capability on Sun RIO + machines. The watchdog timeout period is normally one minute but + can be changed with a boot-time parameter. CP1XXX Hardware Watchdog support CONFIG_WATCHDOG_CP1XXX @@ -18740,12 +22976,22 @@ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called cpwatchdog.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + as a module, say M here and read . If you do not have a CompactPCI model CP1400 or CP1500, or another UltraSPARC-IIi-cEngine boardset with hardware watchdog, - you should say N to this option. + you should say N to this option. +# Choice: ia64type +Itanium +CONFIG_ITANIUM + Select your IA64 processor type. The default is Intel Itanium. + +McKinley +CONFIG_MCKINLEY + Select this to configure for a McKinley processor. + +# Choice: ia64system IA-64 system type CONFIG_IA64_GENERIC This selects the system type of your hardware. A "generic" kernel @@ -18753,19 +22999,21 @@ a kernel for your specific system, it will be faster and smaller. To find out what type of IA-64 system you have, you may want to - check the IA-64 Linux web site at http://www.linux-ia64.org/. + check the IA-64 Linux web site at . As of the time of this writing, most hardware is DIG compliant, so the "DIG-compliant" option is usually the right choice. - HP-simulator For the HP simulator (http://software.hp.com/ia64linux/). + HP-simulator For the HP simulator + (). SN1-simulator For the SGI SN1 simulator. - DIG-compliant For DIG ("Developer's Interface Guide") compliant system. + DIG-compliant For DIG ("Developer's Interface Guide") compliant + system. If you don't know what to do, choose "generic". +# Choice: pagesize Kernel page size CONFIG_IA64_PAGE_SIZE_4KB - This lets you select the page size of the kernel. For best IA-64 performance, a page size of 8KB or 16KB is recommended. For best IA-32 compatibility, a page size of 4KB should be selected (the vast @@ -18783,8 +23031,8 @@ Enable Itanium A-step specific code CONFIG_ITANIUM_ASTEP_SPECIFIC Select this option to build a kernel for an Itanium prototype system - with an A-step CPU. You have an A-step CPU if the "revision" field in - /proc/cpuinfo is 0. + with an A-step CPU. You have an A-step CPU if the "revision" field + in /proc/cpuinfo is 0. Enable Itanium B-step specific code CONFIG_ITANIUM_BSTEP_SPECIFIC @@ -18794,9 +23042,33 @@ Enable Itanium B0-step specific code CONFIG_ITANIUM_B0_SPECIFIC - Select this option to bild a kernel for an Itanium prototype system - with a B0-step CPU. You have a B0-step CPU if the "revision" field in - /proc/cpuinfo is 1. + Select this option to build a kernel for an Itanium prototype system + with a B0-step CPU. You have a B0-step CPU if the "revision" field + in /proc/cpuinfo is 1. + +Enable Itanium C-step specific code +CONFIG_ITANIUM_CSTEP_SPECIFIC + Select this option to build a kernel for an Itanium prototype system + with a C-step CPU. You have a C-step CPU if the "revision" field in + /proc/cpuinfo is in the range of 5 to 8. + +Enable Itanium B1-step specific code +CONFIG_ITANIUM_B1_SPECIFIC + Select this option to build a kernel for an Itanium prototype system + with a B1-step CPU. You have a B1-step CPU if the "revision" field + in /proc/cpuinfo is 2. + +Enable Itanium B2-step specific code +CONFIG_ITANIUM_B2_SPECIFIC + Select this option to build a kernel for an Itanium prototype system + with a B2-step CPU. You have a B2-step CPU if the "revision" field + in /proc/cpuinfo is 3. + +Enable Itanium C0-step specific code +CONFIG_ITANIUM_C0_SPECIFIC + Select this option to build a kernel for an Itanium prototype system + with a C0-step CPU. You have a C0-step CPU if the "revision" field + in /proc/cpuinfo is 5. Force interrupt redirection CONFIG_IA64_HAVE_IRQREDIR @@ -18804,38 +23076,56 @@ redirect interrupts to different CPUs. Select N here if you're unsure. -Enable use of global TLB purge instruction (ptc.g) -CONFIG_ITANIUM_PTCG - Say Y here if you want the kernel to use the IA-64 "ptc.g" - instruction to flush the TLB on all CPUs. Select N here if - you're unsure. - -Enable SoftSDV hacks -CONFIG_IA64_SOFTSDV_HACKS - Say Y here to enable hacks to make the kernel work on the Intel - SoftSDV simulator. Select N here if you're unsure. - -Enable AzusA hacks -CONFIG_IA64_AZUSA_HACKS - Say Y here to enable hacks to make the kernel work on the NEC - AzusA platform. Select N here if you're unsure. - -Force socket buffers below 4GB? -CONFIG_SKB_BELOW_4GB - Most of today's network interface cards (NICs) support DMA to - the low 32 bits of the address space only. On machines with - more then 4GB of memory, this can cause the system to slow - down if there is no I/O TLB hardware. Turning this option on - avoids the slow-down by forcing socket buffers to be allocated - from memory below 4GB. The downside is that your system could - run out of memory below 4GB before all memory has been used up. - If you're unsure how to answer this question, answer Y. - Enable IA-64 Machine Check Abort CONFIG_IA64_MCA Say Y here to enable machine check support for IA-64. If you're unsure, answer Y. +Disable IA-64 Virtual Hash Page Table +CONFIG_DISABLE_VHPT + The Virtual Hash Page Table (VHPT) enhances virtual address + translation performance. Normally you want the VHPT active but you + can select this option to disable the VHPT for debugging. If you're + unsure, answer N. + +Enable McKinley A-step specific code +CONFIG_MCKINLEY_ASTEP_SPECIFIC + Select this option to build a kernel for an IA64 McKinley system + with any A-stepping CPU. + +Enable McKinley A0/A1-step specific code +CONFIG_MCKINLEY_A0_SPECIFIC + Select this option to build a kernel for an IA64 McKinley system + with an A0 or A1 stepping CPU. + +Turn on compare-and-exchange bug checking (slow!) +CONFIG_IA64_DEBUG_CMPXCHG + Selecting this option turns on bug checking for the IA64 + compare-and-exchange instructions. This is slow! Itaniums + from step B3 or later don't have this problem. If you're unsure, + select N. + +IA64 IRQ bug checking +CONFIG_IA64_DEBUG_IRQ + Selecting this option turns on bug checking for the IA64 irq_save + and restore instructions. It's useful for tracking down spinlock + problems, but slow! If you're unsure, select N. + +Early printk support (requires VGA!) +CONFIG_IA64_EARLY_PRINTK + Selecting this option uses the VGA screen for printk() output before + the consoles are initialised. It is useful for debugging problems + early in the boot process, but only if you have a VGA screen + attached. If you're unsure, select N. + +Print possible IA64 hazards to console +CONFIG_IA64_PRINT_HAZARDS + Selecting this option prints more information for Illegal Dependency + Faults, that is, for Read after Write, Write after Write or Write + after Read violations. This option is ignored if you are compiling + for an Itanium A step processor (CONFIG_ITANIUM_ASTEP_SPECIFIC). If + you're unsure, select Y. + Performance monitor support CONFIG_PERFMON Selects whether support for the IA-64 performance monitor hardware @@ -18853,12 +23143,416 @@ To use this option, you have to check that the "/proc file system support" (CONFIG_PROC_FS) is enabled, too. +Kernel support for IA-32 emulation +CONFIG_IA32_SUPPORT + IA64 processors can run IA32 (that is, x86) binaries by emulating + the IA32 instruction set. Say Y here to build in kernel support for + this. If in doubt, say Y. + +/proc/efi/vars support +CONFIG_IA64_EFIVARS + If you say Y here, you are able to get EFI (Extensible Firmware + Interface) variable information in /proc/efi/vars. You may read, + write, create, and destroy EFI variables through this interface. + + To use this option, you have to check that the "/proc file system + support" (CONFIG_PROC_FS) is enabled, too. + +Directly Connected Compact Flash support +CONFIG_CF_ENABLER + Compact Flash is a small, removable mass storage device introduced + in 1994 originally as a PCMCIA device. If you say `Y' here, you + compile in support for Compact Flash devices directly connected to + a SuperH processor. A Compact Flash FAQ is available at + . + + If your board has "Directly Connected" CompactFlash at area 5 or 6, + you may want to enable this option. Then, you can use CF as + primary IDE drive (only tested for SanDisk). + + If in doubt, select 'N'. + +Kernel debugging +CONFIG_DEBUG_KERNEL + Say Y here if you are developing drivers or trying to debug and + identify kernel problems. + +Debug memory allocations +CONFIG_DEBUG_SLAB + Say Y here to have the kernel do limited verification on memory + allocation as well as poisoning memory on free to catch use of freed + memory. + +Memory mapped I/O debug support +CONFIG_DEBUG_IOVIRT + Say Y here to get warned whenever an attempt is made to do I/O on + obviously invalid addresses such as those generated when ioremap() + calls are forgotten. Memory mapped I/O will go through an extra + check to catch access to unmapped ISA addresses, an access method + that can still be used by old drivers that are being ported from + 2.0/2.2. + +Spinlock debugging +CONFIG_DEBUG_SPINLOCK + Say Y here and build SMP to catch missing spinlock initialization + and certain other kinds of spinlock errors commonly made. This is + best used in conjunction with the NMI watchdog so that spinlock + deadlocks are also debuggable. + +Verbose BUG() reporting (adds 70K) +CONFIG_DEBUG_BUGVERBOSE + Say Y here to make BUG() panics output the file name and line number + of the BUG call as well as the EIP and oops trace. This aids + debugging but costs about 70-100K of memory. + +Include kgdb kernel debugger +CONFIG_KGDB + Include in-kernel hooks for kgdb, the Linux kernel source level + debugger. This project has a web page at + . + +Include xmon kernel debugger +CONFIG_XMON + Include in-kernel hooks for the xmon kernel monitor/debugger + supported by the PPC port. + +Include kgdb kernel debugger +CONFIG_KWDB + Include in-kernel hooks for kdb, the source level debugger for the + PA-RISC port. + +IODC console +CONFIG_IODC_CONSOLE + IODC is HP's pre-PCI standard for device identification (a la PCI + vendor, device IDs), detection, configuration, initialization and so + on. It also can provide firmware function to do the actual IO, + which are slow, not really defined for runtime usage and generally + not desirable. + + See + for the gory details. + + Say Y here to enable use of the IODC firmware functions for console + I/O. This is only useful on older PA-RISC workstations. If in + doubt, say Y. + +U2/Uturn I/O MMU +CONFIG_IOMMU_CCIO + Say Y here to enable DMA management routines for the first + generation of PA-RISC cache-coherent machines. Programs the + U2/Uturn chip in "Virtual Mode" and use the I/O MMU. + +LBA/Elroy PCI support +CONFIG_PCI_LBA + Say Y here to give the PA-RISC kernel access to PCI configuration + and IO-port space on PA-RISC workstations equipped with a Lower Bus + Adapter (LBA). This includes A, B, C, J, L, and N-class machines + with 4-digit model numbers, also the A300. + +LASI I/O support +CONFIG_GSC_LASI + Say Y here to directly support the LASI controller chip found on + PA-RISC workstations. Linux-oriented documentation for this chip + can be found at . + +LASI/ASP builtin parallel-port +CONFIG_PARPORT_GSC + Say Y here to build in low-level parallel-support for PC-style + hardware integrated in the LASI-Controller (on the GSC Bus) for + HP-PARISC workstations. + +Fujitsu Vendor Specific +CONFIG_BLK_DEV_IDEDISK_FUJITSU + Enable vendor-specific code for Fujitsu IDE disks. Unless you are + the IDE maintainer, you probably do not want to mess with this. + +IBM Vendor Specific +CONFIG_BLK_DEV_IDEDISK_IBM + Enable vendor-specific code for IBM IDE disks. Unless you are the + IDE maintainer, you probably do not want to mess with this. + +Maxtor Vendor Specific +CONFIG_BLK_DEV_IDEDISK_MAXTOR + Enable vendor-specific code for Maxtor IDE disks. Unless you are + the IDE maintainer, you probably do not want to mess with this. + +Quantum Vendor Specific +CONFIG_BLK_DEV_IDEDISK_QUANTUM + Enable vendor-specific code for Quantum IDE disks. Unless you are + the IDE maintainer, you probably do not want to mess with this. + +Seagate Vendor Specific +CONFIG_BLK_DEV_IDEDISK_SEAGATE + Enable vendor-specific code for Seagate IDE disks. Unless you are + the IDE maintainer, you probably do not want to mess with this. + +Western Digital Vendor Specific +CONFIG_BLK_DEV_IDEDISK_WD + Enable vendor-specific code for Western Digital IDE disks. Unless + you are the IDE maintainer, you probably do not want to mess with + this. + +TiVo Commerial Application Specific +CONFIG_BLK_DEV_TIVO + Enable vendor-specific code for TiVo IDE disks. Unless you are the + IDE maintainer, you probably do not want to mess with this. + +# Choice: superhsys +Generic +CONFIG_SH_GENERIC + Select Generic if configuring for a generic SuperH system. + The "generic" option compiles in *all* the possible hardware + support and relies on the sh_mv= kernel commandline option to choose + at runtime which routines to use. "MV" stands for "machine vector"; + each of the machines below is described by a machine vector. + + Select SolutionEngine if configuring for a Hitachi SH7709 + or SH7750 evalutation board. + + Select Overdrive if configuring for a ST407750 Overdrive board. + More information at + . + + Select HP620 if configuring for a HP Jornada HP620. + More information (hardware only) at + . + + Select HP680 if configuring for a HP Jornada HP680. + More information (hardware only) at + . + + Select HP690 if configuring for a HP Jornada HP690. + More information (hardware only) at + . + + Select CqREEK if configuring for a CqREEK SH7708 or SH7750. + More information at + . + + Select DMIDA if configuring for a DataMyte 4000 Industrial + Digital Assistant. More information at . + + Select EC3104 if configuring for a system with an Eclipse + International EC3104 chip, e.g. the Harris AD2000. + + Select Dreamcast if configuring for a SEGA Dreamcast. + More information at + . There is a + Dreamcast project is at . + + Select BareCPU if you know what this means, and it applies + to your system. + +SolutionEngine +CONFIG_SH_SOLUTION_ENGINE + Select SolutionEngine if configuring for a Hitachi SH7709 + or SH7750 evalutation board. + +7751 SolutionEngine +CONFIG_SH_7751_SOLUTION_ENGINE + Select 7751 SolutionEngine if configuring for a Hitachi SH7751 + evalutation board. + +Overdrive +CONFIG_SH_OVERDRIVE + Select Overdrive if configuring for a ST407750 Overdrive board. + More information at + . + +HP620 +CONFIG_SH_HP620 + Select HP620 if configuring for a HP jornada HP620. + More information (hardware only) at + . + +HP680 +CONFIG_SH_HP680 + Select HP680 if configuring for a HP Jornada HP680. + More information (hardware only) at + . + +HP690 +CONFIG_SH_HP690 + Select HP690 if configuring for a HP Jornada HP690. + More information (hardware only) + at . + +CqREEK +CONFIG_SH_CQREEK + Select CqREEK if configuring for a CqREEK SH7708 or SH7750. + More information at + . + +DMIDA +CONFIG_SH_DMIDA + Select DMIDA if configuring for a DataMyte 4000 Industrial + Digital Assistant. More information at . + +EC3104 +CONFIG_SH_EC3104 + Select EC3104 if configuring for a system with an Eclipse + International EC3104 chip, e.g. the Harris AD2000. + +Dreamcast +CONFIG_SH_DREAMCAST + Select Dreamcast if configuring for a SEGA Dreamcast. + More information at + . There is a + Dreamcast project is at . + +BareCPU +CONFIG_SH_UNKNOWN + "Bare CPU" aka "unknown" means an SH-based system which is not one + of the specific ones mentioned above, which means you need to enter + all sorts of stuff like CONFIG_MEMORY_START because the config + system doesn't already know what it is. You get a machine vector + without any platform-specific code in it, so things like the RTC may + not work. + + This option is for the early stages of porting to a new machine. + +# Choice: superhtype +SH7707 +CONFIG_CPU_SUBTYPE_SH7707 + Select the type of SuperH processor you have. + + Select SH7707 if you have a 60 Mhz SH-3 HD6417707 CPU. + + Select SH7708 if you have a 60 Mhz SH-3 HD6417708S or + if you have a 100 Mhz SH-3 HD6417708R CPU. + + Select SH7709 if you have a 80 Mhz SH-3 HD6417709 CPU. + + Select SH7750 if you have a 200 Mhz SH-4 HD6417750 CPU. + +SH7708 +CONFIG_CPU_SUBTYPE_SH7708 + Select SH7708 if you have a 60 Mhz SH-3 HD6417708S or + if you have a 100 Mhz SH-3 HD6417708R CPU. + +SH7709 +CONFIG_CPU_SUBTYPE_SH7709 + Select SH7709 if you have a 80 Mhz SH-3 HD6417709 CPU. + +SH7750 +CONFIG_CPU_SUBTYPE_SH7750 + Select SH7750 if you have a 200 Mhz SH-4 HD6417750 CPU. + +Physical memory start address +CONFIG_MEMORY_START + The physical memory start address will be automatically + set to 08000000, unless you selected one of the following + processor types: SolutionEngine, Overdrive, HP620, HP680, HP690, + in which case the start address will be set to 0c000000. + + Tweak this only when porting to a new machine which is not already + known by the config system. Changing it from the known correct + value on any of the known systems will only lead to disaster. + +Hitachi HD64461 companion chip support +CONFIG_HD64461 + The Hitachi HD64461 provides an interface for + the SH7709 CPU, supporting a LCD controller, + CRT color controller, IrDA up to 4 Mbps, and a + PCMCIA controller supporting 2 slots. + + More information is available at + . + + Say Y if you want support for the HD64461. + Otherwise, say N. + +HD64461 PCMCIA enabler +CONFIG_HD64461_ENABLER + Say Y here if you want to enable PCMCIA support + via the HD64461 companion chip. + Otherwise, say N. + +HD64461 virtualized IRQ number +CONFIG_HD64461_IRQ + The default setting of the HD64461 IRQ is 36. + + Do not change this unless you know what you are doing. + +Hitachi HD64465 companion chip support +CONFIG_HD64465 + The Hitachi HD64465 provides an interface for + the SH7750 CPU, supporting a LCD controller, + CRT color controller, IrDA, USB, PCMCIA, + keyboard controller, and a printer interface. + + More information is available at + . + + Say Y if you want support for the HD64465. + Otherwise, say N. + +HD64465 virtualized IRQ number +CONFIG_HD64465_IRQ + The default setting of the HD64465 IRQ is 5. + + Do not change this unless you know what you are doing. + +HD64465 start address +CONFIG_HD64465_IOBASE + The default setting of the HD64465 IO base address is 0xb0000000. + + Do not change this unless you know what you are doing. + +Early printk support +CONFIG_SH_EARLY_PRINTK + Say Y here to redirect kernel messages to the serial port + used by the SH-IPL bootloader, starting very early in the boot + process and ending when the kernel's serial console is initialised. + This option is only useful porting the kernel to a new machine, + when the kernel may crash or hang before the serial console is + initialised. + +SuperH SCI (serial) support +CONFIG_SH_SCI + Selecting this option will allow the Linux kernel to transfer data + over SCI (Serial Communication Interface) and/or SCIF (Serial + Communication Interface with FIFO) which are built into the Hitachi + SuperH processor. The option provides 1 to 3 (depending + on the CPU model) standard Linux tty devices, /dev/ttySC[012]; one + of these is normally used as the system console. + + If in doubt, press "y". + +Use LinuxSH standard BIOS +CONFIG_SH_STANDARD_BIOS + Say Y here if your target has the gdb-sh-stub + package from www.m17n.org (or any conforming standard LinuxSH BIOS) + in FLASH or EPROM. The kernel will use standard BIOS calls during + boot for various housekeeping tasks (including calls to read and + write characters to a system console, get a MAC address from an + on-board Ethernet interface, and shut down the hardware). Note this + does not work with machines with an existing operating system in + mask ROM and no flash (WindowsCE machines fall in this category). + If unsure, say N. + +GDB Stub kernel debug +CONFIG_DEBUG_KERNEL_WITH_GDB_STUB + If you say Y here, it will be possible to remotely debug the SuperH + kernel using gdb, if you have the gdb-sh-stub package from + www.m17n.org (or any conforming standard LinuxSH BIOS) in FLASH or + EPROM. This enlarges your kernel image disk size by several + megabytes but allows you to load, run and debug the kernel image + remotely using gdb. This is only useful for kernel hackers. If + unsure, say N. + +Console output to GDB +CONFIG_GDB_CONSOLE + If you are using GDB for remote debugging over a serial port and + would like kernel messages to be formatted into GDB $O packets so + that GDB prints them as program output, say 'Y'. + # # A couple of things I keep forgetting: -# capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet, -# Intel, IRQ, ISDN, Linux, MSDOS, NetWare, NetWinder, +# capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet, +# Intel, IRQ, ISDN, Linux, MSDOS, NetWare, NetWinder, # NFS, PCI, SCSI, SPARC -# two words: file system, hard drive, hard disk, home page, +# two words: file system, hard drive, hard disk, home page, # user space, web site # other: it's safe to save; daemon; use --, not - or ---; # use KB for 1024 bytes, not kB or K. @@ -18869,13 +23563,13 @@ # LocalWords: CONFIG coprocessor DX Pentium SX lilo loadlin HOWTO ftp metalab # LocalWords: unc edu docs emu README kB BLK DEV FD Thinkpad fd MFM RLL IDE gz # LocalWords: cdrom diskless netboot nfs xzvf ATAPI MB ide pavia rubini pl pd -# LocalWords: HD CDROMs IDECD NEC MITSUMI filesystem XT XD PCI BIOS cezar ATEN +# LocalWords: HD CD-ROMs IDECD NEC MITSUMI filesystem XT XD PCI BIOS cezar ATEN # LocalWords: ISA EISA Microchannel VESA BIOSes IPC SYSVIPC ipc Ctrl dmesg hlt # LocalWords: BINFMT Linkable http ac uk jo html GCC SPARC AVANTI CABRIOLET EB # LocalWords: netscape gcc LD CC toplevel MODVERSIONS insmod rmmod modprobe IP -# LocalWords: genksyms INET loopback gatewaying ethernet PPP ARP Arp MEMSIZE +# LocalWords: genksyms INET loopback gatewaying Ethernet PPP ARP Arp MEMSIZE # LocalWords: howto multicasting MULTICAST MBONE firewalling ipfw ACCT resp ip -# LocalWords: proc acct IPIP encapsulator decapsulator klogd PCTCP RARP EXT PS +# LocalWords: proc acct IPIP encapsulator decapsulator klogd RARP EXT PS # LocalWords: telnetting subnetted NAGLE rlogin NOSR ttyS TGA techinfo mbone nl # LocalWords: Mb SKB IPX Novell dosemu DDP ATALK vmalloc visar ehome # LocalWords: SD CHR scsi thingy SG CD LUNs LUN jukebox Adaptec BusLogic EATA @@ -18929,11 +23623,11 @@ # LocalWords: FC DC dc PPA IOMEGA's ppa RNFS FMV Fujitsu ARPD arpd loran layes # LocalWords: FRAD indiana framerelay DLCI DCLIs Sangoma SDLA mrouted sync sec # LocalWords: Starmode Metricom MosquitoNet mosquitonet kbit nfsroot Digiboard -# LocalWords: DIGI Xe Xeve digiboard UMISC touchscreens mtu ethernets HBAs MEX +# LocalWords: DIGI Xe Xeve digiboard UMISC touchscreens mtu Ethernets HBAs MEX # LocalWords: Shifflett netcom js jshiffle WIC DECchip ELCP EtherPower dst RTC # LocalWords: rtc SMP lp Digi Intl RightSwitch DGRS dgrs AFFS Amiga UFS SDL AP # LocalWords: Solaris RISCom riscom syncPPP PCBIT pcbit sparc anu au artoo MFB -# LocalWords: hitchcock Crynwr cnam pktdrvr NCSA's CyDROM CyCDROM FreeBSD NeXT +# LocalWords: hitchcock Crynwr cnam pktdrvr NCSA's CyDROM CyCD-ROM FreeBSD NeXT # LocalWords: NeXTstep disklabel disklabels SMD FFS tm AmigaOS diskfiles Un IQ # LocalWords: Bernd informatik rwth aachen uae affs multihosting bytecode java # LocalWords: applets applet JDK ncsa cabi SNI Alphatronix readme LANs scarab @@ -18949,7 +23643,7 @@ # LocalWords: mgetty sendfax gert greenie muc lowlevel Lasermate LanManager io # LocalWords: OOPSes trackball binghamton mobileip ncr IOMAPPED settags ns ser # LocalWords: setsync NEGO MPARITY autotuning prefetch PIIX cdwrite utils rc -# LocalWords: PCWATCHDOG berkprod bitgate boldt ucsb jf kyoto jp euc Tetsuyasu +# LocalWords: PCWATCHDOG berkprod bitgate boldt ucsb jf kyoto jp euc Tetsuyasu # LocalWords: YAMADA tetsu cauchy nslab ntt nevod perm su doc kaf kheops wsc # LocalWords: traduc Bourgin dbourgin menuconfig kfill READMEs HOWTOs Virge WA # LocalWords: IDEDISK IDEFLOPPY EIDE firewalls QMAGIC ZMAGIC LocalWords opti @@ -19012,7 +23706,7 @@ # LocalWords: prio Micom xIO dwmw rimi OMIRR omirr omirrd unicode ntfs cmu NIC # LocalWords: Braam braam Schmidt's freiburg nls codepages codepage Romanian # LocalWords: Slovak Slovenian Sorbian Nordic iso Catalan Faeroese Galician SZ -# LocalWords: Valencian Slovene Esperanto Estonian Latvian Byelorussian KOI mt +# LocalWords: Valencian Slovene Esperanto Estonian Latvian Belarusian KOI mt # LocalWords: charset Inuit Greenlandic Sami Lappish koi Alexey Kuznetsov's sa # LocalWords: Specialix specialix DTR RTS RTSCTS cycladesZ Exabyte ftape's inr # LocalWords: Iomega's LBFM claus ZFTAPE VFS zftape zft William's lzrw DFLT kb @@ -19078,7 +23772,7 @@ # LocalWords: uit dagb irda LSAP IrLMP RR's IrLAP IR alloc skb's kfree skb's # LocalWords: GZIP IrLAN NetbeamIR ESI JetEye IrOBEX IrCOMM TTY's minicom dti # LocalWords: ircomm ircomm pluto thiguchi IrTTY Linux's bps NetWinder MIR NSC -# LocalWords: ACTiSYS Dongle dongle dongles esi actisys IrMate tekram BVM MVME +# LocalWords: ACTiSYS dongle dongles esi actisys IrMate tekram BVM MVME # LocalWords: BVME BVME WRITETHROUGH copyback writethrough fwmark syncookie tu # LocalWords: alphalinux GOBIOS csn chemnitz nat ACARD AMI MegaRAID megaraid # LocalWords: QNXFS ISI isicom xterms Apollos VPN RCPCI rcpci sgi visws pcmcia @@ -19090,7 +23784,7 @@ # LocalWords: SKMC USB UHCI OHCI intel compaq usb ohci HCD Virt Compaq's hcd # LocalWords: VROOTHUB KBD ARRs MCRs NWBUTTON nwbutton NUM WaveArtist APNE cpu # LocalWords: apne blackhawke PlanB lu mlan planb NWFPE FPA nwfpe unbootable -# LocalWords: FPEmulator ds vmlinux initialisation discardable pgtable PGT mdw +# LocalWords: FPEmulator ds vmlinux initialization discardable pgtable PGT mdw # LocalWords: quicklist pagetable arthur StrongARM podule podules Autodetect # LocalWords: dodgy IrPORT irport Litelink litelink SuSE rtfm internet hda CY # LocalWords: multmode DriveReady SeekComplete DriveStatusError miscompile AEC @@ -19119,7 +23813,7 @@ # LocalWords: VWSND eg ESSSOLO CFU CFNR scribed eiconctrl eicon hylafax KFPU # LocalWords: EXTRAPREC fpu mainboards KHTTPD kHTTPd khttpd Xcelerator SBNI tw # LocalWords: LOGIBUSMOUSE Granch granch sbni Raylink NOHIGHMEM Athlon SIM sim -# LocalWords: hpl Tourrilhes DuraLAN starfile Davicom davicom dmfe auk tms tr +# LocalWords: hpl Tourrilhes DuraLAN starfire Davicom davicom dmfe auk tms tr # LocalWords: TokenExpress Belkin Peracom eTek DVDs infradead Cxxx Adlib AV ZX # LocalWords: NeoMagic CPi CPt Celeron decapsulation Undeletion BFS bfs nVidia # LocalWords: OnStream Irongate Riva phonedev QuickNet LineJack PhoneJack IXJ @@ -19139,7 +23833,7 @@ # LocalWords: DEVS FireWire PCILynx pcilynx LOCALRAM miro's DV RAWIO GRED Mk # LocalWords: Diffserv DSMARK Ingress Qdisc TCINDEX TMSPCI tmspci Ringode JE # LocalWords: MADGEMC madgemc TokenRing SMCTR TokenCard smctr Wacom Graphire -# LocalWords: WMFORCE mousedev ConnectTech HandSpring Xirlink IBMCAM ibmcam SN +# LocalWords: mousedev ConnectTech HandSpring Xirlink IBMCAM ibmcam SN # LocalWords: DEVICEFS yyy Cymraeg Dwave SIMM JSFLASH JavaStation's multilink # LocalWords: nsc ircc DDB Vrc CMN TB PROMs Vino rivafb DDC Matroxes MGA TVO # LocalWords: MAVEN fbdev crtc maven matroxset NTSC PCA SBA AAL SKFP DAS SAS @@ -19169,3 +23863,11 @@ # LocalWords: DMX Domex dmx wellington ftdi sio Accton Billington Corega FEter # LocalWords: MELCO LUA PNA Linksys SNC chkdsk AWACS Webcam RAMFS Ramfs ramfs # LocalWords: ramfiles MAKEDEV pty WDTPCI APA apa +# +# The following sets edit modes for GNU EMACS +# Local Variables: +# case-fold-search:nil +# fill-prefix:" " +# adaptive-fill:nil +# fill-column:70 +# End: diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/README.nsp_cs.eng linux.ac/Documentation/README.nsp_cs.eng --- linux.vanilla/Documentation/README.nsp_cs.eng Wed Jul 4 19:50:38 2001 +++ linux.ac/Documentation/README.nsp_cs.eng Wed Oct 10 01:47:27 2001 @@ -8,9 +8,9 @@ for Linux. 2. My Linux environment -Linux kernel: 2.4.0 / 2.2.18 -pcmcia-cs: 3.1.24 -gcc: gcc-2.95.2 +Linux kernel: 2.4.7 / 2.2.19 +pcmcia-cs: 3.1.27 +gcc: gcc-2.95.4 PC card: I-O data PCSC-F (NinjaSCSI-3) I-O data CBSC-II in 16 bit mode (NinjaSCSI-32Bi) SCSI device: I-O data CDPS-PX24 (CD-ROM drive) @@ -55,6 +55,8 @@ [4] Extract this driver's archive somewhere, and edit Makefile, then do make. $ tar -zxvf nsp_cs-x.x.tar.gz $ cd nsp_cs-x.x +$ emacs Makefile +... $ make [5] Copy nsp_cs.o to suitable plase, like /lib/modules//pcmcia/ . @@ -95,7 +97,7 @@ bind "nsp_cs" ------------------------------------- -[7] Boot (or reboot) pcmcia-cs. +[7] Start (or restart) pcmcia-cs. # /etc/rc.d/rc.pcmcia start (BSD style) or # /etc/init.d/pcmcia start (SYSV style) @@ -111,7 +113,8 @@ your data. Please backup your data when you use this driver. 6. Known Bugs - Some write error occurs when you use slow device. + In 2.4 kernel, you can't use 640MB Optical disk. This error comes from +high level SCSI driver. 7. Testing Please send me some reports(bug reports etc..) of this software. @@ -124,4 +127,4 @@ See GPL. -2001/02/01 yokota@netlab.is.tsukuba.ac.jp +2001/08/08 yokota@netlab.is.tsukuba.ac.jp diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/arm/README linux.ac/Documentation/arm/README --- linux.vanilla/Documentation/arm/README Thu Apr 12 03:02:27 2001 +++ linux.ac/Documentation/arm/README Wed Oct 10 01:47:27 2001 @@ -162,7 +162,7 @@ Please follow this format - it is an automated system. You should - receive a reply within one day. + receive a reply in short order. --- Russell King (26/01/2001) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/arm/SA1100/ADSBitsy linux.ac/Documentation/arm/SA1100/ADSBitsy --- linux.vanilla/Documentation/arm/SA1100/ADSBitsy Thu Jan 1 01:00:00 1970 +++ linux.ac/Documentation/arm/SA1100/ADSBitsy Wed Oct 10 01:47:27 2001 @@ -0,0 +1,43 @@ +ADS Bitsy Single Board Computer +(It is different from Bitsy(iPAQ) of Compaq) + +For more details, contact Applied Data Systems or see +http://www.applieddata.net/products.html + +The Linux support for this product has been provided by +Woojung Huh + +Use 'make adsbitsy_config' before any 'make config'. +This will set up defaults for ADS Bitsy support. + +The kernel zImage is linked to be loaded and executed at 0xc0400000. + +Linux can be used with the ADS BootLoader that ships with the +newer rev boards. See their documentation on how to load Linux. + +Supported peripherals: +- SA1100 LCD frame buffer (8/16bpp...sort of) +- SA1111 USB Master +- SA1100 serial port +- pcmcia, compact flash +- touchscreen(ucb1200) +- console on LCD screen +- serial ports (ttyS[0-2]) + - ttyS0 is default for serial console + +To do: +- everything else! :-) + +Notes: + +- The flash on board is divided into 3 partitions. + You should be careful to use flash on board. + It's partition is different from GraphicsClient Plus and GraphicsMaster + +- 16bpp mode requires a different cable than what ships with the board. + Contact ADS or look through the manual to wire your own. Currently, + if you compile with 16bit mode support and switch into a lower bpp + mode, the timing is off so the image is corrupted. This will be + fixed soon. + +Any contribution can be sent to nico@cam.org and will be greatly welcome! diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/arm/SA1100/GraphicsMaster linux.ac/Documentation/arm/SA1100/GraphicsMaster --- linux.vanilla/Documentation/arm/SA1100/GraphicsMaster Thu Jan 1 01:00:00 1970 +++ linux.ac/Documentation/arm/SA1100/GraphicsMaster Wed Oct 10 01:47:27 2001 @@ -0,0 +1,53 @@ +ADS GraphicsMaster Single Board Computer + +For more details, contact Applied Data Systems or see +http://www.applieddata.net/products.html + +The original Linux support for this product has been provided by +Nicolas Pitre . Continued development work by +Woojung Huh + +Use 'make graphicsmaster_config' before any 'make config'. +This will set up defaults for GraphicsMaster support. + +The kernel zImage is linked to be loaded and executed at 0xc0400000. + +Linux can be used with the ADS BootLoader that ships with the +newer rev boards. See their documentation on how to load Linux. + +Supported peripherals: +- SA1100 LCD frame buffer (8/16bpp...sort of) +- SA1111 USB Master +- on-board SMC 92C96 ethernet NIC +- SA1100 serial port +- flash memory access (MTD/JFFS) +- pcmcia, compact flash +- touchscreen(ucb1200) +- ps/2 keyboard +- console on LCD screen +- serial ports (ttyS[0-2]) + - ttyS0 is default for serial console +- Smart I/O (ADC, keypad, digital inputs, etc) + See http://www.applieddata.com/developers/linux for IOCTL documentation + and example user space code. ps/2 keybd is multiplexed through this driver + +To do: +- everything else! :-) + +Notes: + +- The flash on board is divided into 3 partitions. mtd0 is where + the zImage is stored. It's been marked as read-only to keep you + from blasting over the bootloader. :) mtd1 is + for the ramdisk.gz image. mtd2 is user flash space and can be + utilized for either JFFS or if you're feeling crazy, running ext2 + on top of it. If you're not using the ADS bootloader, you're + welcome to blast over the mtd1 partition also. + +- 16bpp mode requires a different cable than what ships with the board. + Contact ADS or look through the manual to wire your own. Currently, + if you compile with 16bit mode support and switch into a lower bpp + mode, the timing is off so the image is corrupted. This will be + fixed soon. + +Any contribution can be sent to nico@cam.org and will be greatly welcome! diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/arm/SA1100/Pangolin linux.ac/Documentation/arm/SA1100/Pangolin --- linux.vanilla/Documentation/arm/SA1100/Pangolin Fri Sep 7 17:28:38 2001 +++ linux.ac/Documentation/arm/SA1100/Pangolin Wed Oct 10 01:47:27 2001 @@ -2,7 +2,7 @@ by Dialogue Technology (http://www.dialogue.com.tw/). It has EISA slots for ease of configuration with SDRAM/Flash memory card, USB/Serial/Audio card, Compact Flash card, -and TFT-LCD card. +PCMCIA/IDE card and TFT-LCD card. To compile for Pangolin, you must issue the following commands: @@ -18,3 +18,7 @@ - UDA1341 sound driver - SA1100 LCD controller for 800x600 16bpp TFT-LCD - MQ-200 driver for 800x600 16bpp TFT-LCD +- Penmount(touch panel) driver +- PCMCIA driver +- SMC91C94 LAN driver +- IDE driver (experimental) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/cciss.txt linux.ac/Documentation/cciss.txt --- linux.vanilla/Documentation/cciss.txt Sat Feb 3 20:13:19 2001 +++ linux.ac/Documentation/cciss.txt Thu Oct 11 09:12:04 2001 @@ -8,8 +8,9 @@ * SA 5300 * SA 5i * SA 532 + * SA 5312 -If notes are not already created in the /dev/cciss directory +If nodes are not already created in the /dev/cciss directory # mkdev.cciss [ctlrs] @@ -47,3 +48,78 @@ /dev/cciss/c1d1p1 Controller 1, disk 1, partition 1 /dev/cciss/c1d1p2 Controller 1, disk 1, partition 2 /dev/cciss/c1d1p3 Controller 1, disk 1, partition 3 + +SCSI tape drive and medium changer support +------------------------------------------ + +SCSI sequential access devices and medium changer devices are supported and +appropriate device nodes are automatically created. (e.g. +/dev/st0, /dev/st1, etc. See the "st" man page for more details.) +You must enable "SCSI tape drive support for Smart Array 5xxx" and +"SCSI support" in your kernel configuration to be able to use SCSI +tape drives with your Smart Array 5xxx controller. + +Additionally, note that the driver will not engage the SCSI core at init +time. The driver must be directed to dynamically engage the SCSI core via +the /proc filesystem entry which the "block" side of the driver creates as +/proc/driver/cciss/cciss* at runtime. This is because at driver init time, +the SCSI core may not yet be initialized (because the driver is a block +driver) and attempting to register it with the SCSI core in such a case +would cause a hang. This is best done via an initialization script +(typically in /etc/init.d, but could vary depending on distibution). +For example: + + for x in /proc/driver/cciss/cciss[0-9]* + do + echo "engage scsi" > $x + done + +Once the SCSI core is engaged by the driver, it cannot be disengaged +(except by unloading the driver, if it happens to be linked as a module.) + +Note also that if no sequential access devices or medium changers are +detected, the SCSI core will not be engaged by the action of the above +script. + +Hot plug support for SCSI tape drives +------------------------------------- + +Hot plugging of SCSI tape drives is supported, with some caveats. +The cciss driver must be informed that changes to the SCSI bus +have been made, in addition to and prior to informing the the SCSI +mid layer. This may be done via the /proc filesystem. For example: + + echo "rescan" > /proc/scsi/cciss0/1 + +This causes the adapter to query the adapter about changes to the +physical SCSI buses and/or fibre channel arbitrated loop and the +driver to make note of any new or removed sequential access devices +or medium changers. The driver will output messages indicating what +devices have been added or removed and the controller, bus, target and +lun used to address the device. Once this is done, the SCSI mid layer +can be informed of changes to the virtual SCSI bus which the driver +presents to it in the usual way. For example: + + echo add-single-device 3 2 1 0 > /proc/scsi/scsi + +to add a device on controller 3, bus 2, target 1, lun 0. Note that +the driver makes an effort to preserve the devices positions +in the virtual SCSI bus, so if you are only moving tape drives +around on the same adapter and not adding or removing tape drives +from the adapter, informing the SCSI mid layer may not be necessary. + +Note that the naming convention of the /proc filesystem entries +contains a number in addition to the driver name. (E.g. "cciss0" +instead of just "cciss" which you might expect.) This is because +of changes to the 2.4 kernel PCI interface related to PCI hot plug +that imply the driver must register with the SCSI mid layer once per +adapter instance rather than once per driver. + +Note: ONLY sequential access devices and medium changers are presented +as SCSI devices to the SCSI mid layer by the cciss driver. Specifically, +physical SCSI disk drives are NOT presented to the SCSI mid layer. The +physical SCSI disk drives are controlled directly by the array controller +hardware and it is important to prevent the OS from attempting to directly +access these devices too, as if the array controller were merely a SCSI +controller in the same way that we are allowing it to access SCSI tape drives. + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/filesystems/ramfs.txt linux.ac/Documentation/filesystems/ramfs.txt --- linux.vanilla/Documentation/filesystems/ramfs.txt Thu Jan 1 01:00:00 1970 +++ linux.ac/Documentation/filesystems/ramfs.txt Wed Oct 10 01:47:27 2001 @@ -0,0 +1,47 @@ + ramfs - An automatically resizing memory based filesystem + + + Ramfs is a file system which keeps all files in RAM. It allows read + and write access. In contrast to RAM disks, which get allocated a + fixed amount of RAM, ramfs grows and shrinks to accommodate the + files it contains. + + You can mount the ramfs with: + mount -t ramfs none /mnt/wherever + + Then just create and use files. When the filesystem is unmounted, all + its contents are lost. + + NOTE! This filesystem is probably most useful not as a real + filesystem, but as an example of how virtual filesystems can be + written. + +Resource limits: + +By default a ramfs will be limited to using half of (physical) memory +for storing file contents, a bit over that when the metadata is +included. The resource usage limits of ramfs can be controlled with +the following mount options: + + maxsize=NNN + Sets the maximum allowed memory usage of the +filesystem to NNN kilobytes. This will be rounded down to a multiple +of the page size. The default is half of physical memory. NB. unlike +most of the other limits, setting this to zero does *not* mean no +limit, but will actually limit the size of the filesystem data to zero +pages. There might be a use for this in some perverse situation. + + maxfilesize=NNN + Sets the maximum size of a single file on the +filesystem to NNN kilobytes. This will be rounded down to a multiple +of the page size. If NNN=0 there is no limit. The default is no limit. + + maxdentries=NNN + Sets the maximum number of directory entries (hard +links) on the filesystem to NNN. If NNN=0 there is no limit. By +default this is set to maxsize/4. + + maxinodes=NNN + Sets the maximum number of inodes (i.e. distinct +files) on the filesystem to NNN. If NNN=0 there is no limit. The +default is no limit (but there can never be more inodes than dentries). diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/filesystems/vfat.txt linux.ac/Documentation/filesystems/vfat.txt --- linux.vanilla/Documentation/filesystems/vfat.txt Thu Apr 12 03:02:27 2001 +++ linux.ac/Documentation/filesystems/vfat.txt Wed Oct 10 01:47:27 2001 @@ -48,8 +48,15 @@ r: relaxed, case insensitive n: normal, default setting, currently case insensitive -nocase -- Returning with having the 8.3 format alias kept in - the disk. Default, return lowercase letter. +shortname=lower|win95|winnt|mixed + -- Shortname display/create setting. + lower: convert to lowercase for display, + emulate the Windows 95 rule for create. + win95: emulate the Windows 95 rule for display/create. + winnt: emulate the Windows NT rule for display/create. + mixed: emulate the Windows NT rule for display, + emulate the Windows 95 rule for create. + Default setting is `lower'. : 0,1,yes,no,true,false diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/i2c/dev-interface linux.ac/Documentation/i2c/dev-interface --- linux.vanilla/Documentation/i2c/dev-interface Fri Jul 28 20:50:51 2000 +++ linux.ac/Documentation/i2c/dev-interface Wed Oct 10 01:47:27 2001 @@ -70,6 +70,9 @@ /* buf[0] contains the read byte */ } +IMPORTANT: because of the use of inline functions, you *have* to use +'-O' or some variation when you compile your program! + Full interface description ========================== diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/i2c/summary linux.ac/Documentation/i2c/summary --- linux.vanilla/Documentation/i2c/summary Thu Dec 16 21:59:38 1999 +++ linux.ac/Documentation/i2c/summary Wed Oct 10 01:47:27 2001 @@ -1,9 +1,9 @@ -This is an explanation of what i2c is, and what is supported. +This is an explanation of what i2c is, and what is supported in this package. I2C and SMBus ============= -I2C (pronounce: I square C) is a protocol developed by Philips. It is a +I2C (pronounce: I squared C) is a protocol developed by Philips. It is a slow two-wire protocol (10-100 kHz), but it suffices for many types of devices. @@ -25,6 +25,7 @@ Adapter Device -> Driver Client + An Algorithm driver contains general code that can be used for a whole class of I2C adapters. Each specific adapter driver depends on one algorithm driver. @@ -35,29 +36,40 @@ For a given configuration, you will need a driver for your I2C bus (usually a separate Adapter and Algorithm driver), and drivers for your I2C devices -(usually one driver for each device). +(usually one driver for each device). There are no I2C device drivers +in this package. See the lm_sensors project http://www.lm-sensors.nu +for device drivers. + +Included Bus Drivers +==================== +Note that not only stable drivers are patched into the kernel by 'mkpatch'. -Included Drivers -================ Base modules ------------ i2c-core: The basic I2C code, including the /proc interface i2c-dev: The /dev interface +i2c-proc: The /proc interface for device (client) drivers Algorithm drivers ----------------- -i2c-algo-bit: A bit-banging algorithm -i2c-algo-pcf: A PCF 8584 style algorithm +i2c-algo-8xx: An algorithm for CPM's I2C device in Motorola 8xx processors (NOT BUILT BY DEFAULT) +i2c-algo-bit: A bit-banging algorithm +i2c-algo-pcf: A PCF 8584 style algorithm +i2c-algo-ppc405: An algorithm for the I2C device in IBM 405xx processors (NOT BUILT BY DEFAULT) Adapter drivers --------------- i2c-elektor: Elektor ISA card (uses i2c-algo-pcf) i2c-elv: ELV parallel port adapter (uses i2c-algo-bit) +i2c-pcf-epp: PCF8584 on a EPP parallel port (uses i2c-algo-pcf) (BROKEN - missing i2c-pcf-epp.h) i2c-philips-par: Philips style parallel port adapter (uses i2c-algo-bit) +i2c-ppc405: IBM 405xx processor I2C device (uses i2c-algo-ppc405) (NOT BUILT BY DEFAULT) +i2c-pport: Primitive parallel port adapter (uses i2c-algo-bit) +i2c-rpx: RPX board Motorola 8xx I2C device (uses i2c-algo-8xx) (NOT BUILT BY DEFAULT) i2c-velleman: Velleman K9000 parallel port adapter (uses i2c-algo-bit) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/i2c/writing-clients linux.ac/Documentation/i2c/writing-clients --- linux.vanilla/Documentation/i2c/writing-clients Fri Jul 28 20:50:51 2000 +++ linux.ac/Documentation/i2c/writing-clients Wed Oct 10 01:47:27 2001 @@ -33,7 +33,7 @@ /* detach_client */ &foo_detach_client, /* command */ &foo_command, /* May be NULL */ /* inc_use */ &foo_inc_use, /* May be NULL */ - /* dec_use */ &foo_dev_use /* May be NULL */ + /* dec_use */ &foo_dec_use /* May be NULL */ } The name can be chosen freely, and may be upto 40 characters long. Please @@ -190,7 +190,7 @@ detection algorithm. You do not have to use this parameter interface; but don't try to use -function i2c_probe() (or sensors_detect()) if you don't. +function i2c_probe() (or i2c_detect()) if you don't. NOTE: If you want to write a `sensors' driver, the interface is slightly different! See below. @@ -344,17 +344,17 @@ return i2c_probe(adapter,&addr_data,&foo_detect_client); } -For `sensors' drivers, use the sensors_detect function instead: +For `sensors' drivers, use the i2c_detect function instead: int foo_attach_adapter(struct i2c_adapter *adapter) { - return sensors_detect(adapter,&addr_data,&foo_detect_client); + return i2c_detect(adapter,&addr_data,&foo_detect_client); } Remember, structure `addr_data' is defined by the macros explained above, so you do not have to define it yourself. -The i2c_probe or sensors_detect function will call the foo_detect_client +The i2c_probe or i2c_detect function will call the foo_detect_client function only for those i2c addresses that actually have a device on them (unless a `force' parameter was used). In addition, addresses that are already in use (by some other registered client) are skipped. @@ -363,9 +363,9 @@ The detect client function -------------------------- -The detect client function is called by i2c_probe or sensors_detect. +The detect client function is called by i2c_probe or i2c_detect. The `kind' parameter contains 0 if this call is due to a `force' -parameter, and 0 otherwise (for sensors_detect, it contains 0 if +parameter, and 0 otherwise (for i2c_detect, it contains 0 if this call is due to the generic `force' parameter, and the chip type number if it is due to a specific `force' parameter). @@ -530,7 +530,7 @@ /* SENSORS ONLY BEGIN */ /* Register a new directory entry with module sensors. See below for the `template' structure. */ - if ((i = sensors_register_entry(new_client, type_name, + if ((i = i2c_register_entry(new_client, type_name, foo_dir_table_template,THIS_MODULE)) < 0) { err = i; goto ERROR4; @@ -574,8 +574,8 @@ int err,i; /* SENSORS ONLY START */ - /* Deregister with the `sensors' module. */ - sensors_deregister_entry(((struct lm78_data *)(client->data))->sysctl_id); + /* Deregister with the `i2c-proc' module. */ + i2c_deregister_entry(((struct lm78_data *)(client->data))->sysctl_id); /* SENSORS ONLY END */ /* Try to detach the client from i2c space */ @@ -772,12 +772,12 @@ First, I will give an example definition. static ctl_table foo_dir_table_template[] = { - { FOO_SYSCTL_FUNC1, "func1", NULL, 0, 0644, NULL, &sensors_proc_real, - &sensors_sysctl_real,NULL,&foo_func }, - { FOO_SYSCTL_FUNC2, "func2", NULL, 0, 0644, NULL, &sensors_proc_real, - &sensors_sysctl_real,NULL,&foo_func }, - { FOO_SYSCTL_DATA, "data", NULL, 0, 0644, NULL, &sensors_proc_real, - &sensors_sysctl_real,NULL,&foo_data }, + { FOO_SYSCTL_FUNC1, "func1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real,NULL,&foo_func }, + { FOO_SYSCTL_FUNC2, "func2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real,NULL,&foo_func }, + { FOO_SYSCTL_DATA, "data", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real,NULL,&foo_data }, { 0 } }; @@ -791,8 +791,8 @@ fourth should always be 0. The fifth is the mode of the /proc file; 0644 is safe, as the file will be owned by root:root. -The seventh and eighth parameters should be &sensors_proc_real and -&sensors_sysctl_real if you want to export lists of reals (scaled +The seventh and eighth parameters should be &i2c_proc_real and +&i2c_sysctl_real if you want to export lists of reals (scaled integers). You can also use your own function for them, as usual. Finally, the last parameter is the call-back to gather the data (see below) if you use the *_proc_real functions. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/memory.txt linux.ac/Documentation/memory.txt --- linux.vanilla/Documentation/memory.txt Tue Dec 22 16:31:07 1998 +++ linux.ac/Documentation/memory.txt Wed Oct 10 01:47:27 2001 @@ -37,7 +37,7 @@ * Not overclocking your CPU. * Having the memory tested in a memory tester or exchanged - with the vendor. + with the vendor. Consider testing it with memtest86 yourself. * Exchanging your CPU, cache, or motherboard for one that works. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/s390/CommonIO linux.ac/Documentation/s390/CommonIO --- linux.vanilla/Documentation/s390/CommonIO Wed Jul 25 22:12:01 2001 +++ linux.ac/Documentation/s390/CommonIO Wed Oct 10 01:47:28 2001 @@ -101,6 +101,17 @@ the device driver will be notified if possible, so the device will become available to the system. + You can also add ranges of devices to be ignored by piping to + /proc/cio_ignore; "add , , ..." will ignore the + specified devices. + + Note: Already known devices cannot be ignored; this also applies to devices + which are gone after a machine check. + + For example, if device abcd is already known and all other devices a000-afff + are not known, "echo add 0xa000-0xaccc, 0xaf00-0xafff > /proc/cio_ignore" + will add af00-afff to the list of ignored devices and skip a000-accc. + * /proc/s390dbf/cio_*/ (S/390 debug feature) @@ -122,3 +133,7 @@ /proc/s390dbf/cio_*/level a number between 0 and 6; see the documentation on the S/390 debug feature (Documentation/s390/s390dbf.txt) for details. +* /proc/irq_count + + This entry counts how many times s390_process_IRQ has been called for each + CPU. This info is in /proc/interrupts on other architectures. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/s390/chandev.8 linux.ac/Documentation/s390/chandev.8 --- linux.vanilla/Documentation/s390/chandev.8 Wed Jul 25 22:12:01 2001 +++ linux.ac/Documentation/s390/chandev.8 Wed Oct 10 01:47:28 2001 @@ -124,17 +124,16 @@ .B (ctc|escon|lcs|osad|qeth), read_devno,write_devno, .It -devif_num of -1 indicates you don't care what device interface number is chosen, omitting it indicates this is a range of devices for which you want to force to be detected as a particular type. -The data_devno field is only valid for qeth devices when not forcing a range of devices. -all parameters after & including memory_usage_in_k can be set optionally if not set they +devif_num of -1 indicates you don't care what device interface number is chosen, omitting it indicates this is a range of devices for which you want to force to be detected as a particular type, qeth devices can't be forced as a range as it makes no sense for them. +The data_devno field is only valid for qeth devices, all parameters including & after memory_usage_in_k can be set optionally, if not set they go to default values. memory_usage_in_k ( 0 the default ) means let the driver choose,checksum_received_ip_pkts & use_hw_stats are set to false .It e.g. ctc0,0x7c00,0x7c01 .It Tells the channel layer to force ctc0 if detected to use cuu's 7c00 & 7c01 port,port_no is the relative adapter no on lcs, on ctc/escon this field is the ctc/escon protocol number ( default 0 ), don't do checksumming on received ip packets & as ctc doesn't have hardware stats so it ignores this parameter. This can be used for instance to force a device if it presents bad sense data to the IO layer & thus autodetection fails. .It -qeth,0x7c00,0x7d00,-1,4096 -All devices between 0x7c00 & 7d00 should be detected as gigabit ethernet, let the driver use 4096k for each instance, don't care what port relative adapter number is chosen, don't checksum received ip packets & use hw stats . +lcs,0x7c00,0x7d00,-1,4096 +All devices between 0x7c00 & 7d00 should be detected as lcs, let the driver use 4096k for each instance, don't care what port relative adapter number is chosen, don't checksum received ip packets & use hw stats . .It qeth1,0x7c00,0x7c01,0x7c02 .It @@ -368,6 +367,12 @@ Force drivers modules to stay loaded even if no device is found, this is useful for debugging & one wishes to examine debug entries in /proc/s390dbf/ to find out why a module failed to load. +.It +e.g. +.It +persist,-1 forces all devices to persist. +.It +persist,0 forces all channel devices to be non persistent. .El .It diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/s390/s390dbf.txt linux.ac/Documentation/s390/s390dbf.txt --- linux.vanilla/Documentation/s390/s390dbf.txt Thu Apr 12 03:02:27 2001 +++ linux.ac/Documentation/s390/s390dbf.txt Wed Oct 10 01:47:28 2001 @@ -336,7 +336,7 @@ Example: > ls /proc/s390dbf/dasd -hex_ascii level raw +flush hex_ascii level raw > cat /proc/s390dbf/dasd/hex_ascii | sort +1 00 00974733272:680099 2 - 02 0006ad7e 07 ea 4a 90 | .... 00 00974733272:682210 2 - 02 0006ade6 46 52 45 45 | FREE @@ -362,6 +362,20 @@ > echo "5" > /proc/s390dbf/dasd/level > cat /proc/s390dbf/dasd/level 5 + +Flushing debug areas +-------------------- +Debug areas can be flushed with piping the number of the desired +area (0...n) to the proc file "flush". When using "-" all debug areas +are flushed. + +Examples: + +1. Flush debug area 0: +> echo "0" > /proc/s390dbf/dasd/flush + +2. Flush all debug areas: +> echo "-" > /proc/s390dbf/dasd/flush lcrash Interface ---------------- diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/sonypi.txt linux.ac/Documentation/sonypi.txt --- linux.vanilla/Documentation/sonypi.txt Tue Aug 14 00:38:29 2001 +++ linux.ac/Documentation/sonypi.txt Wed Oct 10 01:47:28 2001 @@ -15,6 +15,8 @@ - capture button events (only on Vaio Picturebook series) - Fn keys - bluetooth button (only on C1VR model) + - back button (PCG-GR7/K model) + - lid open/close events (Z600NE model) Those events (see linux/sonypi.h) can be polled using the character device node /dev/sonypi (major 10, minor auto allocated or specified as a option). @@ -36,6 +38,14 @@ Module options: --------------- +Several options can be passed to the sonypi driver, either by adding them +to /etc/modules.conf file, when the driver is compiled as a module or by +adding the following to the kernel command line (in your bootloader): + + sonypi=minor[[[[,camera],fnkeyinit],verbose],compat] + +where: + minor: minor number of the misc device /dev/sonypi, default is -1 (automatic allocation, see /proc/misc or kernel logs) @@ -48,6 +58,11 @@ get enabled unless you set this parameter to 1 verbose: print unknown events from the sonypi device + + compat: uses some compatibility code for enabling the sonypi + events. If the driver worked for you in the past + (prior to version 1.5) and does not work anymore, + add this option and report to the author. Module use: ----------- diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/sysctl/vm.txt linux.ac/Documentation/sysctl/vm.txt --- linux.vanilla/Documentation/sysctl/vm.txt Mon Sep 10 22:36:25 2001 +++ linux.ac/Documentation/sysctl/vm.txt Wed Oct 10 01:47:28 2001 @@ -21,6 +21,7 @@ - buffermem - freepages - kswapd +- max_map_count - overcommit_memory - page-cluster - pagecache @@ -167,6 +168,19 @@ and don't use much of it. Look at: mm/mmap.c::vm_enough_memory() for more information. + +============================================================== + +max_map_count: + +This file contains the maximum number of memory map areas a +process may have. Memory map areas are used as a side-effect +of calling malloc, directly by mmap and mprotect, and also +when loading shared libraries. + +While most applications need less than a thousand maps, +certain programs, particularly malloc debuggers, may consume +lots of them, e.g. up to one or two maps per allocation. ============================================================== diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/video4linux/README.buz linux.ac/Documentation/video4linux/README.buz --- linux.vanilla/Documentation/video4linux/README.buz Thu Jan 6 22:46:18 2000 +++ linux.ac/Documentation/video4linux/README.buz Thu Jan 1 01:00:00 1970 @@ -1,212 +0,0 @@ -Iomega Buz Driver for Linux -=========================== - -by Rainer Johanni - -Compiling and Loading the Driver -================================ - -You must run a 2.2.x kernel in order to use this driver. - -To compile the driver, just type make. - -Besides the files in this directory, the driver needs the -'videodev' and the 'i2c' module from the Linux kernel. -In order to get these modules available, enable module support -for VIDEODEV and BTTV (which implies i2c) in your kernel -configuration. You find these devices in the menu -"Character Devices" in your Kernel Configuration. - -Before you load the driver you must have a video device -at major device node 81. If you don't have it yet, do the -following (as root!): - -cd /dev -mknod video0 c 81 0 -ln -s video0 video - -Edit the 'update' script if you want to give the driver -special options and then type (as root) - -./update - -to insert all the necessary modules into the kernel. - -If you want to make full use of the Video for Linux uncompressed -grabbing facilities, you must either - -- obtain and install the "big_physarea patch" for your kernel and - set aside the necessary memory during boot time. - There seem to be several versions of this patch against - various kernel versions floating around in the net, - you may obtain one e.g. from: - http://www.polyware.nl/~middelin/patch/bigphysarea-2.2.1.tar.gz - You also have to compile your driver AFTER installing that patch - in order to get it working - - or - -- start your kernel with the mem=xxx option, where xxx is your - real memory minus the memory needed for the buffers. - For doing this add an entry in lilo.conf (if you use lilo): - append "mem=xxxM" - or add a line in your linux.par file (if you use loadlin): - mem=xxxM - -The second method is by far easier, however it is dangerous -if more than one driver at a time has the idea to use the memory -leftover by setting the mem=xxx parameter below the actual -memory size. - -Read also below how to use this memory! - - - -Driver Options -============== - -You are able to customize the behavior of the driver by giving -it some options at start time. - -default_input, default_norm ---------------------------- - -As soon as the driver is loaded, the Buz samples video signals -from one of its input ports and displays it on its output. -The driver uses the Composite Input and the video norm PAL for this. -If you want to change this default behavior, set default_input=1 -(for S-VHS input) or default_norm=1 for NTSC. - -v4l_nbufs, v4l_bufsize ----------------------- - -In order to make to make full use of the Video for Linux picture -grabbing facilities of the driver (which are needed by many -Video for Linux applications), the driver needs a set of -physically contiguous buffers for grabbing. These parameters -determine how many buffers of which size the driver will -allocate at open (the open will fail if it is unable to do so!). - -These values do not affect the MJPEG grabbing facilities of the driver, -they are needed for uncompressed image grabbing only!!! - -v4l_nbufs is the number of buffers to allocate, a value of 2 (the default) -should be sufficient in almost all cases. Only special applications -(streaming captures) will need more buffers and then mostly the -MJPEG capturing features of the Buz will be more appropriate. -So leave this parameter at it's default unless you know what you do. - -The things for v4l_bufsize are more complicated: -v4l_bufsize is set by default to 128 [KB] which is the maximum -amount of physically contiguous memory Linux is able to allocate -without kernel changes. This is sufficient for grabbing 24 bit color images -up to sizes of approx. 240x180 pixels (240*180*3 = 129600, 128 KB = 131072). - -In order to be able to capture bigger images you have either to -- obtain and install the "big_physarea patch" and set aside - the necessary memory during boot time or -- start your kernel with the mem=xxx option, where xxx is your - real memory minus the memory needed for the buffers. -In that case, useful settings for v4l_bufsize are -- 1296 [Kb] for grabbing 24 bit images of max size 768*576 -- 1728 [Kb] for 32bit images of same size (4*768*576 = 1728 Kb!) -You may reduce these numbers accordingly if you know you are only -grabbing 720 pixels wide images or NTSC images (max height 480). - -In some cases it may happen that Linux isn't even able to obtain -the default 128 KB buffers. If you don't need uncompressed image -grabbing at all, set v4l_bufsize to an arbitrary small value (e.g. 4) -in order to be able to open the video device. - -vidmem ------- - -The video mem address of the video card. -The driver has a little database for some videocards -to determine it from there. If your video card is not in there -you have either to give it to the driver as a parameter -or set in in a VIDIOCSFBUF ioctl - -The videocard database is contained in the file "videocards.h" -Gernot Ziegler wants to keep an actual version of that file. -If your card is not contained in that file, look at -http://www.lysator.liu.se/~gz/buz/ for an actual version of -"videocards.h". - -triton, natoma --------------- - -The driver tries to detect if you have a triton or natome chipset -in order to take special measures for these chipsets. -If this detection fails but you are sure you have such a chipset, -set the corresponding variable to 1. -This is a very special option and may go away in the future. - - - -Programming interface -===================== - -This driver should be fully compliant to Video for Linux, so all -tools working with Video for Linux should work with (hopefully) -no problems. - -A description of the Video for Linux programming interface can be found at: -http://roadrunner.swansea.linux.org.uk/v4lapi.shtml - -Besides the Video for Linux interface, the driver has a "proprietary" -interface for accessing the Buz's MJPEG capture and playback facilities. - -The ioctls for that interface are as follows: - -BUZIOC_G_PARAMS -BUZIOC_S_PARAMS - -Get and set the parameters of the buz. The user should always -do a BUZIOC_G_PARAMS (with a struct buz_params) to obtain the default -settings, change what he likes and then make a BUZIOC_S_PARAMS call. -A typical application should at least set the members -input, norm and decimation of the struct buz_params. -For a full description of all members see "buz.h" - -BUZIOC_REQBUFS - -Before being able to capture/playback, the user has to request -the buffers he is wanting to use. Fill the structure -buz_requestbuffers with the size (recommended: 256*1024) and -the number (recommended 32 up to 256). There are no such restrictions -as for the Video for Linux buffers, you should LEAVE SUFFICIENT -MEMORY for your system however, else strange things will happen .... -On return, the buz_requestbuffers structure contains number and -size of the actually allocated buffers. -You should use these numbers for doing a mmap of the buffers -into the user space. -The BUZIOC_REQBUFS ioctl also makes it happen, that the next mmap -maps the MJPEG buffer instead of the V4L buffers. - -BUZIOC_QBUF_CAPT -BUZIOC_QBUF_PLAY - -Queue a buffer for capture or playback. The first call also starts -streaming capture. When streaming capture is going on, you may -only queue further buffers or issue syncs until streaming -capture is switched off again with a argument of -1 to -a BUZIOC_QBUF_CAPT/BUZIOC_QBUF_PLAY ioctl. - -BUZIOC_SYNC - -Issue this ioctl when all buffers are queued. This ioctl will -block until the first buffer becomes free for saving its -data to disk (after BUZIOC_QBUF_CAPT) or for reuse (after BUZIOC_QBUF_PLAY). - -BUZIOC_G_STATUS - -Get the status of the input lines (video source connected/norm). -This ioctl may be subject to change. - - - - - -See the examples directory delivered with this driver -for actual coding examples! diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/video4linux/meye.txt linux.ac/Documentation/video4linux/meye.txt --- linux.vanilla/Documentation/video4linux/meye.txt Wed Jul 4 22:41:33 2001 +++ linux.ac/Documentation/video4linux/meye.txt Wed Oct 10 01:47:28 2001 @@ -4,7 +4,10 @@ Copyright (C) 2000 Andrew Tridgell This driver enable the use of video4linux compatible applications with the -Motion Eye camera. +Motion Eye camera. This driver requires the "Sony Vaio Programmable I/O +Control Device" driver (which can be found in the "Character drivers" +section of the kernel configuration utility) to be compiled and installed +(using its "camera=1" parameter). It can do at maximum 30 fps @ 320x240 or 15 fps @ 640x480. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/x86_64/mm.txt linux.ac/Documentation/x86_64/mm.txt --- linux.vanilla/Documentation/x86_64/mm.txt Thu Jan 1 01:00:00 1970 +++ linux.ac/Documentation/x86_64/mm.txt Wed Oct 10 01:47:28 2001 @@ -0,0 +1,148 @@ +The paging design used on the x86-64 linux kernel port in 2.4.x provides: + +o per process virtual address space limit of 512 Gigabytes +o top of userspace stack located at address 0x0000007fffffffff +o PAGE_OFFSET = 0xffff800000000000 +o start of the kernel = 0xffffffff800000000 +o global RAM per system 2^64-PAGE_OFFSET-sizeof(kernel) = 128 Terabytes - 2 Gigabytes +o no need of any common code change +o no need to use highmem to handle the 128 Terabytes of RAM + +Description: + + Userspace is able to modify and it sees only the 3rd/2nd/1st level + pagetables (pgd_offset() implicitly walks the 1st slot of the 4th + level pagetable and it returns an entry into the 3rd level pagetable). + This is where the per-process 512 Gigabytes limit cames from. + + The common code pgd is the PDPE, the pmd is the PDE, the + pte is the PTE. The PML4E remains invisible to the common + code. + + The kernel uses all the first 47 bits of the negative half + of the virtual address space to build the direct mapping using + 2 Mbytes page size. The kernel virtual addresses have bit number + 47 always set to 1 (and in turn also bits 48-63 are set to 1 too, + due the sign extension). This is where the 128 Terabytes - 2 Gigabytes global + limit of RAM cames from. + + Since the per-process limit is 512 Gigabytes (due to kernel common + code 3 level pagetable limitation), the higher virtual address mapped + into userspace is 0x7fffffffff and it makes sense to use it + as the top of the userspace stack to allow the stack to grow as + much as possible. + + Setting the PAGE_OFFSET to 2^39 (after the last userspace + virtual address) wouldn't make much difference compared to + setting PAGE_OFFSET to 0xffff800000000000 because we have an + hole into the virtual address space. The last byte mapped by the + 255th slot in the 4th level pagetable is at virtual address + 0x00007fffffffffff and the first byte mapped by the 256th slot in the + 4th level pagetable is at address 0xffff800000000000. Due to this + hole we can't trivially build a direct mapping across all the + 512 slots of the 4th level pagetable, so we simply use only the + second (negative) half of the 4th level pagetable for that purpose + (that provides us 128 Terabytes of contigous virtual addresses). + Strictly speaking we could build a direct mapping also across the hole + using some DISCONTIGMEM trick, but we don't need such a large + direct mapping right now. + +Future: + + During 2.5.x we can break the 512 Gigabytes per-process limit + possibly by removing from the common code any knowledge about the + architectural dependent physical layout of the virtual to physical + mapping. + + Once the 512 Gigabytes limit will be removed the kernel stack will + be moved (most probably to virtual address 0x00007fffffffffff). + Nothing will break in userspace due that move, as nothing breaks + in IA32 compiling the kernel with CONFIG_2G. + +Linus agreed on not breaking common code and to live with the 512 Gigabytes +per-process limitation for the 2.4.x timeframe and he has given me and Andi +some very useful hints... (thanks! :) + +Thanks also to H. Peter Anvin for his interesting and useful suggestions on +the x86-64-discuss lists! + +Other memory management related issues follows: + +PAGE_SIZE: + + If somebody is wondering why these days we still have a so small + 4k pagesize (16 or 32 kbytes would be much better for performance + of course), the PAGE_SIZE have to remain 4k for 32bit apps to + provide 100% backwards compatible IA32 API (we can't allow silent + fs corruption or as best a loss of coherency with the page cache + by allocating MAP_SHARED areas in MAP_ANONYMOUS memory with a + do_mmap_fake). I think it could be possible to have a dynamic page + size between 32bit and 64bit apps but it would need extremely + intrusive changes in the common code as first for page cache and + we sure don't want to depend on them right now even if the + hardware would support that. + +PAGETABLE SIZE: + + In turn we can't afford to have pagetables larger than 4k because + we could not be able to allocate them due physical memory + fragmentation, and failing to allocate the kernel stack is a minor + issue compared to failing the allocation of a pagetable. If we + fail the allocation of a pagetable the only thing we can do is to + sched_yield polling the freelist (deadlock prone) or to segfault + the task (not even the sighandler would be sure to run). + +KERNEL STACK: + + 1st stage: + + The kernel stack will be at first allocated with an order 2 allocation + (16k) (the utilization of the stack for a 64bit platform really + isn't exactly the double of a 32bit platform because the local + variables may not be all 64bit wide, but not much less). This will + make things even worse than they are right now on IA32 with + respect of failing fork/clone due memory fragmentation. + + 2nd stage: + + We'll benchmark if reserving one register as task_struct + pointer will improve performance of the kernel (instead of + recalculating the task_struct pointer starting from the stack + pointer each time). My guess is that recalculating will be faster + but it worth a try. + + If reserving one register for the task_struct pointer + will be faster we can as well split task_struct and kernel + stack. task_struct can be a slab allocation or a + PAGE_SIZEd allocation, and the kernel stack can then be + allocated in a order 1 allocation. Really this is risky, + since 8k on a 64bit platform is going to be less than 7k + on a 32bit platform but we could try it out. This would + reduce the fragmentation problem of an order of magnitude + making it equal to the current IA32. + + We must also consider the x86-64 seems to provide in hardware a + per-irq stack that could allow us to remove the irq handler + footprint from the regular per-process-stack, so it could allow + us to live with a smaller kernel stack compared to the other + linux architectures. + + 3rd stage: + + Before going into production if we still have the order 2 + allocation we can add a sysctl that allows the kernel stack to be + allocated with vmalloc during memory fragmentation. This have to + remain turned off during benchmarks :) but it should be ok in real + life. + +Order of PAGE_CACHE_SIZE and other allocations: + + On the long run we can increase the PAGE_CACHE_SIZE to be + an order 2 allocations and also the slab/buffercache etc.ec.. + could be all done with order 2 allocations. To make the above + to work we should change lots of common code thus it can be done + only once the basic port will be in a production state. Having + a working PAGE_CACHE_SIZE would be a benefit also for + IA32 and other architectures of course. + +Andrea SuSE diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/MAINTAINERS linux.ac/MAINTAINERS --- linux.vanilla/MAINTAINERS Thu Oct 11 13:52:06 2001 +++ linux.ac/MAINTAINERS Thu Oct 11 15:36:24 2001 @@ -748,6 +748,13 @@ M: tigran@veritas.com S: Maintained +INTERMEZZO FILE SYSTEM +P: Peter J. Braam +M: braam@clusterfs.com +W: http://www.inter-mezzo.org/ +L: intermezzo-discuss@lists.sourceforge.net +S: Maintained + IP MASQUERADING: P: Juanjo Ciarlante M: jjciarla@raiz.uncu.edu.ar @@ -875,6 +882,13 @@ L: linuxppc-dev@lists.linuxppc.org S: Maintained +LINUX FOR 64BIT POWERPC +P: David Engebretsen +M: engebret@us.ibm.com +W: http://linuxppc64.org +L: linuxppc64-dev@lists.linuxppc.org +S: Supported + LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP Dynamic Disks) P: Richard Russon (FlatCap) M: ldm@flatcap.org @@ -1053,6 +1067,12 @@ L: linux-net@vger.kernel.org S: Maintained +NINJA SCSI-3 / NINJA SCSI-32Bi PCMCIA SCSI HOST ADAPTER DRIVER +P: YOKOTA Hiroshi +M: yokota@netlab.is.tsukuba.ac.jp +W: http://www.netlab.is.tsukuba.ac.jp/~yokota/izumi/ninja/ +S: Maintained + NON-IDE/NON-SCSI CDROM DRIVERS [GENERAL] (come on, crew - mark your responsibility) P: Eberhard Moenkeberg M: emoenke@gwdg.de @@ -1160,6 +1180,11 @@ L: linux-ppp@vger.kernel.org S: Maintained +PPP OVER ATM (RFC 2364) +P: Mitchell Blank Jr +M: mitch@sfgoth.com +S: Maintained + PPP OVER ETHERNET P: Michal Ostrowski M: mostrows@styx.uwaterloo.ca @@ -1399,6 +1424,13 @@ W: http://www.linuxtr.net S: Maintained +TOSHIBA SMM DRIVER +P: Jonathan Buzzard +M: jonathan@buzzard.org.uk +L: tlinux-users@tce.toshiba-dme.co.jp +W: http://www.buzzard.org.uk/toshiba/ +S: Maintained + TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE P: Ollie Lho M: ollie@sis.com.tw @@ -1604,6 +1636,14 @@ L: linux-usb-devel@lists.sourceforge.net W: http://usb.in.tum.de S: Maintained + +USER-MODE PORT +P: Jeff Dike +M: jdike@karaya.com +L: user-mode-linux-devel@lists.sourceforge.net +L: user-mode-linux-user@lists.sourceforge.net +W: http://user-mode-linux.sourceforge.net +S: Maintained VFAT FILESYSTEM: P: Gordon Chaffee @@ -1662,11 +1702,24 @@ M: mingo@redhat.com S: Maintained +X86-64 port +P: Andi Kleen +M: ak@suse.de +L: discuss@x86-64.org +W: http://www.x86-64.org +S: Maintained + YAM DRIVER FOR AX.25 P: Jean-Paul Roubelat M: jpr@f6fbb.org L: linux-hams@vger.kernel.org S: Maintained + +YMFPCI YAMAHA PCI SOUND +P: Pete Zaitcev +M: zaitcev@yahoo.com +L: linux-kernel@vger.kernel.org +S: Maintained Z85230 SYNCHRONOUS DRIVER P: Alan Cox diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Makefile linux.ac/Makefile --- linux.vanilla/Makefile Thu Oct 11 13:52:16 2001 +++ linux.ac/Makefile Thu Oct 11 13:53:01 2001 @@ -1,11 +1,20 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 12 -EXTRAVERSION = +EXTRAVERSION = -ac1 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) -ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) +KERNELPATH=kernel-$(shell echo $(KERNELRELEASE) | sed -e "s/-//") + +# 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 +# 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) CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ else if [ -x /bin/bash ]; then echo /bin/bash; \ @@ -136,7 +145,8 @@ drivers/net/net.o \ drivers/media/media.o DRIVERS-$(CONFIG_AGP) += drivers/char/agp/agp.o -DRIVERS-$(CONFIG_DRM) += drivers/char/drm/drm.o +DRIVERS-$(CONFIG_DRM_NEW) += drivers/char/drm/drm.o +DRIVERS-$(CONFIG_DRM_OLD) += drivers/char/drm-4.0/drm.o DRIVERS-$(CONFIG_NUBUS) += drivers/nubus/nubus.a DRIVERS-$(CONFIG_ISDN) += drivers/isdn/isdn.a DRIVERS-$(CONFIG_NET_FC) += drivers/net/fc/fc.o @@ -165,10 +175,9 @@ DRIVERS-$(CONFIG_DIO) += drivers/dio/dio.a DRIVERS-$(CONFIG_SBUS) += drivers/sbus/sbus_all.o DRIVERS-$(CONFIG_ZORRO) += drivers/zorro/driver.o -DRIVERS-$(CONFIG_FC4) += drivers/fc4/fc4.a DRIVERS-$(CONFIG_ALL_PPC) += drivers/macintosh/macintosh.o DRIVERS-$(CONFIG_MAC) += drivers/macintosh/macintosh.o -DRIVERS-$(CONFIG_ISAPNP) += drivers/pnp/pnp.o +DRIVERS-$(CONFIG_PNP) += drivers/pnp/pnp.o DRIVERS-$(CONFIG_SGI_IP22) += drivers/sgi/sgi.a DRIVERS-$(CONFIG_VT) += drivers/video/video.o DRIVERS-$(CONFIG_PARIDE) += drivers/block/paride/paride.a @@ -176,7 +185,7 @@ DRIVERS-$(CONFIG_TC) += drivers/tc/tc.a DRIVERS-$(CONFIG_USB) += drivers/usb/usbdrv.o DRIVERS-$(CONFIG_INPUT) += drivers/input/inputdrv.o -DRIVERS-$(CONFIG_I2O) += drivers/i2o/i2o.o +DRIVERS-$(CONFIG_I2O) += drivers/message/i2o/i2o.o DRIVERS-$(CONFIG_IRDA) += drivers/net/irda/irda.o DRIVERS-$(CONFIG_I2C) += drivers/i2c/i2c.o DRIVERS-$(CONFIG_PHONE) += drivers/telephony/telephony.o @@ -202,7 +211,7 @@ drivers/scsi/aic7xxx/aicasm/aicasm_scan.c \ drivers/scsi/aic7xxx/aicasm/y.tab.h \ drivers/scsi/aic7xxx/aicasm/aicasm \ - drivers/scsi/53c700-mem.c \ + drivers/scsi/53c700_d.h \ net/khttpd/make_times_h \ net/khttpd/times.h \ submenu* @@ -223,7 +232,7 @@ drivers/sound/pndsperm.c \ drivers/sound/pndspini.c \ drivers/atm/fore200e_*_fw.c drivers/atm/.fore200e_*.fw \ - .version .config* config.in config.old \ + .version* .config* config.in config.old \ scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp \ scripts/lxdialog/*.o scripts/lxdialog/lxdialog \ .menuconfig.log \ @@ -300,8 +309,8 @@ $(TOPDIR)/include/linux/compile.h: include/linux/compile.h newversion: - . scripts/mkversion > .tmpversion - @mv -f .tmpversion .version + . scripts/mkversion > .version.tmp + @mv -f .version.tmp .version include/linux/compile.h: $(CONFIGURATION) include/linux/version.h newversion @echo -n \#define UTS_VERSION \"\#`cat .version` > .ver diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/config.in linux.ac/arch/alpha/config.in --- linux.vanilla/arch/alpha/config.in Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/alpha/config.in Wed Oct 10 01:47:28 2001 @@ -7,6 +7,7 @@ define_bool CONFIG_UID16 n define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y +define_bool CONFIG_GENERIC_BUST_SPINLOCK n mainmenu_name "Kernel configuration of Linux for Alpha machines" @@ -291,6 +292,10 @@ source drivers/scsi/Config.in fi endmenu + +if [ "$CONFIG_PCI" = "y" ]; then + source drivers/message/fusion/Config.in +fi if [ "$CONFIG_NET" = "y" ]; then mainmenu_option next_comment diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/irq.c linux.ac/arch/alpha/kernel/irq.c --- linux.vanilla/arch/alpha/kernel/irq.c Mon Sep 17 21:16:30 2001 +++ linux.ac/arch/alpha/kernel/irq.c Wed Oct 10 01:47:28 2001 @@ -38,7 +38,7 @@ static void register_irq_proc(unsigned int irq); -volatile unsigned long irq_err_count; +unsigned long irq_err_count; /* * Special irq handlers. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/irq_alpha.c linux.ac/arch/alpha/kernel/irq_alpha.c --- linux.vanilla/arch/alpha/kernel/irq_alpha.c Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/alpha/kernel/irq_alpha.c Thu Oct 11 15:36:29 2001 @@ -18,6 +18,8 @@ unsigned long __irq_attempt[NR_IRQS]; #endif +extern unsigned long irq_err_count; + /* Hack minimum IPL during interrupt processing for broken hardware. */ #ifdef CONFIG_ALPHA_BROKEN_IRQ_MASK int __min_ipl; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/process.c linux.ac/arch/alpha/kernel/process.c --- linux.vanilla/arch/alpha/kernel/process.c Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/alpha/kernel/process.c Thu Oct 11 15:36:36 2001 @@ -50,6 +50,7 @@ */ unsigned long init_user_stack[1024] = { STACK_MAGIC, }; +static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/setup.c linux.ac/arch/alpha/kernel/setup.c --- linux.vanilla/arch/alpha/kernel/setup.c Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/alpha/kernel/setup.c Thu Oct 11 16:50:25 2001 @@ -1044,9 +1044,22 @@ /* - * BUFFER is PAGE_SIZE bytes long. + * get_cpuinfo - Get information on one CPU for use by the procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * BUFFER is PAGE_SIZE - 1K bytes long. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. */ -int get_cpuinfo(char *buffer) + +int get_cpuinfo(char *buffer, unsigned *cpu_np) { extern struct unaligned_stat { unsigned long count, va, pc; @@ -1063,6 +1076,14 @@ char *systype_name; char *sysvariation_name; int len, nr_processors; + unsigned n; + + /* Unlike some archs, don't have per-CPU info, so just toggle 0/1 */ + n = *cpu_np; + *cpu_np = 1; + if (n != 0) { + return (0); + } cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset); cpu_index = (unsigned) (cpu->type - 1); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/mm/fault.c linux.ac/arch/alpha/mm/fault.c --- linux.vanilla/arch/alpha/mm/fault.c Tue Sep 18 00:15:02 2001 +++ linux.ac/arch/alpha/mm/fault.c Fri Oct 12 09:43:09 2001 @@ -24,10 +24,37 @@ #include #include #include +#include #include #include +extern spinlock_t timerlist_lock; +void bust_spinlocks(int yes) +{ + spin_lock_init(&timerlist_lock); + if (yes) { + oops_in_progress = 1; +#ifdef CONFIG_SMP + spin_lock_init(&global_irq_lock); /* Many serial drivers do __global_cli() */ +#endif + } else { + int loglevel_save = console_loglevel; + unblank_screen(); + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console up */ + printk(" "); + console_loglevel = loglevel_save; + } +} + + + extern void die_if_kernel(char *,struct pt_regs *,long, unsigned long *); @@ -140,7 +167,6 @@ goto bad_area; } - survive: /* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo @@ -195,12 +221,6 @@ * us unable to handle the page fault gracefully. */ out_of_memory: - if (current->pid == 1) { - current->policy |= SCHED_YIELD; - schedule(); - down_read(&mm->mmap_sem); - goto survive; - } printk(KERN_ALERT "VM: killing process %s(%d)\n", current->comm, current->pid); if (!user_mode(regs)) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/mm/init.c linux.ac/arch/alpha/mm/init.c --- linux.vanilla/arch/alpha/mm/init.c Fri Sep 21 04:02:03 2001 +++ linux.ac/arch/alpha/mm/init.c Wed Oct 10 01:47:28 2001 @@ -403,7 +403,7 @@ si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/Makefile linux.ac/arch/arm/Makefile --- linux.vanilla/arch/arm/Makefile Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/Makefile Wed Oct 10 01:47:28 2001 @@ -45,8 +45,6 @@ CFLAGS +=$(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float AFLAGS +=$(apcs-y) $(arch-y) -mno-fpu -LIBGCC := $(shell $(CC) $(CFLAGS) --print-libgcc-file-name) - ifeq ($(CONFIG_CPU_26),y) PROCESSOR = armo ifeq ($(CONFIG_ROM_KERNEL),y) @@ -123,7 +121,7 @@ endif ifeq ($(CONFIG_ARCH_CLPS711X),y) -TEXTADDR = 0xc0018000 +TEXTADDR = 0xc0028000 MACHINE = clps711x endif @@ -131,7 +129,7 @@ MACHINE = anakin endif -export LIBGCC MACHINE PROCESSOR TEXTADDR GZFLAGS +export MACHINE PROCESSOR TEXTADDR GZFLAGS # Only set INCDIR if its not already defined above # Grr, ?= doesn't work as all the other assignment operators do. Make bug? @@ -155,7 +153,7 @@ SUBDIRS += arch/arm/kernel arch/arm/mm arch/arm/lib arch/arm/nwfpe \ arch/arm/fastfpe CORE_FILES := arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES) -LIBS := arch/arm/lib/lib.a $(LIBS) $(LIBGCC) +LIBS := arch/arm/lib/lib.a $(LIBS) ifeq ($(CONFIG_FPE_NWFPE),y) LIBS := arch/arm/nwfpe/math-emu.o $(LIBS) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/boot/Makefile linux.ac/arch/arm/boot/Makefile --- linux.vanilla/arch/arm/boot/Makefile Wed Jun 27 22:12:04 2001 +++ linux.ac/arch/arm/boot/Makefile Wed Oct 10 01:47:28 2001 @@ -69,12 +69,21 @@ ZBSSADDR = 0xf03e0000 endif -ifeq ($(CONFIG_ARCH_P720T),y) -ZTEXTADDR = 0xc0018000 +# The standard locations for stuff on CLPS711x type processors +ifeq ($(CONFIG_ARCH_CLPS711X),y) +ZTEXTADDR = 0xc0028000 PARAMS_PHYS = 0xc0000100 +endif + +# Should probably have some agreement on these... +ifeq ($(CONFIG_ARCH_P720T),y) INITRD_PHYS = 0xc0400000 INITRD_VIRT = 0xc0400000 endif +ifeq ($(CONFIG_ARCH_CDB89712),y) +INITRD_PHYS = 0x00700000 +INITRD_VIRT = 0xc0300000 +endif ifeq ($(CONFIG_ARCH_SA1100),y) ZTEXTADDR = 0xc0008000 @@ -89,6 +98,12 @@ endif ifeq ($(CONFIG_SA1100_GRAPHICSCLIENT),y) ZTEXTADDR = 0xC0200000 +endif +ifeq ($(CONFIG_SA1100_GRAPHICSMASTER),y) + ZTEXTADDR = 0xC0400000 +endif +ifeq ($(CONFIG_SA1100_ADSBITSY),y) + ZTEXTADDR = 0xC0400000 endif ifeq ($(CONFIG_SA1100_YOPY),y) ZTEXTADDR = 0x00080000 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/boot/bootp/Makefile linux.ac/arch/arm/boot/bootp/Makefile --- linux.vanilla/arch/arm/boot/bootp/Makefile Fri Feb 9 00:32:44 2001 +++ linux.ac/arch/arm/boot/bootp/Makefile Wed Oct 10 01:47:28 2001 @@ -3,7 +3,6 @@ # ZSYSTEM =$(TOPDIR)/arch/arm/boot/zImage -INITRD =$(ZSYSTEM) ZLDFLAGS =-p -X -T bootp.lds \ --defsym initrd_addr=$(INITRD_PHYS) \ --defsym initrd_virt=$(INITRD_VIRT) \ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/boot/bootp/init.S linux.ac/arch/arm/boot/bootp/init.S --- linux.vanilla/arch/arm/boot/bootp/init.S Fri Feb 9 00:32:44 2001 +++ linux.ac/arch/arm/boot/bootp/init.S Wed Oct 10 01:47:28 2001 @@ -39,7 +39,7 @@ * method by looking at the first word; this should either indicate a page * size of 4K, 16K or 32K. */ - ldmia r13, {r5-r8} @ get size and addr of initrd + ldmia r13, {r4-r8} @ get size and addr of initrd @ r5 = ATAG_INITRD @ r6 = initrd start @ r7 = initrd end @@ -48,10 +48,25 @@ teq r9, #0x1000 @ 4K? teqne r9, #0x4000 @ 16K? teqne r9, #0x8000 @ 32K? - beq no_taglist + beq param_struct + + ldr r9, [r8, #4] @ get first tag + teq r9, r4 + bne taglist @ ok, we have a tag list + +/* + * We didn't find a valid tag list - create one. + */ + str r4, [r8, #4] + mov r4, #8 + str r4, [r8, #0] + mov r4, #0 + str r4, [r8, #8] /* * find the end of the tag list, and then add an INITRD tag on the end. + * If there is already an INITRD tag, then we ignore it; the last INITRD + * tag takes precidence. */ taglist: ldr r9, [r8, #0] @ tag length teq r9, #0 @ last tag? @@ -63,7 +78,10 @@ stmia r8, {r4, r5, r6, r7, r9} mov pc, r12 @ call kernel -no_taglist: add r8, r8, #16*4 +/* + * We found a param struct. Modify the param struct for the initrd + */ +param_struct: add r8, r8, #16*4 stmia r8, {r6,r7} @ save in param_struct mov pc, r12 @ call kernel @@ -83,6 +101,7 @@ .word kernel_addr .word kernel_len + .word 0x54410001 @ r4 = ATAG_CORE .word 0x54410005 @ r5 = ATAG_INITRD .word initrd_virt @ r6 .word initrd_len @ r7 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/boot/compressed/Makefile linux.ac/arch/arm/boot/compressed/Makefile --- linux.vanilla/arch/arm/boot/compressed/Makefile Wed Jun 27 22:12:04 2001 +++ linux.ac/arch/arm/boot/compressed/Makefile Wed Oct 10 01:47:28 2001 @@ -51,7 +51,7 @@ endif ifeq ($(CONFIG_ARCH_SA1100),y) -OBJS += head-sa1100.o setup-sa1100.o +OBJS += head-sa1100.o ifeq ($(CONFIG_SA1100_NANOENGINE),y) OBJS += hw-bse.o endif @@ -64,6 +64,8 @@ else SEDFLAGS += s/BSS_START/ALIGN(4)/ endif + +LIBGCC := $(shell $(CC) $(CFLAGS) --print-libgcc-file-name) all: vmlinux diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/boot/compressed/head-sa1100.S linux.ac/arch/arm/boot/compressed/head-sa1100.S --- linux.vanilla/arch/arm/boot/compressed/head-sa1100.S Fri Feb 9 00:32:44 2001 +++ linux.ac/arch/arm/boot/compressed/head-sa1100.S Wed Oct 10 01:47:28 2001 @@ -17,6 +17,24 @@ @ Preserve r8/r7 i.e. kernel entry values +#if defined(CONFIG_SA1100_GRAPHICSCLIENT) && !defined(CONFIG_ANGELBOOT) + mov r7, #MACH_TYPE_GRAPHICSCLIENT + mov r8, #0 +#endif +#if defined(CONFIG_SA1100_GRAPHICSMASTER) && !defined(CONFIG_ANGELBOOT) + mov r7, #MACH_TYPE_GRAPHICSMASTER + mov r8, #0 +#endif +#if defined(CONFIG_SA1100_ADSBITSY) && !defined(CONFIG_ANGELBOOT) + mov r7, #MACH_TYPE_ADSBITSY + mov r8, #0 +#endif + +#ifdef CONFIG_SA1100_PFS168 + @ REVISIT_PFS168: Temporary until firmware updated to use assigned machine number + mov r7, #MACH_TYPE_PFS168 +#endif + #ifdef CONFIG_SA1100_VICTOR teq r7, #MACH_TYPE_VICTOR bne 10f @@ -51,7 +69,6 @@ bic r0, r0, #0x1000 @ clear Icache mcr p15, 0, r0, c1, c0, 0 -#ifdef CONFIG_ANGELBOOT /* * Pause for a short time so that we give enough time * for the host to start a terminal up. @@ -59,5 +76,4 @@ mov r0, #0x00200000 1: subs r0, r0, #1 bne 1b -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/boot/compressed/setup-sa1100.S linux.ac/arch/arm/boot/compressed/setup-sa1100.S --- linux.vanilla/arch/arm/boot/compressed/setup-sa1100.S Wed Jun 27 22:12:04 2001 +++ linux.ac/arch/arm/boot/compressed/setup-sa1100.S Thu Jan 1 01:00:00 1970 @@ -1,163 +0,0 @@ -/* - * linux/arch/arm/boot/compressed/setup-sa1100.S - * - * Copyright (C) 2000 Nicolas Pitre - * - * SA1100 setup routines, to be used after BSS has been cleared. - * - * John G Dorsey 2000/05/25 : - * Runtime test for Neponset added. - */ - -#include -#include -#include - - .text - -GPIO_BASE: .long 0x90040000 -#define GPLR 0x00 -#define GPDR 0x04 -#define GPSR 0x08 -#define GAFR 0x1c - -PPC_BASE: .long 0x90060000 -#define PPAR 0x08 - -IC_BASE: .long 0x90050000 -#define ICMR 0x04 - -UART1_BASE: .long 0x80010000 -UART3_BASE: .long 0x80050000 -#define UTCR0 0x00 -#define UTCR1 0x04 -#define UTCR2 0x08 -#define UTCR3 0x0c -#define UTSR0 0x1c -#define UTSR1 0x20 - -#ifndef CONFIG_SA1100_DEFAULT_BAUDRATE -#define CONFIG_SA1100_DEFAULT_BAUDRATE 9600 -#endif - -#define BAUD_DIV ((230400/CONFIG_SA1100_DEFAULT_BAUDRATE)-1) - -SCR_loc: .long SYMBOL_NAME(SCR_value) -#define GPIO_2_9 0x3fc - - -/* - * void sa1100_setup( int arch_id ); - * - * This is called from decompress_kernel() with the arch_decomp_setup() macro. - */ - -ENTRY(sa1100_setup) - mov r3, r0 @ keep machine type in r3 - - @ Clear all interrupt sources - ldr r0, IC_BASE - mov r1, #0 - str r1, [r0, #ICMR] - -@ Read System Configuration "Register" for Assabet. -@ (taken from "Intel StrongARM SA-1110 Microprocessor Development Board -@ User's Guide," p.4-9) - - teq r3, #MACH_TYPE_ASSABET - bne skip_SCR - - ldr r0, GPIO_BASE - ldr r1, [r0, #GPDR] - and r1, r1, #GPIO_2_9 - str r1, [r0, #GPDR] - mov r1, #GPIO_2_9 - str r1, [r0, #GPSR] - ldr r1, [r0, #GPDR] - bic r1, r1, #GPIO_2_9 - str r1, [r0, #GPDR] - - mov r2, #100 -1: ldr r1, [r0, #GPLR] - subs r2, r2, #1 - bne 1b - - and r2, r1, #GPIO_2_9 - ldr r1, SCR_loc - str r2, [r1] - - ldr r1, [r0, #GPDR] - and r1, r1, #GPIO_2_9 - str r1, [r0, #GPDR] - -skip_SCR: - - @ Initialize UART (if bootloader has not done it yet)... - teq r3, #MACH_TYPE_BRUTUS - teqne r3, #MACH_TYPE_ASSABET - teqne r3, #MACH_TYPE_ITSY - teqne r3, #MACH_TYPE_OMNIMETER - teqne r3, #MACH_TYPE_JORNADA720 - teqne r3, #MACH_TYPE_GRAPHICSCLIENT - teqne r3, #MACH_TYPE_FLEXANET - bne skip_uart - - @ UART3 if Assabet is used with Neponset - teq r3, #MACH_TYPE_ASSABET @ if Assabet - tsteq r2, #(1 << 9) @ ... and Neponset present - ldreq r0, UART3_BASE - beq uart_init - - @ UART3 on GraphicsClient - teq r3, #MACH_TYPE_GRAPHICSCLIENT - ldreq r0, UART3_BASE - beq uart_init - - @ At least for Brutus, the UART1 is used through - @ the alternate GPIO function... - teq r3, #MACH_TYPE_BRUTUS - bne uart1 - -alt_GPIO_uart: ldr r0, GPIO_BASE - ldr r1, [r0, #GPDR] - bic r1, r1, #1<<15 - orr r1, r1, #1<<14 - str r1, [r0, #GPDR] - ldr r1, [r0, #GAFR] - orr r1, r1, #(1<<15)|(1<<14) - str r1, [r0, #GAFR] - ldr r0, PPC_BASE - ldr r1, [r0, #PPAR] - orr r1, r1, #1<<12 - str r1, [r0, #PPAR] - -uart1: ldr r0, UART1_BASE - -uart_init: -1: ldr r1, [r0, #UTSR1] - tst r1, #1<<0 @ TBY - bne 1b - mov r1, #0 - str r1, [r0, #UTCR3] - mov r1, #0x08 @ 8N1 - str r1, [r0, #UTCR0] - mov r1, #BAUD_DIV - str r1, [r0, #UTCR2] - mov r1, r1, lsr #8 - str r1, [r0, #UTCR1] - mov r1, #0x03 @ RXE + TXE - str r1, [r0, #UTCR3] - mov r1, #0xff @ flush status reg - str r1, [r0, #UTSR0] -skip_uart: - - @ Extra specific setup calls - @ The machine type is passed in r0 - mov r0, r3 -#ifdef CONFIG_SA1100_NANOENGINE - teq r0, #MACH_TYPE_NANOENGINE - beq SYMBOL_NAME(bse_setup) -#endif - -out: mov pc, lr - diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/config.in linux.ac/arch/arm/config.in --- linux.vanilla/arch/arm/config.in Mon Aug 13 01:36:24 2001 +++ linux.ac/arch/arm/config.in Wed Oct 10 01:47:28 2001 @@ -11,6 +11,7 @@ define_bool CONFIG_UID16 y define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n +define_bool CONFIG_GENERIC_BUST_SPINLOCK n mainmenu_option next_comment @@ -68,12 +69,13 @@ comment 'SA11x0 Implementations' dep_bool ' Assabet' CONFIG_SA1100_ASSABET $CONFIG_ARCH_SA1100 dep_bool ' Include support for Neponset' CONFIG_ASSABET_NEPONSET $CONFIG_SA1100_ASSABET +dep_bool ' ADS Bitsy' CONFIG_SA1100_ADSBITSY $CONFIG_ARCH_SA1100 dep_bool ' Brutus' CONFIG_SA1100_BRUTUS $CONFIG_ARCH_SA1100 dep_bool ' CerfBoard' CONFIG_SA1100_CERF $CONFIG_ARCH_SA1100 if [ "$CONFIG_SA1100_CERF" = "y" ]; then bool ' 32MB Cerf support' CONFIG_SA1100_CERF_32MB fi -dep_bool ' Compaq iPAQ H3600 (Bitsy)' CONFIG_SA1100_BITSY $CONFIG_ARCH_SA1100 +dep_bool ' Compaq iPAQ H3600' CONFIG_SA1100_H3600 $CONFIG_ARCH_SA1100 #dep_bool ' Empeg' CONFIG_SA1100_EMPEG $CONFIG_ARCH_SA1100 dep_bool ' Extenex HandHeld Theater (Squashtail)' CONFIG_SA1100_EXTENEX1 $CONFIG_ARCH_SA1100 if [ "$CONFIG_SA1100_EXTENEX1" = "y" ]; then @@ -82,6 +84,7 @@ dep_bool ' FlexaNet' CONFIG_SA1100_FLEXANET $CONFIG_ARCH_SA1100 dep_bool ' FreeBird-v1.1' CONFIG_SA1100_FREEBIRD $CONFIG_ARCH_SA1100 dep_bool ' GraphicsClient Plus' CONFIG_SA1100_GRAPHICSCLIENT $CONFIG_ARCH_SA1100 +dep_bool ' GraphicsMaster' CONFIG_SA1100_GRAPHICSMASTER $CONFIG_ARCH_SA1100 dep_bool ' HP Jornada 720' CONFIG_SA1100_JORNADA720 $CONFIG_ARCH_SA1100 dep_bool ' HuW WebPanel' CONFIG_SA1100_HUW_WEBPANEL $CONFIG_ARCH_SA1100 dep_bool ' Itsy' CONFIG_SA1100_ITSY $CONFIG_ARCH_SA1100 @@ -101,14 +104,38 @@ if [ "$CONFIG_ASSABET_NEPONSET" = "y" -o \ "$CONFIG_SA1100_JORNADA720" = "y" -o \ "$CONFIG_SA1100_PFS168" = "y" -o \ - "$CONFIG_SA1100_XP860" = "y" ]; then + "$CONFIG_SA1100_XP860" = "y" -o \ + "$CONFIG_SA1100_GRAPHICSMASTER" = "y" -o \ + "$CONFIG_SA1100_ADSBITSY" = "y" ]; then define_bool CONFIG_SA1111 y + define_int CONFIG_FORCE_MAX_ZONEORDER 9 fi endmenu mainmenu_option next_comment comment 'CLPS711X/EP721X Implementations' +dep_bool ' CDB89712' CONFIG_ARCH_CDB89712 $CONFIG_ARCH_CLPS711X +dep_bool ' CLEP7312' CONFIG_ARCH_CLEP7312 $CONFIG_ARCH_CLPS711X +dep_bool ' EDB7211' CONFIG_ARCH_EDB7211 $CONFIG_ARCH_CLPS711X dep_bool ' P720T' CONFIG_ARCH_P720T $CONFIG_ARCH_CLPS711X + +# XXX Maybe these should indicate register compatibility +# instead of being mutually exclusive. +if [ "$CONFIG_ARCH_EDB7211" = "y" ]; then + define_bool CONFIG_ARCH_EP7211 y +else + define_bool CONFIG_ARCH_EP7211 n +fi +if [ "$CONFIG_ARCH_P720T" = "y" ]; then + define_bool CONFIG_ARCH_EP7212 y +else + define_bool CONFIG_ARCH_EP7212 n +fi + +if [ "$CONFIG_ARCH_EP7211" = "y" -o \ + "$CONFIG_ARCH_EP7212" = "y" ]; then + bool ' EP72xx ROM boot' CONFIG_EP72XX_ROM_BOOT +fi endmenu # Definitions to make life easier @@ -196,7 +223,9 @@ fi # ARM720T -if [ "$CONFIG_ARCH_CLPS711X" = "y" -o "$CONFIG_ARCH_L7200" = "y" ]; then +if [ "$CONFIG_ARCH_CLPS711X" = "y" -o \ + "$CONFIG_ARCH_L7200" = "y" -o \ + "$CONFIG_ARCH_CDB89712" = "y" ]; then define_bool CONFIG_CPU_ARM720T y else if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then @@ -255,12 +284,13 @@ define_bool CONFIG_CPU_SA1100 n fi -#if [ "$CONFIG_CPU_32" = "y" ]; then -# bool 'Support Thumb instructions' CONFIG_ARM_THUMB -#fi +if [ "$CONFIG_CPU_32" = "y" ]; then + dep_bool 'Support Thumb instructions (experimental)' CONFIG_ARM_THUMB $CONFIG_EXPERIMENTAL +fi # Select various configuration options depending on the machine type -if [ "$CONFIG_ARCH_SA1100" = "y" ]; then +if [ "$CONFIG_ARCH_EDB7211" = "y" -o \ + "$CONFIG_ARCH_SA1100" = "y" ]; then define_bool CONFIG_DISCONTIGMEM y else define_bool CONFIG_DISCONTIGMEM n @@ -289,6 +319,8 @@ "$CONFIG_ARCH_SHARK" = "y" -o \ "$CONFIG_ARCH_CLPS7500" = "y" -o \ "$CONFIG_ARCH_EBSA110" = "y" -o \ + "$CONFIG_ARCH_CDB89712" = "y" -o \ + "$CONFIG_ARCH_EDB7211" = "y" -o \ "$CONFIG_ARCH_SA1100" = "y" ]; then define_bool CONFIG_ISA y else @@ -318,6 +350,7 @@ bool 'System V IPC' CONFIG_SYSVIPC bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT bool 'Sysctl support' CONFIG_SYSCTL +comment 'At least one math emulation must be selected' tristate 'NWFPE math emulation' CONFIG_FPE_NWFPE dep_tristate 'FastFPE math emulation (experimental)' CONFIG_FPE_FASTFPE $CONFIG_EXPERIMENTAL choice 'Kernel core (/proc/kcore) format' \ @@ -335,6 +368,7 @@ "$CONFIG_ARCH_PERSONAL_SERVER" = "y" -o \ "$CONFIG_ARCH_CATS" = "y" -o \ "$CONFIG_ARCH_P720T" = "y" -o \ + "$CONFIG_ARCH_CDB89712" = "y" -o \ "$CONFIG_ARCH_ANAKIN" = "y" ]; then string 'Default kernel command string' CONFIG_CMDLINE "" fi @@ -346,6 +380,7 @@ "$CONFIG_ARCH_CO285" = "y" -o \ "$CONFIG_ARCH_SA1100" = "y" -o \ "$CONFIG_ARCH_INTEGRATOR" = "y" -o \ + "$CONFIG_ARCH_CDB89712" = "y" -o \ "$CONFIG_ARCH_P720T" = "y" ]; then bool 'Timer and CPU usage LEDs' CONFIG_LEDS if [ "$CONFIG_LEDS" = "y" ]; then @@ -429,7 +464,7 @@ source drivers/ieee1394/Config.in -source drivers/i2o/Config.in +source drivers/message/i2o/Config.in mainmenu_option next_comment comment 'ISDN subsystem' @@ -509,6 +544,8 @@ fi endmenu fi + +source drivers/misc/Config.in source drivers/usb/Config.in diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/Makefile linux.ac/arch/arm/kernel/Makefile --- linux.vanilla/arch/arm/kernel/Makefile Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/kernel/Makefile Wed Oct 10 01:47:29 2001 @@ -41,7 +41,7 @@ export-objs := armksyms.o dma.o ecard.o fiq.o io.o oldlatches.o time.o no-irq-arch := $(CONFIG_ARCH_INTEGRATOR) $(CONFIG_ARCH_CLPS711X) \ - $(CONFIG_ARCH_FOOTBRIDGE) $(CONFIG_ARCH_EBSA110) \ + $(CONFIG_FOOTBRIDGE) $(CONFIG_ARCH_EBSA110) \ $(CONFIG_ARCH_SA1100) ifneq ($(findstring y,$(no-irq-arch)),y) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/armksyms.c linux.ac/arch/arm/kernel/armksyms.c --- linux.vanilla/arch/arm/kernel/armksyms.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/kernel/armksyms.c Wed Oct 10 01:47:29 2001 @@ -54,22 +54,16 @@ * compiler... (prototypes are not correct though, but that * doesn't really matter since they're not versioned). */ -extern void __gcc_bcmp(void); extern void __ashldi3(void); extern void __ashrdi3(void); -extern void __cmpdi2(void); -extern void __divdi3(void); extern void __divsi3(void); extern void __lshrdi3(void); -extern void __moddi3(void); extern void __modsi3(void); extern void __muldi3(void); -extern void __negdi2(void); extern void __ucmpdi2(void); extern void __udivdi3(void); extern void __udivmoddi4(void); extern void __udivsi3(void); -extern void __umoddi3(void); extern void __umodsi3(void); extern void ret_from_exception(void); @@ -213,23 +207,27 @@ EXPORT_SYMBOL(uaccess_user); #endif +EXPORT_SYMBOL_NOVERS(__get_user_1); +EXPORT_SYMBOL_NOVERS(__get_user_2); +EXPORT_SYMBOL_NOVERS(__get_user_4); +EXPORT_SYMBOL_NOVERS(__get_user_8); + +EXPORT_SYMBOL_NOVERS(__put_user_1); +EXPORT_SYMBOL_NOVERS(__put_user_2); +EXPORT_SYMBOL_NOVERS(__put_user_4); +EXPORT_SYMBOL_NOVERS(__put_user_8); + /* gcc lib functions */ -EXPORT_SYMBOL_NOVERS(__gcc_bcmp); EXPORT_SYMBOL_NOVERS(__ashldi3); EXPORT_SYMBOL_NOVERS(__ashrdi3); -EXPORT_SYMBOL_NOVERS(__cmpdi2); -EXPORT_SYMBOL_NOVERS(__divdi3); EXPORT_SYMBOL_NOVERS(__divsi3); EXPORT_SYMBOL_NOVERS(__lshrdi3); -EXPORT_SYMBOL_NOVERS(__moddi3); EXPORT_SYMBOL_NOVERS(__modsi3); EXPORT_SYMBOL_NOVERS(__muldi3); -EXPORT_SYMBOL_NOVERS(__negdi2); EXPORT_SYMBOL_NOVERS(__ucmpdi2); EXPORT_SYMBOL_NOVERS(__udivdi3); EXPORT_SYMBOL_NOVERS(__udivmoddi4); EXPORT_SYMBOL_NOVERS(__udivsi3); -EXPORT_SYMBOL_NOVERS(__umoddi3); EXPORT_SYMBOL_NOVERS(__umodsi3); /* bitops */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/bios32.c linux.ac/arch/arm/kernel/bios32.c --- linux.vanilla/arch/arm/kernel/bios32.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/kernel/bios32.c Wed Oct 10 01:47:29 2001 @@ -288,8 +288,11 @@ if (dev) { for (i = 0; i < 3; i++) { - bus->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; - bus->resource[i]->name = bus->name; + if(root->resource[i]) { + bus->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; + bus->resource[i]->end = root->resource[i]->end; + bus->resource[i]->name = bus->name; + } } bus->resource[0]->flags |= pci_bridge_check_io(dev); bus->resource[1]->flags |= IORESOURCE_MEM; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/compat.c linux.ac/arch/arm/kernel/compat.c --- linux.vanilla/arch/arm/kernel/compat.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/kernel/compat.c Wed Oct 10 01:47:29 2001 @@ -8,7 +8,7 @@ * published by the Free Software Foundation. * * We keep the old params compatibility cruft in one place (here) - * so we don't end up with lots of + * so we don't end up with lots of mess around other places. */ #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/dma-isa.c linux.ac/arch/arm/kernel/dma-isa.c --- linux.vanilla/arch/arm/kernel/dma-isa.c Mon Sep 18 23:15:24 2000 +++ linux.ac/arch/arm/kernel/dma-isa.c Wed Oct 10 01:47:29 2001 @@ -148,17 +148,22 @@ void __init isa_init_dma(dma_t *dma) { - int dmac_found; - + /* + * Try to autodetect presence of an ISA DMA controller. + * We do some minimal initialisation, and check that + * channel 0's DMA address registers are writeable. + */ outb(0xff, 0x0d); outb(0xff, 0xda); + /* + * Write high and low address, and then read them back + * in the same order. + */ outb(0x55, 0x00); outb(0xaa, 0x00); - dmac_found = inb(0x00) == 0x55 && inb(0x00) == 0xaa; - - if (dmac_found) { + if (inb(0) == 0x55 && inb(0) == 0xaa) { int channel, i; for (channel = 0; channel < 8; channel++) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/entry-armv.S linux.ac/arch/arm/kernel/entry-armv.S --- linux.vanilla/arch/arm/kernel/entry-armv.S Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/kernel/entry-armv.S Wed Oct 10 01:47:29 2001 @@ -404,6 +404,7 @@ .endm #elif defined(CONFIG_ARCH_L7200) +#include .equ irq_base_addr, IO_BASE_2 @@ -625,8 +626,8 @@ * This routine must not corrupt r9 */ #ifdef MULTI_CPU - ldr r2, .LCprocfns - mov lr, pc + ldr r2, .LCprocfns @ pass r0, r3 to + mov lr, pc @ processor code ldr pc, [r2] @ call processor specific code #else bl cpu_data_abort @@ -722,16 +723,16 @@ .align 5 __dabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go stmia sp, {r0 - r12} @ save r0 - r12 - ldr r4, .LCabt - add r3, sp, #S_PC - ldmia r4, {r0 - r2} @ Get USR pc, cpsr - stmia r3, {r0 - r2} @ Save USR pc, cpsr, old_r0 - stmdb r3, {sp, lr}^ - alignment_trap r4, r7, __temp_abt + ldr r7, .LCabt + add r5, sp, #S_PC + ldmia r7, {r0, r3, r4} @ Get USR pc, cpsr + stmia r5, {r0, r3, r4} @ Save USR pc, cpsr, old_r0 + stmdb r5, {sp, lr}^ + alignment_trap r7, r7, __temp_abt zero_fp #ifdef MULTI_CPU - ldr r2, .LCprocfns - mov lr, pc + ldr r2, .LCprocfns @ pass r0, r3 to + mov lr, pc @ processor code ldr pc, [r2] @ call processor specific code #else bl cpu_data_abort diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/entry-common.S linux.ac/arch/arm/kernel/entry-common.S --- linux.vanilla/arch/arm/kernel/entry-common.S Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/kernel/entry-common.S Wed Oct 10 01:47:29 2001 @@ -123,9 +123,8 @@ .align 5 ENTRY(vector_swi) save_user_regs - mask_pc lr, lr zero_fp - ldr scno, [lr, #-4] @ get SWI instruction + get_scno arm710_bug_check scno, ip #ifdef CONFIG_ALIGNMENT_TRAP diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/entry-header.S linux.ac/arch/arm/kernel/entry-header.S --- linux.vanilla/arch/arm/kernel/entry-header.S Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/kernel/entry-header.S Wed Oct 10 01:47:29 2001 @@ -75,9 +75,9 @@ stmia sp, {r0 - r12} @ Calling r0 - r12 add r8, sp, #S_PC stmdb r8, {sp, lr}^ @ Calling sp, lr - mrs r7, spsr + mrs r8, spsr @ called from non-FIQ mode, so ok. str lr, [sp, #S_PC] @ Save calling PC - str r7, [sp, #S_PSR] @ Save CPSR + str r8, [sp, #S_PSR] @ Save CPSR str r0, [sp, #S_OLD_R0] @ Save OLD_R0 .endm @@ -186,16 +186,34 @@ #endif + /* * These are the registers used in the syscall handler, and allow us to - * have in theory up to 7 arguments to a function. Note that tbl == why - * is intentional. + * have in theory up to 7 arguments to a function - r0 to r6. + * + * r7 is reserved for the system call number for thumb mode. + * + * Note that tbl == why is intentional. * * We must set at least "tsk" and "why" when calling ret_with_reschedule. */ -scno .req r9 @ syscall number +scno .req r7 @ syscall number tbl .req r8 @ syscall table pointer why .req r8 @ Linux syscall (!= 0) -tsk .req r7 @ current task +tsk .req r9 @ current task + +/* + * Get the system call number. + */ + .macro get_scno +#ifdef CONFIG_ARM_THUMB + tst r8, #T_BIT @ this is SPSR from save_user_regs + addne scno, r7, #OS_NUMBER << 20 @ put OS number in + ldreq scno, [lr, #-4] +#else + mask_pc lr, lr + ldr scno, [lr, #-4] @ get SWI instruction +#endif + .endm diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/head-armv.S linux.ac/arch/arm/kernel/head-armv.S --- linux.vanilla/arch/arm/kernel/head-armv.S Mon Aug 13 01:36:24 2001 +++ linux.ac/arch/arm/kernel/head-armv.S Wed Oct 10 01:47:29 2001 @@ -418,5 +418,4 @@ mov r7, #0 @ unknown architecture mov pc, lr 2: ldmib r4, {r5, r6, r7} @ found, get results - mov r7, r7, lsr #18 @ pagetable byte offset mov pc, lr diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/init_task.c linux.ac/arch/arm/kernel/init_task.c --- linux.vanilla/arch/arm/kernel/init_task.c Mon Sep 17 23:29:09 2001 +++ linux.ac/arch/arm/kernel/init_task.c Wed Oct 10 01:47:29 2001 @@ -9,6 +9,7 @@ #include #include +static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/setup.c linux.ac/arch/arm/kernel/setup.c --- linux.vanilla/arch/arm/kernel/setup.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/kernel/setup.c Wed Oct 10 01:47:29 2001 @@ -260,10 +260,10 @@ struct resource *res; int i; - kernel_code.start = __virt_to_bus(init_mm.start_code); - kernel_code.end = __virt_to_bus(init_mm.end_code - 1); - kernel_data.start = __virt_to_bus(init_mm.end_code); - kernel_data.end = __virt_to_bus(init_mm.brk - 1); + kernel_code.start = __virt_to_phys(init_mm.start_code); + kernel_code.end = __virt_to_phys(init_mm.end_code - 1); + kernel_data.start = __virt_to_phys(init_mm.end_code); + kernel_data.end = __virt_to_phys(init_mm.brk - 1); for (i = 0; i < mi->nr_banks; i++) { unsigned long virt_start, virt_end; @@ -520,9 +520,44 @@ #endif } -int get_cpuinfo(char * buffer) +static const char *hwcap_str[] = { + "swp", + "half", + "thumb", + "26bit", + "fastmult", + "fpa", + "vfp", + "edsp", + NULL +}; + +/* + * get_cpuinfo - Get information on one CPU for use by the procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. + */ + +int get_cpuinfo(char *buffer, unsigned *cpu_np) { char *p = buffer; + unsigned n; + int i; + + /* No SMP at the moment, so just toggle 0/1 */ + n = *cpu_np; + *cpu_np = 1; + if (n != 0) { + return (0); + } p += sprintf(p, "Processor\t: %s %s rev %d (%s)\n", proc_info.manufacturer, proc_info.cpu_name, @@ -531,6 +566,15 @@ p += sprintf(p, "BogoMIPS\t: %lu.%02lu\n", loops_per_jiffy / (500000/HZ), (loops_per_jiffy / (5000/HZ)) % 100); + + /* dump out the processor features */ + p += sprintf(p, "Features\t: "); + + for (i = 0; hwcap_str[i]; i++) + if (elf_hwcap & (1 << i)) + p += sprintf(p, "%s ", hwcap_str[i]); + + p += sprintf(p, "\n\n"); p += sprintf(p, "Hardware\t: %s\n", machine_name); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/signal.c linux.ac/arch/arm/kernel/signal.c --- linux.vanilla/arch/arm/kernel/signal.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/kernel/signal.c Wed Oct 10 01:47:29 2001 @@ -1,7 +1,7 @@ /* * linux/arch/arm/kernel/signal.c * - * Copyright (C) 1995, 1996 Russell King + * Copyright (C) 1995-2001 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -19,7 +19,9 @@ #include #include #include +#include #include +#include #include #include @@ -29,8 +31,23 @@ #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -#define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)) -#define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn)) +/* + * For ARM syscalls, we encode the syscall number into the instruction. + */ +#define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)) +#define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn)) + +/* + * For Thumb syscalls, we pass the syscall number via r7. We therefore + * need two 16-bit instructions. + */ +#define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_sigreturn - __NR_SYSCALL_BASE)) +#define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_rt_sigreturn - __NR_SYSCALL_BASE)) + +static const unsigned long retcodes[4] = { + SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN, + SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN +}; asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall); @@ -208,11 +225,11 @@ sigset_t set; /* - * Since we stacked the signal on a word boundary, + * Since we stacked the signal on a 64-bit boundary, * then 'sp' should be word aligned here. If it's * not, then the user is trying to mess with us. */ - if (regs->ARM_sp & 3) + if (regs->ARM_sp & 7) goto badframe; frame = (struct sigframe *)regs->ARM_sp; @@ -251,11 +268,11 @@ sigset_t set; /* - * Since we stacked the signal on a word boundary, + * Since we stacked the signal on a 64-bit boundary, * then 'sp' should be word aligned here. If it's * not, then the user is trying to mess with us. */ - if (regs->ARM_sp & 3) + if (regs->ARM_sp & 7) goto badframe; frame = (struct rt_sigframe *)regs->ARM_sp; @@ -319,8 +336,8 @@ return err; } -static inline void *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, - unsigned long framesize) +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize) { unsigned long sp = regs->ARM_sp; @@ -331,77 +348,103 @@ sp = current->sas_ss_sp + current->sas_ss_size; /* - * No matter what happens, 'sp' must be word - * aligned otherwise nasty things could happen + * ATPCS B01 mandates 8-byte alignment */ - /* ATPCS B01 mandates 8-byte alignment */ return (void *)((sp - framesize) & ~7); } -static void setup_frame(int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs *regs) +static int +setup_return(struct pt_regs *regs, struct k_sigaction *ka, + unsigned long *rc, void *frame, int usig) { - struct sigframe *frame; + unsigned long handler = (unsigned long)ka->sa.sa_handler; unsigned long retcode; - int err = 0; - - frame = get_sigframe(ka, regs, sizeof(*frame)); - - if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) - goto segv_and_exit; + int thumb = 0; +#ifdef CONFIG_CPU_32 + unsigned long cpsr = regs->ARM_cpsr; - err |= setup_sigcontext(&frame->sc, /*&frame->fpstate,*/ regs, set->sig[0]); + /* + * Maybe we need to deliver a 32-bit signal to a 26-bit task. + */ + if (ka->sa.sa_flags & SA_THIRTYTWO) + cpsr = (cpsr & ~MODE_MASK) | USR_MODE; - if (_NSIG_WORDS > 1) { - err |= __copy_to_user(frame->extramask, &set->sig[1], - sizeof(frame->extramask)); +#ifdef CONFIG_ARM_THUMB + if (elf_hwcap & HWCAP_THUMB) { + /* + * The LSB of the handler determines if we're going to + * be using THUMB or ARM mode for this signal handler. + */ + thumb = handler & 1; + + if (thumb) + cpsr |= T_BIT; + else + cpsr &= ~T_BIT; } +#endif +#endif - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ if (ka->sa.sa_flags & SA_RESTORER) { retcode = (unsigned long)ka->sa.sa_restorer; } else { - retcode = (unsigned long)&frame->retcode; - __put_user_error(SWI_SYS_SIGRETURN, &frame->retcode, err); - flush_icache_range(retcode, retcode + 4); - } + unsigned int idx = thumb; - if (err) - goto segv_and_exit; + if (ka->sa.sa_flags & SA_SIGINFO) + idx += 2; - if (current->exec_domain && current->exec_domain->signal_invmap && sig < 32) - regs->ARM_r0 = current->exec_domain->signal_invmap[sig]; - else - regs->ARM_r0 = sig; + if (__put_user(retcodes[idx], rc)) + return 1; + + flush_icache_range((unsigned long)rc, + (unsigned long)(rc + 1)); + + retcode = ((unsigned long)rc) + thumb; + } + + regs->ARM_r0 = usig; regs->ARM_sp = (unsigned long)frame; regs->ARM_lr = retcode; - regs->ARM_pc = (unsigned long)ka->sa.sa_handler; -#if defined(CONFIG_CPU_32) - /* Maybe we need to deliver a 32-bit signal to a 26-bit task. */ - if (ka->sa.sa_flags & SA_THIRTYTWO) - regs->ARM_cpsr = USR_MODE; + regs->ARM_pc = handler & (thumb ? ~1 : ~3); + +#ifdef CONFIG_CPU_32 + regs->ARM_cpsr = cpsr; #endif - if (valid_user_regs(regs)) - return; -segv_and_exit: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + return 0; } -static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *set, struct pt_regs *regs) +static int +setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *regs) { - struct rt_sigframe *frame; - unsigned long retcode; + struct sigframe *frame = get_sigframe(ka, regs, sizeof(*frame)); int err = 0; - frame = get_sigframe(ka, regs, sizeof(struct rt_sigframe)); + if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) + return 1; + + err |= setup_sigcontext(&frame->sc, /*&frame->fpstate,*/ regs, set->sig[0]); + + if (_NSIG_WORDS > 1) { + err |= __copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + } + + if (err == 0) + err = setup_return(regs, ka, &frame->retcode, frame, usig); + + return err; +} + +static int +setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) +{ + struct rt_sigframe *frame = get_sigframe(ka, regs, sizeof(*frame)); + int err = 0; if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) - goto segv_and_exit; + return 1; __put_user_error(&frame->info, &frame->pinfo, err); __put_user_error(&frame->uc, &frame->puc, err); @@ -414,47 +457,20 @@ regs, set->sig[0]); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa.sa_flags & SA_RESTORER) { - retcode = (unsigned long)ka->sa.sa_restorer; - } else { - retcode = (unsigned long)&frame->retcode; - __put_user_error(SWI_SYS_RT_SIGRETURN, &frame->retcode, err); - flush_icache_range(retcode, retcode + 4); - } - - if (err) - goto segv_and_exit; - - if (current->exec_domain && current->exec_domain->signal_invmap && sig < 32) - regs->ARM_r0 = current->exec_domain->signal_invmap[sig]; - else - regs->ARM_r0 = sig; + if (err == 0) + err = setup_return(regs, ka, &frame->retcode, frame, usig); - /* - * For realtime signals we must also set the second and third - * arguments for the signal handler. - * -- Peter Maydell 2000-12-06 - */ - regs->ARM_r1 = (unsigned long)frame->pinfo; - regs->ARM_r2 = (unsigned long)frame->puc; - - regs->ARM_sp = (unsigned long)frame; - regs->ARM_lr = retcode; - regs->ARM_pc = (unsigned long)ka->sa.sa_handler; -#if defined(CONFIG_CPU_32) - /* Maybe we need to deliver a 32-bit signal to a 26-bit task. */ - if (ka->sa.sa_flags & SA_THIRTYTWO) - regs->ARM_cpsr = USR_MODE; -#endif - if (valid_user_regs(regs)) - return; + if (err == 0) { + /* + * For realtime signals we must also set the second and third + * arguments for the signal handler. + * -- Peter Maydell 2000-12-06 + */ + regs->ARM_r1 = (unsigned long)frame->pinfo; + regs->ARM_r2 = (unsigned long)frame->puc; + } -segv_and_exit: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + return err; } /* @@ -464,22 +480,47 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) { - /* Set up the stack frame */ + struct task_struct *tsk = current; + int usig = sig; + int ret; + + /* + * translate the signal + */ + if (usig < 32 && tsk->exec_domain && tsk->exec_domain->signal_invmap) + usig = tsk->exec_domain->signal_invmap[usig]; + + /* + * Set up the stack frame + */ if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs); + ret = setup_rt_frame(usig, ka, info, oldset, regs); else - setup_frame(sig, ka, oldset, regs); + ret = setup_frame(usig, ka, oldset, regs); - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; + /* + * Check that the resulting registers are actually sane. + */ + ret |= !valid_user_regs(regs); - if (!(ka->sa.sa_flags & SA_NODEFER)) { - spin_lock_irq(¤t->sigmask_lock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - sigaddset(¤t->blocked,sig); - recalc_sigpending(current); - spin_unlock_irq(¤t->sigmask_lock); + if (ret == 0) { + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(&tsk->sigmask_lock); + sigorsets(&tsk->blocked, &tsk->blocked, + &ka->sa.sa_mask); + sigaddset(&tsk->blocked, sig); + recalc_sigpending(tsk); + spin_unlock_irq(&tsk->sigmask_lock); + } + return; } + + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, tsk); } /* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/time.c linux.ac/arch/arm/kernel/time.c --- linux.vanilla/arch/arm/kernel/time.c Fri Feb 9 00:32:44 2001 +++ linux.ac/arch/arm/kernel/time.c Wed Oct 10 01:47:29 2001 @@ -33,7 +33,6 @@ #include extern int setup_arm_irq(int, struct irqaction *); -extern void setup_timer(void); extern rwlock_t xtime_lock; extern unsigned long wall_jiffies; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/traps.c linux.ac/arch/arm/kernel/traps.c --- linux.vanilla/arch/arm/kernel/traps.c Mon Sep 10 21:04:33 2001 +++ linux.ac/arch/arm/kernel/traps.c Wed Oct 10 01:47:29 2001 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -176,7 +177,7 @@ printk("Process %s (pid: %d, stackpage=%08lx)\n", current->comm, current->pid, 4096+(unsigned long)tsk); - if (!user_mode(regs)) { + if (!user_mode(regs) || in_interrupt()) { mm_segment_t fs; /* @@ -209,7 +210,7 @@ asmlinkage void do_undefinstr(int address, struct pt_regs *regs, int mode) { - unsigned long addr; + unsigned long *pc; siginfo_t info; /* @@ -217,11 +218,11 @@ * whether we're in Thumb mode or not. */ regs->ARM_pc -= thumb_mode(regs) ? 2 : 4; - addr = instruction_pointer(regs); + pc = (unsigned long *)instruction_pointer(regs); #ifdef CONFIG_DEBUG_USER - printk(KERN_INFO "%s (%d): undefined instruction: pc=%08lx\n", - current->comm, current->pid, addr); + printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n", + current->comm, current->pid, pc); dump_instr(regs); #endif @@ -231,7 +232,7 @@ info.si_signo = SIGILL; info.si_errno = 0; info.si_code = ILL_ILLOPC; - info.si_addr = (void *)addr; + info.si_addr = pc; force_sig_info(SIGILL, &info, current); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/Makefile linux.ac/arch/arm/lib/Makefile --- linux.vanilla/arch/arm/lib/Makefile Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/lib/Makefile Wed Oct 10 01:47:29 2001 @@ -13,15 +13,20 @@ copy_page.o delay.o findbit.o memchr.o memcpy.o \ memset.o memzero.o setbit.o strncpy_from_user.o \ strnlen_user.o strchr.o strrchr.o testchangebit.o \ - testclearbit.o testsetbit.o uaccess.o + testclearbit.o testsetbit.o uaccess.o getuser.o \ + putuser.o ashldi3.o ashrdi3.o lshrdi3.o muldi3.o \ + ucmpdi2.o udivdi3.o lib1funcs.o obj-m := obj-n := +obj-$(CONFIG_VT)+= kbd.o + obj-arc := ecard.o io-acorn.o floppydma.o obj-rpc := ecard.o io-acorn.o floppydma.o obj-clps7500 := io-acorn.o obj-l7200 := io-acorn.o obj-shark := io-shark.o +obj-edb7211 := io-acorn.o obj-y += $(obj-$(MACHINE)) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/ashldi3.c linux.ac/arch/arm/lib/ashldi3.c --- linux.vanilla/arch/arm/lib/ashldi3.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/lib/ashldi3.c Wed Oct 10 01:47:29 2001 @@ -0,0 +1,61 @@ +/* More subroutines needed by GCC output code on some machines. */ +/* Compile this one with gcc. */ +/* Copyright (C) 1989, 92-98, 1999 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with other files, + some of which are compiled with GCC, to produce an executable, + this library does not by itself cause the resulting executable + to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. + */ +/* support functions required by the kernel. based on code from gcc-2.95.3 */ +/* I Molton 29/07/01 */ + +#include "gcclib.h" + +DItype +__ashldi3 (DItype u, word_type b) +{ + DIunion w; + word_type bm; + DIunion uu; + + if (b == 0) + return u; + + uu.ll = u; + + bm = (sizeof (SItype) * BITS_PER_UNIT) - b; + if (bm <= 0) + { + w.s.low = 0; + w.s.high = (USItype)uu.s.low << -bm; + } + else + { + USItype carries = (USItype)uu.s.low >> bm; + w.s.low = (USItype)uu.s.low << b; + w.s.high = ((USItype)uu.s.high << b) | carries; + } + + return w.ll; +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/ashrdi3.c linux.ac/arch/arm/lib/ashrdi3.c --- linux.vanilla/arch/arm/lib/ashrdi3.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/lib/ashrdi3.c Wed Oct 10 01:47:29 2001 @@ -0,0 +1,61 @@ +/* More subroutines needed by GCC output code on some machines. */ +/* Compile this one with gcc. */ +/* Copyright (C) 1989, 92-98, 1999 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with other files, + some of which are compiled with GCC, to produce an executable, + this library does not by itself cause the resulting executable + to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. + */ +/* support functions required by the kernel. based on code from gcc-2.95.3 */ +/* I Molton 29/07/01 */ + +#include "gcclib.h" + +DItype +__ashrdi3 (DItype u, word_type b) +{ + DIunion w; + word_type bm; + DIunion uu; + + if (b == 0) + return u; + + uu.ll = u; + + bm = (sizeof (SItype) * BITS_PER_UNIT) - b; + if (bm <= 0) + { + /* w.s.high = 1..1 or 0..0 */ + w.s.high = uu.s.high >> (sizeof (SItype) * BITS_PER_UNIT - 1); + w.s.low = uu.s.high >> -bm; + } + else + { + USItype carries = (USItype)uu.s.high << bm; + w.s.high = uu.s.high >> b; + w.s.low = ((USItype)uu.s.low >> b) | carries; + } + + return w.ll; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/gcclib.h linux.ac/arch/arm/lib/gcclib.h --- linux.vanilla/arch/arm/lib/gcclib.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/lib/gcclib.h Wed Oct 10 01:47:29 2001 @@ -0,0 +1,25 @@ +/* gcclib.h -- definitions for various functions 'borrowed' from gcc-2.95.3 */ +/* I Molton 29/07/01 */ + +#define BITS_PER_UNIT 8 +#define SI_TYPE_SIZE (sizeof (SItype) * BITS_PER_UNIT) + +typedef unsigned int UQItype __attribute__ ((mode (QI))); +typedef int SItype __attribute__ ((mode (SI))); +typedef unsigned int USItype __attribute__ ((mode (SI))); +typedef int DItype __attribute__ ((mode (DI))); +typedef int word_type __attribute__ ((mode (__word__))); +typedef unsigned int UDItype __attribute__ ((mode (DI))); + +#if 0 /* FIXME: endian test here!!! */ + struct DIstruct {SItype high, low;}; +#else + struct DIstruct {SItype low, high;}; +#endif + +typedef union +{ + struct DIstruct s; + DItype ll; +} DIunion; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/getuser.S linux.ac/arch/arm/lib/getuser.S --- linux.vanilla/arch/arm/lib/getuser.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/lib/getuser.S Wed Oct 10 01:47:29 2001 @@ -0,0 +1,96 @@ +/* + * linux/arch/arm/lib/getuser.S + * + * Copyright (C) 2001 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Idea from x86 version, (C) Copyright 1998 Linus Torvalds + * + * These functions have a non-standard call interface to make them more + * efficient, especially as they return an error value in addition to + * the "real" return value. + * + * __get_user_X + * + * Inputs: r0 contains the address + * Outputs: r0 is the error code + * r1, r2 contains the zero-extended value + * lr corrupted + * + * No other registers must be altered. (see include/asm-arm/uaccess.h + * for specific ASM register usage). + * + * Note that ADDR_LIMIT is either 0 or 0xc0000000. + * Note also that it is intended that __get_user_bad is not global. + */ +#include + + .global __get_user_1 +__get_user_1: + bic r1, sp, #0x1f00 + bic r1, r1, #0x00ff + ldr r1, [r1, #TSK_ADDR_LIMIT] + sub r1, r1, #1 + cmp r0, r1 +1: ldrlsbt r1, [r0] + movls r0, #0 + movls pc, lr + b __get_user_bad + + .global __get_user_2 +__get_user_2: + bic r2, sp, #0x1f00 + bic r2, r2, #0x00ff + ldr r2, [r2, #TSK_ADDR_LIMIT] + sub r2, r2, #2 + cmp r0, r2 +2: ldrlsbt r1, [r0], #1 +3: ldrlsbt r2, [r0] + orrls r1, r1, r2, lsl #8 + movls r0, #0 + movls pc, lr + b __get_user_bad + + .global __get_user_4 +__get_user_4: + bic r1, sp, #0x1f00 + bic r1, r1, #0x00ff + ldr r1, [r1, #TSK_ADDR_LIMIT] + sub r1, r1, #4 + cmp r0, r1 +4: ldrlst r1, [r0] + movls r0, #0 + movls pc, lr + b __get_user_bad + + .global __get_user_8 +__get_user_8: + bic r2, sp, #0x1f00 + bic r2, r2, #0x00ff + ldr r2, [r2, #TSK_ADDR_LIMIT] + sub r2, r2, #8 + cmp r0, r2 +5: ldrlst r1, [r0], #4 +6: ldrlst r2, [r0] + movls r0, #0 + movls pc, lr + + /* fall through */ + +__get_user_bad: + mov r2, #0 + mov r1, #0 + mov r0, #-14 + mov pc, lr + +.section __ex_table, "a" + .long 1b, __get_user_bad + .long 2b, __get_user_bad + .long 3b, __get_user_bad + .long 4b, __get_user_bad + .long 5b, __get_user_bad + .long 6b, __get_user_bad +.previous diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/kbd.c linux.ac/arch/arm/lib/kbd.c --- linux.vanilla/arch/arm/lib/kbd.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/lib/kbd.c Wed Oct 10 01:47:29 2001 @@ -0,0 +1,13 @@ +#include +#include + +int (*k_setkeycode)(unsigned int, unsigned int); +int (*k_getkeycode)(unsigned int); +int (*k_translate)(unsigned char, unsigned char *, char); +char (*k_unexpected_up)(unsigned char); +void (*k_leds)(unsigned char); + +#ifdef CONFIG_MAGIC_SYSRQ +int k_sysrq_key; +unsigned char *k_sysrq_xlate; +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/lib1funcs.S linux.ac/arch/arm/lib/lib1funcs.S --- linux.vanilla/arch/arm/lib/lib1funcs.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/lib/lib1funcs.S Wed Oct 10 01:47:29 2001 @@ -0,0 +1,320 @@ +@ libgcc1 routines for ARM cpu. +@ Division routines, written by Richard Earnshaw, (rearnsha@armltd.co.uk) + +/* Copyright (C) 1995, 1996, 1998 Free Software Foundation, Inc. + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file with other programs, and to distribute +those programs without any restriction coming from the use of this +file. (The General Public License restrictions do apply in other +respects; for example, they cover modification of the file, and +distribution when not linked into another program.) + +This file is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with other files, + some of which are compiled with GCC, to produce an executable, + this library does not by itself cause the resulting executable + to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. + */ +/* This code is derived from gcc 2.95.3 */ +/* I Molton 29/07/01 */ + +#include +#include +#include +#include + +#ifdef CONFIG_CPU_26 +#define RET movs +#define RETc(x) mov##x##s +#define RETCOND ^ +#else +#define RET mov +#define RETc(x) mov##x +#define RETCOND +#endif + +dividend .req r0 +divisor .req r1 +result .req r2 +overdone .req r2 +curbit .req r3 +ip .req r12 +sp .req r13 +lr .req r14 +pc .req r15 + +ENTRY(__udivsi3) + cmp divisor, #0 + beq Ldiv0 + mov curbit, #1 + mov result, #0 + cmp dividend, divisor + bcc Lgot_result_udivsi3 +1: + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. + cmp divisor, #0x10000000 + cmpcc divisor, dividend + movcc divisor, divisor, lsl #4 + movcc curbit, curbit, lsl #4 + bcc 1b + +2: + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. + cmp divisor, #0x80000000 + cmpcc divisor, dividend + movcc divisor, divisor, lsl #1 + movcc curbit, curbit, lsl #1 + bcc 2b + +3: + @ Test for possible subtractions, and note which bits + @ are done in the result. On the final pass, this may subtract + @ too much from the dividend, but the result will be ok, since the + @ "bit" will have been shifted out at the bottom. + cmp dividend, divisor + subcs dividend, dividend, divisor + orrcs result, result, curbit + cmp dividend, divisor, lsr #1 + subcs dividend, dividend, divisor, lsr #1 + orrcs result, result, curbit, lsr #1 + cmp dividend, divisor, lsr #2 + subcs dividend, dividend, divisor, lsr #2 + orrcs result, result, curbit, lsr #2 + cmp dividend, divisor, lsr #3 + subcs dividend, dividend, divisor, lsr #3 + orrcs result, result, curbit, lsr #3 + cmp dividend, #0 @ Early termination? + movnes curbit, curbit, lsr #4 @ No, any more bits to do? + movne divisor, divisor, lsr #4 + bne 3b +Lgot_result_udivsi3: + mov r0, result + RET pc, lr + +Ldiv0: + str lr, [sp, #-4]! + bl __div0 + mov r0, #0 @ about as wrong as it could be + ldmia sp!, {pc}RETCOND + +/* __umodsi3 ----------------------- */ + +ENTRY(__umodsi3) + cmp divisor, #0 + beq Ldiv0 + mov curbit, #1 + cmp dividend, divisor + RETc(cc) pc, lr +1: + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. + cmp divisor, #0x10000000 + cmpcc divisor, dividend + movcc divisor, divisor, lsl #4 + movcc curbit, curbit, lsl #4 + bcc 1b + +2: + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. + cmp divisor, #0x80000000 + cmpcc divisor, dividend + movcc divisor, divisor, lsl #1 + movcc curbit, curbit, lsl #1 + bcc 2b + +3: + @ Test for possible subtractions. On the final pass, this may + @ subtract too much from the dividend, so keep track of which + @ subtractions are done, we can fix them up afterwards... + mov overdone, #0 + cmp dividend, divisor + subcs dividend, dividend, divisor + cmp dividend, divisor, lsr #1 + subcs dividend, dividend, divisor, lsr #1 + orrcs overdone, overdone, curbit, ror #1 + cmp dividend, divisor, lsr #2 + subcs dividend, dividend, divisor, lsr #2 + orrcs overdone, overdone, curbit, ror #2 + cmp dividend, divisor, lsr #3 + subcs dividend, dividend, divisor, lsr #3 + orrcs overdone, overdone, curbit, ror #3 + mov ip, curbit + cmp dividend, #0 @ Early termination? + movnes curbit, curbit, lsr #4 @ No, any more bits to do? + movne divisor, divisor, lsr #4 + bne 3b + + @ Any subtractions that we should not have done will be recorded in + @ the top three bits of "overdone". Exactly which were not needed + @ are governed by the position of the bit, stored in ip. + @ If we terminated early, because dividend became zero, + @ then none of the below will match, since the bit in ip will not be + @ in the bottom nibble. + ands overdone, overdone, #0xe0000000 + RETc(eq) pc, lr @ No fixups needed + tst overdone, ip, ror #3 + addne dividend, dividend, divisor, lsr #3 + tst overdone, ip, ror #2 + addne dividend, dividend, divisor, lsr #2 + tst overdone, ip, ror #1 + addne dividend, dividend, divisor, lsr #1 + RET pc, lr + +ENTRY(__divsi3) + eor ip, dividend, divisor @ Save the sign of the result. + mov curbit, #1 + mov result, #0 + cmp divisor, #0 + rsbmi divisor, divisor, #0 @ Loops below use unsigned. + beq Ldiv0 + cmp dividend, #0 + rsbmi dividend, dividend, #0 + cmp dividend, divisor + bcc Lgot_result_divsi3 + +1: + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. + cmp divisor, #0x10000000 + cmpcc divisor, dividend + movcc divisor, divisor, lsl #4 + movcc curbit, curbit, lsl #4 + bcc 1b + +2: + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. + cmp divisor, #0x80000000 + cmpcc divisor, dividend + movcc divisor, divisor, lsl #1 + movcc curbit, curbit, lsl #1 + bcc 2b + +3: + @ Test for possible subtractions, and note which bits + @ are done in the result. On the final pass, this may subtract + @ too much from the dividend, but the result will be ok, since the + @ "bit" will have been shifted out at the bottom. + cmp dividend, divisor + subcs dividend, dividend, divisor + orrcs result, result, curbit + cmp dividend, divisor, lsr #1 + subcs dividend, dividend, divisor, lsr #1 + orrcs result, result, curbit, lsr #1 + cmp dividend, divisor, lsr #2 + subcs dividend, dividend, divisor, lsr #2 + orrcs result, result, curbit, lsr #2 + cmp dividend, divisor, lsr #3 + subcs dividend, dividend, divisor, lsr #3 + orrcs result, result, curbit, lsr #3 + cmp dividend, #0 @ Early termination? + movnes curbit, curbit, lsr #4 @ No, any more bits to do? + movne divisor, divisor, lsr #4 + bne 3b +Lgot_result_divsi3: + mov r0, result + cmp ip, #0 + rsbmi r0, r0, #0 + RET pc, lr + +ENTRY(__modsi3) + mov curbit, #1 + cmp divisor, #0 + rsbmi divisor, divisor, #0 @ Loops below use unsigned. + beq Ldiv0 + @ Need to save the sign of the dividend, unfortunately, we need + @ ip later on; this is faster than pushing lr and using that. + str dividend, [sp, #-4]! + cmp dividend, #0 + rsbmi dividend, dividend, #0 + cmp dividend, divisor + bcc Lgot_result_modsi3 + +1: + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. + cmp divisor, #0x10000000 + cmpcc divisor, dividend + movcc divisor, divisor, lsl #4 + movcc curbit, curbit, lsl #4 + bcc 1b + +2: + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. + cmp divisor, #0x80000000 + cmpcc divisor, dividend + movcc divisor, divisor, lsl #1 + movcc curbit, curbit, lsl #1 + bcc 2b + +3: + @ Test for possible subtractions. On the final pass, this may + @ subtract too much from the dividend, so keep track of which + @ subtractions are done, we can fix them up afterwards... + mov overdone, #0 + cmp dividend, divisor + subcs dividend, dividend, divisor + cmp dividend, divisor, lsr #1 + subcs dividend, dividend, divisor, lsr #1 + orrcs overdone, overdone, curbit, ror #1 + cmp dividend, divisor, lsr #2 + subcs dividend, dividend, divisor, lsr #2 + orrcs overdone, overdone, curbit, ror #2 + cmp dividend, divisor, lsr #3 + subcs dividend, dividend, divisor, lsr #3 + orrcs overdone, overdone, curbit, ror #3 + mov ip, curbit + cmp dividend, #0 @ Early termination? + movnes curbit, curbit, lsr #4 @ No, any more bits to do? + movne divisor, divisor, lsr #4 + bne 3b + + @ Any subtractions that we should not have done will be recorded in + @ the top three bits of "overdone". Exactly which were not needed + @ are governed by the position of the bit, stored in ip. + @ If we terminated early, because dividend became zero, + @ then none of the below will match, since the bit in ip will not be + @ in the bottom nibble. + ands overdone, overdone, #0xe0000000 + beq Lgot_result_modsi3 + tst overdone, ip, ror #3 + addne dividend, dividend, divisor, lsr #3 + tst overdone, ip, ror #2 + addne dividend, dividend, divisor, lsr #2 + tst overdone, ip, ror #1 + addne dividend, dividend, divisor, lsr #1 +Lgot_result_modsi3: + ldr ip, [sp], #4 + cmp ip, #0 + rsbmi dividend, dividend, #0 + RET pc, lr diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/longlong.h linux.ac/arch/arm/lib/longlong.h --- linux.vanilla/arch/arm/lib/longlong.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/lib/longlong.h Wed Oct 10 01:47:29 2001 @@ -0,0 +1,184 @@ +/* longlong.h -- based on code from gcc-2.95.3 + + definitions for mixed size 32/64 bit arithmetic. + Copyright (C) 1991, 92, 94, 95, 96, 1997, 1998 Free Software Foundation, Inc. + + This definition file is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2, or (at your option) any later version. + + This definition file 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. */ + +/* Borrowed from GCC 2.95.3, I Molton 29/07/01 */ + +#ifndef SI_TYPE_SIZE +#define SI_TYPE_SIZE 32 +#endif + +#define __BITS4 (SI_TYPE_SIZE / 4) +#define __ll_B (1L << (SI_TYPE_SIZE / 2)) +#define __ll_lowpart(t) ((USItype) (t) % __ll_B) +#define __ll_highpart(t) ((USItype) (t) / __ll_B) + +/* Define auxiliary asm macros. + + 1) umul_ppmm(high_prod, low_prod, multipler, multiplicand) + multiplies two USItype integers MULTIPLER and MULTIPLICAND, + and generates a two-part USItype product in HIGH_PROD and + LOW_PROD. + + 2) __umulsidi3(a,b) multiplies two USItype integers A and B, + and returns a UDItype product. This is just a variant of umul_ppmm. + + 3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator, + denominator) divides a two-word unsigned integer, composed by the + integers HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and + places the quotient in QUOTIENT and the remainder in REMAINDER. + HIGH_NUMERATOR must be less than DENOMINATOR for correct operation. + If, in addition, the most significant bit of DENOMINATOR must be 1, + then the pre-processor symbol UDIV_NEEDS_NORMALIZATION is defined to 1. + + 4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator, + denominator). Like udiv_qrnnd but the numbers are signed. The + quotient is rounded towards 0. + + 5) count_leading_zeros(count, x) counts the number of zero-bits from + the msb to the first non-zero bit. This is the number of steps X + needs to be shifted left to set the msb. Undefined for X == 0. + + 6) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1, + high_addend_2, low_addend_2) adds two two-word unsigned integers, + composed by HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and + LOW_ADDEND_2 respectively. The result is placed in HIGH_SUM and + LOW_SUM. Overflow (i.e. carry out) is not stored anywhere, and is + lost. + + 7) sub_ddmmss(high_difference, low_difference, high_minuend, + low_minuend, high_subtrahend, low_subtrahend) subtracts two + two-word unsigned integers, composed by HIGH_MINUEND_1 and + LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and LOW_SUBTRAHEND_2 + respectively. The result is placed in HIGH_DIFFERENCE and + LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere, + and is lost. + + If any of these macros are left undefined for a particular CPU, + C macros are used. */ + +#if defined (__arm__) +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("adds %1, %4, %5 + adc %0, %2, %3" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "%r" ((USItype) (ah)), \ + "rI" ((USItype) (bh)), \ + "%r" ((USItype) (al)), \ + "rI" ((USItype) (bl))) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("subs %1, %4, %5 + sbc %0, %2, %3" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "r" ((USItype) (ah)), \ + "rI" ((USItype) (bh)), \ + "r" ((USItype) (al)), \ + "rI" ((USItype) (bl))) +#define umul_ppmm(xh, xl, a, b) \ +{register USItype __t0, __t1, __t2; \ + __asm__ ("%@ Inlined umul_ppmm + mov %2, %5, lsr #16 + mov %0, %6, lsr #16 + bic %3, %5, %2, lsl #16 + bic %4, %6, %0, lsl #16 + mul %1, %3, %4 + mul %4, %2, %4 + mul %3, %0, %3 + mul %0, %2, %0 + adds %3, %4, %3 + addcs %0, %0, #65536 + adds %1, %1, %3, lsl #16 + adc %0, %0, %3, lsr #16" \ + : "=&r" ((USItype) (xh)), \ + "=r" ((USItype) (xl)), \ + "=&r" (__t0), "=&r" (__t1), "=r" (__t2) \ + : "r" ((USItype) (a)), \ + "r" ((USItype) (b)));} +#define UMUL_TIME 20 +#define UDIV_TIME 100 +#endif /* __arm__ */ + +#define __umulsidi3(u, v) \ + ({DIunion __w; \ + umul_ppmm (__w.s.high, __w.s.low, u, v); \ + __w.ll; }) + +#define __udiv_qrnnd_c(q, r, n1, n0, d) \ + do { \ + USItype __d1, __d0, __q1, __q0; \ + USItype __r1, __r0, __m; \ + __d1 = __ll_highpart (d); \ + __d0 = __ll_lowpart (d); \ + \ + __r1 = (n1) % __d1; \ + __q1 = (n1) / __d1; \ + __m = (USItype) __q1 * __d0; \ + __r1 = __r1 * __ll_B | __ll_highpart (n0); \ + if (__r1 < __m) \ + { \ + __q1--, __r1 += (d); \ + if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\ + if (__r1 < __m) \ + __q1--, __r1 += (d); \ + } \ + __r1 -= __m; \ + \ + __r0 = __r1 % __d1; \ + __q0 = __r1 / __d1; \ + __m = (USItype) __q0 * __d0; \ + __r0 = __r0 * __ll_B | __ll_lowpart (n0); \ + if (__r0 < __m) \ + { \ + __q0--, __r0 += (d); \ + if (__r0 >= (d)) \ + if (__r0 < __m) \ + __q0--, __r0 += (d); \ + } \ + __r0 -= __m; \ + \ + (q) = (USItype) __q1 * __ll_B | __q0; \ + (r) = __r0; \ + } while (0) + +#define UDIV_NEEDS_NORMALIZATION 1 +#define udiv_qrnnd __udiv_qrnnd_c + +extern const UQItype __clz_tab[]; +#define count_leading_zeros(count, x) \ + do { \ + USItype __xr = (x); \ + USItype __a; \ + \ + if (SI_TYPE_SIZE <= 32) \ + { \ + __a = __xr < ((USItype)1<<2*__BITS4) \ + ? (__xr < ((USItype)1<<__BITS4) ? 0 : __BITS4) \ + : (__xr < ((USItype)1<<3*__BITS4) ? 2*__BITS4 : 3*__BITS4); \ + } \ + else \ + { \ + for (__a = SI_TYPE_SIZE - 8; __a > 0; __a -= 8) \ + if (((__xr >> __a) & 0xff) != 0) \ + break; \ + } \ + \ + (count) = SI_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a); \ + } while (0) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/lshrdi3.c linux.ac/arch/arm/lib/lshrdi3.c --- linux.vanilla/arch/arm/lib/lshrdi3.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/lib/lshrdi3.c Wed Oct 10 01:47:29 2001 @@ -0,0 +1,61 @@ +/* More subroutines needed by GCC output code on some machines. */ +/* Compile this one with gcc. */ +/* Copyright (C) 1989, 92-98, 1999 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with other files, + some of which are compiled with GCC, to produce an executable, + this library does not by itself cause the resulting executable + to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. + */ +/* support functions required by the kernel. based on code from gcc-2.95.3 */ +/* I Molton 29/07/01 */ + +#include "gcclib.h" + +DItype +__lshrdi3 (DItype u, word_type b) +{ + DIunion w; + word_type bm; + DIunion uu; + + if (b == 0) + return u; + + uu.ll = u; + + bm = (sizeof (SItype) * BITS_PER_UNIT) - b; + if (bm <= 0) + { + w.s.high = 0; + w.s.low = (USItype)uu.s.high >> -bm; + } + else + { + USItype carries = (USItype)uu.s.high << bm; + w.s.high = (USItype)uu.s.high >> b; + w.s.low = ((USItype)uu.s.low >> b) | carries; + } + + return w.ll; +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/muldi3.c linux.ac/arch/arm/lib/muldi3.c --- linux.vanilla/arch/arm/lib/muldi3.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/lib/muldi3.c Wed Oct 10 01:47:29 2001 @@ -0,0 +1,77 @@ +/* More subroutines needed by GCC output code on some machines. */ +/* Compile this one with gcc. */ +/* Copyright (C) 1989, 92-98, 1999 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with other files, + some of which are compiled with GCC, to produce an executable, + this library does not by itself cause the resulting executable + to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. + */ +/* support functions required by the kernel. based on code from gcc-2.95.3 */ +/* I Molton 29/07/01 */ + +#include "gcclib.h" + +#define umul_ppmm(xh, xl, a, b) \ +{register USItype __t0, __t1, __t2; \ + __asm__ ("%@ Inlined umul_ppmm + mov %2, %5, lsr #16 + mov %0, %6, lsr #16 + bic %3, %5, %2, lsl #16 + bic %4, %6, %0, lsl #16 + mul %1, %3, %4 + mul %4, %2, %4 + mul %3, %0, %3 + mul %0, %2, %0 + adds %3, %4, %3 + addcs %0, %0, #65536 + adds %1, %1, %3, lsl #16 + adc %0, %0, %3, lsr #16" \ + : "=&r" ((USItype) (xh)), \ + "=r" ((USItype) (xl)), \ + "=&r" (__t0), "=&r" (__t1), "=r" (__t2) \ + : "r" ((USItype) (a)), \ + "r" ((USItype) (b)));} + + +#define __umulsidi3(u, v) \ + ({DIunion __w; \ + umul_ppmm (__w.s.high, __w.s.low, u, v); \ + __w.ll; }) + + +DItype +__muldi3 (DItype u, DItype v) +{ + DIunion w; + DIunion uu, vv; + + uu.ll = u, + vv.ll = v; + + w.ll = __umulsidi3 (uu.s.low, vv.s.low); + w.s.high += ((USItype) uu.s.low * (USItype) vv.s.high + + (USItype) uu.s.high * (USItype) vv.s.low); + + return w.ll; +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/putuser.S linux.ac/arch/arm/lib/putuser.S --- linux.vanilla/arch/arm/lib/putuser.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/lib/putuser.S Wed Oct 10 01:47:29 2001 @@ -0,0 +1,94 @@ +/* + * linux/arch/arm/lib/putuser.S + * + * Copyright (C) 2001 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Idea from x86 version, (C) Copyright 1998 Linus Torvalds + * + * These functions have a non-standard call interface to make + * them more efficient, especially as they return an error + * value in addition to the "real" return value. + * + * __put_user_X + * + * Inputs: r0 contains the address + * r1, r2 contains the value + * Outputs: r0 is the error code + * lr corrupted + * + * No other registers must be altered. (see include/asm-arm/uaccess.h + * for specific ASM register usage). + * + * Note that ADDR_LIMIT is either 0 or 0xc0000000 + * Note also that it is intended that __put_user_bad is not global. + */ +#include + + .global __put_user_1 +__put_user_1: + bic r2, sp, #0x1f00 + bic r2, r2, #0x00ff + ldr r2, [r2, #TSK_ADDR_LIMIT] + sub r2, r2, #1 + cmp r0, r2 +1: strlsbt r1, [r0] + movls r0, #0 + movls pc, lr + b __put_user_bad + + .global __put_user_2 +__put_user_2: + bic r2, sp, #0x1f00 + bic r2, r2, #0x00ff + ldr r2, [r2, #TSK_ADDR_LIMIT] + sub r2, r2, #2 + cmp r0, r2 +2: strlsbt r1, [r0], #1 + movls r1, r1, lsr #8 +3: strlsbt r1, [r0] + movls r0, #0 + movls pc, lr + b __put_user_bad + + .global __put_user_4 +__put_user_4: + bic r2, sp, #0x1f00 + bic r2, r2, #0x00ff + ldr r2, [r2, #TSK_ADDR_LIMIT] + sub r2, r2, #4 + cmp r0, r2 +4: strlst r1, [r0] + movls r0, #0 + movls pc, lr + b __put_user_bad + + .global __put_user_8 +__put_user_8: + bic ip, sp, #0x1f00 + bic ip, ip, #0x00ff + ldr ip, [ip, #TSK_ADDR_LIMIT] + sub ip, ip, #8 + cmp r0, ip +5: strlst r1, [r0], #4 +6: strlst r2, [r0] + movls r0, #0 + movls pc, lr + + /* fall through */ + +__put_user_bad: + mov r0, #-14 + mov pc, lr + +.section __ex_table, "a" + .long 1b, __put_user_bad + .long 2b, __put_user_bad + .long 3b, __put_user_bad + .long 4b, __put_user_bad + .long 5b, __put_user_bad + .long 6b, __put_user_bad +.previous diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/ucmpdi2.c linux.ac/arch/arm/lib/ucmpdi2.c --- linux.vanilla/arch/arm/lib/ucmpdi2.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/lib/ucmpdi2.c Wed Oct 10 01:47:29 2001 @@ -0,0 +1,51 @@ +/* More subroutines needed by GCC output code on some machines. */ +/* Compile this one with gcc. */ +/* Copyright (C) 1989, 92-98, 1999 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with other files, + some of which are compiled with GCC, to produce an executable, + this library does not by itself cause the resulting executable + to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. + */ +/* support functions required by the kernel. based on code from gcc-2.95.3 */ +/* I Molton 29/07/01 */ + +#include "gcclib.h" + +word_type +__ucmpdi2 (DItype a, DItype b) +{ + DIunion au, bu; + + au.ll = a, bu.ll = b; + + if ((USItype) au.s.high < (USItype) bu.s.high) + return 0; + else if ((USItype) au.s.high > (USItype) bu.s.high) + return 2; + if ((USItype) au.s.low < (USItype) bu.s.low) + return 0; + else if ((USItype) au.s.low > (USItype) bu.s.low) + return 2; + return 1; +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/udivdi3.c linux.ac/arch/arm/lib/udivdi3.c --- linux.vanilla/arch/arm/lib/udivdi3.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/lib/udivdi3.c Wed Oct 10 01:47:29 2001 @@ -0,0 +1,231 @@ +/* More subroutines needed by GCC output code on some machines. */ +/* Compile this one with gcc. */ +/* Copyright (C) 1989, 92-98, 1999 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with other files, + some of which are compiled with GCC, to produce an executable, + this library does not by itself cause the resulting executable + to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. + */ +/* support functions required by the kernel. based on code from gcc-2.95.3 */ +/* I Molton 29/07/01 */ + +#include "gcclib.h" +#include "longlong.h" + +static const UQItype __clz_tab[] = +{ + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +}; + +UDItype +__udivmoddi4 (UDItype n, UDItype d, UDItype *rp) +{ + DIunion ww; + DIunion nn, dd; + DIunion rr; + USItype d0, d1, n0, n1, n2; + USItype q0, q1; + USItype b, bm; + + nn.ll = n; + dd.ll = d; + + d0 = dd.s.low; + d1 = dd.s.high; + n0 = nn.s.low; + n1 = nn.s.high; + + if (d1 == 0) + { + if (d0 > n1) + { + /* 0q = nn / 0D */ + + count_leading_zeros (bm, d0); + + if (bm != 0) + { + /* Normalize, i.e. make the most significant bit of the + denominator set. */ + + d0 = d0 << bm; + n1 = (n1 << bm) | (n0 >> (SI_TYPE_SIZE - bm)); + n0 = n0 << bm; + } + + udiv_qrnnd (q0, n0, n1, n0, d0); + q1 = 0; + + /* Remainder in n0 >> bm. */ + } + else + { + /* qq = NN / 0d */ + + if (d0 == 0) + d0 = 1 / d0; /* Divide intentionally by zero. */ + + count_leading_zeros (bm, d0); + + if (bm == 0) + { + /* From (n1 >= d0) /\ (the most significant bit of d0 is set), + conclude (the most significant bit of n1 is set) /\ (the + leading quotient digit q1 = 1). + + This special case is necessary, not an optimization. + (Shifts counts of SI_TYPE_SIZE are undefined.) */ + + n1 -= d0; + q1 = 1; + } + else + { + /* Normalize. */ + + b = SI_TYPE_SIZE - bm; + + d0 = d0 << bm; + n2 = n1 >> b; + n1 = (n1 << bm) | (n0 >> b); + n0 = n0 << bm; + + udiv_qrnnd (q1, n1, n2, n1, d0); + } + + /* n1 != d0... */ + + udiv_qrnnd (q0, n0, n1, n0, d0); + + /* Remainder in n0 >> bm. */ + } + + if (rp != 0) + { + rr.s.low = n0 >> bm; + rr.s.high = 0; + *rp = rr.ll; + } + } + else + { + if (d1 > n1) + { + /* 00 = nn / DD */ + + q0 = 0; + q1 = 0; + + /* Remainder in n1n0. */ + if (rp != 0) + { + rr.s.low = n0; + rr.s.high = n1; + *rp = rr.ll; + } + } + else + { + /* 0q = NN / dd */ + + count_leading_zeros (bm, d1); + if (bm == 0) + { + /* From (n1 >= d1) /\ (the most significant bit of d1 is set), + conclude (the most significant bit of n1 is set) /\ (the + quotient digit q0 = 0 or 1). + + This special case is necessary, not an optimization. */ + + /* The condition on the next line takes advantage of that + n1 >= d1 (true due to program flow). */ + if (n1 > d1 || n0 >= d0) + { + q0 = 1; + sub_ddmmss (n1, n0, n1, n0, d1, d0); + } + else + q0 = 0; + + q1 = 0; + + if (rp != 0) + { + rr.s.low = n0; + rr.s.high = n1; + *rp = rr.ll; + } + } + else + { + USItype m1, m0; + /* Normalize. */ + + b = SI_TYPE_SIZE - bm; + + d1 = (d1 << bm) | (d0 >> b); + d0 = d0 << bm; + n2 = n1 >> b; + n1 = (n1 << bm) | (n0 >> b); + n0 = n0 << bm; + + udiv_qrnnd (q0, n1, n2, n1, d1); + umul_ppmm (m1, m0, q0, d0); + + if (m1 > n1 || (m1 == n1 && m0 > n0)) + { + q0--; + sub_ddmmss (m1, m0, m1, m0, d1, d0); + } + + q1 = 0; + + /* Remainder in (n1n0 - m1m0) >> bm. */ + if (rp != 0) + { + sub_ddmmss (n1, n0, n1, n0, m1, m0); + rr.s.low = (n1 << b) | (n0 >> bm); + rr.s.high = n1 >> bm; + *rp = rr.ll; + } + } + } + } + + ww.s.low = q0; + ww.s.high = q1; + return ww.ll; +} + +UDItype +__udivdi3 (UDItype n, UDItype d) +{ + return __udivmoddi4 (n, d, (UDItype *) 0); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-ebsa110/io.c linux.ac/arch/arm/mach-ebsa110/io.c --- linux.vanilla/arch/arm/mach-ebsa110/io.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mach-ebsa110/io.c Wed Oct 10 01:47:29 2001 @@ -279,6 +279,9 @@ __raw_readsb(ISAIO_BASE + off, from, len); } +EXPORT_SYMBOL(outsb); +EXPORT_SYMBOL(insb); + void outsw(unsigned int port, const void *from, int len) { u32 off; @@ -308,6 +311,9 @@ __raw_readsw(ISAIO_BASE + off, from, len); } + +EXPORT_SYMBOL(outsw); +EXPORT_SYMBOL(insw); void outsl(unsigned int port, const void *from, int len) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-integrator/cpu.c linux.ac/arch/arm/mach-integrator/cpu.c --- linux.vanilla/arch/arm/mach-integrator/cpu.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mach-integrator/cpu.c Wed Oct 10 01:47:29 2001 @@ -3,7 +3,7 @@ * * Copyright (C) 2001 Deep Blue Solutions Ltd. * - * $Id: cpu.c,v 1.1 2001/06/17 10:12:37 rmk Exp $ + * $Id: cpu.c,v 1.2 2001/09/22 12:11:17 rmk Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -70,7 +70,7 @@ * Validate the speed in khz. If it is outside our * range, then return the lowest. */ -unsigned int cpufreq_validatespeed(unsigned int freq_khz) +unsigned int integrator_validatespeed(unsigned int freq_khz) { struct vco vco; @@ -87,7 +87,7 @@ return vco_to_freq(vco, 1); } -void cpufreq_setspeed(unsigned int freq_khz) +void integrator_setspeed(unsigned int freq_khz) { struct vco vco = freq_to_vco(freq_khz, 1); u_int cm_osc; @@ -122,6 +122,7 @@ #ifdef CONFIG_CPU_FREQ cpufreq_init(cpu_freq_khz); + cpufreq_setfunctions(integrator_validatespeed, integrator_setspeed); #endif cm_stat = __raw_readl(CM_STAT); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-integrator/dma.c linux.ac/arch/arm/mach-integrator/dma.c --- linux.vanilla/arch/arm/mach-integrator/dma.c Sat Mar 3 02:38:39 2001 +++ linux.ac/arch/arm/mach-integrator/dma.c Wed Oct 10 01:47:29 2001 @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include -#include +#include #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-integrator/pci_v3.c linux.ac/arch/arm/mach-integrator/pci_v3.c --- linux.vanilla/arch/arm/mach-integrator/pci_v3.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mach-integrator/pci_v3.c Wed Oct 10 01:47:29 2001 @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/Makefile linux.ac/arch/arm/mach-sa1100/Makefile --- linux.vanilla/arch/arm/mach-sa1100/Makefile Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mach-sa1100/Makefile Wed Oct 10 01:47:29 2001 @@ -14,31 +14,38 @@ obj-n := obj- := -export-objs := assabet.o bitsy.o freebird.o huw_webpanel.o yopy.o \ +export-objs := assabet.o h3600.o freebird.o huw_webpanel.o yopy.o \ generic.o hwtimer.o irq.o usb_ctl.o usb_recv.o usb_send.o \ - dma-sa1100.o dma-sa1111.o pcipool.o + dma-sa1100.o dma-sa1111.o pcipool.o sa1111-pcibuf.o # Common support (must be linked before board specific support) obj-y += generic.o irq.o dma-sa1100.o -obj-$(CONFIG_SA1111) += sa1111.o dma-sa1111.o sa1111-pcibuf.o pcipool.o # This needs to be cleaned up. We probably need to have SA1100 # and SA1110 config symbols. +# +# We link the CPU support next, so that RAM timings can be tuned. ifeq ($(CONFIG_CPU_FREQ),y) obj-$(CONFIG_SA1100_ASSABET) += cpu-sa1110.o obj-$(CONFIG_SA1100_LART) += cpu-sa1100.o endif +# Next, the SA1111 stuff. +obj-$(CONFIG_SA1111) += sa1111.o dma-sa1111.o +obj-$(CONFIG_USB_OHCI_SA1111) += sa1111-pcibuf.o pcipool.o + # Specific board support +obj-$(CONFIG_SA1100_ADSBITSY) += adsbitsy.o obj-$(CONFIG_SA1100_ASSABET) += assabet.o obj-$(CONFIG_ASSABET_NEPONSET) += neponset.o -obj-$(CONFIG_SA1100_BITSY) += bitsy.o obj-$(CONFIG_SA1100_BRUTUS) += brutus.o obj-$(CONFIG_SA1100_CERF) += cerf.o obj-$(CONFIG_SA1100_EMPEG) += empeg.o obj-$(CONFIG_SA1100_FLEXANET) += flexanet.o obj-$(CONFIG_SA1100_FREEBIRD) += freebird.o obj-$(CONFIG_SA1100_GRAPHICSCLIENT) += graphicsclient.o +obj-$(CONFIG_SA1100_GRAPHICSMASTER) += graphicsmaster.o +obj-$(CONFIG_SA1100_H3600) += h3600.o obj-$(CONFIG_SA1100_HUW_WEBPANEL) += huw_webpanel.o obj-$(CONFIG_SA1100_ITSY) += itsy.o obj-$(CONFIG_SA1100_JORNADA720) += jornada720.o @@ -63,6 +70,7 @@ leds-$(CONFIG_SA1100_GRAPHICSCLIENT) += leds-graphicsclient.o leds-$(CONFIG_SA1100_LART) += leds-lart.o leds-$(CONFIG_SA1100_PFS168) += leds-pfs168.o +leds-$(CONFIG_SA1100_GRAPHICSMASTER) += leds-graphicsmaster.o obj-$(CONFIG_LEDS) += $(leds-y) include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/adsbitsy.c linux.ac/arch/arm/mach-sa1100/adsbitsy.c --- linux.vanilla/arch/arm/mach-sa1100/adsbitsy.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/mach-sa1100/adsbitsy.c Wed Oct 10 01:47:29 2001 @@ -0,0 +1,180 @@ +/* + * linux/arch/arm/mach-sa1100/adsbitsy.c + * + * Author: Woojung Huh + * + * Pieces specific to the ADS Bitsy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "generic.h" +#include "sa1111.h" + +static int __init adsbitsy_init(void) +{ + int ret; + + if (!machine_is_adsbitsy()) + return -ENODEV; + + /* + * Ensure that the memory bus request/grant signals are setup, + * and the grant is held in its inactive state + */ + sa1110_mb_disable(); + + /* + * Reset SA1111 + */ + GPCR |= GPIO_GPIO26; + udelay(1000); + GPSR |= GPIO_GPIO26; + + /* + * Probe for SA1111. + */ + ret = sa1111_probe(); + if (ret < 0) + return ret; + + /* + * We found it. Wake the chip up. + */ + sa1111_wake(); + + /* + * The SDRAM configuration of the SA1110 and the SA1111 must + * match. This is very important to ensure that SA1111 accesses + * don't corrupt the SDRAM. Note that this ungates the SA1111's + * MBGNT signal, so we must have called sa1110_mb_disable() + * beforehand. + */ + sa1111_configure_smc(1, + FExtr(MDCNFG, MDCNFG_SA1110_DRAC0), + FExtr(MDCNFG, MDCNFG_SA1110_TDL0)); + + /* + * Enable PWM control for LCD + */ + SKPCR |= SKPCR_PWMCLKEN; + SKPWM0 = 0x7F; // VEE + SKPEN0 = 1; + SKPWM1 = 0x01; // Backlight + SKPEN1 = 1; + + /* + * We only need to turn on DCLK whenever we want to use the + * DMA. It can otherwise be held firmly in the off position. + */ + SKPCR |= SKPCR_DCLKEN; + + /* + * Enable the SA1110 memory bus request and grant signals. + */ + sa1110_mb_enable(); + + set_GPIO_IRQ_edge(GPIO_GPIO0, GPIO_RISING_EDGE); + sa1111_init_irq(SA1100_GPIO_TO_IRQ(0)); + + return 0; +} + +__initcall(adsbitsy_init); + +static void __init adsbitsy_init_irq(void) +{ + /* First the standard SA1100 IRQs */ + sa1100_init_irq(); +} + + +/* + * Initialization fixup + */ + +static void __init +fixup_adsbitsy(struct machine_desc *desc, struct param_struct *params, + char **cmdline, struct meminfo *mi) +{ + SET_BANK( 0, 0xc0000000, 32*1024*1024 ); + mi->nr_banks = 1; + + ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + setup_ramdisk( 1, 0, 0, 8192 ); + setup_initrd( __phys_to_virt(0xc0800000), 4*1024*1024 ); +} + +static struct map_desc adsbitsy_io_desc[] __initdata = { + /* virtual physical length domain r w c b */ + { 0xe8000000, 0x08000000, 0x01000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 1 */ + { 0xf4000000, 0x18000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* SA1111 */ + LAST_DESC +}; + +static int adsbitsy_uart_open(struct uart_port *port, struct uart_info *info) +{ + if (port->mapbase == _Ser1UTCR0) { + Ser1SDCR0 |= SDCR0_UART; + // Set RTS Output and High (should be done in the set_mctrl fn) + GPDR |= GPIO_GPIO15; + GPCR |= GPIO_GPIO15; + // Set CTS Input + GPDR &= ~GPIO_GPIO14; + } else if (port->mapbase == _Ser2UTCR0) { + Ser2UTCR4 = Ser2HSCR0 = 0; + // Set RTS Output and High (should be done in the set_mctrl fn) + GPDR |= GPIO_GPIO17; + GPCR |= GPIO_GPIO17; + // Set CTS Input + GPDR &= ~GPIO_GPIO16; + } else if (port->mapbase == _Ser2UTCR0) { + // Set RTS Output and High (should be done in the set_mctrl fn) + GPDR |= GPIO_GPIO19; + GPCR |= GPIO_GPIO19; + // Set CTS Input + GPDR &= ~GPIO_GPIO18; + } + return 0; +} + +static struct sa1100_port_fns adsbitsy_port_fns __initdata = { + open: adsbitsy_uart_open, +}; + +static void __init adsbitsy_map_io(void) +{ + sa1100_map_io(); + iotable_init(adsbitsy_io_desc); + + sa1110_register_uart_fns(&adsbitsy_port_fns); + sa1100_register_uart(0, 3); + sa1100_register_uart(1, 1); + sa1100_register_uart(2, 2); +} + +MACHINE_START(ADSBITSY, "ADS Bitsy") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + FIXUP(fixup_adsbitsy) + MAPIO(adsbitsy_map_io) + INITIRQ(adsbitsy_init_irq) +MACHINE_END diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/assabet.c linux.ac/arch/arm/mach-sa1100/assabet.c --- linux.vanilla/arch/arm/mach-sa1100/assabet.c Mon Aug 13 01:36:24 2001 +++ linux.ac/arch/arm/mach-sa1100/assabet.c Wed Oct 10 01:47:29 2001 @@ -47,6 +47,7 @@ * or BCR_clear(). */ BCR = BCR_value = BCR_DB1111; + NCR_0 = 0; #ifndef CONFIG_ASSABET_NEPONSET printk( "Warning: Neponset detected but full support " @@ -101,6 +102,8 @@ SCR_value = scr; } +extern void convert_to_tag_list(struct param_struct *params, int mem_init); + static void __init fixup_assabet(struct machine_desc *desc, struct param_struct *params, char **cmdline, struct meminfo *mi) @@ -114,6 +117,12 @@ if (machine_has_neponset()) printk("Neponset expansion board detected\n"); + /* + * Apparantly bootldr uses a param_struct. Groan. + */ + if (t->hdr.tag != ATAG_CORE) + convert_to_tag_list(params, 1); + if (t->hdr.tag != ATAG_CORE) { t->hdr.tag = ATAG_CORE; t->hdr.size = tag_size(tag_core); @@ -265,7 +274,6 @@ neponset_map_io(); #endif - sa1100_register_uart(1, 2); if (machine_has_neponset()) { /* * When Neponset is attached, the first UART should be @@ -295,7 +303,7 @@ * excessive power drain. --rmk */ GPDR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM; - GPCR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM; + GPCR = GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/bitsy.c linux.ac/arch/arm/mach-sa1100/bitsy.c --- linux.vanilla/arch/arm/mach-sa1100/bitsy.c Mon Aug 13 01:36:24 2001 +++ linux.ac/arch/arm/mach-sa1100/bitsy.c Thu Jan 1 01:00:00 1970 @@ -1,175 +0,0 @@ -/* - * linux/arch/arm/mach-sa1100/bitsy.c - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include "generic.h" - - -/* - * Bitsy has extended, write-only memory-mapped GPIO's - */ - -static int bitsy_egpio = EGPIO_BITSY_RS232_ON; - -void clr_bitsy_egpio(unsigned long x) -{ - bitsy_egpio &= ~x; - BITSY_EGPIO = bitsy_egpio; -} - -void set_bitsy_egpio(unsigned long x) -{ - bitsy_egpio |= x; - BITSY_EGPIO = bitsy_egpio; -} - -EXPORT_SYMBOL(clr_bitsy_egpio); -EXPORT_SYMBOL(set_bitsy_egpio); - - -/* - * low-level UART features - */ - -static void bitsy_uart_set_mctrl(struct uart_port *port, u_int mctrl) -{ - if (port->mapbase == _Ser3UTCR0) { - if (mctrl & TIOCM_RTS) - GPCR = GPIO_BITSY_COM_RTS; - else - GPSR = GPIO_BITSY_COM_RTS; - } -} - -static int bitsy_uart_get_mctrl(struct uart_port *port) -{ - int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; - - if (port->mapbase == _Ser3UTCR0) { - int gplr = GPLR; - if (gplr & GPIO_BITSY_COM_DCD) - ret &= ~TIOCM_CD; - if (gplr & GPIO_BITSY_COM_CTS) - ret &= ~TIOCM_CTS; - } - - return ret; -} - -static void bitsy_dcd_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - struct uart_info *info = dev_id; - /* Note: should only call this if something has changed */ - uart_handle_dcd_change(info, GPLR & GPIO_BITSY_COM_DCD); -} - -static void bitsy_cts_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - struct uart_info *info = dev_id; - /* Note: should only call this if something has changed */ - uart_handle_cts_change(info, GPLR & GPIO_BITSY_COM_CTS); -} - -static void bitsy_uart_pm(struct uart_port *port, u_int state, u_int oldstate) -{ - if (port->mapbase == _Ser2UTCR0) { - if (state == 0) { - set_bitsy_egpio(EGPIO_BITSY_IR_ON); - } else { - clr_bitsy_egpio(EGPIO_BITSY_IR_ON); - } - } else if (port->mapbase == _Ser3UTCR0) { - if (state == 0) { - set_bitsy_egpio(EGPIO_BITSY_RS232_ON); - } else { - clr_bitsy_egpio(EGPIO_BITSY_RS232_ON); - } - } -} - -static int bitsy_uart_open(struct uart_port *port, struct uart_info *info) -{ - int ret = 0; - - if (port->mapbase == _Ser2UTCR0) { - Ser2UTCR4 = UTCR4_HSE; - Ser2HSCR0 = 0; - Ser2HSSR0 = HSSR0_EIF | HSSR0_TUR | - HSSR0_RAB | HSSR0_FRE; - } else if (port->mapbase == _Ser3UTCR0) { - GPDR &= ~(GPIO_BITSY_COM_DCD|GPIO_BITSY_COM_CTS); - GPDR |= GPIO_BITSY_COM_RTS; - set_GPIO_IRQ_edge(GPIO_BITSY_COM_DCD|GPIO_BITSY_COM_CTS, - GPIO_BOTH_EDGES); - - ret = request_irq(IRQ_GPIO_BITSY_COM_DCD, bitsy_dcd_intr, - 0, "RS232 DCD", info); - if (ret) - return ret; - - ret = request_irq(IRQ_GPIO_BITSY_COM_CTS, bitsy_cts_intr, - 0, "RS232 CTS", info); - if (ret) - free_irq(IRQ_GPIO_BITSY_COM_DCD, info); - } - return ret; -} - -static void bitsy_uart_close(struct uart_port *port, struct uart_info *info) -{ - if (port->mapbase == _Ser3UTCR0) { - free_irq(IRQ_GPIO_BITSY_COM_DCD, info); - free_irq(IRQ_GPIO_BITSY_COM_CTS, info); - } -} - -static struct sa1100_port_fns bitsy_port_fns __initdata = { - set_mctrl: bitsy_uart_set_mctrl, - get_mctrl: bitsy_uart_get_mctrl, - pm: bitsy_uart_pm, - open: bitsy_uart_open, - close: bitsy_uart_close, -}; - -static struct map_desc bitsy_io_desc[] __initdata = { - /* virtual physical length domain r w c b */ - { 0xe8000000, 0x00000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 0 */ - { 0xf0000000, 0x49000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* EGPIO 0 */ - { 0xf1000000, 0x10000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* static memory bank 2 */ - { 0xf3000000, 0x40000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* static memory bank 4 */ - LAST_DESC -}; - -static void __init bitsy_map_io(void) -{ - sa1100_map_io(); - iotable_init(bitsy_io_desc); - - sa1100_register_uart_fns(&bitsy_port_fns); - sa1100_register_uart(0, 3); - sa1100_register_uart(1, 1); /* isn't this one driven elsewhere? */ - sa1100_register_uart(2, 2); -} - -MACHINE_START(BITSY, "Compaq iPAQ") - BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) - BOOT_PARAMS(0xc0000100) - MAPIO(bitsy_map_io) - INITIRQ(sa1100_init_irq) -MACHINE_END diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/cpu-sa1110.c linux.ac/arch/arm/mach-sa1100/cpu-sa1110.c --- linux.vanilla/arch/arm/mach-sa1100/cpu-sa1110.c Mon Aug 13 01:36:24 2001 +++ linux.ac/arch/arm/mach-sa1100/cpu-sa1110.c Wed Oct 10 01:47:29 2001 @@ -3,7 +3,7 @@ * * Copyright (C) 2001 Russell King * - * $Id: cpu-sa1110.c,v 1.3 2001/08/12 15:41:53 rmk Exp $ + * $Id: cpu-sa1110.c,v 1.5 2001/09/10 13:25:58 rmk Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -258,14 +258,13 @@ { struct sdram_params *sdram = NULL; unsigned int cur_freq = cpufreq_get(smp_processor_id()); - int ret = -ENODEV; if (machine_is_assabet()) sdram = &tc59sm716_cl3_params; if (sdram) { printk(KERN_DEBUG "SDRAM: tck: %d trcd: %d trp: %d" - " twr: %d refresh: %d cas_latency: %d", + " twr: %d refresh: %d cas_latency: %d\n", sdram->tck, sdram->trcd, sdram->trp, sdram->twr, sdram->refresh, sdram->cas_latency); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/dma-sa1100.c linux.ac/arch/arm/mach-sa1100/dma-sa1100.c --- linux.vanilla/arch/arm/mach-sa1100/dma-sa1100.c Mon Aug 13 01:36:24 2001 +++ linux.ac/arch/arm/mach-sa1100/dma-sa1100.c Wed Oct 10 01:47:29 2001 @@ -68,26 +68,30 @@ { dma_regs_t *regs = dma->regs; int status; - int use_bufa; status = regs->RdDCSR; /* If both DMA buffers are started, there's nothing else we can do. */ - if ((status & DCSR_STRTA) && (status & DCSR_STRTB)) { + if ((status & (DCSR_STRTA | DCSR_STRTB)) == (DCSR_STRTA | DCSR_STRTB)) { DPRINTK("start: st %#x busy\n", status); return -EBUSY; } - use_bufa = (((status & DCSR_BIU) && (status & DCSR_STRTB)) || - (!(status & DCSR_BIU) && !(status & DCSR_STRTA))); - if (use_bufa) { - regs->ClrDCSR = DCSR_DONEA | DCSR_STRTA; + if (((status & DCSR_BIU) && (status & DCSR_STRTB)) || + (!(status & DCSR_BIU) && !(status & DCSR_STRTA))) { + if (status & DCSR_DONEA) { + /* give a chance for the interrupt to be processed */ + goto irq_pending; + } regs->DBSA = dma_ptr; regs->DBTA = size; regs->SetDCSR = DCSR_STRTA | DCSR_IE | DCSR_RUN; DPRINTK("start a=%#x s=%d on A\n", dma_ptr, size); } else { - regs->ClrDCSR = DCSR_DONEB | DCSR_STRTB; + if (status & DCSR_DONEB) { + /* give a chance for the interrupt to be processed */ + goto irq_pending; + } regs->DBSB = dma_ptr; regs->DBTB = size; regs->SetDCSR = DCSR_STRTB | DCSR_IE | DCSR_RUN; @@ -95,6 +99,9 @@ } return 0; + +irq_pending: + return -EAGAIN; } @@ -204,11 +211,16 @@ DPRINTK("IRQ: b=%#x st=%#x\n", (int) dma->curr->id, status); - dma->regs->ClrDCSR = DCSR_ERROR | DCSR_DONEA | DCSR_DONEB; - if (!(status & (DCSR_DONEA | DCSR_DONEB))) - return; + if (status & (DCSR_ERROR)) { + printk(KERN_ERR "DMA on \"%s\" caused an error\n", dma->device_id); + dma->regs->ClrDCSR = DCSR_ERROR; + } - sa1100_dma_done (dma); + dma->regs->ClrDCSR = status & (DCSR_DONEA | DCSR_DONEB); + if (status & DCSR_DONEA) + sa1100_dma_done (dma); + if (status & DCSR_DONEB) + sa1100_dma_done (dma); } @@ -435,7 +447,7 @@ dma->curr = NULL; } dma->spin_ref = 0; - dma->regs->ClrDCSR = DCSR_STRTA|DCSR_STRTB; + dma->regs->ClrDCSR = DCSR_STRTA|DCSR_STRTB|DCSR_DONEA|DCSR_DONEB; process_dma(dma); local_irq_restore(flags); return 0; @@ -455,7 +467,6 @@ if (dma->stopped) { int flags; save_flags_cli(flags); - dma->regs->ClrDCSR = DCSR_STRTA|DCSR_STRTB|DCSR_RUN|DCSR_IE; dma->stopped = 0; dma->spin_ref = 0; process_dma(dma); @@ -478,7 +489,7 @@ if (channel_is_sa1111_sac(channel)) sa1111_reset_sac_dma(channel); else - dma->regs->ClrDCSR = DCSR_STRTA|DCSR_STRTB|DCSR_RUN|DCSR_IE; + dma->regs->ClrDCSR = DCSR_STRTA|DCSR_STRTB|DCSR_DONEA|DCSR_DONEB|DCSR_RUN|DCSR_IE; buf = dma->curr; if (!buf) buf = dma->tail; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/dma-sa1111.c linux.ac/arch/arm/mach-sa1100/dma-sa1111.c --- linux.vanilla/arch/arm/mach-sa1100/dma-sa1111.c Mon Aug 13 01:36:24 2001 +++ linux.ac/arch/arm/mach-sa1100/dma-sa1111.c Wed Oct 10 01:47:29 2001 @@ -314,7 +314,7 @@ if(physaddr<(1<<20)) return 0; - switch(FExtr(SMCR, SMCR_DRAC)){ + switch(FExtr(SBI_SMCR, SMCR_DRAC)){ case 01: /* 10 row + bank address bits, A<20> must not be set */ if(physaddr & (1<<20)) return -1; @@ -341,7 +341,7 @@ break; default: printk(KERN_ERR "%s(): invalid SMCR DRAC value 0%o\n", - __FUNCTION__, FExtr(SMCR, SMCR_DRAC)); + __FUNCTION__, FExtr(SBI_SMCR, SMCR_DRAC)); return -1; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/generic.c linux.ac/arch/arm/mach-sa1100/generic.c --- linux.vanilla/arch/arm/mach-sa1100/generic.c Mon Aug 13 01:36:24 2001 +++ linux.ac/arch/arm/mach-sa1100/generic.c Wed Oct 10 01:47:29 2001 @@ -69,7 +69,7 @@ * Validate the speed in khz. If we can't generate the precise * frequency requested, round it down (to be on the safe side). */ -unsigned int cpufreq_validatespeed(unsigned int khz) +unsigned int sa1100_validatespeed(unsigned int khz) { int i; @@ -87,7 +87,7 @@ * above, we can match for an exact frequency. If we don't find * an exact match, we will to set the lowest frequency to be safe. */ -void cpufreq_setspeed(unsigned int khz) +void sa1100_setspeed(unsigned int khz) { int i; @@ -103,6 +103,7 @@ static int __init sa1100_init_clock(void) { cpufreq_init(get_cclk_frequency() * 100); + cpufreq_setfunctions(sa1100_validatespeed, sa1100_setspeed); return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/graphicsclient.c linux.ac/arch/arm/mach-sa1100/graphicsclient.c --- linux.vanilla/arch/arm/mach-sa1100/graphicsclient.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mach-sa1100/graphicsclient.c Wed Oct 10 01:47:29 2001 @@ -131,14 +131,15 @@ mi->nr_banks = 2; ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); - setup_ramdisk( 1, 0, 0, 4096 ); + setup_ramdisk( 1, 0, 0, 8192 ); setup_initrd( __phys_to_virt(0xc0800000), 4*1024*1024 ); } static struct map_desc graphicsclient_io_desc[] __initdata = { /* virtual physical length domain r w c b */ - { 0xe8000000, 0x08000000, 0x01000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 1 */ + { 0xe8000000, 0x08000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 1 */ { 0xf0000000, 0x10000000, 0x00400000, DOMAIN_IO, 0, 1, 0, 0 }, /* CPLD */ + { 0xf1000000, 0x18000000, 0x00400000, DOMAIN_IO, 0, 1, 0, 0 }, /* CAN */ LAST_DESC }; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/graphicsmaster.c linux.ac/arch/arm/mach-sa1100/graphicsmaster.c --- linux.vanilla/arch/arm/mach-sa1100/graphicsmaster.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/mach-sa1100/graphicsmaster.c Wed Oct 10 01:47:29 2001 @@ -0,0 +1,225 @@ +/* + * linux/arch/arm/mach-sa1100/graphicsmaster.c + * + * Pieces specific to the GraphicsMaster board + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "generic.h" +#include "sa1111.h" + +static int __init graphicsmaster_init(void) +{ + int ret; + + if (!machine_is_graphicsmaster()) + return -ENODEV; + + /* + * Ensure that the memory bus request/grant signals are setup, + * and the grant is held in its inactive state + */ + sa1110_mb_disable(); + + /* + * Probe for SA1111. + */ + ret = sa1111_probe(); + if (ret < 0) + return ret; + + /* + * We found it. Wake the chip up. + */ + sa1111_wake(); + + /* + * The SDRAM configuration of the SA1110 and the SA1111 must + * match. This is very important to ensure that SA1111 accesses + * don't corrupt the SDRAM. Note that this ungates the SA1111's + * MBGNT signal, so we must have called sa1110_mb_disable() + * beforehand. + */ + sa1111_configure_smc(1, + FExtr(MDCNFG, MDCNFG_SA1110_DRAC0), + FExtr(MDCNFG, MDCNFG_SA1110_TDL0)); + + /* + * Enable PWM control for LCD + */ + SKPCR |= SKPCR_PWMCLKEN; + SKPWM0 = 0x7F; // VEE + SKPEN0 = 1; + SKPWM1 = 0x01; // Backlight + SKPEN1 = 1; + + /* + * We only need to turn on DCLK whenever we want to use the + * DMA. It can otherwise be held firmly in the off position. + */ + SKPCR |= SKPCR_DCLKEN; + + /* + * Enable the SA1110 memory bus request and grant signals. + */ + sa1110_mb_enable(); + + sa1111_init_irq(ADS_EXT_IRQ(0)); + + return 0; +} + +__initcall(graphicsmaster_init); + +/* + * Handlers for GraphicsMaster's external IRQ logic + */ + +static void ADS_IRQ_demux( int irq, void *dev_id, struct pt_regs *regs ) +{ + int i; + + while( (irq = ADS_INT_ST1 | (ADS_INT_ST2 << 8)) ){ + for( i = 0; i < 16; i++ ) + if( irq & (1<nr_banks = 1; + SET_BANK( 1, 0xc8000000, 16*1024*1024 ); + mi->nr_banks = 2; + + ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + setup_ramdisk( 1, 0, 0, 8192 ); + setup_initrd( __phys_to_virt(0xc0800000), 4*1024*1024 ); +} + +static struct map_desc graphicsmaster_io_desc[] __initdata = { + /* virtual physical length domain r w c b */ + { 0xe8000000, 0x08000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 1 */ + { 0xf0000000, 0x10000000, 0x00400000, DOMAIN_IO, 1, 1, 0, 0 }, /* CPLD */ + { 0xf1000000, 0x40000000, 0x00400000, DOMAIN_IO, 1, 1, 0, 0 }, /* CAN */ + { 0xf4000000, 0x18000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* SA-1111 */ + LAST_DESC +}; + +static void __init graphicsmaster_map_io(void) +{ + sa1100_map_io(); + iotable_init(graphicsmaster_io_desc); + + sa1100_register_uart(0, 3); + sa1100_register_uart(1, 1); + sa1100_register_uart(2, 2); +} + +MACHINE_START(GRAPHICSMASTER, "ADS GraphicsMaster") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + FIXUP(fixup_graphicsmaster) + MAPIO(graphicsmaster_map_io) + INITIRQ(graphicsmaster_init_irq) +MACHINE_END diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/h3600.c linux.ac/arch/arm/mach-sa1100/h3600.c --- linux.vanilla/arch/arm/mach-sa1100/h3600.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/mach-sa1100/h3600.c Wed Oct 10 01:47:29 2001 @@ -0,0 +1,189 @@ +/* + * linux/arch/arm/mach-sa1100/h3600.c + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "generic.h" + + +/* + * Bitsy has extended, write-only memory-mapped GPIO's + */ + +static int h3600_egpio = EGPIO_H3600_RS232_ON; + +void clr_h3600_egpio(unsigned long x) +{ + h3600_egpio &= ~x; + H3600_EGPIO = h3600_egpio; +} + +void set_h3600_egpio(unsigned long x) +{ + h3600_egpio |= x; + H3600_EGPIO = h3600_egpio; +} + +EXPORT_SYMBOL(clr_h3600_egpio); +EXPORT_SYMBOL(set_h3600_egpio); + + +/* + * Low-level UART features. + * + * Note that RTS, CTS and DCD are all active low. + */ + +static void h3600_uart_set_mctrl(struct uart_port *port, u_int mctrl) +{ + if (port->mapbase == _Ser3UTCR0) { + if (mctrl & TIOCM_RTS) + GPCR = GPIO_H3600_COM_RTS; + else + GPSR = GPIO_H3600_COM_RTS; + } +} + +static int h3600_uart_get_mctrl(struct uart_port *port) +{ + int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; + + if (port->mapbase == _Ser3UTCR0) { + int gplr = GPLR; + if (gplr & GPIO_H3600_COM_DCD) + ret &= ~TIOCM_CD; + if (gplr & GPIO_H3600_COM_CTS) + ret &= ~TIOCM_CTS; + } + + return ret; +} + +static void h3600_dcd_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info = dev_id; + /* Note: should only call this if something has changed */ + uart_handle_dcd_change(info, !(GPLR & GPIO_H3600_COM_DCD)); +} + +static void h3600_cts_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info = dev_id; + /* Note: should only call this if something has changed */ + uart_handle_cts_change(info, !(GPLR & GPIO_H3600_COM_CTS)); +} + +static void h3600_uart_pm(struct uart_port *port, u_int state, u_int oldstate) +{ + if (port->mapbase == _Ser2UTCR0) { + if (state == 0) { + set_h3600_egpio(EGPIO_H3600_IR_ON); + } else { + clr_h3600_egpio(EGPIO_H3600_IR_ON); + } + } else if (port->mapbase == _Ser3UTCR0) { + if (state == 0) { + set_h3600_egpio(EGPIO_H3600_RS232_ON); + } else { + clr_h3600_egpio(EGPIO_H3600_RS232_ON); + } + } +} + +static int h3600_uart_open(struct uart_port *port, struct uart_info *info) +{ + int ret = 0; + + if (port->mapbase == _Ser2UTCR0) { + Ser2UTCR4 = UTCR4_HSE; + Ser2HSCR0 = 0; + Ser2HSSR0 = HSSR0_EIF | HSSR0_TUR | + HSSR0_RAB | HSSR0_FRE; + } else if (port->mapbase == _Ser3UTCR0) { + GPDR &= ~(GPIO_H3600_COM_DCD|GPIO_H3600_COM_CTS); + GPDR |= GPIO_H3600_COM_RTS; + set_GPIO_IRQ_edge(GPIO_H3600_COM_DCD|GPIO_H3600_COM_CTS, + GPIO_BOTH_EDGES); + + ret = request_irq(IRQ_GPIO_H3600_COM_DCD, h3600_dcd_intr, + 0, "RS232 DCD", info); + if (ret) + return ret; + + ret = request_irq(IRQ_GPIO_H3600_COM_CTS, h3600_cts_intr, + 0, "RS232 CTS", info); + if (ret) + free_irq(IRQ_GPIO_H3600_COM_DCD, info); + } + return ret; +} + +static void h3600_uart_close(struct uart_port *port, struct uart_info *info) +{ + if (port->mapbase == _Ser3UTCR0) { + free_irq(IRQ_GPIO_H3600_COM_DCD, info); + free_irq(IRQ_GPIO_H3600_COM_CTS, info); + } +} + +static struct sa1100_port_fns h3600_port_fns __initdata = { + set_mctrl: h3600_uart_set_mctrl, + get_mctrl: h3600_uart_get_mctrl, + pm: h3600_uart_pm, + open: h3600_uart_open, + close: h3600_uart_close, +}; + +static struct map_desc h3600_io_desc[] __initdata = { + /* virtual physical length domain r w c b */ + { 0xe8000000, 0x00000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 0 */ + { 0xf0000000, 0x49000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* EGPIO 0 */ + { 0xf1000000, 0x10000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* static memory bank 2 */ + { 0xf3000000, 0x40000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* static memory bank 4 */ + LAST_DESC +}; + +static void __init h3600_map_io(void) +{ + sa1100_map_io(); + iotable_init(h3600_io_desc); + + sa1100_register_uart_fns(&h3600_port_fns); + sa1100_register_uart(0, 3); + sa1100_register_uart(1, 1); /* isn't this one driven elsewhere? */ + sa1100_register_uart(2, 2); + + /* + * Default GPIO settings. + */ + GPCR = 0x0fffffff; + GPDR = 0x0401f3fc; + + /* + * Ensure those pins are outputs and driving low. + */ + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); +} + +MACHINE_START(H3600, "Compaq iPAQ") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + BOOT_PARAMS(0xc0000100) + MAPIO(h3600_map_io) + INITIRQ(sa1100_init_irq) +MACHINE_END diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/jornada720.c linux.ac/arch/arm/mach-sa1100/jornada720.c --- linux.vanilla/arch/arm/mach-sa1100/jornada720.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mach-sa1100/jornada720.c Wed Oct 10 01:47:29 2001 @@ -46,7 +46,8 @@ PPDR |= PPC_LDD3 | PPC_LDD4; /* initialize extra IRQs */ - sa1111_init_irq(1); /* chained on GPIO 1 */ + set_GPIO_IRQ_edge(GPIO_GPIO(1), GPIO_RISING_EDGE); + sa1111_init_irq(SA1100_GPIO_TO_IRQ(1)); /* chained on GPIO 1 */ sa1100_register_uart(0, 3); sa1100_register_uart(1, 1); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/leds-graphicsmaster.c linux.ac/arch/arm/mach-sa1100/leds-graphicsmaster.c --- linux.vanilla/arch/arm/mach-sa1100/leds-graphicsmaster.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/mach-sa1100/leds-graphicsmaster.c Wed Oct 10 01:47:29 2001 @@ -0,0 +1,104 @@ +/* + * linux/arch/arm/mach-sa1100/leds-graphicsmaster.c + * + * GraphicsClient Plus LEDs support + * Woojung Huh, Feb 13, 2001 + */ +#include +#include + +#include +#include +#include + +#include "leds.h" + + +#define LED_STATE_ENABLED 1 +#define LED_STATE_CLAIMED 2 + +static unsigned int led_state; +static unsigned int hw_led_state; + +#define LED_TIMER ADS_LED0 /* green heartbeat */ +#define LED_USER ADS_LED1 /* amber, boots to on */ +#define LED_IDLE ADS_LED2 /* red has the idle led, if any */ + +#define LED_MASK (ADS_LED0|ADS_LED1|ADS_LED2) + +void graphicsmaster_leds_event(led_event_t evt) +{ + unsigned long flags; + + save_flags_cli(flags); + + switch (evt) { + case led_start: + hw_led_state = 0; /* gc leds are positive logic */ + led_state = LED_STATE_ENABLED; + break; + + case led_stop: + led_state &= ~LED_STATE_ENABLED; + break; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = LED_MASK; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = LED_MASK; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state ^= LED_TIMER; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~LED_IDLE; + break; + + case led_idle_end: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= LED_IDLE; + break; +#endif + + case led_green_on: + break; + + case led_green_off: + break; + + case led_amber_on: + hw_led_state |= LED_USER; + break; + + case led_amber_off: + hw_led_state &= ~LED_USER; + break; + + case led_red_on: + break; + + case led_red_off: + break; + + default: + break; + } + + if (led_state & LED_STATE_ENABLED) { + GPSR = hw_led_state; + GPCR = hw_led_state ^ LED_MASK; + } + + restore_flags(flags); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/leds.c linux.ac/arch/arm/mach-sa1100/leds.c --- linux.vanilla/arch/arm/mach-sa1100/leds.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mach-sa1100/leds.c Wed Oct 10 01:47:29 2001 @@ -30,6 +30,8 @@ leds_event = lart_leds_event; if (machine_is_pfs168()) leds_event = pfs168_leds_event; + if (machine_is_graphicsmaster()) + leds_event = graphicsmaster_leds_event; leds_event(led_start); return 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/leds.h linux.ac/arch/arm/mach-sa1100/leds.h --- linux.vanilla/arch/arm/mach-sa1100/leds.h Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mach-sa1100/leds.h Wed Oct 10 01:47:29 2001 @@ -5,4 +5,4 @@ extern void graphicsclient_leds_event(led_event_t evt); extern void lart_leds_event(led_event_t evt); extern void pfs168_leds_event(led_event_t evt); - +extern void graphicsmaster_leds_event(led_event_t evt); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/neponset.c linux.ac/arch/arm/mach-sa1100/neponset.c --- linux.vanilla/arch/arm/mach-sa1100/neponset.c Mon Aug 13 01:36:24 2001 +++ linux.ac/arch/arm/mach-sa1100/neponset.c Wed Oct 10 01:47:29 2001 @@ -74,36 +74,73 @@ static int __init neponset_init(void) { - /* only on assabet */ + int ret; + + /* + * The Neponset is only present on the Assabet machine type. + */ if (!machine_is_assabet()) - return 0; + return -ENODEV; + + /* + * Ensure that the memory bus request/grant signals are setup, + * and the grant is held in its inactive state, whether or not + * we actually have a Neponset attached. + */ + sa1110_mb_disable(); + + if (!machine_has_neponset()) { + printk(KERN_DEBUG "Neponset expansion board not present\n"); + return -ENODEV; + } + + if (WHOAMI != 0x11) { + printk(KERN_WARNING "Neponset board detected, but " + "wrong ID: %02x\n", WHOAMI); + return -ENODEV; + } + + /* + * Neponset has SA1111 connected to CS4. We know that after + * reset the chip will be configured for variable latency IO. + */ + /* FIXME: setup MSC2 */ + + /* + * Probe for a SA1111. + */ + ret = sa1111_probe(); + if (ret < 0) + return ret; + + /* + * We found it. Wake the chip up. + */ + sa1111_wake(); + + /* + * The SDRAM configuration of the SA1110 and the SA1111 must + * match. This is very important to ensure that SA1111 accesses + * don't corrupt the SDRAM. Note that this ungates the SA1111's + * MBGNT signal, so we must have called sa1110_mb_disable() + * beforehand. + */ + sa1111_configure_smc(1, + FExtr(MDCNFG, MDCNFG_SA1110_DRAC0), + FExtr(MDCNFG, MDCNFG_SA1110_TDL0)); + + /* + * We only need to turn on DCLK whenever we want to use the + * DMA. It can otherwise be held firmly in the off position. + */ + SKPCR |= SKPCR_DCLKEN; - if (machine_has_neponset()) { - LEDS = WHOAMI; + /* + * Enable the SA1110 memory bus request and grant signals. + */ + sa1110_mb_enable(); - if (sa1111_init() < 0) - return -EINVAL; - /* - * Assabet is populated by default with two Samsung - * KM416S8030T-G8 - * 128Mb SDRAMs, which are organized as 12-bit (row addr) x - * 9-bit - * (column addr), according to the data sheet. Apparently, the - * bank selects factor into the row address, as Angel sets up - * the - * SA-1110 to use 14x9 addresses. The SDRAM datasheet specifies - * that when running at 100-125MHz, the CAS latency for -8 - * parts - * is 3 cycles, which is consistent with Angel. - */ - SMCR = (SMCR_DTIM | SMCR_MBGE | - FInsrt(FExtr(MDCNFG, MDCNFG_SA1110_DRAC0), SMCR_DRAC) | - ((FExtr(MDCNFG, MDCNFG_SA1110_TDL0)==3) ? SMCR_CLAT : 0)); - SKPCR |= SKPCR_DCLKEN; - - neponset_init_irq(); - } else - printk("Neponset expansion board not present\n"); + neponset_init_irq(); return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/pangolin.c linux.ac/arch/arm/mach-sa1100/pangolin.c --- linux.vanilla/arch/arm/mach-sa1100/pangolin.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mach-sa1100/pangolin.c Wed Oct 10 01:47:29 2001 @@ -20,7 +20,7 @@ fixup_pangolin(struct machine_desc *desc, struct param_struct *params, char **cmdline, struct meminfo *mi) { - SET_BANK( 0, 0xc0000000, 64*1024*1024 ); + SET_BANK( 0, 0xc0000000, 128*1024*1024 ); mi->nr_banks = 1; ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/pfs168.c linux.ac/arch/arm/mach-sa1100/pfs168.c --- linux.vanilla/arch/arm/mach-sa1100/pfs168.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mach-sa1100/pfs168.c Wed Oct 10 01:47:29 2001 @@ -20,14 +20,53 @@ static int __init pfs168_init(void) { - if (sa1111_init() < 0) - return -EINVAL; - SMCR = (SMCR_DTIM | SMCR_MBGE | - FInsrt(FExtr(MDCNFG, MDCNFG_SA1110_DRAC0), SMCR_DRAC) | - ((FExtr(MDCNFG, MDCNFG_SA1110_TDL0)==3) ? SMCR_CLAT : 0)); + int ret; + + if (!machine_is_pfs168()) + return -ENODEV; + + /* + * Ensure that the memory bus request/grant signals are setup, + * and the grant is held in its inactive state + */ + sa1110_mb_disable(); + + /* + * Probe for SA1111. + */ + ret = sa1111_probe(); + if (ret < 0) + return ret; + + /* + * We found it. Wake the chip up. + */ + sa1111_wake(); + + /* + * The SDRAM configuration of the SA1110 and the SA1111 must + * match. This is very important to ensure that SA1111 accesses + * don't corrupt the SDRAM. Note that this ungates the SA1111's + * MBGNT signal, so we must have called sa1110_mb_disable() + * beforehand. + */ + sa1111_configure_smc(1, + FExtr(MDCNFG, MDCNFG_SA1110_DRAC0), + FExtr(MDCNFG, MDCNFG_SA1110_TDL0)); + + /* + * We only need to turn on DCLK whenever we want to use the + * DMA. It can otherwise be held firmly in the off position. + */ SKPCR |= SKPCR_DCLKEN; - sa1111_init_irq(25); /* SA1111 IRQ on GPIO 25 */ + /* + * Enable the SA1110 memory bus request and grant signals. + */ + sa1110_mb_enable(); + + set_GPIO_IRQ_edge(GPIO_GPIO(25), GPIO_RISING_EDGE); + sa1111_init_irq(SA1100_GPIO_TO_IRQ(25)); /* SA1111 IRQ on GPIO 25 */ return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/sa1111-pcibuf.c linux.ac/arch/arm/mach-sa1100/sa1111-pcibuf.c --- linux.vanilla/arch/arm/mach-sa1100/sa1111-pcibuf.c Mon Aug 13 01:36:24 2001 +++ linux.ac/arch/arm/mach-sa1100/sa1111-pcibuf.c Wed Oct 10 01:47:29 2001 @@ -13,7 +13,6 @@ * * 06/13/2001 - created. */ - #include #include #include @@ -64,13 +63,6 @@ return 0; } -static void -free_safe_buffers(void) -{ - pci_pool_destroy(small_buffer_cache); - pci_pool_destroy(large_buffer_cache); -} - /* allocate a 'safe' buffer and keep track of it */ static char * alloc_safe_buffer(char *unsafe, int size, dma_addr_t *pbus) @@ -183,17 +175,11 @@ * we assume calls to map_single are symmetric with calls to unmap_single... */ dma_addr_t -pci_map_single(struct pci_dev *hwdev, void *virtptr, +sa1111_map_single(struct pci_dev *hwdev, void *virtptr, size_t size, int direction) { dma_addr_t busptr; - /* hack; usb-ohci.c never sends hwdev==NULL, all others do */ - if (hwdev == NULL) { - consistent_sync(virtptr, size, direction); - return virt_to_bus(virtptr); - } - mapped_alloc_size += size; if (0) printk("pci_map_single(hwdev=%p,ptr=%p,size=%d,dir=%x) " @@ -235,7 +221,7 @@ * (basically return things back to the way they should be) */ void -pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, +sa1111_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size, int direction) { char *safe, *unsafe; @@ -267,13 +253,21 @@ } } -EXPORT_SYMBOL(pci_map_single); -EXPORT_SYMBOL(pci_unmap_single); +EXPORT_SYMBOL(sa1111_map_single); +EXPORT_SYMBOL(sa1111_unmap_single); -static void __init sa1111_init_safe_buffers(void) +static int __init sa1111_init_safe_buffers(void) { printk("Initializing SA1111 buffer pool for DMA workaround\n"); init_safe_buffers(NULL); + return 0; +} + +static void free_safe_buffers(void) +{ + pci_pool_destroy(small_buffer_cache); + pci_pool_destroy(large_buffer_cache); } -__initcall(sa1111_init_safe_buffers); +module_init(sa1111_init_safe_buffers); +module_exit(free_safe_buffers); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/sa1111.c linux.ac/arch/arm/mach-sa1100/sa1111.c --- linux.vanilla/arch/arm/mach-sa1100/sa1111.c Mon Aug 13 01:36:24 2001 +++ linux.ac/arch/arm/mach-sa1100/sa1111.c Wed Oct 10 01:47:29 2001 @@ -15,7 +15,6 @@ * All initialization functions provided here are intended to be called * from machine specific code with proper arguments when required. */ - #include #include #include @@ -23,8 +22,6 @@ #include #include #include -#include -#include #include #include @@ -33,64 +30,6 @@ #include "sa1111.h" -static int sa1111_ohci_hcd_init(void); - -/* - * SA1111 initialization - */ - -int __init sa1111_init(void) -{ - unsigned long id = SKID; - - if((id & SKID_ID_MASK) == SKID_SA1111_ID) - printk( KERN_INFO "SA-1111 Microprocessor Companion Chip: " - "silicon revision %lx, metal revision %lx\n", - (id & SKID_SIREV_MASK)>>4, (id & SKID_MTREV_MASK)); - else { - printk(KERN_ERR "Could not detect SA-1111!\n"); - return -EINVAL; - } - - /* - * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111: - * (SA-1110 Developer's Manual, section 9.1.2.1) - */ - GAFR |= GPIO_32_768kHz; - GPDR |= GPIO_32_768kHz; - TUCR = TUCR_3_6864MHz; - - /* Now, set up the PLL and RCLK in the SA-1111: */ - SKCR = SKCR_PLL_BYPASS | SKCR_RDYEN | SKCR_OE_EN; - udelay(100); - SKCR = SKCR_PLL_BYPASS | SKCR_RCLKEN | SKCR_RDYEN | SKCR_OE_EN; - - /* - * SA-1111 Register Access Bus should now be available. Clocks for - * any other SA-1111 functional blocks must be enabled separately - * using the SKPCR. - */ - - /* - * If the system is going to use the SA-1111 DMA engines, set up - * the memory bus request/grant pins. Also configure the shared - * memory controller on the SA-1111 (SA-1111 Developer's Manual, - * section 3.2.3) and power up the DMA bus clock: - */ - GAFR |= (GPIO_MBGNT | GPIO_MBREQ); - GPDR |= GPIO_MBGNT; - GPDR &= ~GPIO_MBREQ; - TUCR |= TUCR_MR; - -#ifdef CONFIG_USB_OHCI - /* setup up sa1111 usb host controller h/w */ - sa1111_ohci_hcd_init(); -#endif - - return 0; -} - - /* * SA1111 Interrupt support */ @@ -159,7 +98,7 @@ INTEN1 |= 1 << ((irq - SA1111_IRQ(32))); } -void __init sa1111_init_irq(int gpio_nr) +void __init sa1111_init_irq(int irq_nr) { int irq; @@ -181,144 +120,146 @@ for (irq = SA1111_IRQ(0); irq <= SA1111_IRQ(26); irq++) { irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; + irq_desc[irq].probe_ok = 0; irq_desc[irq].mask_ack = sa1111_mask_and_ack_lowirq; irq_desc[irq].mask = sa1111_mask_lowirq; irq_desc[irq].unmask = sa1111_unmask_lowirq; } for (irq = SA1111_IRQ(32); irq <= SA1111_IRQ(54); irq++) { irq_desc[irq].valid = 1; - irq_desc[irq].probe_ok = 1; + irq_desc[irq].probe_ok = 0; irq_desc[irq].mask_ack = sa1111_mask_and_ack_highirq; irq_desc[irq].mask = sa1111_mask_highirq; irq_desc[irq].unmask = sa1111_unmask_highirq; } - /* Not every machines has the SA1111 interrupt routed to a GPIO */ - if (gpio_nr >= 0) { - set_GPIO_IRQ_edge (GPIO_GPIO(gpio_nr), GPIO_RISING_EDGE); - setup_arm_irq (SA1100_GPIO_TO_IRQ(gpio_nr), &sa1111_irq); - } + /* Register SA1111 interrupt */ + if (irq_nr >= 0) + setup_arm_irq(irq_nr, &sa1111_irq); } -/* ----------------- */ - -#ifdef CONFIG_USB_OHCI - -#if defined(CONFIG_SA1100_XP860) || defined(CONFIG_ASSABET_NEPONSET) || defined(CONFIG_SA1100_PFS168) -#define PwrSensePolLow 1 -#define PwrCtrlPolLow 1 -#else -#define PwrSensePolLow 0 -#define PwrCtrlPolLow 0 -#endif - /* - * The SA-1111 errata says that the DMA hardware needs to be exercised - * before the clocks are turned on to work properly. This code does - * a tiny dma transfer to prime to hardware. + * Probe for a SA1111 chip. */ -static void __init sa1111_dma_setup(void) + +int __init sa1111_probe(void) { - dma_addr_t vbuf; - void * pbuf; + unsigned long id = SBI_SKID; + int ret = -ENODEV; - /* DMA init & setup */ + if ((id & SKID_ID_MASK) == SKID_SA1111_ID) { + printk(KERN_INFO "SA-1111 Microprocessor Companion Chip: " + "silicon revision %lx, metal revision %lx\n", + (id & SKID_SIREV_MASK)>>4, (id & SKID_MTREV_MASK)); + ret = 0; + } else { + printk(KERN_DEBUG "SA-1111 not detected: ID = %08lx\n", id); + } - /* WARNING: The SA-1111 L3 function is used as part of this - * SA-1111 DMA errata workaround. - * - * N.B., When the L3 function is enabled, it uses GPIO_B<4:5> - * and takes precedence over the PS/2 mouse and GPIO_B - * functions. Refer to "Intel StrongARM SA-1111 Microprocessor - * Companion Chip, Sect 10.2" for details. So this "fix" may - * "break" support of either PS/2 mouse or GPIO_B if - * precautions are not taken to avoid collisions in - * configuration and use of these pins. AFAIK, no precautions - * are taken at this time. So it is likely that the action - * taken here may cause problems in PS/2 mouse and/or GPIO_B - * pin use elsewhere. - * - * But wait, there's more... What we're doing here is - * obviously altogether a bad idea. We're indiscrimanately bit - * flipping config for a few different functions here which - * are "owned" by other drivers. This needs to be handled - * better than it is being done here at this time. */ - - /* prime the dma engine with a tiny dma */ - SKPCR |= SKPCR_I2SCLKEN; - SKAUD |= SKPCR_L3CLKEN | SKPCR_SCLKEN; - - SACR0 |= 0x00003305; - SACR1 = 0x00000000; - - /* we need memory below 1mb */ - pbuf = consistent_alloc(GFP_KERNEL | GFP_DMA, 4, &vbuf); + return ret; +} - SADTSA = (unsigned long)pbuf; - SADTCA = 4; +/* + * Bring the SA1111 out of reset. This requires a set procedure: + * 1. nRESET asserted (by hardware) + * 2. CLK turned on from SA1110 + * 3. nRESET deasserted + * 4. VCO turned on, PLL_BYPASS turned off + * 5. Wait lock time, then assert RCLKEn + * 7. PCR set to allow clocking of individual functions + * + * Until we've done this, the only registers we can access are: + * SBI_SKCR + * SBI_SMCR + * SBI_SKID + */ +void sa1111_wake(void) +{ + /* + * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111: + * (SA-1110 Developer's Manual, section 9.1.2.1) + */ + GAFR |= GPIO_32_768kHz; + GPDR |= GPIO_32_768kHz; + TUCR = TUCR_3_6864MHz; - SADTCS |= 0x00000011; - SKPCR |= SKPCR_DCLKEN; + /* + * Turn VCO on, and disable PLL Bypass. + */ + SBI_SKCR &= ~SKCR_VCO_OFF; + SBI_SKCR |= SKCR_PLL_BYPASS | SKCR_OE_EN; - /* wait */ + /* + * Wait lock time. SA1111 manual _doesn't_ + * specify a figure for this! We choose 100us. + */ udelay(100); - SACR0 &= ~(0x00000002); - SACR0 &= ~(0x00000001); - - /* */ - SACR0 |= 0x00000004; - SACR0 &= ~(0x00000004); + /* + * Enable RCLK. We also ensure that RDYEN is set. + */ + SBI_SKCR |= SKCR_RCLKEN | SKCR_RDYEN; - SKAUD &= ~(SKPCR_L3CLKEN | SKPCR_SCLKEN); + /* + * Wait 14 RCLK cycles for the chip to finish coming out + * of reset. (RCLK=24MHz). This is 590ns. + */ + udelay(1); - SKPCR &= ~SKPCR_I2SCLKEN; + /* + * Ensure all clocks are initially off. + */ + SKPCR = 0; +} - consistent_free(pbuf, 4, vbuf); +void sa1111_doze(void) +{ + if (SKPCR & SKPCR_UCLKEN) { + printk("SA1111 doze mode refused\n"); + return; + } + SBI_SKCR &= ~SKCR_RCLKEN; } -#ifdef CONFIG_USB_OHCI /* - * reset the SA-1111 usb controller and turn on it's clocks + * Configure the SA1111 shared memory controller. */ -static int __init sa1111_ohci_hcd_init(void) +void sa1111_configure_smc(int sdram, unsigned int drac, unsigned int cas_latency) { - volatile unsigned long *Reset = (void *)SA1111_p2v(_SA1111(0x051c)); - volatile unsigned long *Status = (void *)SA1111_p2v(_SA1111(0x0518)); - - /* turn on clocks */ - SKPCR |= SKPCR_UCLKEN; - udelay(100); - - /* force a reset */ - *Reset = 0x01; - *Reset |= 0x02; - udelay(100); + unsigned int smcr = SMCR_DTIM | SMCR_MBGE | FInsrt(drac, SMCR_DRAC); - *Reset = 0; + if (cas_latency == 3) + smcr |= SMCR_CLAT; - /* take out of reset */ - /* set power sense and control lines (this from the diags code) */ - *Reset = ( PwrSensePolLow << 6 ) - | ( PwrCtrlPolLow << 7 ); - - *Status = 0; + SBI_SMCR = smcr; +} - udelay(10); +/* + * Disable the memory bus request/grant signals on the SA1110 to + * ensure that we don't receive spurious memory requests. We set + * the MBGNT signal false to ensure the SA1111 doesn't own the + * SDRAM bus. + */ +void __init sa1110_mb_disable(void) +{ + PGSR &= ~GPIO_MBGNT; + GPCR = GPIO_MBGNT; + GPDR = (GPDR & ~GPIO_MBREQ) | GPIO_MBGNT; - /* compensate for dma bug */ - sa1111_dma_setup(); + GAFR &= ~(GPIO_MBGNT | GPIO_MBREQ); - return 0; } -void sa1111_ohci_hcd_cleanup(void) +/* + * If the system is going to use the SA-1111 DMA engines, set up + * the memory bus request/grant pins. + */ +void __init sa1110_mb_enable(void) { - /* turn the USB clock off */ - SKPCR &= ~SKPCR_UCLKEN; -} -#endif - + PGSR &= ~GPIO_MBGNT; + GPCR = GPIO_MBGNT; + GPDR = (GPDR & ~GPIO_MBREQ) | GPIO_MBGNT; -#endif /* CONFIG_USB_OHCI */ + GAFR |= (GPIO_MBGNT | GPIO_MBREQ); + TUCR |= TUCR_MR; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/sa1111.h linux.ac/arch/arm/mach-sa1100/sa1111.h --- linux.vanilla/arch/arm/mach-sa1100/sa1111.h Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mach-sa1100/sa1111.h Wed Oct 10 01:47:29 2001 @@ -2,7 +2,33 @@ * linux/arch/arm/mach-sa1100/sa1111.h */ -extern int __init sa1111_init(void); -extern void __init sa1111_init_irq(int gpio_nr); +/* + * These two don't really belong in here. + */ +extern void sa1110_mb_enable(void); +extern void sa1110_mb_disable(void); + +/* + * Probe for a SA1111 chip. + */ +extern int sa1111_probe(void); + +/* + * Wake up a SA1111 chip. + */ +extern void sa1111_wake(void); + +/* + * Doze the SA1111 chip. + */ +extern void sa1111_doze(void); + +/* + * Configure the SA1111 shared memory controller. + */ +extern void sa1111_configure_smc(int sdram, unsigned int drac, unsigned int cas_latency); + + +extern void sa1111_init_irq(int irq_nr); extern void sa1111_IRQ_demux( int irq, void *dev_id, struct pt_regs *regs ); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mach-sa1100/xp860.c linux.ac/arch/arm/mach-sa1100/xp860.c --- linux.vanilla/arch/arm/mach-sa1100/xp860.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mach-sa1100/xp860.c Wed Oct 10 01:47:29 2001 @@ -28,10 +28,27 @@ while(1); } +/* + * Note: I replaced the sa1111_init() without the full SA1111 initialisation + * because this machine doesn't appear to use the DMA features. If this is + * wrong, please look at neponset.c to fix it properly. + */ static int __init xp860_init(void) { pm_power_off = xp860_power_off; - sa1111_init(); + + /* + * Probe for SA1111. + */ + ret = sa1111_probe(); + if (ret < 0) + return ret; + + /* + * We found it. Wake the chip up. + */ + sa1111_wake(); + return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/extable.c linux.ac/arch/arm/mm/extable.c --- linux.vanilla/arch/arm/mm/extable.c Mon Sep 18 23:15:25 2000 +++ linux.ac/arch/arm/mm/extable.c Wed Oct 10 01:47:29 2001 @@ -30,6 +30,8 @@ return 0; } +extern spinlock_t modlist_lock; + unsigned long search_exception_table(unsigned long addr) { @@ -38,18 +40,24 @@ #ifndef CONFIG_MODULES /* There is only the kernel to search. */ ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); - if (ret) return ret; #else /* The kernel is the last "module" -- no need to treat it special. */ + unsigned long flags; struct module *mp; + + ret = 0; + spin_lock_irqsave(&modlist_lock, flags); for (mp = module_list; mp != NULL; mp = mp->next) { - if (mp->ex_table_start == NULL) + if (mp->ex_table_start == NULL || + !(mp->flags & (MOD_RUNNING | MOD_INITIALIZING))) continue; ret = search_one_table(mp->ex_table_start, mp->ex_table_end - 1, addr); - if (ret) return ret; + if (ret) + break; } + spin_unlock_irqrestore(&modlist_lock, flags); #endif - return 0; + return ret; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/fault-armv.c linux.ac/arch/arm/mm/fault-armv.c --- linux.vanilla/arch/arm/mm/fault-armv.c Fri Sep 7 17:28:38 2001 +++ linux.ac/arch/arm/mm/fault-armv.c Wed Oct 10 01:47:29 2001 @@ -599,10 +599,10 @@ if (!inf->fn(addr, error_code, regs)) return; bad: - force_sig(inf->sig, current); printk(KERN_ALERT "Unhandled fault: %s (%X) at 0x%08lx\n", inf->name, fsr, addr); show_pte(current->mm, addr); + force_sig(inf->sig, current); die_if_kernel("Oops", regs, 0); return; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/fault-common.c linux.ac/arch/arm/mm/fault-common.c --- linux.vanilla/arch/arm/mm/fault-common.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mm/fault-common.c Wed Oct 10 01:47:29 2001 @@ -301,6 +301,10 @@ tsk->thread.error_code = error_code; tsk->thread.trap_no = 14; force_sig(SIGBUS, tsk); +#ifdef CONFIG_DEBUG_USER + printk(KERN_DEBUG "%s: sigbus at 0x%08lx, pc=0x%08lx\n", + current->comm, addr, instruction_pointer(regs)); +#endif /* Kernel mode? Handle exceptions or die */ if (user_mode(regs)) @@ -339,13 +343,10 @@ if (addr < TASK_SIZE) return do_page_fault(addr, error_code, regs); - tsk = current; - mm = tsk->active_mm; - offset = __pgd_offset(addr); + pgd = cpu_get_pgd() + offset; pgd_k = init_mm.pgd + offset; - pgd = mm->pgd + offset; if (pgd_none(*pgd_k)) goto bad_area; @@ -365,6 +366,9 @@ return 0; bad_area: + tsk = current; + mm = tsk->active_mm; + do_bad_area(tsk, mm, addr, error_code, regs); return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/init.c linux.ac/arch/arm/mm/init.c --- linux.vanilla/arch/arm/mm/init.c Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mm/init.c Wed Oct 10 01:47:29 2001 @@ -96,10 +96,22 @@ } #endif +/* This is currently broken + * PG_skip is used on sparc/sparc64 architectures to "skip" certain + * parts of the address space. + * + * #define PG_skip 10 + * #define PageSkip(page) (machine_is_riscpc() && test_bit(PG_skip, &(page)->flags)) + * if (PageSkip(page)) { + * page = page->next_hash; + * if (page == NULL) + * break; + * } + */ void show_mem(void) { int free = 0, total = 0, reserved = 0; - int shared = 0, cached = 0, node; + int shared = 0, cached = 0, slab = 0, node; printk("Mem-info:\n"); show_free_areas(); @@ -112,23 +124,13 @@ end = page + NODE_DATA(node)->node_size; do { -/* This is currently broken - * PG_skip is used on sparc/sparc64 architectures to "skip" certain - * parts of the address space. - * - * #define PG_skip 10 - * #define PageSkip(page) (machine_is_riscpc() && test_bit(PG_skip, &(page)->flags)) - * if (PageSkip(page)) { - * page = page->next_hash; - * if (page == NULL) - * break; - * } - */ total++; if (PageReserved(page)) reserved++; else if (PageSwapCache(page)) cached++; + else if (PageSlab(page)) + slab++; else if (!page_count(page)) free++; else @@ -140,6 +142,7 @@ printk("%d pages of RAM\n", total); printk("%d free pages\n", free); printk("%d reserved pages\n", reserved); + printk("%d slab pages\n", slab); printk("%d pages shared\n", shared); printk("%d pages swap cached\n", cached); #ifndef CONFIG_NO_PGT_CACHE @@ -375,6 +378,8 @@ */ if (machine_is_archimedes() || machine_is_a5k()) reserve_bootmem_node(pgdat, 0x02000000, 0x00080000); + if (machine_is_edb7211()) + reserve_bootmem_node(pgdat, 0xc0000000, 0x00020000); if (machine_is_p720t()) reserve_bootmem_node(pgdat, PHYS_OFFSET, 0x00014000); #ifdef CONFIG_SA1111 @@ -471,6 +476,7 @@ if (map_pg != bootmap_pfn + bootmap_pages) BUG(); + } /* @@ -528,6 +534,12 @@ (bdata->node_boot_start >> PAGE_SHIFT); /* + * If this zone has zero size, skip it. + */ + if (!zone_size[0]) + continue; + + /* * For each bank in this node, calculate the size of the * holes. holes = node_size - sum(bank_sizes_in_node) */ @@ -598,8 +610,12 @@ create_memmap_holes(&meminfo); /* this will put all unused low memory onto the freelists */ - for (node = 0; node < numnodes; node++) - totalram_pages += free_all_bootmem_node(NODE_DATA(node)); + for (node = 0; node < numnodes; node++) { + pg_data_t *pgdat = NODE_DATA(node); + + if (pgdat->node_size != 0) + totalram_pages += free_all_bootmem_node(pgdat); + } #ifdef CONFIG_SA1111 /* now that our DMA memory is actually so designated, we can free it */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/mm-ftvpci.c linux.ac/arch/arm/mm/mm-ftvpci.c --- linux.vanilla/arch/arm/mm/mm-ftvpci.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/arm/mm/mm-ftvpci.c Wed Oct 10 01:47:29 2001 @@ -0,0 +1,32 @@ +/* + * linux/arch/arm/mm/mm-nexuspci.c + * from linux/arch/arm/mm/mm-ebsa110.c + * + * Copyright (C) 1998-1999 Phil Blundell + * Copyright (C) 1998-1999 Russell King + * + * Extra MM routines for the FTV/PCI architecture + */ +#include +#include +#include + +#include +#include +#include + +#include + +static struct map_desc nexuspci_io_desc[] __initdata = { + { INTCONT_BASE, INTCONT_START, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { PLX_BASE, PLX_START, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { PCIO_BASE, PLX_IO_START, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, + { DUART_BASE, DUART_START, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { STATUS_BASE, STATUS_START, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + LAST_DESC +}; + +void __init nexuspci_map_io(void) +{ + iotable_init(nexuspci_io_desc); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/mm-nexuspci.c linux.ac/arch/arm/mm/mm-nexuspci.c --- linux.vanilla/arch/arm/mm/mm-nexuspci.c Mon Sep 18 23:15:25 2000 +++ linux.ac/arch/arm/mm/mm-nexuspci.c Thu Jan 1 01:00:00 1970 @@ -1,32 +0,0 @@ -/* - * linux/arch/arm/mm/mm-nexuspci.c - * from linux/arch/arm/mm/mm-ebsa110.c - * - * Copyright (C) 1998-1999 Phil Blundell - * Copyright (C) 1998-1999 Russell King - * - * Extra MM routines for the FTV/PCI architecture - */ -#include -#include -#include - -#include -#include -#include - -#include - -static struct map_desc nexuspci_io_desc[] __initdata = { - { INTCONT_BASE, INTCONT_START, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, - { PLX_BASE, PLX_START, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, - { PCIO_BASE, PLX_IO_START, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, - { DUART_BASE, DUART_START, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, - { STATUS_BASE, STATUS_START, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, - LAST_DESC -}; - -void __init nexuspci_map_io(void) -{ - iotable_init(nexuspci_io_desc); -} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/proc-arm720.S linux.ac/arch/arm/mm/proc-arm720.S --- linux.vanilla/arch/arm/mm/proc-arm720.S Sun Aug 12 19:13:59 2001 +++ linux.ac/arch/arm/mm/proc-arm720.S Wed Oct 10 01:47:29 2001 @@ -108,6 +108,7 @@ * Function: arm720_data_abort () * * Params : r0 = address of aborted instruction + * : r3 = saved SPSR * * Purpose : obtain information about current aborted instruction * @@ -143,6 +144,8 @@ mov pc, lr ENTRY(cpu_arm720_data_abort) + tst r3, #T_BIT + bne .data_thumb_abort ldr r4, [r0] @ read instruction causing problem tst r4, r4, lsr #21 @ C = bit 20 sbc r1, r1, r1 @ r1 = C - 1 @@ -169,7 +172,7 @@ Ldata_unknown: @ Part of jumptable mov r0, r2 mov r1, r4 - mov r2, r3 + mov r2, sp bl baddataabort b ret_from_exception @@ -255,6 +258,77 @@ addeq r7, r0, r2 b Ldata_saver7 +.data_thumb_abort: + ldrh r4, [r0] @ read instruction + tst r4, r4, lsr #12 @ C = bit 11 + sbc r1, r1, r1 @ r1 = C - 1 + and r2, r4, #15 << 12 + add pc, pc, r2, lsr #10 @ lookup in table + nop + +/* 0 */ b Ldata_unknown +/* 1 */ b Ldata_unknown +/* 2 */ b Ldata_unknown +/* 3 */ b Ldata_unknown +/* 4 */ b Ldata_unknown +/* 5 */ b .data_thumb_reg +/* 6 */ b Ldata_simple +/* 7 */ b Ldata_simple +/* 8 */ b Ldata_simple +/* 9 */ b Ldata_simple +/* A */ b Ldata_unknown +/* B */ b .data_thumb_pushpop +/* C */ b .data_thumb_ldmstm +/* D */ b Ldata_unknown +/* E */ b Ldata_unknown +/* F */ b Ldata_unknown + +.data_thumb_reg: + tst r4, #1 << 9 + beq Ldata_simple + tst r4, #1 << 10 @ If 'S' (signed) bit is set + movne r1, #0 @ it must be a load instr + b Ldata_simple + +.data_thumb_pushpop: + tst r4, #1 << 10 + beq Ldata_unknown + mov r7, #0x11 + and r0, r4, r7 + and r2, r4, r7, lsl #1 + add r0, r0, r2, lsr #1 + and r2, r4, r7, lsl #2 + add r0, r0, r2, lsr #2 + and r2, r4, r7, lsl #3 + add r0, r0, r2, lsr #3 + add r0, r0, r0, lsr #4 + and r2, r4, #0x0100 @ catch 'R' bit for push/pop + add r0, r0, r2, lsr #8 + and r0, r0, #15 @ number of regs to transfer + ldr r7, [sp, #13 << 2] + tst r4, #1 << 11 + addne r7, r7, r0, lsl #2 @ increment SP if PUSH + subeq r7, r7, r0, lsr #2 @ decrement SP if POP + str r7, [sp, #13 << 2] + b Ldata_simple + +.data_thumb_ldmstm: + mov r7, #0x11 + and r0, r4, r7 + and r2, r4, r7, lsl #1 + add r0, r0, r2, lsr #1 + and r2, r4, r7, lsl #2 + add r0, r0, r2, lsr #2 + and r2, r4, r7, lsl #3 + add r0, r0, r2, lsr #3 + add r0, r0, r0, lsr #4 + and r0, r0, #15 @ number of regs to transfer + and r5, r4, #7 << 8 + ldr r7, [sp, r5, lsr #6] + sub r7, r7, r0, lsr #2 @ always decrement + str r7, [sp, r5, lsr #6] + b Ldata_simple + /* * Function: arm720_check_bugs (void) * : arm720_proc_init (void) @@ -437,7 +511,7 @@ .align /* - * See /include/asm-arm for a definition of this structure. + * See linux/include/asm-arm/procinfo.h for a definition of this structure. */ .section ".proc.info", #alloc, #execinstr @@ -450,7 +524,7 @@ b __arm720_setup @ cpu_flush .long cpu_arch_name @ arch_name .long cpu_elf_name @ elf_name - .long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT @ elf_hwcap + .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_26BIT @ elf_hwcap .long cpu_arm720_info @ info .long arm720_processor_functions .size __arm720_proc_info, . - __arm720_proc_info diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/tools/Makefile linux.ac/arch/arm/tools/Makefile --- linux.vanilla/arch/arm/tools/Makefile Fri Sep 7 17:28:38 2001 +++ linux.ac/arch/arm/tools/Makefile Wed Oct 10 01:47:29 2001 @@ -12,13 +12,15 @@ # Generate the constants.h header file using the compiler. We get # the compiler to spit out assembly code, and then mundge it into -# what we want. +# what we want. We do this in several stages so make picks up on +# any errors that occur along the way. $(TOPDIR)/include/asm-arm/constants.h: constants-hdr getconstants.c - $(CC) $(CFLAGS) -S -o - getconstants.c | \ - sed 's/^\(#define .* \)[#$$]\(.*\)/\1\2/;/^#define/!d' | \ - cat constants-hdr - > $@.tmp - cmp $@.tmp $@ >/dev/null 2>&1 || mv $@.tmp $@; $(RM) $@.tmp + $(CC) $(CFLAGS) -S -o - getconstants.c > $@.tmp.1 + sed 's/^\(#define .* \)[#$$]\(.*\)/\1\2/;/^#define/!d' $@.tmp.1 > $@.tmp.2 + cat constants-hdr $@.tmp.2 > $@.tmp + cmp $@.tmp $@ >/dev/null 2>&1 || mv $@.tmp $@ + $(RM) $@.tmp* # Build our dependencies, and then generate the constants and # mach-types header files. If we do it now, mkdep will pick diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/tools/getconstants.c linux.ac/arch/arm/tools/getconstants.c --- linux.vanilla/arch/arm/tools/getconstants.c Wed Jun 27 22:12:04 2001 +++ linux.ac/arch/arm/tools/getconstants.c Wed Oct 10 01:47:29 2001 @@ -14,8 +14,14 @@ #include #include -#ifndef __APCS_32__ -#error APCS-32 required +/* + * Make sure that the compiler and target are compatible. + */ +#if defined(__APCS_32__) && defined(CONFIG_CPU_26) +#error Your compiler targets APCS-32 but this kernel requires APCS-26 +#endif +#if defined(__APCS_26__) && defined(CONFIG_CPU_32) +#error Your compiler targets APCS-26 but this kernel requires APCS-32 #endif #define OFF_TSK(n) (unsigned long)&(((struct task_struct *)0)->n) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/tools/mach-types linux.ac/arch/arm/tools/mach-types --- linux.vanilla/arch/arm/tools/mach-types Fri Sep 7 17:28:38 2001 +++ linux.ac/arch/arm/tools/mach-types Wed Oct 10 01:47:29 2001 @@ -6,7 +6,7 @@ # To add an entry into this database, please see Documentation/arm/README, # or contact rmk@arm.linux.org.uk # -# Last update: Thu Aug 23 12:38:13 2001 +# Last update: Fri Oct 5 18:40:53 2001 # # machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number # @@ -71,7 +71,7 @@ jupiter SA1100_JUPITER JUPITER 59 psionw ARCH_PSIONW PSIONW 60 aln SA1100_ALN ALN 61 -camelot ARCH_CAMELOT CAMELOT 62 +epxa10db ARCH_CAMELOT CAMELOT 62 gds2200 SA1100_GDS2200 GDS2200 63 psion_series7 SA1100_PSION_SERIES7 PSION_SERIES7 64 xfile SA1100_XFILE XFILE 65 @@ -114,7 +114,7 @@ gator SA1100_GATOR GATOR 103 granite ARCH_GRANITE GRANITE 104 consus SA1100_CONSUS CONSUS 105 -aaec2000_aaed20 ARCH_AAEC2000_AAED2000 AAEC2000_AAED2000 106 +agilent_aaed2000 ARCH_AAEC2000_AAED2000 AAEC2000_AAED2000 106 cdb89712 ARCH_CDB89712 CDB89712 107 graphicsmaster SA1100_GRAPHICSMASTER GRAPHICSMASTER 108 adsbitsy SA1100_ADSBITSY ADSBITSY 109 @@ -122,3 +122,12 @@ plce ARCH_PLCE PLCE 111 pt_system3 SA1100_PT_SYSTEM3 PT_SYSTEM3 112 medalb ARCH_MEDALB MEDALB 113 +eagle ARCH_EAGLE EAGLE 114 +dsc21 ARCH_DSC21 DSC21 115 +dsc24 ARCH_DSC24 DSC24 116 +ti5472 ARCH_TI5472 TI5472 117 +autcpu12 ARCH_AUTCPU12 AUTCPU12 118 +uengine ARCH_UENGINE UENGINE 119 +bluestem SA1100_BLUESTEM BLUESTEM 120 +xingu8 ARCH_XINGU8 XINGU8 121 +bushstb ARCH_BUSHSTB BUSHSTB 122 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/cris/config.in linux.ac/arch/cris/config.in --- linux.vanilla/arch/cris/config.in Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/cris/config.in Wed Oct 10 01:47:29 2001 @@ -7,6 +7,7 @@ define_bool CONFIG_UID16 y define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n +define_bool CONFIG_GENERIC_BUST_SPINLOCK n mainmenu_option next_comment comment 'Code maturity level options' diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/cris/kernel/process.c linux.ac/arch/cris/kernel/process.c --- linux.vanilla/arch/cris/kernel/process.c Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/cris/kernel/process.c Wed Oct 10 01:47:30 2001 @@ -77,6 +77,7 @@ * setup. */ +static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/cris/kernel/setup.c linux.ac/arch/cris/kernel/setup.c --- linux.vanilla/arch/cris/kernel/setup.c Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/cris/kernel/setup.c Thu Oct 11 17:55:38 2001 @@ -218,15 +218,35 @@ }; /* - * BUFFER is PAGE_SIZE bytes long. + * get_cpuinfo - Get information on one CPU for use by the procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * BUFFER is PAGE_SIZE - 1K bytes long. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. */ -int get_cpuinfo(char *buffer) +int get_cpuinfo(char *buffer, unsigned *cpu_np) { int revision; + unsigned n; /* read the version register in the CPU and print some stuff */ revision = rdvr(); + + /* No SMP at the moment, so just toggle 0/1 */ + n = *cpu_np; + *cpu_np = 1; + if (n != 0) { + return (0); + } return sprintf(buffer, "cpu\t\t: CRIS\n" diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/boot/setup.S linux.ac/arch/i386/boot/setup.S --- linux.vanilla/arch/i386/boot/setup.S Sun Aug 5 21:13:19 2001 +++ linux.ac/arch/i386/boot/setup.S Wed Oct 10 01:47:30 2001 @@ -232,8 +232,8 @@ # Move rest of setup code/data to here movw $2048, %di # four sectors loaded by LILO subw %si, %si - movw %cs, %ax # aka SETUPSEG - movw %ax, %es + pushw %cs + popw %es movw $SYSSEG, %ax movw %ax, %ds rep @@ -253,6 +253,7 @@ call prtstr no_sig_loop: + hlt jmp no_sig_loop good_sig: @@ -641,18 +642,40 @@ movw %ax, %ds movw %dx, %ss end_move_self: # now we are at the right place - lidt idt_48 # load idt with 0,0 - xorl %eax, %eax # Compute gdt_base - movw %ds, %ax # (Convert %ds:gdt to a linear ptr) - shll $4, %eax - addl $gdt, %eax - movl %eax, (gdt_48+2) - lgdt gdt_48 # load gdt with whatever is - # appropriate -# that was painless, now we enable a20 +# +# Enable A20. This is at the very best an annoying procedure. +# A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin. +# + +A20_TEST_LOOPS = 32 # Iterations per wait +A20_ENABLE_LOOPS = 255 # Total loops to try + + +a20_try_loop: + + # First, see if we are on a system with no A20 gate. +a20_none: + call a20_test + jnz a20_done + + # Next, try the BIOS (INT 0x15, AX=0x2401) +a20_bios: + movw $0x2401, %ax + pushfl # Be paranoid about flags + int $0x15 + popfl + + call a20_test + jnz a20_done + + # Try enabling A20 through the keyboard controller +a20_kbc: call empty_8042 + call a20_test # Just in case the BIOS worked + jnz a20_done # but had a delayed reaction. + movb $0xD1, %al # command write outb %al, $0x64 call empty_8042 @@ -661,29 +684,62 @@ outb %al, $0x60 call empty_8042 -# -# You must preserve the other bits here. Otherwise embarrasing things -# like laptops powering off on boot happen. Corrected version by Kira -# Brown from Linux 2.2 -# - inb $0x92, %al # - orb $02, %al # "fast A20" version - outb %al, $0x92 # some chips have only this - -# wait until a20 really *is* enabled; it can take a fair amount of -# time on certain systems; Toshiba Tecras are known to have this -# problem. The memory location used here (0x200) is the int 0x80 -# vector, which should be safe to use. - - xorw %ax, %ax # segment 0x0000 - movw %ax, %fs - decw %ax # segment 0xffff (HMA) - movw %ax, %gs -a20_wait: - incw %ax # unused memory location <0xfff0 - movw %ax, %fs:(0x200) # we use the "int 0x80" vector - cmpw %gs:(0x210), %ax # and its corresponding HMA addr - je a20_wait # loop until no longer aliased + # Wait until a20 really *is* enabled; it can take a fair amount of + # time on certain systems; Toshiba Tecras are known to have this + # problem. +a20_kbc_wait: + xorw %cx, %cx +a20_kbc_wait_loop: + call a20_test + jnz a20_done + loop a20_kbc_wait_loop + + # Final attempt: use "configuration port A" +a20_fast: + inb $0x92, %al # Configuration Port A + orb $0x02, %al # "fast A20" version + andb $0xFE, %al # don't accidentally reset + outb %al, $0x92 + + # Wait for configuration port A to take effect +a20_fast_wait: + xorw %cx, %cx +a20_fast_wait_loop: + call a20_test + jnz a20_done + loop a20_fast_wait_loop + + # A20 is still not responding. Try frobbing it again. + # + decb (a20_tries) + jnz a20_try_loop + + movw $a20_err_msg, %si + call prtstr + +a20_die: + hlt + jmp a20_die + +a20_tries: + .byte A20_ENABLE_LOOPS + +a20_err_msg: + .ascii "linux: fatal error: A20 gate not responding!" + .byte 13, 10, 0 + + # If we get here, all is good +a20_done: + +# set up gdt and idt + lidt idt_48 # load idt with 0,0 + xorl %eax, %eax # Compute gdt_base + movw %ds, %ax # (Convert %ds:gdt to a linear ptr) + shll $4, %eax + addl $gdt, %eax + movl %eax, (gdt_48+2) + lgdt gdt_48 # load gdt with whatever is + # appropriate # make sure any possible coprocessor is properly reset.. xorw %ax, %ax @@ -839,6 +895,37 @@ bootsect_panic_mess: .string "INT15 refuses to access high mem, giving up." + + +# This routine tests whether or not A20 is enabled. If so, it +# exits with zf = 0. +# +# The memory address used, 0x200, is the int $0x80 vector, which +# should be safe. + +A20_TEST_ADDR = 4*0x80 + +a20_test: + pushw %cx + pushw %ax + xorw %cx, %cx + movw %cx, %fs # Low memory + decw %cx + movw %cx, %gs # High memory area + movw $A20_TEST_LOOPS, %cx + movw %fs:(A20_TEST_ADDR), %ax + pushw %ax +a20_test_wait: + incw %ax + movw %ax, %fs:(A20_TEST_ADDR) + call delay # Serialize and make delay constant + cmpw %gs:(A20_TEST_ADDR+0x10), %ax + loope a20_test_wait + + popw %fs:(A20_TEST_ADDR) + popw %ax + popw %cx + ret # This routine checks that the keyboard command queue is empty # (after emptying the output buffers) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/config.in linux.ac/arch/i386/config.in --- linux.vanilla/arch/i386/config.in Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/i386/config.in Thu Oct 11 17:57:59 2001 @@ -10,6 +10,8 @@ define_bool CONFIG_UID16 y +define_bool CONFIG_GENERIC_BUST_SPINLOCK n + mainmenu_option next_comment comment 'Code maturity level options' bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL @@ -52,6 +54,7 @@ define_int CONFIG_X86_L1_CACHE_SHIFT 4 define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n + define_bool CONFIG_X86_PPRO_FENCE y else define_bool CONFIG_X86_WP_WORKS_OK y define_bool CONFIG_X86_INVLPG y @@ -66,17 +69,20 @@ define_int CONFIG_X86_L1_CACHE_SHIFT 4 define_bool CONFIG_X86_USE_STRING_486 y define_bool CONFIG_X86_ALIGNMENT_16 y + define_bool CONFIG_X86_PPRO_FENCE y fi if [ "$CONFIG_M586" = "y" ]; then define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_USE_STRING_486 y define_bool CONFIG_X86_ALIGNMENT_16 y + define_bool CONFIG_X86_PPRO_FENCE y fi if [ "$CONFIG_M586TSC" = "y" ]; then define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_USE_STRING_486 y define_bool CONFIG_X86_ALIGNMENT_16 y define_bool CONFIG_X86_TSC y + define_bool CONFIG_X86_PPRO_FENCE y fi if [ "$CONFIG_M586MMX" = "y" ]; then define_int CONFIG_X86_L1_CACHE_SHIFT 5 @@ -84,6 +90,7 @@ define_bool CONFIG_X86_ALIGNMENT_16 y define_bool CONFIG_X86_TSC y define_bool CONFIG_X86_GOOD_APIC y + define_bool CONFIG_X86_PPRO_FENCE y fi if [ "$CONFIG_M686" = "y" ]; then define_int CONFIG_X86_L1_CACHE_SHIFT 5 @@ -91,6 +98,7 @@ define_bool CONFIG_X86_GOOD_APIC y define_bool CONFIG_X86_PGE y define_bool CONFIG_X86_USE_PPRO_CHECKSUM y + define_bool CONFIG_X86_PPRO_FENCE y fi if [ "$CONFIG_MPENTIUMIII" = "y" ]; then define_int CONFIG_X86_L1_CACHE_SHIFT 5 @@ -135,18 +143,21 @@ define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_ALIGNMENT_16 y define_bool CONFIG_X86_USE_PPRO_CHECKSUM y + define_bool CONFIG_X86_OOSTORE y fi if [ "$CONFIG_MWINCHIP2" = "y" ]; then define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_ALIGNMENT_16 y define_bool CONFIG_X86_TSC y define_bool CONFIG_X86_USE_PPRO_CHECKSUM y + define_bool CONFIG_X86_OOSTORE y fi if [ "$CONFIG_MWINCHIP3D" = "y" ]; then define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_ALIGNMENT_16 y define_bool CONFIG_X86_TSC y define_bool CONFIG_X86_USE_PPRO_CHECKSUM y + define_bool CONFIG_X86_OOSTORE y fi tristate 'Toshiba Laptop support' CONFIG_TOSHIBA @@ -315,7 +326,7 @@ source drivers/ieee1394/Config.in -source drivers/i2o/Config.in +source drivers/message/i2o/Config.in if [ "$CONFIG_NET" = "y" ]; then mainmenu_option next_comment @@ -398,6 +409,7 @@ bool 'Kernel debugging' CONFIG_DEBUG_KERNEL if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; then + bool ' Debug high memory support' CONFIG_DEBUG_HIGHMEM bool ' Debug memory allocations' CONFIG_DEBUG_SLAB bool ' Memory mapped I/O debugging' CONFIG_DEBUG_IOVIRT bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/defconfig linux.ac/arch/i386/defconfig --- linux.vanilla/arch/i386/defconfig Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/i386/defconfig Thu Oct 11 13:57:38 2001 @@ -112,6 +112,7 @@ # CONFIG_PNP=y CONFIG_ISAPNP=y +CONFIG_PNPBIOS=y # # Block devices @@ -176,7 +177,6 @@ # # CONFIG_PHONE is not set # CONFIG_PHONE_IXJ is not set -# CONFIG_PHONE_IXJ_PCMCIA is not set # # ATA/IDE/MFM/RLL support @@ -293,7 +293,6 @@ # 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 @@ -718,7 +717,6 @@ CONFIG_USB_STORAGE=y # CONFIG_USB_STORAGE_DEBUG is not set # CONFIG_USB_STORAGE_FREECOM is not set -# CONFIG_USB_STORAGE_ISD200 is not set # CONFIG_USB_STORAGE_DPCM is not set # CONFIG_USB_ACM is not set # CONFIG_USB_PRINTER is not set @@ -753,11 +751,10 @@ # # USB Network adaptors # +# CONFIG_USB_PLUSB is not set # CONFIG_USB_PEGASUS is not set # CONFIG_USB_CATC is not set -# CONFIG_USB_CDCETHER is not set -# CONFIG_USB_KAWETH is not set -# CONFIG_USB_USBNET is not set +# CONFIG_USB_NET1080 is not set # # USB port drivers @@ -777,4 +774,5 @@ # # Kernel hacking # -# CONFIG_DEBUG_KERNEL is not set +CONFIG_DEBUG_IOVIRT=y +# CONFIG_MAGIC_SYSRQ is not set diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/Makefile linux.ac/arch/i386/kernel/Makefile --- linux.vanilla/arch/i386/kernel/Makefile Tue Sep 18 07:03:09 2001 +++ linux.ac/arch/i386/kernel/Makefile Wed Oct 10 01:47:30 2001 @@ -18,7 +18,8 @@ obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \ - pci-dma.o i386_ksyms.o i387.o bluesmoke.o dmi_scan.o + pci-dma.o i386_ksyms.o i387.o bluesmoke.o dmi_scan.o \ + bootflag.o ifdef CONFIG_PCI diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/apm.c linux.ac/arch/i386/kernel/apm.c --- linux.vanilla/arch/i386/kernel/apm.c Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/i386/kernel/apm.c Thu Oct 11 10:06:02 2001 @@ -578,11 +578,18 @@ #ifdef CONFIG_APM_CPU_IDLE static int apm_do_idle(void) { - u32 dummy; + u32 eax; - if (apm_bios_call_simple(APM_FUNC_IDLE, 0, 0, &dummy)) + if (apm_bios_call_simple(APM_FUNC_IDLE, 0, 0, &eax)) + { + static unsigned long t; + if(time_after(jiffies, t+10*HZ)) + { + printk(KERN_DEBUG "apm_do_idle failed (%d)\n", (eax >> 8) & 0xff); + t = jiffies; + } return 0; - + } #ifdef ALWAYS_CALL_BUSY clock_slowed = 1; #else @@ -1159,7 +1166,12 @@ if (apm_do_idle()) { unsigned long start = jiffies; while ((!exit_kapmd) && system_idle()) { - apm_do_idle(); + if(!apm_do_idle()) + { + /* Idle request failed.. go to + sleep for a bit */ + schedule_timeout(HZ); + } if ((jiffies - start) > APM_CHECK_TIMEOUT) { apm_event_handler(); start = jiffies; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/bluesmoke.c linux.ac/arch/i386/kernel/bluesmoke.c --- linux.vanilla/arch/i386/kernel/bluesmoke.c Sat Sep 8 20:23:14 2001 +++ linux.ac/arch/i386/kernel/bluesmoke.c Wed Oct 10 01:47:30 2001 @@ -6,6 +6,8 @@ #include #include +static int mce_disabled __initdata = 0; + /* * Machine Check Handler For PII/PIII */ @@ -111,7 +113,7 @@ * Set up machine check reporting for Intel processors */ -void __init intel_mcheck_init(struct cpuinfo_x86 *c) +static void __init intel_mcheck_init(struct cpuinfo_x86 *c) { u32 l, h; int i; @@ -130,6 +132,9 @@ if(c->x86 == 5) { + /* Default P5 to off as its often misconnected */ + if(mce_disabled != -1) + return; machine_check_vector = pentium_machine_check; wmb(); /* Read registers before enabling */ @@ -137,11 +142,8 @@ rdmsr(MSR_IA32_P5_MC_TYPE, l, h); if(done==0) printk(KERN_INFO "Intel old style machine check architecture supported.\n"); - /* Enable MCE */ - __asm__ __volatile__ ( - "movl %%cr4, %%eax\n\t" - "orl $0x40, %%eax\n\t" - "movl %%eax, %%cr4\n\t" : : : "eax"); + /* Enable MCE */ + set_in_cr4(X86_CR4_MCE); printk(KERN_INFO "Intel old style machine check reporting enabled on CPU#%d.\n", smp_processor_id()); return; } @@ -195,10 +197,7 @@ lo|= (1<<2); /* Enable EIERRINT (int 18 MCE) */ lo&= ~(1<<4); /* Enable MCE */ wrmsr(MSR_IDT_FCR1, lo, hi); - __asm__ __volatile__ ( - "movl %%cr4, %%eax\n\t" - "orl $0x40, %%eax\n\t" - "movl %%eax, %%cr4\n\t" : : : "eax"); + set_in_cr4(X86_CR4_MCE); printk(KERN_INFO "Winchip machine check reporting enabled on CPU#%d.\n", smp_processor_id()); } @@ -208,11 +207,10 @@ */ -static int mce_disabled = 0; void __init mcheck_init(struct cpuinfo_x86 *c) { - if(mce_disabled) + if(mce_disabled==1) return; switch(c->x86_vendor) @@ -230,6 +228,8 @@ case X86_VENDOR_CENTAUR: winchip_mcheck_init(c); break; + default: + break; } } @@ -238,4 +238,12 @@ mce_disabled = 1; return 0; } + +static int __init mcheck_enable(char *str) +{ + mce_disabled = -1; + return 0; +} + __setup("nomce", mcheck_disable); +__setup("mce", mcheck_enable); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/bootflag.c linux.ac/arch/i386/kernel/bootflag.c --- linux.vanilla/arch/i386/kernel/bootflag.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/i386/kernel/bootflag.c Thu Oct 11 09:43:00 2001 @@ -0,0 +1,250 @@ +/* + * Implement 'Simple Boot Flag Specification 1.0' + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#define SBF_RESERVED (0x78) +#define SBF_PNPOS (1<<0) +#define SBF_BOOTING (1<<1) +#define SBF_DIAG (1<<2) +#define SBF_PARITY (1<<7) + + +struct sbf_boot +{ + u8 sbf_signature[4]; + u32 sbf_len; + u8 sbf_revision __attribute((packed)); + u8 sbf_csum __attribute((packed)); + u8 sbf_oemid[6] __attribute((packed)); + u8 sbf_oemtable[8] __attribute((packed)); + u8 sbf_revdata[4] __attribute((packed)); + u8 sbf_creator[4] __attribute((packed)); + u8 sbf_crearev[4] __attribute((packed)); + u8 sbf_cmos __attribute((packed)); + u8 sbf_spare[3] __attribute((packed)); +}; + + +static int sbf_port __initdata = -1; + +static int __init sbf_struct_valid(unsigned long tptr) +{ + u8 *ap; + u8 v; + unsigned int i; + struct sbf_boot sb; + + memcpy_fromio(&sb, tptr, sizeof(sb)); + + if(sb.sbf_len != 40 && sb.sbf_len != 39) + // 39 on IBM ThinkPad A21m, BIOS version 1.02b (KXET24WW; 2000-12-19). + return 0; + + ap = (u8 *)&sb; + v= 0; + + for(i=0;i>=1; + } + return x; +} + +static void __init sbf_write(u8 v) +{ + unsigned long flags; + if(sbf_port != -1) + { + v &= ~SBF_PARITY; + if(!parity(v)) + v|=SBF_PARITY; + + printk(KERN_INFO "SBF: Setting boot flags 0x%x\n",v); + + spin_lock_irqsave(&rtc_lock, flags); + CMOS_WRITE(v, sbf_port); + spin_unlock_irqrestore(&rtc_lock, flags); + } +} + +static u8 __init sbf_read(void) +{ + u8 v; + unsigned long flags; + if(sbf_port == -1) + return 0; + spin_lock_irqsave(&rtc_lock, flags); + v = CMOS_READ(sbf_port); + spin_unlock_irqrestore(&rtc_lock, flags); + return v; +} + +static int __init sbf_value_valid(u8 v) +{ + if(v&SBF_RESERVED) /* Reserved bits */ + return 0; + if(!parity(v)) + return 0; + return 1; +} + + +static void __init sbf_bootup(void) +{ + u8 v = sbf_read(); + if(!sbf_value_valid(v)) + printk(KERN_WARNING "SBF: Simple boot flag value 0x%x read from CMOS RAM was invalid\n",v); + v &= ~SBF_RESERVED; + v &= ~SBF_BOOTING; + v &= ~SBF_DIAG; +#if defined(CONFIG_PNPBIOS) + v |= SBF_PNPOS; +#endif + sbf_write(v); +} + +static int __init sbf_init(void) +{ + unsigned int i; + void *rsdt; + u32 rsdtlen = 0; + u32 rsdtbase = 0; + u8 sum = 0; + int n; + + u8 *p; + + for(i=0xE0000; i <= 0xFFFE0; i+=16) + { + p = phys_to_virt(i); + + if(memcmp(p, "RSD PTR ", 8)) + continue; + + sum = 0; + for(n=0; n<20; n++) + sum+=p[n]; + + if(sum != 0) + continue; + + /* So it says RSD PTR and it checksums... */ + + /* + * Process the RDSP pointer + */ + + rsdtbase = *(u32 *)(p+16); + + /* + * RSDT length is ACPI 2 only, for ACPI 1 we must map + * and remap. + */ + + if(p[15]>1) + rsdtlen = *(u32 *)(p+20); + else + rsdtlen = 36; + + if(rsdtlen < 36 || rsdtlen > 1024) + continue; + break; + } + if(i>0xFFFE0) + return 0; + + + rsdt = ioremap(rsdtbase, rsdtlen); + if(rsdt == 0) + return 0; + + i = readl(rsdt + 4); + + /* + * Remap if needed + */ + + if(i > rsdtlen) + { + rsdtlen = i; + iounmap(rsdt); + rsdt = ioremap(rsdtbase, rsdtlen); + if(rsdt == 0) + return 0; + } + + for(n = 0; n < i; n++) + sum += readb(rsdt + n); + + if(sum) + { + iounmap(rsdt); + return 0; + } + + /* Ok the RSDT checksums too */ + + for(n = 36; n+3 < i; n += 4) + { + unsigned long rp = readl(rsdt+n); + int len = 4096; + + if(rp > 0xFFFFFFFFUL - len) + len = 0xFFFFFFFFUL - rp; + + /* Too close to the end!! */ + if(len < 20) + continue; + rp = (unsigned long)ioremap(rp, 4096); + if(rp == 0) + continue; + if(sbf_struct_valid(rp)) + { + /* Found the BOOT table and processed it */ + printk(KERN_INFO "SBF: Simple Boot Flag extension found and enabled.\n"); + } + iounmap((void *)rp); + } + iounmap(rsdt); + sbf_bootup(); + return 0; +} + +module_init(sbf_init); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/cpuid.c linux.ac/arch/i386/kernel/cpuid.c --- linux.vanilla/arch/i386/kernel/cpuid.c Thu Jul 26 21:37:38 2001 +++ linux.ac/arch/i386/kernel/cpuid.c Wed Oct 10 01:47:30 2001 @@ -163,3 +163,4 @@ MODULE_AUTHOR("H. Peter Anvin "); MODULE_DESCRIPTION("x86 generic CPUID driver"); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/dmi_scan.c linux.ac/arch/i386/kernel/dmi_scan.c --- linux.vanilla/arch/i386/kernel/dmi_scan.c Tue Sep 18 06:52:35 2001 +++ linux.ac/arch/i386/kernel/dmi_scan.c Thu Oct 11 09:28:56 2001 @@ -7,8 +7,10 @@ #include #include #include -#include #include +#include + +unsigned long dmi_broken; struct dmi_header { @@ -85,7 +87,7 @@ } -int __init dmi_iterate(void (*decode)(struct dmi_header *)) +static int __init dmi_iterate(void (*decode)(struct dmi_header *)) { unsigned char buf[20]; long fp=0xE0000L; @@ -313,14 +315,12 @@ return 0; } -#if defined(CONFIG_SONYPI) || defined(CONFIG_SONYPI_MODULE) /* - * Check for a Sony Vaio system in order to enable the use of - * the sonypi driver (we don't want this driver to be used on - * other systems, even if they have the good PCI IDs). + * Check for a Sony Vaio system * - * This one isn't a bug detect for those who asked, we simply want to - * activate Sony specific goodies like the camera and jogdial.. + * On a Sony system we want to enable the use of the sonypi + * driver for Sony-specific goodies like the camera and jogdial. + * We also want to avoid using certain functions of the PnP BIOS. */ int is_sony_vaio_laptop; @@ -333,7 +333,6 @@ } return 0; } -#endif /* * This bios swaps the APM minute reporting bytes over (Many sony laptops @@ -365,13 +364,41 @@ } /* - * Some Bioses enable the PS/2 mouse (touchpad) at resume, even if it - * was disabled before the suspend. Linux gets terribly confused by that. + * ASUS K7V-RM has broken ACPI table defining sleep modes */ -typedef void (pm_kbd_func) (void); -extern pm_kbd_func *pm_kbd_request_override; +static __init int broken_acpi_Sx(struct dmi_blacklist *d) +{ + printk(KERN_WARNING "Detected ASUS mainboard with broken ACPI sleep table\n"); + dmi_broken |= BROKEN_ACPI_Sx; + return 0; +} + +/* + * Toshiba keyboard likes to repeat keys when they are not repeated. + */ +static __init int broken_toshiba_keyboard(struct dmi_blacklist *d) +{ + printk(KERN_WARNING "Toshiba with broken keyboard detected. If your keyboard sometimes generates 3 keypresses instead of one, contact pavel@ucw.cz\n"); + return 0; +} + +/* + * Toshiba fails to preserve interrupts over S1 + */ + +static __init int init_ints_after_s1(struct dmi_blacklist *d) +{ + printk(KERN_WARNING "Toshiba with broken S1 detected.\n"); + dmi_broken |= BROKEN_INIT_AFTER_S1; + return 0; +} + +/* + * Some Bioses enable the PS/2 mouse (touchpad) at resume, even if it + * was disabled before the suspend. Linux gets terribly confused by that. + */ static __init int broken_ps2_resume(struct dmi_blacklist *d) { #ifdef CONFIG_VT @@ -380,12 +407,11 @@ pm_kbd_request_override = pckbd_pm_resume; printk(KERN_INFO "%s machine detected. Mousepad Resume Bug workaround enabled.\n", d->ident); } -#endif +#endif return 0; } - /* * Process the DMI blacklists */ @@ -459,13 +485,11 @@ MATCH(DMI_BIOS_VENDOR,"SystemSoft"), MATCH(DMI_BIOS_VERSION,"Version R2.08") } }, -#if defined(CONFIG_SONYPI) || defined(CONFIG_SONYPI_MODULE) { sony_vaio_laptop, "Sony Vaio", { /* This is a Sony Vaio laptop */ MATCH(DMI_SYS_VENDOR, "Sony Corporation"), MATCH(DMI_PRODUCT_NAME, "PCG-"), NO_MATCH, NO_MATCH, } }, -#endif { swab_apm_power_in_minutes, "Sony VAIO", { /* Handle problems with APM on Sony Vaio PCG-N505X(DE) */ MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"), MATCH(DMI_BIOS_VERSION, "R0206H"), @@ -543,7 +567,19 @@ MATCH(DMI_PRODUCT_NAME, "Dell PowerEdge 8450"), NO_MATCH, NO_MATCH, NO_MATCH } }, - + { broken_acpi_Sx, "ASUS K7V-RM", { /* Bad ACPI Sx table */ + MATCH(DMI_BIOS_VERSION,"ASUS K7V-RM ACPI BIOS Revision 1003A"), + MATCH(DMI_BOARD_NAME, ""), + NO_MATCH, NO_MATCH + } }, + { broken_toshiba_keyboard, "Toshiba Satellite 4030cdt", { /* Keyboard generates spurious repeats */ + MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"), + NO_MATCH, NO_MATCH, NO_MATCH + } }, + { init_ints_after_s1, "Toshiba Satellite 4030cdt", { /* Reinitialization of 8259 is needed after S1 resume */ + MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"), + NO_MATCH, NO_MATCH, NO_MATCH + } }, { NULL, } }; @@ -646,12 +682,10 @@ } } -static int __init dmi_scan_machine(void) +int __init dmi_scan_machine(void) { int err = dmi_iterate(dmi_decode); if(err == 0) dmi_check_blacklist(); return err; } - -module_init(dmi_scan_machine); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/entry.S linux.ac/arch/i386/kernel/entry.S --- linux.vanilla/arch/i386/kernel/entry.S Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/i386/kernel/entry.S Thu Oct 11 13:58:02 2001 @@ -105,10 +105,14 @@ popl %edi; \ popl %ebp; \ popl %eax; \ + cmpl $__KERNEL_DS,(%esp); \ + je 4f ; \ 1: popl %ds; \ 2: popl %es; \ addl $4,%esp; \ 3: iret; \ +4: addl $12,%esp; \ + iret; \ .section .fixup,"ax"; \ 4: movl $0,(%esp); \ jmp 1b; \ @@ -284,9 +288,11 @@ pushl %esi # push the error code pushl %edx # push the pt_regs pointer movl $(__KERNEL_DS),%edx + cmpl %edx,%ecx + jz 1f movl %edx,%ds movl %edx,%es - GET_CURRENT(%ebx) +1: GET_CURRENT(%ebx) call *%edi addl $8,%esp jmp ret_from_exception diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/head.S linux.ac/arch/i386/kernel/head.S --- linux.vanilla/arch/i386/kernel/head.S Wed Jun 20 19:00:53 2001 +++ linux.ac/arch/i386/kernel/head.S Wed Oct 10 01:47:30 2001 @@ -429,14 +429,15 @@ * change anything. */ ENTRY(gdt_table) - .quad 0x0000000000000000 /* NULL descriptor */ - .quad 0x0000000000000000 /* not used */ + .quad 0x0000000000000000 /* 0x00 NULL descriptor */ + .quad 0x0000000000000000 /* 0x08 not used */ .quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */ .quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */ .quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */ .quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */ - .quad 0x0000000000000000 /* not used */ - .quad 0x0000000000000000 /* not used */ + .quad 0x0000000000000000 /* 0x30 not used */ + .quad 0x0000000000000000 /* 0x38 not used */ + /* * The APM segments have byte granularity and their bases * and limits are set at run time. @@ -445,7 +446,19 @@ .quad 0x00409a0000000000 /* 0x48 APM CS code */ .quad 0x00009a0000000000 /* 0x50 APM CS 16 code (16 bit) */ .quad 0x0040920000000000 /* 0x58 APM DS data */ - .fill NR_CPUS*4,8,0 /* space for TSS's and LDT's */ + + /* PNPBIOS segments */ + .quad 0x00c09a0000000000 /* 0x60 32-bit code */ + .quad 0x00809a0000000000 /* 0x68 16-bit code */ + .quad 0x0080920000000000 /* 0x70 16-bit data */ + .quad 0x0080920000000000 /* 0x78 16-bit data */ + .quad 0x0080920000000000 /* 0x80 16-bit data */ + .quad 0x0000000000000000 /* 0x88 not used */ + .quad 0x0000000000000000 /* 0x90 not used */ + .quad 0x0000000000000000 /* 0x98 not used */ + + /* Per CPU segments */ + .fill NR_CPUS*4,8,0 /* 0xa0 space for TSS's and LDT's */ /* * This is to aid debugging, the various locking macros will be putting diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/i386_ksyms.c linux.ac/arch/i386/kernel/i386_ksyms.c --- linux.vanilla/arch/i386/kernel/i386_ksyms.c Tue Sep 18 06:52:35 2001 +++ linux.ac/arch/i386/kernel/i386_ksyms.c Wed Oct 10 01:47:30 2001 @@ -28,6 +28,8 @@ #include #include #include +#include +#include extern void dump_thread(struct pt_regs *, struct user *); extern spinlock_t rtc_lock; @@ -170,8 +172,5 @@ EXPORT_SYMBOL(do_BUG); #endif -#if defined(CONFIG_SONYPI) || defined(CONFIG_SONYPI_MODULE) -extern int is_sony_vaio_laptop; EXPORT_SYMBOL(is_sony_vaio_laptop); -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/init_task.c linux.ac/arch/i386/kernel/init_task.c --- linux.vanilla/arch/i386/kernel/init_task.c Mon Sep 17 23:29:09 2001 +++ linux.ac/arch/i386/kernel/init_task.c Wed Oct 10 01:47:30 2001 @@ -6,6 +6,7 @@ #include #include +static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/io_apic.c linux.ac/arch/i386/kernel/io_apic.c --- linux.vanilla/arch/i386/kernel/io_apic.c Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/i386/kernel/io_apic.c Thu Oct 11 13:58:09 2001 @@ -750,6 +750,7 @@ ) UNEXPECTED_IO_APIC(); + printk(KERN_DEBUG "....... : PRQ implemented: %X\n", reg_01.PRQ); printk(KERN_DEBUG "....... : IO APIC version: %04X\n", reg_01.version); if ( (reg_01.version != 0x01) && /* 82489DX IO-APICs */ (reg_01.version != 0x10) && /* oldest IO-APICs */ @@ -1237,14 +1238,17 @@ ack_APIC_irq(); if (!(v & (1 << (i & 0x1f)))) { +#ifdef APIC_LOCKUP_DEBUG + struct irq_pin_list *entry; +#endif + #ifdef APIC_MISMATCH_DEBUG atomic_inc(&irq_mis_count); #endif spin_lock(&ioapic_lock); __mask_and_edge_IO_APIC_irq(irq); #ifdef APIC_LOCKUP_DEBUG - for (;;) { - struct irq_pin_list *entry = irq_2_pin + irq; + for (entry = irq_2_pin + irq;;) { unsigned int reg; if (entry->pin == -1) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/microcode.c linux.ac/arch/i386/kernel/microcode.c --- linux.vanilla/arch/i386/kernel/microcode.c Fri Sep 14 22:04:08 2001 +++ linux.ac/arch/i386/kernel/microcode.c Wed Oct 10 01:47:30 2001 @@ -65,6 +65,7 @@ MODULE_DESCRIPTION("Intel CPU (IA-32) microcode update driver"); MODULE_AUTHOR("Tigran Aivazian "); +MODULE_LICENSE("GPL"); EXPORT_NO_SYMBOLS; #define MICRO_DEBUG 0 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/msr.c linux.ac/arch/i386/kernel/msr.c --- linux.vanilla/arch/i386/kernel/msr.c Thu Jul 26 21:37:38 2001 +++ linux.ac/arch/i386/kernel/msr.c Wed Oct 10 01:47:30 2001 @@ -271,3 +271,4 @@ MODULE_AUTHOR("H. Peter Anvin "); MODULE_DESCRIPTION("x86 generic MSR driver"); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/mtrr.c linux.ac/arch/i386/kernel/mtrr.c --- linux.vanilla/arch/i386/kernel/mtrr.c Fri Sep 21 18:55:22 2001 +++ linux.ac/arch/i386/kernel/mtrr.c Wed Oct 10 01:47:30 2001 @@ -1253,7 +1253,8 @@ break; case MTRR_IF_INTEL: - /* For Intel PPro stepping <= 7, must be 4 MiB aligned */ + /* For Intel PPro stepping <= 7, must be 4 MiB aligned + and not touch 0x70000000->0x7003FFFF */ if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 1 && @@ -1263,6 +1264,12 @@ { printk (KERN_WARNING "mtrr: base(0x%lx000) is not 4 MiB aligned\n", base); return -EINVAL; + } + if (!(base + size < 0x70000000 || base > 0x7003FFFF) && + (type == MTRR_TYPE_WRCOMB || type == MTRR_TYPE_WRBACK)) + { + printk (KERN_WARNING "mtrr: writable mtrr between 0x70000000 and 0x7003FFFF may hang the CPU.\n"); + return -EINVAL; } } /* Fall through */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/pci-irq.c linux.ac/arch/i386/kernel/pci-irq.c --- linux.vanilla/arch/i386/kernel/pci-irq.c Wed Jul 4 23:39:28 2001 +++ linux.ac/arch/i386/kernel/pci-irq.c Wed Oct 10 01:47:30 2001 @@ -15,6 +15,7 @@ #include #include +#include #include #include "pci-i386.h" @@ -222,15 +223,18 @@ /* * Cyrix: nibble offset 0x5C + * 0x5C bits 7:4 is INTB bits 3:0 is INTA + * 0x5D bits 7:4 is INTD bits 3:0 is INTC */ + static int pirq_cyrix_get(struct pci_dev *router, struct pci_dev *dev, int pirq) { - return read_config_nybble(router, 0x5C, pirq-1); + return read_config_nybble(router, 0x5C, pirq^1); } static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) { - write_config_nybble(router, 0x5C, pirq-1, irq); + write_config_nybble(router, 0x5C, pirq^1, irq); return 1; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/setup.c linux.ac/arch/i386/kernel/setup.c --- linux.vanilla/arch/i386/kernel/setup.c Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/i386/kernel/setup.c Thu Oct 11 16:55:40 2001 @@ -229,8 +229,7 @@ #define SIO_PM_GP_EN 0x80 -static void -visws_get_board_type_and_rev(void) +static void __init visws_get_board_type_and_rev(void) { int raw; @@ -402,7 +401,7 @@ } } -void __init add_memory_region(unsigned long long start, +static void __init add_memory_region(unsigned long long start, unsigned long long size, int type) { int x = e820.nr_map; @@ -667,7 +666,7 @@ */ #define LOWMEMSIZE() (0x9f000) -void __init setup_memory_region(void) +static void __init setup_memory_region(void) { char *who = "BIOS-e820"; @@ -699,7 +698,7 @@ } /* setup_memory_region */ -static inline void parse_mem_cmdline (char ** cmdline_p) +static void __init parse_mem_cmdline (char ** cmdline_p) { char c = ' ', *to = command_line, *from = COMMAND_LINE; int len = 0; @@ -1048,6 +1047,32 @@ __setup("notsc", tsc_setup); #endif + +static void __init interpret_eblcr(struct cpuinfo_x86 *c, u32 lo, int coding) +{ + static unsigned int __initdata mult[32] = { 10, 6, 8, 0, 11, 7, 9, 0, + 10, 14, 16, 12, 0, 15, 0, 13 , + 0, 0, 0, 0, 0, 0, 17, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; + int mul = (lo>>22)&15; + + /* The mobile pIII added bit 27. This is zero on other intel and + on the cyrix III */ + + if(lo&(1>>27)) + mul+=16; + + c->clockmul = mult[mul]; + + if (mult[mul]==0) + { + printk(KERN_INFO "Unknown CPU/BUS multiplier (%d, %x).\n", + mul, lo); + return; + } +} + + static int __init get_model_name(struct cpuinfo_x86 *c) { unsigned int *v; @@ -1252,7 +1277,6 @@ break; case 6: /* An Athlon/Duron. We can trust the BIOS probably */ - mcheck_init(c); break; } @@ -1263,11 +1287,11 @@ /* * Read Cyrix DEVID registers (DIR) to get more detailed info. about the CPU */ -static void do_cyrix_devid(unsigned char *dir0, unsigned char *dir1) +static void __init do_cyrix_devid(unsigned char *dir0, unsigned char *dir1) { unsigned char ccr2, ccr3; unsigned long flags; - + /* we test for DEVID by checking whether CCR3 is writable */ local_irq_save(flags); ccr3 = getCx86(CX86_CCR3); @@ -1303,7 +1327,7 @@ * Actually since bugs.h doesnt even reference this perhaps someone should * fix the documentation ??? */ -unsigned char Cx86_dir0_msb __initdata = 0; +static unsigned char Cx86_dir0_msb __initdata = 0; static char Cx86_model[][9] __initdata = { "Cx486", "Cx486", "5x86 ", "6x86", "MediaGX ", "6x86MX ", @@ -1336,7 +1360,7 @@ static void __init check_cx686_slop(struct cpuinfo_x86 *c) { unsigned long flags; - + if (Cx86_dir0_msb == 3) { unsigned char ccr3, ccr5; @@ -1503,6 +1527,248 @@ return; } +#ifdef CONFIG_X86_OOSTORE + +static u32 __init power2(u32 x) +{ + u32 s=1; + while(s<=x) + s<<=1; + return s>>=1; +} + +/* + * Set up an actual MCR + */ + +static void __init winchip_mcr_insert(int reg, u32 base, u32 size, int key) +{ + u32 lo, hi; + + hi = base & ~0xFFF; + lo = ~(size-1); /* Size is a power of 2 so this makes a mask */ + lo &= ~0xFFF; /* Remove the ctrl value bits */ + lo |= key; /* Attribute we wish to set */ + wrmsr(reg+MSR_IDT_MCR0, lo, hi); + mtrr_centaur_report_mcr(reg, lo, hi); /* Tell the mtrr driver */ +} + +/* + * Figure what we can cover with MCR's + * + * Shortcut: We know you can't put 4Gig of RAM on a winchip + */ + +static u32 __init ramtop(void) /* 16388 */ +{ + int i; + u32 top = 0; + u32 clip = 0xFFFFFFFFUL; + + for (i = 0; i < e820.nr_map; i++) { + unsigned long start, end; + + if (e820.map[i].addr > 0xFFFFFFFFUL) + continue; + /* + * Don't MCR over reserved space. Ignore the ISA hole + * we frob around that catastrophy already + */ + + if (e820.map[i].type == E820_RESERVED) + { + if(e820.map[i].addr >= 0x100000UL && e820.map[i].addr < clip) + clip = e820.map[i].addr; + continue; + } + start = e820.map[i].addr; + end = e820.map[i].addr + e820.map[i].size; + if (start >= end) + continue; + if (end > top) + top = end; + } + /* Everything below 'top' should be RAM except for the ISA hole. + Because of the limited MCR's we want to map NV/ACPI into our + MCR range for gunk in RAM + + Clip might cause us to MCR insufficient RAM but that is an + acceptable failure mode and should only bite obscure boxes with + a VESA hole at 15Mb + + The second case Clip sometimes kicks in is when the EBDA is marked + as reserved. Again we fail safe with reasonable results + */ + + if(top>clip) + top=clip; + + return top; +} + +/* + * Compute a set of MCR's to give maximum coverage + */ + +static int __init winchip_mcr_compute(int nr, int key) +{ + u32 mem = ramtop(); + u32 root = power2(mem); + u32 base = root; + u32 top = root; + u32 floor = 0; + int ct = 0; + + while(ct high && fspace > low) + { + winchip_mcr_insert(ct, floor, fspace, key); + floor += fspace; + } + else if(high > low) + { + winchip_mcr_insert(ct, top, high, key); + top += high; + } + else if(low > 0) + { + base -= low; + winchip_mcr_insert(ct, base, low, key); + } + else break; + ct++; + } + /* + * We loaded ct values. We now need to set the mask. The caller + * must do this bit. + */ + + return ct; +} + +static void __init winchip_create_optimal_mcr(void) +{ + int i; + /* + * Allocate up to 6 mcrs to mark as much of ram as possible + * as write combining and weak write ordered. + * + * To experiment with: Linux never uses stack operations for + * mmio spaces so we could globally enable stack operation wc + * + * Load the registers with type 31 - full write combining, all + * writes weakly ordered. + */ + int used = winchip_mcr_compute(6, 31); + + /* + * Wipe unused MCRs + */ + + for(i=used;i<8;i++) + wrmsr(MSR_IDT_MCR0+i, 0, 0); +} + +static void __init winchip2_create_optimal_mcr(void) +{ + u32 lo, hi; + int i; + + /* + * Allocate up to 6 mcrs to mark as much of ram as possible + * as write combining, weak store ordered. + * + * Load the registers with type 25 + * 8 - weak write ordering + * 16 - weak read ordering + * 1 - write combining + */ + + int used = winchip_mcr_compute(6, 25); + + /* + * Mark the registers we are using. + */ + + rdmsr(MSR_IDT_MCR_CTRL, lo, hi); + for(i=0;i>17) & 7; + lo |= key<<6; /* replace with unlock key */ + wrmsr(MSR_IDT_MCR_CTRL, lo, hi); +} + +static void __init winchip2_protect_mcr(void) +{ + u32 lo, hi; + + rdmsr(MSR_IDT_MCR_CTRL, lo, hi); + lo&=~0x1C0; /* blank bits 8-6 */ + wrmsr(MSR_IDT_MCR_CTRL, lo, hi); +} + +#endif + static void __init init_centaur(struct cpuinfo_x86 *c) { enum { @@ -1546,6 +1812,19 @@ fcr_clr=DPDC; printk(KERN_NOTICE "Disabling bugged TSC.\n"); clear_bit(X86_FEATURE_TSC, &c->x86_capability); +#ifdef CONFIG_X86_OOSTORE + winchip_create_optimal_mcr(); + /* Enable + write combining on non-stack, non-string + write combining on string, all types + weak write ordering + + The C6 original lacks weak read order + + Note 0x120 is write only on Winchip 1 */ + + wrmsr(MSR_IDT_MCR_CTRL, 0x01F0001F, 0); +#endif break; case 8: switch(c->x86_mask) { @@ -1561,11 +1840,37 @@ } fcr_set=ECX8|DSMC|DTLOCK|EMMX|EBRPRED|ERETSTK|E2MMX|EAMD3D; fcr_clr=DPDC; +#ifdef CONFIG_X86_OOSTORE + winchip2_unprotect_mcr(); + winchip2_create_optimal_mcr(); + rdmsr(MSR_IDT_MCR_CTRL, lo, hi); + /* Enable + write combining on non-stack, non-string + write combining on string, all types + weak write ordering + */ + lo|=31; + wrmsr(MSR_IDT_MCR_CTRL, lo, hi); + winchip2_protect_mcr(); +#endif break; case 9: name="3"; fcr_set=ECX8|DSMC|DTLOCK|EMMX|EBRPRED|ERETSTK|E2MMX|EAMD3D; fcr_clr=DPDC; +#ifdef CONFIG_X86_OOSTORE + winchip2_unprotect_mcr(); + winchip2_create_optimal_mcr(); + rdmsr(MSR_IDT_MCR_CTRL, lo, hi); + /* Enable + write combining on non-stack, non-string + write combining on string, all types + weak write ordering + */ + lo|=31; + wrmsr(MSR_IDT_MCR_CTRL, lo, hi); + winchip2_protect_mcr(); +#endif break; case 10: name="4"; @@ -1599,7 +1904,6 @@ c->x86_cache_size = (cc>>24)+(dd>>24); } sprintf( c->x86_model_id, "WinChip %s", name ); - mcheck_init(c); break; case 6: @@ -1614,6 +1918,9 @@ get_model_name(c); display_cacheinfo(c); + + rdmsr(MSR_IA32_EBL_CR_POWERON, lo, hi); + interpret_eblcr(c, lo, 0); break; } break; @@ -1857,6 +2164,13 @@ if ( c->x86 == 6 && c->x86_model < 3 && c->x86_mask < 3 ) clear_bit(X86_FEATURE_SEP, &c->x86_capability); + if ( c->x86 == 6 && c->x86_model >= 3) + { + u32 lo, hi; + rdmsr(MSR_IA32_EBL_CR_POWERON, lo, hi); + interpret_eblcr(c, lo, (c->x86_model >= 7) ? 1: 0); + } + /* Names for the Pentium II/Celeron processors detectable only by also checking the cache size. Dixon is NOT a Celeron. */ @@ -1885,7 +2199,6 @@ strcpy(c->x86_model_id, p); /* Enable MCA if available */ - mcheck_init(c); } void __init get_cpu_vendor(struct cpuinfo_x86 *c) @@ -2021,14 +2334,14 @@ } -int __init x86_serial_nr_setup(char *s) +static int __init x86_serial_nr_setup(char *s) { disable_x86_serial_nr = 0; return 1; } __setup("serialnumber", x86_serial_nr_setup); -int __init x86_fxsr_setup(char * s) +static int __init x86_fxsr_setup(char * s) { disable_x86_fxsr = 1; return 1; @@ -2123,7 +2436,6 @@ { unsigned char ccr3, ccr4; unsigned long flags; - printk(KERN_INFO "Enabling CPUID on Cyrix processor.\n"); local_irq_save(flags); ccr3 = getCx86(CX86_CCR3); @@ -2266,7 +2578,7 @@ init_rise(c); break; } - + printk(KERN_DEBUG "CPU: After vendor init, caps: %08x %08x %08x %08x\n", c->x86_capability[0], c->x86_capability[1], @@ -2293,6 +2605,9 @@ /* Disable the PN if appropriate */ squash_the_stupid_serial_number(c); + /* Init Machine Check Exception if available. */ + mcheck_init(c); + /* If the model name is still unset, do table lookup. */ if ( !c->x86_model_id[0] ) { char *p; @@ -2373,12 +2688,25 @@ } /* - * Get CPU information for use by the procfs. + * get_cpuinfo - Get information on one CPU for use by the procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. */ -int get_cpuinfo(char * buffer) +int get_cpuinfo(char *buffer, unsigned *cpu_np) { char *p = buffer; + struct cpuinfo_x86 *c; + unsigned n; + int i, fpu_exception; /* * These flag bits must match the definitions in . @@ -2413,74 +2741,73 @@ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }; - struct cpuinfo_x86 *c = cpu_data; - int i, n; - for (n = 0; n < NR_CPUS; n++, c++) { - int fpu_exception; -#ifdef CONFIG_SMP - if (!(cpu_online_map & (1< (3*PAGE_SIZE)/4) - break; + n = *cpu_np; + while (n < NR_CPUS && (cpu_online_map & (1 << n)) == 0) { + ++n; + } + if (n >= NR_CPUS) { + *cpu_np = NR_CPUS; + return (0); + } + *cpu_np = n + 1; + c = cpu_data + n; + + p += sprintf(p, "processor\t: %d\n" + "vendor_id\t: %s\n" + "cpu family\t: %d\n" + "model\t\t: %d\n" + "model name\t: %s\n", + n, + c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown", + c->x86, + c->x86_model, + c->x86_model_id[0] ? c->x86_model_id : "unknown"); - p += sprintf(p,"processor\t: %d\n" - "vendor_id\t: %s\n" - "cpu family\t: %d\n" - "model\t\t: %d\n" - "model name\t: %s\n", - n, - c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown", - c->x86, - c->x86_model, - c->x86_model_id[0] ? c->x86_model_id : "unknown"); + if (c->x86_mask || c->cpuid_level >= 0) + p += sprintf(p, "stepping\t: %d\n", c->x86_mask); + else + p += sprintf(p, "stepping\t: unknown\n"); - if (c->x86_mask || c->cpuid_level >= 0) - p += sprintf(p, "stepping\t: %d\n", c->x86_mask); - else - p += sprintf(p, "stepping\t: unknown\n"); + if ( test_bit(X86_FEATURE_TSC, &c->x86_capability) ) { + p += sprintf(p, "cpu MHz\t\t: %lu.%03lu\n", + cpu_khz / 1000, (cpu_khz % 1000)); + } - if ( test_bit(X86_FEATURE_TSC, &c->x86_capability) ) { - p += sprintf(p, "cpu MHz\t\t: %lu.%03lu\n", - cpu_khz / 1000, (cpu_khz % 1000)); - } + /* Cache size */ + if (c->x86_cache_size >= 0) + p += sprintf(p, "cache size\t: %d KB\n", c->x86_cache_size); + + /* We use exception 16 if we have hardware math and we've either seen it or the CPU claims it is internal */ + fpu_exception = c->hard_math && (ignore_irq13 || cpu_has_fpu); + p += sprintf(p, "fdiv_bug\t: %s\n" + "hlt_bug\t\t: %s\n" + "f00f_bug\t: %s\n" + "coma_bug\t: %s\n" + "fpu\t\t: %s\n" + "fpu_exception\t: %s\n" + "cpuid level\t: %d\n" + "wp\t\t: %s\n" + "flags\t\t:", + c->fdiv_bug ? "yes" : "no", + c->hlt_works_ok ? "no" : "yes", + c->f00f_bug ? "yes" : "no", + c->coma_bug ? "yes" : "no", + c->hard_math ? "yes" : "no", + fpu_exception ? "yes" : "no", + c->cpuid_level, + c->wp_works_ok ? "yes" : "no"); + + for ( i = 0 ; i < 32*NCAPINTS ; i++ ) + if ( test_bit(i, &c->x86_capability) && + x86_cap_flags[i] != NULL ) + p += sprintf(p, " %s", x86_cap_flags[i]); + + p += sprintf(p, "\nbogomips\t: %lu.%02lu\n\n", + c->loops_per_jiffy/(500000/HZ), + (c->loops_per_jiffy/(5000/HZ)) % 100); - /* Cache size */ - if (c->x86_cache_size >= 0) - p += sprintf(p, "cache size\t: %d KB\n", c->x86_cache_size); - - /* We use exception 16 if we have hardware math and we've either seen it or the CPU claims it is internal */ - fpu_exception = c->hard_math && (ignore_irq13 || cpu_has_fpu); - p += sprintf(p, "fdiv_bug\t: %s\n" - "hlt_bug\t\t: %s\n" - "f00f_bug\t: %s\n" - "coma_bug\t: %s\n" - "fpu\t\t: %s\n" - "fpu_exception\t: %s\n" - "cpuid level\t: %d\n" - "wp\t\t: %s\n" - "flags\t\t:", - c->fdiv_bug ? "yes" : "no", - c->hlt_works_ok ? "no" : "yes", - c->f00f_bug ? "yes" : "no", - c->coma_bug ? "yes" : "no", - c->hard_math ? "yes" : "no", - fpu_exception ? "yes" : "no", - c->cpuid_level, - c->wp_works_ok ? "yes" : "no"); - - for ( i = 0 ; i < 32*NCAPINTS ; i++ ) - if ( test_bit(i, &c->x86_capability) && - x86_cap_flags[i] != NULL ) - p += sprintf(p, " %s", x86_cap_flags[i]); - - p += sprintf(p, "\nbogomips\t: %lu.%02lu\n\n", - c->loops_per_jiffy/(500000/HZ), - (c->loops_per_jiffy/(5000/HZ)) % 100); - } - return p - buffer; + return (p - buffer); } unsigned long cpu_initialized __initdata = 0; @@ -2554,6 +2881,53 @@ stts(); } +/* + * Early probe support logic for ppro memory erratum #50 + * + * This is called before we do cpu ident work + */ + +int __init ppro_with_ram_bug(void) +{ + char vendor_id[16]; + int ident; + + /* Must have CPUID */ + if(!have_cpuid_p()) + return 0; + if(cpuid_eax(0)<1) + return 0; + + /* Must be Intel */ + cpuid(0, &ident, + (int *)&vendor_id[0], + (int *)&vendor_id[8], + (int *)&vendor_id[4]); + + if(memcmp(vendor_id, "IntelInside", 12)) + return 0; + + ident = cpuid_eax(1); + + /* Model 6 */ + + if(((ident>>8)&15)!=6) + return 0; + + /* Pentium Pro */ + + if(((ident>>4)&15)!=1) + return 0; + + if((ident&15) < 8) + { + printk(KERN_INFO "Pentium Pro with Errata#50 detected. Taking evasive action.\n"); + return 1; + } + printk(KERN_INFO "Your Pentium Pro seems ok.\n"); + return 0; +} + /* * Local Variables: * mode:c diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/time.c linux.ac/arch/i386/kernel/time.c --- linux.vanilla/arch/i386/kernel/time.c Tue Sep 18 07:03:09 2001 +++ linux.ac/arch/i386/kernel/time.c Wed Oct 10 01:47:31 2001 @@ -501,6 +501,24 @@ count = inb_p(0x40); /* read the latched count */ count |= inb(0x40) << 8; + + + /* VIA686a test code... reset the latch if count > max */ + if (count > LATCH) { + static int last_whine; + outb_p(0x34, 0x43); + outb_p(LATCH & 0xff, 0x40); + outb(LATCH >> 8, 0x40); + count = LATCH - 1; + if(time_after(jiffies, last_whine)) + { + printk(KERN_WARNING "probable hardware bug: clock timer configuration lost - probably a VIA686a motherboard.\n"); + printk(KERN_WARNING "probable hardware bug: restoring chip configuration.\n"); + last_whine = jiffies + HZ; + } + } + + spin_unlock(&i8253_lock); count = ((LATCH-1) - count) * TICK_SIZE; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/traps.c linux.ac/arch/i386/kernel/traps.c --- linux.vanilla/arch/i386/kernel/traps.c Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/i386/kernel/traps.c Thu Oct 11 16:56:25 2001 @@ -266,11 +266,26 @@ return address; } -static void inline do_trap(int trapnr, int signr, char *str, int vm86, +static inline void do_trap(int trapnr, int signr, char *str, int vm86, struct pt_regs * regs, long error_code, siginfo_t *info) { if (vm86 && regs->eflags & VM_MASK) goto vm86_trap; + +#ifdef CONFIG_PNPBIOS + if (regs->xcs == 0x60 || regs->xcs == 0x68) + { + extern u32 pnp_bios_fault_eip, pnp_bios_fault_esp; + extern u32 pnp_bios_is_utter_crap; + pnp_bios_is_utter_crap = 1; + printk(KERN_CRIT "PNPBIOS fault.. attempting recovery.\n"); + __asm__ volatile( + "movl %0, %%esp\n\t" + "jmp %1\n\t" + : "=a" (pnp_bios_fault_esp), "=b" (pnp_bios_fault_eip)); + panic("do_trap: can't hit this"); + } +#endif if (!(regs->xcs & 3)) goto kernel_trap; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/lib/usercopy.c linux.ac/arch/i386/lib/usercopy.c --- linux.vanilla/arch/i386/lib/usercopy.c Tue Sep 18 22:10:43 2001 +++ linux.ac/arch/i386/lib/usercopy.c Wed Oct 10 01:47:31 2001 @@ -165,6 +165,8 @@ unsigned long res, tmp; __asm__ __volatile__( + " testl %0, %0\n" + " jz 3f\n" " andl %0,%%ecx\n" "0: repne; scasb\n" " setne %%al\n" @@ -173,6 +175,8 @@ "1:\n" ".section .fixup,\"ax\"\n" "2: xorl %%eax,%%eax\n" + " jmp 1b\n" + "3: movb $1,%%al\n" " jmp 1b\n" ".previous\n" ".section __ex_table,\"a\"\n" diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/mm/fault.c linux.ac/arch/i386/mm/fault.c --- linux.vanilla/arch/i386/mm/fault.c Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/i386/mm/fault.c Thu Oct 11 14:00:01 2001 @@ -4,6 +4,7 @@ * Copyright (C) 1995 Linus Torvalds */ +#include #include #include #include @@ -55,14 +56,8 @@ start &= PAGE_MASK; for (;;) { - survive: - { - int fault = handle_mm_fault(current->mm, vma, start, 1); - if (!fault) - goto bad_area; - if (fault < 0) - goto out_of_memory; - } + if (handle_mm_fault(current->mm, vma, start, 1) <= 0) + goto bad_area; if (!size) break; size--; @@ -85,14 +80,6 @@ bad_area: return 0; - -out_of_memory: - if (current->pid == 1) { - current->policy |= SCHED_YIELD; - schedule(); - goto survive; - } - goto bad_area; } extern spinlock_t timerlist_lock; @@ -133,6 +120,33 @@ printk("kernel BUG at %s:%d!\n", file, line); } +static void print_pagetable_entries (pgd_t *pgdir, unsigned long address) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + pgd = pgdir + __pgd_offset(address); + printk("pgd entry %p: %016Lx\n", pgd, (long long)pgd_val(*pgd)); + + if (!pgd_present(*pgd)) { + printk("... pgd not present!\n"); + return; + } + pmd = pmd_offset(pgd, address); + printk("pmd entry %p: %016Lx\n", pmd, (long long)pmd_val(*pmd)); + + if (!pmd_present(*pmd)) { + printk("... pmd not present!\n"); + return; + } + pte = pte_offset(pmd, address); + printk("pte entry %p: %016Lx\n", pte, (long long)pte_val(*pte)); + + if (!pte_present(*pte)) + printk("... pte not present!\n"); +} + asmlinkage void do_invalid_op(struct pt_regs *, unsigned long); extern unsigned long idt; @@ -239,7 +253,6 @@ goto bad_area; } - survive: /* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo @@ -334,7 +347,6 @@ printk(KERN_ALERT "*pte = %08lx\n", page); } die("Oops", regs, error_code); - bust_spinlocks(0); do_exit(SIGKILL); /* @@ -343,12 +355,6 @@ */ out_of_memory: up_read(&mm->mmap_sem); - if (tsk->pid == 1) { - tsk->policy |= SCHED_YIELD; - schedule(); - down_read(&mm->mmap_sem); - goto survive; - } printk("VM: killing process %s\n", tsk->comm); if (error_code & 4) do_exit(SIGKILL); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/mm/init.c linux.ac/arch/i386/mm/init.c --- linux.vanilla/arch/i386/mm/init.c Fri Sep 21 03:59:20 2001 +++ linux.ac/arch/i386/mm/init.c Wed Oct 10 01:47:31 2001 @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -133,15 +135,11 @@ pte_t *pte; pgd = swapper_pg_dir + __pgd_offset(vaddr); - if (pgd_none(*pgd)) { - printk("PAE BUG #00!\n"); - return; - } + if (pgd_none(*pgd)) + BUG(); pmd = pmd_offset(pgd, vaddr); - if (pmd_none(*pmd)) { - printk("PAE BUG #01!\n"); - return; - } + if (pmd_none(*pmd)) + BUG(); pte = pte_offset(pmd, vaddr); if (pte_val(*pte)) pte_ERROR(*pte); @@ -185,7 +183,7 @@ pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE); set_pgd(pgd, __pgd(__pa(pmd) + 0x1)); if (pmd != pmd_offset(pgd, 0)) - printk("PAE BUG #02!\n"); + BUG(); } pmd = pmd_offset(pgd, vaddr); #else @@ -439,13 +437,24 @@ return 0; } +static inline int page_kills_ppro(unsigned long pagenr) +{ + if(pagenr >= 0x70000 && pagenr <= 0x7003F) + return 1; + return 0; +} + void __init mem_init(void) { + extern int ppro_with_ram_bug(void); int codesize, reservedpages, datasize, initsize; int tmp; + int bad_ppro; if (!mem_map) BUG(); + + bad_ppro = ppro_with_ram_bug(); #ifdef CONFIG_HIGHMEM highmem_start_page = mem_map + highstart_pfn; @@ -476,6 +485,11 @@ SetPageReserved(page); continue; } + if (bad_ppro && page_kills_ppro(tmp)) + { + SetPageReserved(page); + continue; + } ClearPageReserved(page); set_bit(PG_highmem, &page->flags); atomic_set(&page->count, 1); @@ -572,7 +586,7 @@ void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = totalhigh_pages; @@ -580,3 +594,21 @@ val->mem_unit = PAGE_SIZE; return; } + +#if CONFIG_X86_PAE + +struct kmem_cache_s *pae_pgd_cachep; + +void __init pgtable_cache_init(void) +{ + /* + * PAE pgds must be 16-byte aligned: + */ + pae_pgd_cachep = kmem_cache_create("pae_pgd", 32, 0, + SLAB_HWCACHE_ALIGN | SLAB_MUST_HWCACHE_ALIGN, NULL, NULL); + if (!pae_pgd_cachep) + panic("init_pae(): Cannot alloc pae_pgd SLAB cache"); +} + +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ia64/config.in linux.ac/arch/ia64/config.in --- linux.vanilla/arch/ia64/config.in Tue Jul 31 18:30:08 2001 +++ linux.ac/arch/ia64/config.in Wed Oct 10 01:47:31 2001 @@ -25,6 +25,7 @@ define_bool CONFIG_SBUS n define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n +define_bool CONFIG_GENERIC_BUST_SPINLOCK n if [ "$CONFIG_IA64_HP_SIM" = "n" ]; then define_bool CONFIG_ACPI y @@ -149,7 +150,7 @@ source drivers/mtd/Config.in source drivers/pnp/Config.in source drivers/block/Config.in -source drivers/i2o/Config.in +source drivers/message/i2o/Config.in source drivers/md/Config.in mainmenu_option next_comment diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ia64/ia32/sys_ia32.c linux.ac/arch/ia64/ia32/sys_ia32.c --- linux.vanilla/arch/ia64/ia32/sys_ia32.c Sun Aug 12 23:07:42 2001 +++ linux.ac/arch/ia64/ia32/sys_ia32.c Wed Oct 10 01:47:31 2001 @@ -2866,26 +2866,26 @@ return sys_ioperm((unsigned long)from, (unsigned long)num, on); } -struct dqblk32 { - __u32 dqb_bhardlimit; - __u32 dqb_bsoftlimit; - __u32 dqb_curblocks; +struct mem_dqblk32 { __u32 dqb_ihardlimit; __u32 dqb_isoftlimit; __u32 dqb_curinodes; + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u64 dqb_curspace; __kernel_time_t32 dqb_btime; __kernel_time_t32 dqb_itime; }; extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, - caddr_t addr); + __kernel_caddr_t addr); asmlinkage long sys32_quotactl(int cmd, const char *special, int id, unsigned long addr) { int cmds = cmd >> SUBCMDSHIFT; int err; - struct dqblk d; + struct mem_dqblk d; mm_segment_t old_fs; char *spec; @@ -2895,33 +2895,35 @@ case Q_SETQUOTA: case Q_SETUSE: case Q_SETQLIM: - if (copy_from_user (&d, (struct dqblk32 *)addr, - sizeof (struct dqblk32))) + if (copy_from_user (&d, (struct mem_dqblk32 *)addr, + sizeof (struct mem_dqblk32))) return -EFAULT; - d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; - d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; + d.dqb_itime = ((struct mem_dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct mem_dqblk32 *)&d)->dqb_btime; break; default: return sys_quotactl(cmd, special, - id, (caddr_t)addr); + id, (__kernel_caddr_t)addr); } spec = getname32 (special); err = PTR_ERR(spec); if (IS_ERR(spec)) return err; old_fs = get_fs (); set_fs (KERNEL_DS); - err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d); set_fs (old_fs); putname (spec); + if (err) + return err; if (cmds == Q_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; - ((struct dqblk32 *)&d)->dqb_itime = i; - ((struct dqblk32 *)&d)->dqb_btime = b; - if (copy_to_user ((struct dqblk32 *)addr, &d, - sizeof (struct dqblk32))) + ((struct mem_dqblk32 *)&d)->dqb_itime = i; + ((struct mem_dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct mem_dqblk32 *)addr, &d, + sizeof (struct mem_dqblk32))) return -EFAULT; } - return err; + return 0; } extern asmlinkage long sys_utime(char * filename, struct utimbuf * times); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ia64/kernel/init_task.c linux.ac/arch/ia64/kernel/init_task.c --- linux.vanilla/arch/ia64/kernel/init_task.c Mon Sep 17 23:29:09 2001 +++ linux.ac/arch/ia64/kernel/init_task.c Wed Oct 10 01:47:31 2001 @@ -13,6 +13,7 @@ #include #include +static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ia64/kernel/setup.c linux.ac/arch/ia64/kernel/setup.c --- linux.vanilla/arch/ia64/kernel/setup.c Tue Jul 31 18:30:08 2001 +++ linux.ac/arch/ia64/kernel/setup.c Wed Oct 10 01:47:31 2001 @@ -361,11 +361,21 @@ unw_create_gate_table(); } -/* - * Display cpu info for all cpu's. - */ + /* + * get_cpuinfo - Get information on one CPU for use by the procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. + */ int -get_cpuinfo (char *buffer) +get_cpuinfo(char *buffer, unsigned *cpu_np) { #ifdef CONFIG_SMP # define lpj c->loops_per_jiffy @@ -374,48 +384,57 @@ #endif char family[32], features[128], *cp, *p = buffer; struct cpuinfo_ia64 *c; - unsigned long mask, cpu; + unsigned long mask; + unsigned n; - for (cpu = 0; cpu < smp_num_cpus; ++cpu) { - c = cpu_data(cpu); - mask = c->features; + n = *cpu_np; + while (n < NR_CPUS && (cpu_online_map & (1 << n)) == 0) { + ++n; + } + if (n >= NR_CPUS) { + *cpu_np = NR_CPUS; + return (0); + } + *cpu_np = n + 1; + c = cpu_data((unsigned long)n); - switch (c->family) { - case 0x07: memcpy(family, "Itanium", 8); break; - case 0x1f: memcpy(family, "McKinley", 9); break; - default: sprintf(family, "%u", c->family); break; - } + mask = c->features; - /* build the feature string: */ - memcpy(features, " standard", 10); - cp = features; - if (mask & 1) { - strcpy(cp, " branchlong"); - cp = strchr(cp, '\0'); - mask &= ~1UL; - } - if (mask) - sprintf(cp, " 0x%lx", mask); + switch (c->family) { + case 0x07: memcpy(family, "Itanium", 8); break; + case 0x1f: memcpy(family, "McKinley", 9); break; + default: sprintf(family, "%u", c->family); break; + } - p += sprintf(p, - "processor : %lu\n" - "vendor : %s\n" - "arch : IA-64\n" - "family : %s\n" - "model : %u\n" - "revision : %u\n" - "archrev : %u\n" - "features :%s\n" /* don't change this---it _is_ right! */ - "cpu number : %lu\n" - "cpu regs : %u\n" - "cpu MHz : %lu.%06lu\n" - "itc MHz : %lu.%06lu\n" - "BogoMIPS : %lu.%02lu\n\n", - cpu, c->vendor, family, c->model, c->revision, c->archrev, features, - c->ppn, c->number, c->proc_freq / 1000000, c->proc_freq % 1000000, - c->itc_freq / 1000000, c->itc_freq % 1000000, - lpj*HZ/500000, (lpj*HZ/5000) % 100); + /* build the feature string: */ + memcpy(features, " standard", 10); + cp = features; + if (mask & 1) { + strcpy(cp, " branchlong"); + cp = strchr(cp, '\0'); + mask &= ~1UL; } + if (mask) + sprintf(cp, " 0x%lx", mask); + + p += sprintf(p, + "processor : %u\n" + "vendor : %s\n" + "arch : IA-64\n" + "family : %s\n" + "model : %u\n" + "revision : %u\n" + "archrev : %u\n" + "features :%s\n" /* don't change this---it _is_ right! */ + "cpu number : %lu\n" + "cpu regs : %u\n" + "cpu MHz : %lu.%06lu\n" + "itc MHz : %lu.%06lu\n" + "BogoMIPS : %lu.%02lu\n\n", + n, c->vendor, family, c->model, c->revision, c->archrev, features, + c->ppn, c->number, c->proc_freq / 1000000, c->proc_freq % 1000000, + c->itc_freq / 1000000, c->itc_freq % 1000000, + lpj*HZ/500000, (lpj*HZ/5000) % 100); return p - buffer; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ia64/kernel/sys_ia64.c linux.ac/arch/ia64/kernel/sys_ia64.c --- linux.vanilla/arch/ia64/kernel/sys_ia64.c Tue Jul 31 18:30:08 2001 +++ linux.ac/arch/ia64/kernel/sys_ia64.c Wed Oct 10 01:47:31 2001 @@ -30,13 +30,26 @@ if (len > RGN_MAP_LIMIT) return -ENOMEM; - if (!addr) - addr = TASK_UNMAPPED_BASE; - if (map_shared) - addr = COLOR_ALIGN(addr); - else - addr = PAGE_ALIGN(addr); + if (addr) { + if (map_shared) + addr = COLOR_ALIGN(addr); + else + addr = PAGE_ALIGN(addr); + vmm = find_vma(current->mm, addr); + if (TASK_SIZE - len >= addr && + rgn_offset(addr) + len <= RGN_MAP_LIMIT && + (!vmm || addr + len <= vmm->vm_start)) + return addr; + if (addr > TASK_UNMAPPED_BASE) + addr = 0; + } + if (!addr) { + if (map_shared) + addr = COLOR_ALIGN(TASK_UNMAPPED_BASE); + else + addr = PAGE_ALIGN(TASK_UNMAPPED_BASE); + } for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { /* At this point: (!vmm || addr < vmm->vm_end). */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ia64/mm/init.c linux.ac/arch/ia64/mm/init.c --- linux.vanilla/arch/ia64/mm/init.c Fri Sep 21 04:02:03 2001 +++ linux.ac/arch/ia64/mm/init.c Wed Oct 10 01:47:31 2001 @@ -157,7 +157,7 @@ si_meminfo (struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/config.in linux.ac/arch/m68k/config.in --- linux.vanilla/arch/m68k/config.in Tue Jun 12 03:15:27 2001 +++ linux.ac/arch/m68k/config.in Wed Oct 10 01:47:31 2001 @@ -6,6 +6,7 @@ define_bool CONFIG_UID16 y define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n +define_bool CONFIG_GENERIC_BUST_SPINLOCK n mainmenu_name "Linux/68k Kernel Configuration" diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/kernel/process.c linux.ac/arch/m68k/kernel/process.c --- linux.vanilla/arch/m68k/kernel/process.c Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/m68k/kernel/process.c Thu Oct 11 14:00:08 2001 @@ -38,6 +38,7 @@ * alignment requirements and potentially different initial * setup. */ +static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/kernel/setup.c linux.ac/arch/m68k/kernel/setup.c --- linux.vanilla/arch/m68k/kernel/setup.c Tue Jun 12 03:15:27 2001 +++ linux.ac/arch/m68k/kernel/setup.c Wed Oct 10 01:47:31 2001 @@ -408,10 +408,31 @@ #endif } -int get_cpuinfo(char * buffer) +/* + * get_cpuinfo - Get information on one CPU for use by the procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. + */ +int get_cpuinfo(char *buffer, unsigned *cpu_np) { const char *cpu, *mmu, *fpu; unsigned long clockfreq, clockfactor; + unsigned n; + + /* No SMP at the moment, so just toggle 0/1 */ + n = *cpu_np; + *cpu_np = 1; + if (n != 0) { + return (0); + } #define LOOP_CYCLES_68020 (8) #define LOOP_CYCLES_68030 (8) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mm/init.c linux.ac/arch/m68k/mm/init.c --- linux.vanilla/arch/m68k/mm/init.c Fri Sep 21 04:02:03 2001 +++ linux.ac/arch/m68k/mm/init.c Wed Oct 10 01:47:31 2001 @@ -209,7 +209,7 @@ i = max_mapnr; val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/mips/config.in linux.ac/arch/mips/config.in --- linux.vanilla/arch/mips/config.in Sun Sep 9 18:43:02 2001 +++ linux.ac/arch/mips/config.in Wed Oct 10 01:47:32 2001 @@ -70,6 +70,7 @@ define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n +define_bool CONFIG_GENERIC_BUST_SPINLOCK n # # Select some configuration options automatically for certain systems. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/mips/kernel/init_task.c linux.ac/arch/mips/kernel/init_task.c --- linux.vanilla/arch/mips/kernel/init_task.c Mon Sep 17 23:29:09 2001 +++ linux.ac/arch/mips/kernel/init_task.c Wed Oct 10 01:47:32 2001 @@ -4,6 +4,7 @@ #include #include +static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/mips/kernel/proc.c linux.ac/arch/mips/kernel/proc.c --- linux.vanilla/arch/mips/kernel/proc.c Sun Sep 9 18:43:01 2001 +++ linux.ac/arch/mips/kernel/proc.c Wed Oct 10 01:47:32 2001 @@ -21,12 +21,23 @@ #endif /* - * BUFFER is PAGE_SIZE bytes long. + * get_cpuinfo - Get information on one CPU for use by the procfs. * - * Currently /proc/cpuinfo is being abused to print data about the - * number of date/instruction cacheflushes. + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * BUFFER is PAGE_SIZE - 1K bytes long. + * Currently /proc/cpuinfo is being abused to print data about the + * number of date/instruction cacheflushes. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. */ -int get_cpuinfo(char *buffer) +int get_cpuinfo(char *buffer, unsigned *cpu_np) { char fmt [64]; const char *cpu_name[] = CPU_NAMES; @@ -59,6 +70,14 @@ mach_sibyte_names, mach_toshiba_names, mach_alchemy_names}; unsigned int version = read_32bit_cp0_register(CP0_PRID); int len; + unsigned n; + + /* Unlike some archs, don't have per-CPU info, so just toggle 0/1 */ + n = *cpu_np; + *cpu_np = 1; + if (n != 0) { + return (0); + } len = sprintf(buffer, "cpu\t\t\t: MIPS\n"); len += sprintf(buffer + len, "cpu model\t\t: %s V%d.%d\n", diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/mips64/config.in linux.ac/arch/mips64/config.in --- linux.vanilla/arch/mips64/config.in Sun Sep 9 18:43:02 2001 +++ linux.ac/arch/mips64/config.in Wed Oct 10 01:47:33 2001 @@ -29,6 +29,7 @@ define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n +define_bool CONFIG_GENERIC_BUST_SPINLOCK n # # Select some configuration options automatically based on user selections diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/mips64/kernel/init_task.c linux.ac/arch/mips64/kernel/init_task.c --- linux.vanilla/arch/mips64/kernel/init_task.c Mon Sep 17 23:29:09 2001 +++ linux.ac/arch/mips64/kernel/init_task.c Wed Oct 10 01:47:33 2001 @@ -4,6 +4,7 @@ #include #include +static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/mips64/kernel/proc.c linux.ac/arch/mips64/kernel/proc.c --- linux.vanilla/arch/mips64/kernel/proc.c Sun Sep 9 18:43:01 2001 +++ linux.ac/arch/mips64/kernel/proc.c Wed Oct 10 01:47:33 2001 @@ -19,15 +19,34 @@ unsigned int vced_count, vcei_count; /* - * BUFFER is PAGE_SIZE bytes long. + * get_cpuinfo - Get information on one CPU for use by the procfs. * - * Currently /proc/cpuinfo is being abused to print data about the - * number of date/instruction cacheflushes. + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * BUFFER is PAGE_SIZE bytes long. + * Currently /proc/cpuinfo is being abused to print data about the + * number of date/instruction cacheflushes. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. */ -int get_cpuinfo(char *buffer) +int get_cpuinfo(char *buffer, unsigned *cpu_np) { char fmt [64]; size_t len; + unsigned n; + + /* Unlike some archs, don't have per-CPU info, so just toggle 0/1 */ + n = *cpu_np; + *cpu_np = 1; + if (n != 0) { + return (0); + } len = sprintf(buffer, "cpu\t\t\t: MIPS\n"); #if 0 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/parisc/config.in linux.ac/arch/parisc/config.in --- linux.vanilla/arch/parisc/config.in Wed Apr 18 01:19:25 2001 +++ linux.ac/arch/parisc/config.in Wed Oct 10 01:47:33 2001 @@ -9,6 +9,7 @@ define_bool CONFIG_UID16 n define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n +define_bool CONFIG_GENERIC_BUST_SPINLOCK n mainmenu_option next_comment comment 'Code maturity level options' diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/parisc/kernel/init_task.c linux.ac/arch/parisc/kernel/init_task.c --- linux.vanilla/arch/parisc/kernel/init_task.c Mon Sep 17 23:29:09 2001 +++ linux.ac/arch/parisc/kernel/init_task.c Wed Oct 10 01:47:33 2001 @@ -6,6 +6,7 @@ #include #include +static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/parisc/kernel/setup.c linux.ac/arch/parisc/kernel/setup.c --- linux.vanilla/arch/parisc/kernel/setup.c Fri Feb 9 19:29:44 2001 +++ linux.ac/arch/parisc/kernel/setup.c Wed Oct 10 01:47:33 2001 @@ -568,46 +568,60 @@ #ifdef CONFIG_PROC_FS /* - * Get CPU information for use by procfs. + * get_cpuinfo - Get information on one CPU for use by the procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. */ -int get_cpuinfo(char *buffer) +int get_cpuinfo(char *buffer, unsigned *cpu_np) { char *p = buffer; - int n; + unsigned n; - for(n=0; n= NR_CPUS) { + *cpu_np = NR_CPUS; + return (0); + } + *cpu_np = n + 1; + + p += sprintf(p, "processor\t: %d\n" + "cpu family\t: PA-RISC %s\n", + n, boot_cpu_data.family_name); + + p += sprintf(p, "cpu\t\t: %s\n", boot_cpu_data.cpu_name ); + + /* cpu MHz */ + p += sprintf(p, "cpu MHz\t\t: %d.%06d\n", + boot_cpu_data.cpu_hz / 1000000, + boot_cpu_data.cpu_hz % 1000000 ); + + p += sprintf(p, "model\t\t: %s\n" + "model name\t: %s\n", + boot_cpu_data.pdc.sys_model_name, + boot_cpu_data.model_name); + + p += sprintf(p, "hversion\t: 0x%08x\n" + "sversion\t: 0x%08x\n", + boot_cpu_data.hversion, + boot_cpu_data.sversion ); + + p += get_cache_info(p); + /* print cachesize info ? */ + p += sprintf(p, "bogomips\t: %lu.%02lu\n", + (loops_per_sec+2500)/500000, + ((loops_per_sec+2500)/5000) % 100); return p - buffer; } #endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/parisc/mm/init.c linux.ac/arch/parisc/mm/init.c --- linux.vanilla/arch/parisc/mm/init.c Tue Dec 5 20:29:39 2000 +++ linux.ac/arch/parisc/mm/init.c Wed Oct 10 01:47:33 2001 @@ -458,7 +458,7 @@ i = max_mapnr; val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); #if 0 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/Makefile linux.ac/arch/ppc/Makefile --- linux.vanilla/arch/ppc/Makefile Mon Sep 17 05:25:28 2001 +++ linux.ac/arch/ppc/Makefile Wed Oct 10 01:47:33 2001 @@ -1,4 +1,4 @@ -# BK Id: SCCS/s.Makefile 1.21 08/19/01 20:06:47 paulus +# BK Id: SCCS/s.Makefile 1.23 09/18/01 11:19:05 paulus # # This file is included by the global makefile so that you can add your own # architecture-specific flags and dependencies. Remember to do have actions diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/amiga/config.c linux.ac/arch/ppc/amiga/config.c --- linux.vanilla/arch/ppc/amiga/config.c Mon Sep 17 05:22:50 2001 +++ linux.ac/arch/ppc/amiga/config.c Wed Oct 10 01:47:33 2001 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.config.c 1.7 05/21/01 00:48:24 cort + * BK Id: SCCS/s.config.c 1.12 09/18/01 11:19:06 paulus */ #define m68k_debug_device debug_device diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/boot/common/misc-simple.c linux.ac/arch/ppc/boot/common/misc-simple.c --- linux.vanilla/arch/ppc/boot/common/misc-simple.c Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/boot/common/misc-simple.c Wed Oct 10 01:47:33 2001 @@ -45,6 +45,15 @@ char *cmd_line = cmd_buf; unsigned long initrd_start = 0, initrd_end = 0; + +/* These values must be variables. If not, the compiler optimizer + * will remove some code, causing the size of the code to vary + * when these values are zero. This is bad because we first + * compile with these zero to determine the size and offsets + * in an image, than compile again with these set to the proper + * discovered value. + */ +unsigned int initrd_offset, initrd_size; char *zimage_start; int zimage_size; @@ -69,7 +78,8 @@ * were relocated to. */ puts("loaded at: "); puthex(load_addr); - puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); + puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); + puts("\n"); if ( (unsigned long)load_addr != (unsigned long)&start ) { puts("relocated to: "); puthex((unsigned long)&start); @@ -82,45 +92,38 @@ the size of the elf header which we strip -- Cort */ zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET); zimage_size = ZIMAGE_SIZE; + initrd_offset = INITRD_OFFSET; + initrd_size = INITRD_SIZE; - if ( INITRD_OFFSET ) - initrd_start = load_addr - 0x10000 + INITRD_OFFSET; + if ( initrd_offset ) + initrd_start = load_addr - 0x10000 + initrd_offset; else initrd_start = 0; - initrd_end = INITRD_SIZE + initrd_start; + initrd_end = initrd_size + initrd_start; - /* - * Find a place to stick the zimage and initrd and - * relocate them if we have to. -- Cort - */ + /* Relocate the zImage */ avail_ram = (char *)PAGE_ALIGN((unsigned long)_end); puts("zimage at: "); puthex((unsigned long)zimage_start); - puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n"); - if ( (unsigned long)zimage_start <= 0x00800000 ) - { - memcpy( (void *)avail_ram, (void *)zimage_start, zimage_size ); - zimage_start = (char *)avail_ram; - puts("relocated to: "); puthex((unsigned long)zimage_start); - puts(" "); - puthex((unsigned long)zimage_size+(unsigned long)zimage_start); - puts("\n"); + puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); + puts("\n"); + memcpy( (void *)avail_ram, (void *)zimage_start, zimage_size ); + zimage_start = (char *)avail_ram; + puts("relocated to: "); puthex((unsigned long)zimage_start); + puts(" "); + puthex((unsigned long)zimage_size+(unsigned long)zimage_start); + puts("\n"); - /* relocate initrd */ - if ( initrd_start ) - { - puts("initrd at: "); puthex(initrd_start); - puts(" "); puthex(initrd_end); puts("\n"); - avail_ram = (char *)PAGE_ALIGN( - (unsigned long)zimage_size+(unsigned long)zimage_start); - memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE ); - initrd_start = (unsigned long)avail_ram; - initrd_end = initrd_start + INITRD_SIZE; - puts("relocated to: "); puthex(initrd_start); - puts(" "); puthex(initrd_end); puts("\n"); - } - } else if ( initrd_start ) { + if ( initrd_start ) { puts("initrd at: "); puthex(initrd_start); puts(" "); puthex(initrd_end); puts("\n"); + /* relocate initrd */ + avail_ram = (char *)PAGE_ALIGN((unsigned long)zimage_size + + (unsigned long)zimage_start); + memcpy( (void *)avail_ram, (void *)initrd_start, initrd_size ); + initrd_start = (unsigned long)avail_ram; + initrd_end = initrd_start + initrd_size; + puts("relocated to: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); puts("\n"); } avail_ram = (char *)0x00400000; @@ -161,11 +164,9 @@ puts("\n"); /* mappings on early boot can only handle 16M */ - if ( (int)(cmd_line[0]) > (16<<20)) + if ( (u32)(cmd_line) > (16<<20)) puts("cmd_line located > 16M\n"); - if ( initrd_start > (16<<20)) - puts("initrd_start located > 16M\n"); - + puts("Uncompressing Linux..."); gunzip(0, 0x400000, zimage_start, &zimage_size); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/boot/pmac/Makefile linux.ac/arch/ppc/boot/pmac/Makefile --- linux.vanilla/arch/ppc/boot/pmac/Makefile Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/boot/pmac/Makefile Wed Oct 10 01:47:33 2001 @@ -1,4 +1,4 @@ -# BK Id: SCCS/s.Makefile 1.14 07/27/01 20:24:17 trini +# BK Id: SCCS/s.Makefile 1.16 09/28/01 07:39:37 trini # # Makefile for making XCOFF bootable images for booting on PowerMacs # using Open Firmware. @@ -48,9 +48,9 @@ cp ../images/vmlinux.coff $(TFTPIMAGE) cp ../images/vmlinux.elf-pmac $(TFTPIMAGE).elf -znetboot.initrd: vmlinux.coff.initrd vmlinux.initrd.elf-pmac - cp ../images/vmlinux.coff.initrd $(TFTPIMAGE) - cp ../images/vmlinux.elf-pmac.initrd $(TFTPIMAGE).elf +znetboot.initrd: vmlinux.initrd.coff vmlinux.initrd.elf-pmac + cp ../images/vmlinux.initrd.coff $(TFTPIMAGE) + cp ../images/vmlinux.initrd.elf-pmac $(TFTPIMAGE).elf #floppy: zImage # mount -t hfs /dev/fd0 /mnt @@ -61,7 +61,7 @@ $(OBJCOPY) $(OBJCOPY_ARGS) --add-section=image=../images/vmlinux.gz \ dummy.o ../images/$@ -miboot.image.initrd: miboot.image ../images/ramdisk.image.gz +miboot.initrd.image: miboot.image ../images/ramdisk.image.gz $(OBJCOPY) $(OBJCOPY_ARGS) --add-section=initrd=../images/ramdisk.image.gz \ ../images/miboot.image ../images/$@ @@ -83,11 +83,11 @@ rm -f coffboot ln -sf vmlinux.coff ../images/zImage.pmac -vmlinux.coff.initrd: coffboot.initrd $(HACKCOFF) +vmlinux.initrd.coff: coffboot.initrd $(HACKCOFF) $(OBJCOPY) $(OBJCOPY_ARGS) coffboot.initrd ../images/$@ $(HACKCOFF) ../images/$@ rm -f coffboot.initrd - ln -sf vmlinux.coff.initrd ../images/zImage.initrd.pmac + ln -sf vmlinux.initrd.coff ../images/zImage.initrd.pmac vmlinux.elf-pmac: $(CHRPOBJS) $(LIBS) ../common/no_initrd.o $(MKNOTE) ../images/vmlinux.gz $(LD) $(CHRP_LD_ARGS) -o ../images/$@ $(CHRPOBJS) ../common/no_initrd.o $(LIBS) @@ -110,6 +110,6 @@ zImage: vmlinux.coff vmlinux.elf-pmac miboot.image -zImage.initrd: vmlinux.coff.initrd vmlinux.initrd.elf-pmac miboot.image.initrd +zImage.initrd: vmlinux.initrd.coff vmlinux.initrd.elf-pmac miboot.initrd.image include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/boot/prep/misc.c linux.ac/arch/ppc/boot/prep/misc.c --- linux.vanilla/arch/ppc/boot/prep/misc.c Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/boot/prep/misc.c Wed Oct 10 01:47:33 2001 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.misc.c 1.18 07/30/01 17:19:40 trini + * BK Id: SCCS/s.misc.c 1.20 09/24/01 18:42:54 trini * * arch/ppc/boot/prep/misc.c * @@ -47,6 +47,15 @@ RESIDUAL hold_resid_buf; RESIDUAL *hold_residual = &hold_resid_buf; unsigned long initrd_start = 0, initrd_end = 0; + +/* These values must be variables. If not, the compiler optimizer + * will remove some code, causing the size of the code to vary + * when these values are zero. This is bad because we first + * compile with these zero to determine the size and offsets + * in an image, than compile again with these set to the proper + * discovered value. + */ +unsigned int initrd_offset, initrd_size; char *zimage_start; int zimage_size; @@ -302,12 +311,14 @@ size of the elf header which we strip -- Cort */ zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET); zimage_size = ZIMAGE_SIZE; + initrd_offset = INITRD_OFFSET; + initrd_size = INITRD_SIZE; - if ( INITRD_OFFSET ) - initrd_start = load_addr - 0x10000 + INITRD_OFFSET; + if ( initrd_offset ) + initrd_start = load_addr - 0x10000 + initrd_offset; else initrd_start = 0; - initrd_end = INITRD_SIZE + initrd_start; + initrd_end = initrd_size + initrd_start; /* * Find a place to stick the zimage and initrd and @@ -332,9 +343,9 @@ puts(" "); puthex(initrd_end); puts("\n"); avail_ram = (char *)PAGE_ALIGN( (unsigned long)zimage_size+(unsigned long)zimage_start); - memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE ); + memcpy ((void *)avail_ram, (void *)initrd_start, initrd_size ); initrd_start = (unsigned long)avail_ram; - initrd_end = initrd_start + INITRD_SIZE; + initrd_end = initrd_start + initrd_size; puts("relocated to: "); puthex(initrd_start); puts(" "); puthex(initrd_end); puts("\n"); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/config.in linux.ac/arch/ppc/config.in --- linux.vanilla/arch/ppc/config.in Tue Aug 28 15:11:33 2001 +++ linux.ac/arch/ppc/config.in Wed Oct 10 01:47:33 2001 @@ -1,4 +1,4 @@ -# BK Id: SCCS/s.config.in 1.40 08/24/01 20:31:47 paulus +# BK Id: SCCS/s.config.in 1.42 09/19/01 16:58:37 trini # # For a description of the syntax of this configuration file, # see Documentation/kbuild/config-language.txt. @@ -6,6 +6,7 @@ define_bool CONFIG_UID16 n define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y +define_bool CONFIG_GENERIC_BUST_SPINLOCK n mainmenu_name "Linux/PowerPC Kernel Configuration" @@ -95,10 +96,6 @@ Synergy-Gemini CONFIG_GEMINI" CHRP/PowerMac/PReP fi -if [ "$CONFIG_PPC64BRIDGE" != "y" ]; then - bool 'Workarounds for PPC601 bugs' CONFIG_PPC601_SYNC_FIX -fi - if [ "$CONFIG_POWER3" = "n" -a "$CONFIG_POWER4" = "n" -a \ "$CONFIG_6xx" = "n" ]; then define_bool CONFIG_ALL_PPC n @@ -186,6 +183,7 @@ fi if [ "$CONFIG_ALL_PPC" = "y" ]; then + bool 'Workarounds for PPC601 bugs' CONFIG_PPC601_SYNC_FIX bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE bool 'Support for RTAS (RunTime Abstraction Services) in /proc' CONFIG_PPC_RTAS bool 'Support for early boot text console (BootX or OpenFirmware only)' CONFIG_BOOTX_TEXT diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/IVMS8_defconfig linux.ac/arch/ppc/configs/IVMS8_defconfig --- linux.vanilla/arch/ppc/configs/IVMS8_defconfig Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/ppc/configs/IVMS8_defconfig Thu Oct 11 14:01:20 2001 @@ -198,14 +198,10 @@ # CONFIG_BLK_DEV_CMD640 is not set # CONFIG_BLK_DEV_CMD640_ENHANCED is not set # CONFIG_BLK_DEV_ISAPNP is not set -CONFIG_BLK_DEV_MPC8xx_IDE=y -CONFIG_IDE_8xx_PCCARD=y -# CONFIG_IDE_8xx_DIRECT is not set -# CONFIG_IDE_EXT_DIRECT is not set # CONFIG_IDE_CHIPSETS is not set # CONFIG_IDEDMA_AUTO is not set # CONFIG_DMA_NONPCI is not set -CONFIG_BLK_DEV_IDE_MODES=y +# CONFIG_BLK_DEV_IDE_MODES is not set # CONFIG_BLK_DEV_ATARAID is not set # CONFIG_BLK_DEV_ATARAID_PDC is not set # CONFIG_BLK_DEV_ATARAID_HPT is not set @@ -394,11 +390,16 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set @@ -412,7 +413,7 @@ CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -435,6 +436,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -497,6 +499,100 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/SM850_defconfig linux.ac/arch/ppc/configs/SM850_defconfig --- linux.vanilla/arch/ppc/configs/SM850_defconfig Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/configs/SM850_defconfig Wed Oct 10 01:47:33 2001 @@ -191,11 +191,9 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_ARM_AM79C961A is not set # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set -# CONFIG_NCR885E is not set # CONFIG_OAKNET is not set # CONFIG_SUNLANCE is not set # CONFIG_SUNBMAC is not set @@ -214,9 +212,9 @@ # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set -# CONFIG_ACENIC_OMIT_TIGON_I is not set # CONFIG_DL2K is not set # CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set @@ -277,6 +275,10 @@ # Input core support # # CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set # # Macintosh device drivers @@ -306,7 +308,11 @@ # # Joysticks # -# CONFIG_JOYSTICK is not set +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# # # Input core support is needed for joysticks @@ -345,24 +351,30 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_TMPFS=y # CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -385,6 +397,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -449,6 +462,100 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/SPD823TS_defconfig linux.ac/arch/ppc/configs/SPD823TS_defconfig --- linux.vanilla/arch/ppc/configs/SPD823TS_defconfig Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/configs/SPD823TS_defconfig Wed Oct 10 01:47:33 2001 @@ -190,11 +190,9 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_ARM_AM79C961A is not set # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set -# CONFIG_NCR885E is not set # CONFIG_OAKNET is not set # CONFIG_SUNLANCE is not set # CONFIG_SUNBMAC is not set @@ -213,9 +211,9 @@ # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set -# CONFIG_ACENIC_OMIT_TIGON_I is not set # CONFIG_DL2K is not set # CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set @@ -276,6 +274,10 @@ # Input core support # # CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set # # Macintosh device drivers @@ -305,7 +307,11 @@ # # Joysticks # -# CONFIG_JOYSTICK is not set +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# # # Input core support is needed for joysticks @@ -344,24 +350,30 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_TMPFS=y # CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -384,6 +396,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -448,6 +461,100 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/TQM823L_defconfig linux.ac/arch/ppc/configs/TQM823L_defconfig --- linux.vanilla/arch/ppc/configs/TQM823L_defconfig Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/configs/TQM823L_defconfig Wed Oct 10 01:47:33 2001 @@ -191,11 +191,9 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_ARM_AM79C961A is not set # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set -# CONFIG_NCR885E is not set # CONFIG_OAKNET is not set # CONFIG_SUNLANCE is not set # CONFIG_SUNBMAC is not set @@ -214,9 +212,9 @@ # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set -# CONFIG_ACENIC_OMIT_TIGON_I is not set # CONFIG_DL2K is not set # CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set @@ -277,6 +275,10 @@ # Input core support # # CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set # # Macintosh device drivers @@ -306,7 +308,11 @@ # # Joysticks # -# CONFIG_JOYSTICK is not set +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# # # Input core support is needed for joysticks @@ -345,24 +351,30 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_TMPFS=y # CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -385,6 +397,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -449,6 +462,100 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/TQM850L_defconfig linux.ac/arch/ppc/configs/TQM850L_defconfig --- linux.vanilla/arch/ppc/configs/TQM850L_defconfig Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/configs/TQM850L_defconfig Wed Oct 10 01:47:33 2001 @@ -191,11 +191,9 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_ARM_AM79C961A is not set # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set -# CONFIG_NCR885E is not set # CONFIG_OAKNET is not set # CONFIG_SUNLANCE is not set # CONFIG_SUNBMAC is not set @@ -214,9 +212,9 @@ # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set -# CONFIG_ACENIC_OMIT_TIGON_I is not set # CONFIG_DL2K is not set # CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set @@ -277,6 +275,10 @@ # Input core support # # CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set # # Macintosh device drivers @@ -306,7 +308,11 @@ # # Joysticks # -# CONFIG_JOYSTICK is not set +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# # # Input core support is needed for joysticks @@ -345,24 +351,30 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_TMPFS=y # CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -385,6 +397,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -449,6 +462,100 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/TQM860L_defconfig linux.ac/arch/ppc/configs/TQM860L_defconfig --- linux.vanilla/arch/ppc/configs/TQM860L_defconfig Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/ppc/configs/TQM860L_defconfig Thu Oct 11 14:01:28 2001 @@ -395,11 +395,16 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set @@ -413,7 +418,7 @@ # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -436,6 +441,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -506,6 +512,100 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/bseip_defconfig linux.ac/arch/ppc/configs/bseip_defconfig --- linux.vanilla/arch/ppc/configs/bseip_defconfig Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/configs/bseip_defconfig Wed Oct 10 01:47:33 2001 @@ -190,11 +190,9 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_ARM_AM79C961A is not set # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set -# CONFIG_NCR885E is not set # CONFIG_OAKNET is not set # CONFIG_SUNLANCE is not set # CONFIG_SUNBMAC is not set @@ -213,9 +211,9 @@ # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set -# CONFIG_ACENIC_OMIT_TIGON_I is not set # CONFIG_DL2K is not set # CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set @@ -276,6 +274,10 @@ # Input core support # # CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set # # Macintosh device drivers @@ -305,7 +307,11 @@ # # Joysticks # -# CONFIG_JOYSTICK is not set +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# # # Input core support is needed for joysticks @@ -344,24 +350,30 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_TMPFS=y # CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -384,6 +396,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -448,6 +461,100 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/est8260_defconfig linux.ac/arch/ppc/configs/est8260_defconfig --- linux.vanilla/arch/ppc/configs/est8260_defconfig Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/configs/est8260_defconfig Wed Oct 10 01:47:33 2001 @@ -176,11 +176,9 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_ARM_AM79C961A is not set # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set -# CONFIG_NCR885E is not set # CONFIG_OAKNET is not set # CONFIG_SUNLANCE is not set # CONFIG_SUNBMAC is not set @@ -199,9 +197,9 @@ # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set -# CONFIG_ACENIC_OMIT_TIGON_I is not set # CONFIG_DL2K is not set # CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set @@ -263,6 +261,10 @@ # Input core support # # CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set # # Macintosh device drivers @@ -292,7 +294,11 @@ # # Joysticks # -# CONFIG_JOYSTICK is not set +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# # # Input core support is needed for joysticks @@ -331,24 +337,30 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_TMPFS=y # CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -371,6 +383,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -422,6 +435,100 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/gemini_defconfig linux.ac/arch/ppc/configs/gemini_defconfig --- linux.vanilla/arch/ppc/configs/gemini_defconfig Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/configs/gemini_defconfig Wed Oct 10 01:47:33 2001 @@ -202,6 +202,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 @@ -219,6 +220,7 @@ # CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR_D700 is not set # CONFIG_SCSI_NCR53C7xx is not set # CONFIG_SCSI_NCR53C8XX is not set CONFIG_SCSI_SYM53C8XX=y @@ -270,11 +272,9 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_ARM_AM79C961A is not set # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set -CONFIG_NCR885E=y # CONFIG_OAKNET is not set # CONFIG_SUNLANCE is not set # CONFIG_HAPPYMEAL is not set @@ -295,9 +295,9 @@ # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set -# CONFIG_ACENIC_OMIT_TIGON_I is not set # CONFIG_DL2K is not set # CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set @@ -359,6 +359,10 @@ # Input core support # # CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set # # Macintosh device drivers @@ -390,7 +394,11 @@ # # Joysticks # -# CONFIG_JOYSTICK is not set +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# # # Input core support is needed for joysticks @@ -429,24 +437,30 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_TMPFS=y # CONFIG_RAMFS is not set CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -469,6 +483,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set # CONFIG_ROOT_NFS is not set @@ -517,6 +532,100 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/ibmchrp_defconfig linux.ac/arch/ppc/configs/ibmchrp_defconfig --- linux.vanilla/arch/ppc/configs/ibmchrp_defconfig Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/configs/ibmchrp_defconfig Wed Oct 10 01:47:33 2001 @@ -227,6 +227,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 @@ -244,6 +245,7 @@ # CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR_D700 is not set # CONFIG_SCSI_NCR53C7xx is not set # CONFIG_SCSI_NCR53C8XX is not set CONFIG_SCSI_SYM53C8XX=y @@ -295,11 +297,9 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_ARM_AM79C961A is not set # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set -# CONFIG_NCR885E is not set # CONFIG_OAKNET is not set # CONFIG_SUNLANCE is not set # CONFIG_HAPPYMEAL is not set @@ -319,8 +319,6 @@ # CONFIG_APRICOT is not set # CONFIG_CS89x0 is not set # CONFIG_TULIP is not set -# CONFIG_TULIP_MWI is not set -# CONFIG_TULIP_MMIO is not set CONFIG_DE4X5=y # CONFIG_DGRS is not set # CONFIG_DM9102 is not set @@ -347,9 +345,9 @@ # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set -# CONFIG_ACENIC_OMIT_TIGON_I is not set # CONFIG_DL2K is not set # CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set @@ -422,8 +420,6 @@ # CONFIG_FB_IMSTT is not set # CONFIG_FB_S3TRIO is not set # CONFIG_FB_VGA16 is not set -# CONFIG_FB_PVR2 is not set -# CONFIG_FB_PVR2_DEBUG is not set # CONFIG_FB_E1355 is not set CONFIG_FB_MATROX=y CONFIG_FB_MATROX_MILLENIUM=y @@ -432,9 +428,10 @@ # CONFIG_FB_MATROX_G450 is not set # CONFIG_FB_MATROX_MULTIHEAD is not set # CONFIG_FB_ATY is not set +# CONFIG_FB_RADEON is not set # CONFIG_FB_ATY128 is not set -CONFIG_FB_3DFX=y # CONFIG_FB_SIS is not set +CONFIG_FB_3DFX=y # CONFIG_FB_VIRTUAL is not set # CONFIG_FBCON_ADVANCED is not set CONFIG_FBCON_CFB8=y @@ -505,11 +502,37 @@ # # Joysticks # -# CONFIG_JOYSTICK is not set +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_INPUT_NS558 is not set +# CONFIG_INPUT_LIGHTNING is not set +# CONFIG_INPUT_PCIGAME is not set +# CONFIG_INPUT_CS461X is not set +# CONFIG_INPUT_EMU10K1 is not set +# CONFIG_INPUT_SERIO is not set +# CONFIG_INPUT_SERPORT is not set # -# Input core support is needed for joysticks +# Joysticks # +# CONFIG_INPUT_ANALOG is not set +# CONFIG_INPUT_A3D is not set +# CONFIG_INPUT_ADI is not set +# CONFIG_INPUT_COBRA is not set +# CONFIG_INPUT_GF2K is not set +# CONFIG_INPUT_GRIP is not set +# CONFIG_INPUT_INTERACT is not set +# CONFIG_INPUT_TMDC is not set +# CONFIG_INPUT_SIDEWINDER is not set +# CONFIG_INPUT_IFORCE_USB is not set +# CONFIG_INPUT_IFORCE_232 is not set +# CONFIG_INPUT_WARRIOR is not set +# CONFIG_INPUT_MAGELLAN is not set +# CONFIG_INPUT_SPACEORB is not set +# CONFIG_INPUT_SPACEBALL is not set +# CONFIG_INPUT_STINGER is not set +# CONFIG_INPUT_DB9 is not set +# CONFIG_INPUT_GAMECON is not set +# CONFIG_INPUT_TURBOGRAFX is not set # CONFIG_QIC02_TAPE is not set # @@ -544,24 +567,30 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +CONFIG_EXT3_FS=y +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set CONFIG_VFAT_FS=m # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_TMPFS=y # CONFIG_RAMFS is not set CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -584,6 +613,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set # CONFIG_NFS_FS is not set # CONFIG_NFS_V3 is not set # CONFIG_ROOT_NFS is not set @@ -673,6 +703,101 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/mbx_defconfig linux.ac/arch/ppc/configs/mbx_defconfig --- linux.vanilla/arch/ppc/configs/mbx_defconfig Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/configs/mbx_defconfig Wed Oct 10 01:47:33 2001 @@ -184,11 +184,9 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_ARM_AM79C961A is not set # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set -# CONFIG_NCR885E is not set # CONFIG_OAKNET is not set # CONFIG_SUNLANCE is not set # CONFIG_SUNBMAC is not set @@ -207,9 +205,9 @@ # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set -# CONFIG_ACENIC_OMIT_TIGON_I is not set # CONFIG_DL2K is not set # CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set @@ -270,6 +268,10 @@ # Input core support # # CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set # # Macintosh device drivers @@ -298,7 +300,11 @@ # # Joysticks # -# CONFIG_JOYSTICK is not set +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# # # Input core support is needed for joysticks @@ -337,24 +343,30 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_TMPFS=y # CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -377,6 +389,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -441,6 +454,100 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/oak_defconfig linux.ac/arch/ppc/configs/oak_defconfig --- linux.vanilla/arch/ppc/configs/oak_defconfig Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/configs/oak_defconfig Wed Oct 10 01:47:33 2001 @@ -173,11 +173,9 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_ARM_AM79C961A is not set # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set -# CONFIG_NCR885E is not set CONFIG_OAKNET=y # CONFIG_SUNLANCE is not set # CONFIG_SUNBMAC is not set @@ -196,9 +194,9 @@ # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set -# CONFIG_ACENIC_OMIT_TIGON_I is not set # CONFIG_DL2K is not set # CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set @@ -259,6 +257,10 @@ # Input core support # # CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set # # Macintosh device drivers @@ -288,7 +290,11 @@ # # Joysticks # -# CONFIG_JOYSTICK is not set +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# # # Input core support is needed for joysticks @@ -327,24 +333,30 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_TMPFS=y # CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -367,6 +379,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -402,6 +415,100 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/rpxcllf_defconfig linux.ac/arch/ppc/configs/rpxcllf_defconfig --- linux.vanilla/arch/ppc/configs/rpxcllf_defconfig Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/configs/rpxcllf_defconfig Wed Oct 10 01:47:33 2001 @@ -190,11 +190,9 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_ARM_AM79C961A is not set # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set -# CONFIG_NCR885E is not set # CONFIG_OAKNET is not set # CONFIG_SUNLANCE is not set # CONFIG_SUNBMAC is not set @@ -213,9 +211,9 @@ # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set -# CONFIG_ACENIC_OMIT_TIGON_I is not set # CONFIG_DL2K is not set # CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set @@ -276,6 +274,10 @@ # Input core support # # CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set # # Macintosh device drivers @@ -305,7 +307,11 @@ # # Joysticks # -# CONFIG_JOYSTICK is not set +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# # # Input core support is needed for joysticks @@ -344,24 +350,30 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_TMPFS=y # CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -384,6 +396,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -449,6 +462,100 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/rpxlite_defconfig linux.ac/arch/ppc/configs/rpxlite_defconfig --- linux.vanilla/arch/ppc/configs/rpxlite_defconfig Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/configs/rpxlite_defconfig Wed Oct 10 01:47:33 2001 @@ -190,11 +190,9 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_ARM_AM79C961A is not set # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set -# CONFIG_NCR885E is not set # CONFIG_OAKNET is not set # CONFIG_SUNLANCE is not set # CONFIG_SUNBMAC is not set @@ -213,9 +211,9 @@ # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set -# CONFIG_ACENIC_OMIT_TIGON_I is not set # CONFIG_DL2K is not set # CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set @@ -276,6 +274,10 @@ # Input core support # # CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set # # Macintosh device drivers @@ -305,7 +307,11 @@ # # Joysticks # -# CONFIG_JOYSTICK is not set +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# # # Input core support is needed for joysticks @@ -344,24 +350,30 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_TMPFS=y # CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -384,6 +396,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -446,6 +459,100 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/configs/walnut_defconfig linux.ac/arch/ppc/configs/walnut_defconfig --- linux.vanilla/arch/ppc/configs/walnut_defconfig Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/configs/walnut_defconfig Wed Oct 10 01:47:33 2001 @@ -173,11 +173,9 @@ # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_ARM_AM79C961A is not set # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set -# CONFIG_NCR885E is not set # CONFIG_OAKNET is not set # CONFIG_SUNLANCE is not set # CONFIG_SUNBMAC is not set @@ -196,9 +194,9 @@ # Ethernet (1000 Mbit) # # CONFIG_ACENIC is not set -# CONFIG_ACENIC_OMIT_TIGON_I is not set # CONFIG_DL2K is not set # CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set @@ -259,6 +257,10 @@ # Input core support # # CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set # # Macintosh device drivers @@ -291,7 +293,11 @@ # # Joysticks # -# CONFIG_JOYSTICK is not set +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# # # Input core support is needed for joysticks @@ -330,24 +336,30 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set CONFIG_TMPFS=y # CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set -# CONFIG_VXFS_FS is not set +# CONFIG_FREEVXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set @@ -370,6 +382,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -405,6 +418,100 @@ # 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 +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# 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_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# +# 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 + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# 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 # # Bluetooth support diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/gemini_pci.c linux.ac/arch/ppc/kernel/gemini_pci.c --- linux.vanilla/arch/ppc/kernel/gemini_pci.c Tue May 22 01:04:47 2001 +++ linux.ac/arch/ppc/kernel/gemini_pci.c Wed Oct 10 01:47:33 2001 @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/gemini_setup.c linux.ac/arch/ppc/kernel/gemini_setup.c --- linux.vanilla/arch/ppc/kernel/gemini_setup.c Tue Aug 28 14:58:33 2001 +++ linux.ac/arch/ppc/kernel/gemini_setup.c Wed Oct 10 01:47:33 2001 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.gemini_setup.c 1.11 08/20/01 14:34:41 paulus + * BK Id: SCCS/s.gemini_setup.c 1.12 09/25/01 07:54:40 trini */ /* * linux/arch/ppc/kernel/setup.c @@ -150,6 +150,9 @@ void gemini_heartbeat(void) { + /* We only want to do this on 1 CPU */ + if ( smp_processor_id() ) + return; static unsigned long led = GEMINI_LEDBASE+(4*8); static char direction = 8; *(char *)led = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/open_pic.c linux.ac/arch/ppc/kernel/open_pic.c --- linux.vanilla/arch/ppc/kernel/open_pic.c Sat Sep 8 20:38:41 2001 +++ linux.ac/arch/ppc/kernel/open_pic.c Wed Oct 10 01:47:33 2001 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.open_pic.c 1.28 09/08/01 15:47:42 paulus + * BK Id: SCCS/s.open_pic.c 1.30 09/24/01 16:13:57 trini */ /* * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling @@ -517,8 +517,10 @@ void openpic_init_processor(u_int cpumask) { + /* physmask won't work at this point since smp_num_cpus + * isn't valid yet. */ openpic_write(&OpenPIC->Global.Processor_Initialization, - physmask(cpumask)); + cpumask); } static spinlock_t openpic_setup_lock = SPIN_LOCK_UNLOCKED; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/prep_pci.c linux.ac/arch/ppc/kernel/prep_pci.c --- linux.vanilla/arch/ppc/kernel/prep_pci.c Sat Sep 8 20:38:42 2001 +++ linux.ac/arch/ppc/kernel/prep_pci.c Wed Oct 10 01:47:33 2001 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.prep_pci.c 1.26 09/08/01 15:47:42 paulus + * BK Id: SCCS/s.prep_pci.c 1.28 09/15/01 09:13:52 trini */ /* * PReP pci functions. @@ -1223,7 +1223,8 @@ hose->first_busno = 0; hose->last_busno = 0xff; hose->pci_mem_offset = PREP_ISA_MEM_BASE; - hose->io_base_virt = (void *)PREP_ISA_IO_BASE; + hose->io_base_phys = (void *)PREP_ISA_IO_BASE; + hose->io_base_virt = (void *)0x80000000; /* see prep_map_io() */ prep_init_resource(&hose->io_resource, 0, 0x0fffffff, IORESOURCE_IO); prep_init_resource(&hose->mem_resources[0], 0xc0000000, 0xfeffffff, IORESOURCE_MEM); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/prep_setup.c linux.ac/arch/ppc/kernel/prep_setup.c --- linux.vanilla/arch/ppc/kernel/prep_setup.c Sat Sep 8 20:38:42 2001 +++ linux.ac/arch/ppc/kernel/prep_setup.c Wed Oct 10 01:47:34 2001 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.prep_setup.c 1.36 09/08/01 15:47:42 paulus + * BK Id: SCCS/s.prep_setup.c 1.38 09/15/01 09:13:52 trini */ /* * linux/arch/ppc/kernel/setup.c @@ -840,8 +840,8 @@ */ void __init prep_map_io(void) { - io_block_mapping(0x80000000, 0x80000000, 0x10000000, _PAGE_IO); - io_block_mapping(0xf0000000, 0xc0000000, 0x08000000, _PAGE_IO); + io_block_mapping(0x80000000, PREP_ISA_IO_BASE, 0x10000000, _PAGE_IO); + io_block_mapping(0xf0000000, PREP_ISA_MEM_BASE, 0x08000000, _PAGE_IO); } void __init diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/setup.c linux.ac/arch/ppc/kernel/setup.c --- linux.vanilla/arch/ppc/kernel/setup.c Sat Sep 8 20:38:42 2001 +++ linux.ac/arch/ppc/kernel/setup.c Wed Oct 10 01:47:34 2001 @@ -136,7 +136,21 @@ extern u32 cpu_temp_both(unsigned long cpu); #endif /* CONFIG_TAU */ -int get_cpuinfo(char *buffer) +/* + * get_cpuinfo - Get information on one CPU for use by procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. + */ + +int get_cpuinfo(char *buffer, unsigned *cpu_np) { unsigned long len = 0; unsigned long bogosum = 0; @@ -155,127 +169,114 @@ #define CD(x) (x) #endif - for ( i = 0; i < smp_num_cpus ; i++ ) - { - if ( !CPU_PRESENT(i) ) - continue; - if ( i ) - len += sprintf(len+buffer,"\n"); - len += sprintf(len+buffer,"processor\t: %lu\n",i); - len += sprintf(len+buffer,"cpu\t\t: "); - - pvr = GET_PVR; - - if (cur_cpu_spec[i]->pvr_mask) - len += sprintf(len+buffer, "%s", cur_cpu_spec[i]->cpu_name); - else - len += sprintf(len+buffer, "unknown (%08x)", pvr); + i = *cpu_np; + while (i < NR_CPUS && (cpu_online_map & (1 << i)) == 0) { + ++i; + } + if (i >= NR_CPUS) { + /* Insert summary data as a fake CPU at NR_CPUS. */ + if (i > NR_CPUS) { + *cpu_np = NR_CPUS + 1; + return (0); + } +#ifdef CONFIG_SMP + for ( i = 0; i < smp_num_cpus ; i++ ) + { + if ( !CPU_PRESENT(i) ) + continue; + bogosum += CD(loops_per_jiffy); + } + len += sprintf(buffer+len,"total bogomips\t: %lu.%02lu\n", + bogosum/(500000/HZ), + bogosum/(5000/HZ) % 100); +#endif /* CONFIG_SMP */ + + if (ppc_md.get_cpuinfo != NULL) + { + len += ppc_md.get_cpuinfo(buffer+len); + } + *cpu_np = NR_CPUS + 1; + return (len); + } + *cpu_np = i + 1; + + len += sprintf(len+buffer,"processor\t: %lu\n",i); + len += sprintf(len+buffer,"cpu\t\t: "); + + pvr = GET_PVR; + + if (cur_cpu_spec[i]->pvr_mask) + len += sprintf(len+buffer, "%s", cur_cpu_spec[i]->cpu_name); + else + len += sprintf(len+buffer, "unknown (%08x)", pvr); #ifdef CONFIG_ALTIVEC - if (cur_cpu_spec[i]->cpu_features & CPU_FTR_ALTIVEC) - len += sprintf(len+buffer, ", altivec supported"); + if (cur_cpu_spec[i]->cpu_features & CPU_FTR_ALTIVEC) + len += sprintf(len+buffer, ", altivec supported"); #endif - len += sprintf(len+buffer, "\n"); + len += sprintf(len+buffer, "\n"); #ifdef CONFIG_TAU - if (cur_cpu_spec[i]->cpu_features & CPU_FTR_TAU) { + if (cur_cpu_spec[i]->cpu_features & CPU_FTR_TAU) { #ifdef CONFIG_TAU_AVERAGE /* more straightforward, but potentially misleading */ - len += sprintf(len+buffer, "temperature \t: %u C (uncalibrated)\n", - cpu_temp(i)); + len += sprintf(len+buffer, "temperature \t: %u C (uncalibrated)\n", + cpu_temp(i)); #else - /* show the actual temp sensor range */ - u32 temp; - temp = cpu_temp_both(i); - len += sprintf(len+buffer, "temperature \t: %u-%u C (uncalibrated)\n", - temp & 0xff, temp >> 16); + /* show the actual temp sensor range */ + u32 temp; + temp = cpu_temp_both(i); + len += sprintf(len+buffer, "temperature \t: %u-%u C (uncalibrated)\n", + temp & 0xff, temp >> 16); #endif - } + } #endif - /* - * Assume here that all clock rates are the same in a - * smp system. -- Cort - */ + /* + * Assume here that all clock rates are the same in a + * smp system. -- Cort + */ #if defined(CONFIG_ALL_PPC) - if ( have_of ) - { - struct device_node *cpu_node; - int *fp; - - cpu_node = find_type_devices("cpu"); - if ( !cpu_node ) break; - { - int s; - for ( s = 0; (s < i) && cpu_node->next ; - s++, cpu_node = cpu_node->next ) - /* nothing */ ; -#if 0 /* SMP Pmacs don't have all cpu nodes -- Cort */ - if ( s != i ) - printk("get_cpuinfo(): ran out of " - "cpu nodes.\n"); -#endif - } + if (have_of) { + struct device_node *cpu_node; + int s, *fp = NULL; + + cpu_node = find_type_devices("cpu"); + fp = (int *) get_property(cpu_node, "clock-frequency", NULL); + for (s = 0; s < i && cpu_node != NULL; s++) + cpu_node = cpu_node->next; + if (cpu_node != NULL) fp = (int *) get_property(cpu_node, "clock-frequency", NULL); - if ( !fp ) break; + if (fp != NULL) len += sprintf(len+buffer, "clock\t\t: %dMHz\n", *fp / 1000000); - } + } #endif /* CONFIG_ALL_PPC */ - if (ppc_md.setup_residual != NULL) - { - len += ppc_md.setup_residual(buffer + len); - } - - switch (PVR_VER(pvr)) - { - case 0x0020: - maj = PVR_MAJ(pvr) + 1; - min = PVR_MIN(pvr); - break; - case 0x1008: - maj = ((pvr >> 8) & 0xFF) - 1; - min = pvr & 0xFF; - break; - default: - maj = (pvr >> 8) & 0xFF; - min = pvr & 0xFF; - break; - } - - len += sprintf(len+buffer, "revision\t: %hd.%hd (pvr %04x %04x)\n", - maj, min, PVR_VER(pvr), PVR_REV(pvr)); - - len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n", - CD(loops_per_jiffy)/(500000/HZ), - CD(loops_per_jiffy)/(5000/HZ) % 100); - bogosum += CD(loops_per_jiffy); + if (ppc_md.setup_residual != NULL) + { + len += ppc_md.setup_residual(buffer + len); } - -#ifdef CONFIG_SMP - if ( i && smp_num_cpus > 1) - len += sprintf(buffer+len, "\n"); - len += sprintf(buffer+len,"total bogomips\t: %lu.%02lu\n", - bogosum/(500000/HZ), - bogosum/(5000/HZ) % 100); -#endif /* CONFIG_SMP */ - - /* - * Ooh's and aah's info about zero'd pages in idle task - */ - len += sprintf(buffer+len,"zero pages\t: total: %u (%luKb) " - "current: %u (%luKb) hits: %u/%u (%u%%)\n", - atomic_read(&zero_cache_total), - (atomic_read(&zero_cache_total)*PAGE_SIZE)>>10, - atomic_read(&zero_cache_sz), - (atomic_read(&zero_cache_sz)*PAGE_SIZE)>>10, - atomic_read(&zero_cache_hits),atomic_read(&zero_cache_calls), - /* : 1 below is so we don't div by zero */ - (atomic_read(&zero_cache_hits)*100) / - ((atomic_read(&zero_cache_calls))?atomic_read(&zero_cache_calls):1)); - - if (ppc_md.get_cpuinfo != NULL) + + switch (PVR_VER(pvr)) { - len += ppc_md.get_cpuinfo(buffer+len); + case 0x0020: + maj = PVR_MAJ(pvr) + 1; + min = PVR_MIN(pvr); + break; + case 0x1008: + maj = ((pvr >> 8) & 0xFF) - 1; + min = pvr & 0xFF; + break; + default: + maj = (pvr >> 8) & 0xFF; + min = pvr & 0xFF; + break; } + + len += sprintf(len+buffer, "revision\t: %hd.%hd (pvr %04x %04x)\n", + maj, min, PVR_VER(pvr), PVR_REV(pvr)); + + len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n\n", + CD(loops_per_jiffy)/(500000/HZ), + CD(loops_per_jiffy)/(5000/HZ) % 100); return len; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/smp.c linux.ac/arch/ppc/kernel/smp.c --- linux.vanilla/arch/ppc/kernel/smp.c Sat Sep 8 20:38:42 2001 +++ linux.ac/arch/ppc/kernel/smp.c Wed Oct 10 01:47:34 2001 @@ -283,6 +283,7 @@ printk("Entering SMP Mode...\n"); smp_num_cpus = 1; smp_store_cpu_info(0); + cpu_online_map = 1UL; /* * assume for now that the first cpu booted is diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/mm/fault.c linux.ac/arch/ppc/mm/fault.c --- linux.vanilla/arch/ppc/mm/fault.c Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/ppc/mm/fault.c Thu Oct 11 14:02:38 2001 @@ -150,7 +150,6 @@ * make sure we exit gracefully rather than endlessly redo * the fault. */ - survive: switch (handle_mm_fault(mm, vma, address, is_write)) { case 1: current->min_flt++; @@ -196,12 +195,6 @@ */ out_of_memory: up_read(&mm->mmap_sem); - if (current->pid == 1) { - current->policy |= SCHED_YIELD; - schedule(); - down_read(&mm->mmap_sem); - goto survive; - } printk("VM: killing process %s\n", current->comm); if (user_mode(regs)) do_exit(SIGKILL); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/Makefile linux.ac/arch/ppc64/Makefile --- linux.vanilla/arch/ppc64/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/Makefile Wed Oct 10 01:47:34 2001 @@ -0,0 +1,87 @@ +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. Remember to do have actions +# for "archclean" and "archdep" for cleaning up and making dependencies for +# this architecture +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# Changes for PPC by Gary Thomas +# Rewritten by Cort Dougan and Paul Mackerras +# Adjusted for PPC64 by Tom Gall +# + +KERNELLOAD =0xc000000000000000 + +ifeq ($(shell uname -m),ppc64) +CHECKS = checks +endif + +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 \ + -msoft-float -pipe -Wno-uninitialized $(PRINTK) \ + -include $(TOPDIR)/arch/ppc64/mymacros.h -mminimal-toc \ + -fno-builtin +CPP = $(CC) -E $(CFLAGS) + + +HEAD := arch/ppc64/kernel/head.o + +ARCH_SUBDIRS = arch/ppc64/kernel arch/ppc64/mm arch/ppc64/lib +SUBDIRS := $(SUBDIRS) $(ARCH_SUBDIRS) +ARCHIVES := arch/ppc64/kernel/kernel.o arch/ppc64/mm/mm.o arch/ppc64/lib/lib.o $(ARCHIVES) +CORE_FILES := arch/ppc64/kernel/kernel.o arch/ppc64/mm/mm.o arch/ppc64/lib/lib.o $(CORE_FILES) + +ifdef CONFIG_XMON +SUBDIRS += arch/ppc64/xmon +CORE_FILES += arch/ppc64/xmon/x.o +endif +ifdef CONFIG_KDB +SUBDIRS += arch/ppc64/kdb +CORE_FILES += arch/ppc64/kdb/kdba.o +endif + +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot + +checks: + @$(MAKE) -C arch/$(ARCH)/kernel checks + +BOOT_TARGETS = zImage znetboot.initrd zImage.initrd + +ifdef CONFIG_PPC_PSERIES +$(BOOT_TARGETS): $(CHECKS) vmlinux + @$(MAKEBOOT) $@ + +znetboot: $(CHECKS) vmlinux +ifdef CONFIG_ALL_PPC +ifdef CONFIG_SMP + cp -f vmlinux /tftpboot/vmlinux.smp +else + cp -f vmlinux /tftpboot/vmlinux.smp.64 +endif +endif + @$(MAKEBOOT) $@ +endif + +.PHONY: clean_config +clean_config: + rm -f .config arch/ppc64/defconfig + +chrp_config: clean_config + cp -f arch/ppc64/configs/chrp_defconfig arch/ppc64/defconfig + +common_config: clean_config + cp -f arch/ppc64/configs/common_defconfig arch/ppc64/defconfig + +archclean: + rm -f arch/ppc64/kernel/{ppc_defs.h,checks,mk_defs.s,mk_defs_out.c,mk_defs_tpl} + @$(MAKEBOOT) clean + +archmrproper: + +archdep: + $(MAKEBOOT) fastdep diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/boot/Makefile linux.ac/arch/ppc64/boot/Makefile --- linux.vanilla/arch/ppc64/boot/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/boot/Makefile Wed Oct 10 01:47:34 2001 @@ -0,0 +1,94 @@ +# Makefile for making ELF bootable images for booting on CHRP +# using Open Firmware. +# +# Geert Uytterhoeven September 1997 +# +# Based on coffboot by Paul Mackerras +# Simplified for ppc64 by Todd Inglett +# +# NOTE: this code is built for 32 bit in ELF32 format even though +# it packages a 64 bit kernel. We do this to simplify the +# bootloader and increase compatibility with OpenFirmware. +# +# To this end we need to define BOOTCC, etc, as the tools +# needed to build the 32 bit image. These are normally HOSTCC, +# but may be a third compiler if, for example, you are cross +# compiling from an intel box. Once the 64bit ppc gcc is +# stable it will probably simply be a compiler switch to +# compile for 32bit mode. + +BOOTCC = $(HOSTCC) +BOOTCFLAGS = $(HOSTCFLAGS) -I$(HPATH) +BOOTLD = ld +BOOTAS = as +BOOTAFLAGS = -D__ASSEMBLY__ $(HOSTCFLAGS) + +.c.o: + $(BOOTCC) $(BOOTCFLAGS) -c -o $*.o $< +.S.o: + $(BOOTCC) $(BOOTAFLAGS) -traditional -c -o $*.o $< + +CFLAGS = $(CPPFLAGS) -O -fno-builtin -DSTDC_HEADERS +LD_ARGS = -Ttext 0x00400000 + +OBJS = crt0.o start.o main.o zlib.o image.o imagesize.o +#LIBS = $(TOPDIR)/lib/lib.a +LIBS = + +ifeq ($(CONFIG_SMP),y) +TFTPIMAGE=/tftpboot/zImage.chrp.smp +else +TFTPIMAGE=/tftpboot/zImage.chrp +endif + +all: $(TOPDIR)/zImage + +znetboot: zImage + cp zImage $(TFTPIMAGE) + +znetboot.initrd: zImage.initrd + cp zImage.initrd $(TFTPIMAGE) + +floppy: zImage + mcopy zImage a:zImage + +piggyback: piggyback.c + $(HOSTCC) $(HOSTCFLAGS) -DKERNELBASE=$(KERNELBASE) -o piggyback piggyback.c + +addnote: addnote.c + $(HOSTCC) $(HOSTCFLAGS) -o addnote addnote.c + +image.o: piggyback vmlinux.gz + ./piggyback image < vmlinux.gz | $(BOOTAS) -o image.o + +sysmap.o: piggyback ../../../System.map + ./piggyback sysmap < ../../../System.map | $(BOOTAS) -o sysmap.o + +initrd.o: ramdisk.image.gz piggyback + ./piggyback initrd < ramdisk.image.gz | $(BOOTAS) -o initrd.o + +zImage: $(OBJS) no_initrd.o addnote + $(BOOTLD) $(LD_ARGS) -T zImage.lds -o $@ $(OBJS) no_initrd.o $(LIBS) + ./addnote $@ + +zImage.initrd: $(OBJS) initrd.o addnote + $(BOOTLD) $(LD_ARGS) -T zImage.lds -o $@ $(OBJS) initrd.o $(LIBS) + ./addnote $@ + + +vmlinux.gz: $(TOPDIR)/vmlinux + $(OBJCOPY) -S -O binary $(TOPDIR)/vmlinux vmlinux + ls -l vmlinux | awk '{printf "/* generated -- do not edit! */\nint uncompressed_size = %d;\n", $$5}' > imagesize.c + gzip -vf9 vmlinux + +imagesize.c: vmlinux.gz + +clean: + rm -f piggyback note addnote $(OBJS) zImage vmlinux.gz no_initrd.o + +fastdep: + $(TOPDIR)/scripts/mkdep *.[Sch] > .depend + +dep: + $(CPP) $(CPPFLAGS) -M *.S *.c > .depend + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/boot/addnote.c linux.ac/arch/ppc64/boot/addnote.c --- linux.vanilla/arch/ppc64/boot/addnote.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/boot/addnote.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,164 @@ +/* + * Program to hack in a PT_NOTE program header entry in an ELF file. + * This is needed for OF on RS/6000s to load an image correctly. + * Note that OF needs a program header entry for the note, not an + * ELF section. + * + * Copyright 2000 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Usage: addnote zImage + */ +#include +#include +#include +#include + +char arch[] = "PowerPC"; + +#define N_DESCR 6 +unsigned int descr[N_DESCR] = { + 0xffffffff, /* real-mode = true */ + 0x00c00000, /* real-base, i.e. where we expect OF to be */ + 0xffffffff, /* real-size */ + 0xffffffff, /* virt-base */ + 0xffffffff, /* virt-size */ + 0x4000, /* load-base */ +}; + +unsigned char buf[512]; + +#define GET_16BE(off) ((buf[off] << 8) + (buf[(off)+1])) +#define GET_32BE(off) ((GET_16BE(off) << 16) + GET_16BE((off)+2)) + +#define PUT_16BE(off, v) (buf[off] = ((v) >> 8) & 0xff, \ + buf[(off) + 1] = (v) & 0xff) +#define PUT_32BE(off, v) (PUT_16BE((off), (v) >> 16), \ + PUT_16BE((off) + 2, (v))) + +/* Structure of an ELF file */ +#define E_IDENT 0 /* ELF header */ +#define E_PHOFF 28 +#define E_PHENTSIZE 42 +#define E_PHNUM 44 +#define E_HSIZE 52 /* size of ELF header */ + +#define EI_MAGIC 0 /* offsets in E_IDENT area */ +#define EI_CLASS 4 +#define EI_DATA 5 + +#define PH_TYPE 0 /* ELF program header */ +#define PH_OFFSET 4 +#define PH_FILESZ 16 +#define PH_HSIZE 32 /* size of program header */ + +#define PT_NOTE 4 /* Program header type = note */ + +#define ELFCLASS32 1 +#define ELFDATA2MSB 2 + +unsigned char elf_magic[4] = { 0x7f, 'E', 'L', 'F' }; + +int +main(int ac, char **av) +{ + int fd, n, i; + int ph, ps, np; + int nnote, ns; + + if (ac != 2) { + fprintf(stderr, "Usage: %s elf-file\n", av[0]); + exit(1); + } + fd = open(av[1], O_RDWR); + if (fd < 0) { + perror(av[1]); + exit(1); + } + + nnote = strlen(arch) + 1 + (N_DESCR + 3) * 4; + + n = read(fd, buf, sizeof(buf)); + if (n < 0) { + perror("read"); + exit(1); + } + + if (n < E_HSIZE || memcmp(&buf[E_IDENT+EI_MAGIC], elf_magic, 4) != 0) + goto notelf; + + if (buf[E_IDENT+EI_CLASS] != ELFCLASS32 + || buf[E_IDENT+EI_DATA] != ELFDATA2MSB) { + fprintf(stderr, "%s is not a big-endian 32-bit ELF image\n", + av[1]); + exit(1); + } + + ph = GET_32BE(E_PHOFF); + ps = GET_16BE(E_PHENTSIZE); + np = GET_16BE(E_PHNUM); + if (ph < E_HSIZE || ps < PH_HSIZE || np < 1) + goto notelf; + if (ph + (np + 1) * ps + nnote > n) + goto nospace; + + for (i = 0; i < np; ++i) { + if (GET_32BE(ph + PH_TYPE) == PT_NOTE) { + fprintf(stderr, "%s already has a note entry\n", + av[1]); + exit(0); + } + ph += ps; + } + + /* XXX check that the area we want to use is all zeroes */ + for (i = 0; i < ps + nnote; ++i) + if (buf[ph + i] != 0) + goto nospace; + + /* fill in the program header entry */ + ns = ph + ps; + PUT_32BE(ph + PH_TYPE, PT_NOTE); + PUT_32BE(ph + PH_OFFSET, ns); + PUT_32BE(ph + PH_FILESZ, nnote); + + /* fill in the note area we point to */ + /* XXX we should probably make this a proper section */ + PUT_32BE(ns, strlen(arch) + 1); + PUT_32BE(ns + 4, N_DESCR * 4); + PUT_32BE(ns + 8, 0x1275); + strcpy(&buf[ns + 12], arch); + ns += 12 + strlen(arch) + 1; + for (i = 0; i < N_DESCR; ++i) + PUT_32BE(ns + i * 4, descr[i]); + + /* Update the number of program headers */ + PUT_16BE(E_PHNUM, np + 1); + + /* write back */ + lseek(fd, (long) 0, SEEK_SET); + i = write(fd, buf, n); + if (i < 0) { + perror("write"); + exit(1); + } + if (i < n) { + fprintf(stderr, "%s: write truncated\n", av[1]); + exit(1); + } + + exit(0); + + notelf: + fprintf(stderr, "%s does not appear to be an ELF file\n", av[0]); + exit(1); + + nospace: + fprintf(stderr, "sorry, I can't find space in %s to put the note\n", + av[0]); + exit(1); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/boot/crt0.S linux.ac/arch/ppc64/boot/crt0.S --- linux.vanilla/arch/ppc64/boot/crt0.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/boot/crt0.S Wed Oct 10 01:47:34 2001 @@ -0,0 +1,265 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * 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. + * + * NOTE: this code runs in 32 bit mode and is packaged as ELF32. + */ + .text + .globl _start +_start: + lis 9,_start@h + lis 8,_etext@ha + addi 8,8,_etext@l +1: dcbf 0,9 + icbi 0,9 + addi 9,9,0x20 + cmplwi 0,9,8 + blt 1b + sync + isync + + ## Clear out the BSS as per ANSI C requirements + + lis 7,_end@ha + addi 7,7,_end@l # r7 = &_end + lis 8,__bss_start@ha # + addi 8,8,__bss_start@l # r8 = &_bss_start + + ## Determine how large an area, in number of words, to clear + + subf 7,8,7 # r7 = &_end - &_bss_start + 1 + addi 7,7,3 # r7 += 3 + srwi. 7,7,2 # r7 = size in words. + beq 3f # If the size is zero, do not bother + addi 8,8,-4 # r8 -= 4 + mtctr 7 # SPRN_CTR = number of words to clear + li 0,0 # r0 = 0 +2: stwu 0,4(8) # Clear out a word + bdnz 2b # If we are not done yet, keep clearing +3: + + + b start + + + +/* + * Flush the dcache and invalidate the icache for a range of addresses. + * + * flush_cache(addr, len) + */ + .global flush_cache +flush_cache: + addi 4,4,0x1f /* len = (len + 0x1f) / 0x20 */ + rlwinm. 4,4,27,5,31 + mtctr 4 + beqlr +1: dcbf 0,3 + icbi 0,3 + addi 3,3,0x20 + bdnz 1b + sync + isync + blr + + +#define r0 0 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 + + .globl strcpy +strcpy: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strncpy +strncpy: + cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + blr + + .globl strcat +strcat: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r5) + cmpwi 0,r0,0 + bne 1b + addi r5,r5,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strcmp +strcmp: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r5) + cmpwi 1,r3,0 + lbzu r0,1(r4) + subf. r3,r0,r3 + beqlr 1 + beq 1b + blr + + .globl strlen +strlen: + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + blr + + .globl memset +memset: + rlwimi r4,r4,8,16,23 + rlwimi r4,r4,16,0,15 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + rlwinm r0,r5,32-2,2,31 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + + .globl bcopy +bcopy: + mr r6,r3 + mr r3,r4 + mr r4,r6 + b memcpy + + .globl memmove +memmove: + cmplw 0,r3,r4 + bgt backwards_memcpy + /* fall through */ + + .globl memcpy +memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + addi r6,r3,-4 + addi r4,r4,-4 + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) + lwzu r8,8(r4) + stw r7,4(r6) + stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,4(r4) + addi r5,r5,-4 + stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) + stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 + stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl backwards_memcpy +backwards_memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + add r6,r3,r5 + add r4,r4,r5 + beq 2f + andi. r0,r6,3 + mtctr r7 + bne 5f +1: lwz r7,-4(r4) + lwzu r8,-8(r4) + stw r7,-4(r6) + stwu r8,-8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,-4(r4) + subi r5,r5,4 + stwu r0,-4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 +4: lbzu r0,-1(r4) + stbu r0,-1(r6) + bdnz 4b + blr +5: mtctr r0 +6: lbzu r7,-1(r4) + stbu r7,-1(r6) + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl memcmp +memcmp: + cmpwi 0,r5,0 + blelr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r6) + lbzu r0,1(r4) + subf. r3,r0,r3 + bdnzt 2,1b + blr diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/boot/main.c linux.ac/arch/ppc64/boot/main.c --- linux.vanilla/arch/ppc64/boot/main.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/boot/main.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,258 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * Updates for PPC64 by Todd Inglett & Dave Engebretsen. + * + * 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. + */ +#define __KERNEL__ +#include "zlib.h" +#include +#include +#include +#include + +#define _ALIGN(addr,size) (((addr)+(size)-1)&(~((size)-1))) + +extern void *finddevice(const char *); +extern int getprop(void *, const char *, void *, int); +extern void printf(const char *fmt, ...); +extern int sprintf(char *buf, const char *fmt, ...); +void gunzip(void *, int, unsigned char *, int *); +void *claim(unsigned int, unsigned int, unsigned int); +void flush_cache(void *, int); +void pause(void); + +#define RAM_START 0x00000000 +#define RAM_END (64<<20) + +#define BOOT_START ((unsigned long)_start) +#define BOOT_END ((unsigned long)(_end + 0xFFF) & ~0xFFF) + +#define RAM_FREE ((unsigned long)(_end+0x1000)&~0xFFF) + +// Value picked to match that used by yaboot +#define PROG_START 0x01400000 + +char *avail_ram; +char *begin_avail, *end_avail; +char *avail_high; +unsigned int heap_use; +unsigned int heap_max; + +extern char _end[]; +extern char image_data[]; +extern int image_len; +extern char initrd_data[]; +extern int initrd_len; +extern char sysmap_data[]; +extern int sysmap_len; +extern int uncompressed_size; + +static char scratch[128<<10]; /* 128kB of scratch space for gunzip */ + +void +chrpboot(int a1, int a2, void *prom) +{ + unsigned sa, len; + void *dst, *claim_addr; + unsigned char *im; + unsigned initrd_start, initrd_size; + extern char _start; + + printf("chrpboot starting: loaded at 0x%x\n\r", (unsigned)&_start); + printf(" initrd_len = 0x%x\n\r", (unsigned)initrd_len); + + if (initrd_len) { + initrd_size = initrd_len; + initrd_start = (RAM_END - initrd_size) & ~0xFFF; + a1 = initrd_start; + a2 = initrd_size; + claim(initrd_start, RAM_END - initrd_start, 0); + printf("initial ramdisk moving 0x%x <- 0x%x (%x bytes)\n\r", + initrd_start, (unsigned)initrd_data, initrd_size); + memcpy((void *)initrd_start, (void *)initrd_data, initrd_size); + } + + im = image_data; + len = image_len; + uncompressed_size = _ALIGN(uncompressed_size, (1<<12)); /* page align it */ + + for(claim_addr = PROG_START; + claim_addr <= PROG_START * 8; + claim_addr += 0x100000) { + printf(" trying: 0x%08lx\n\r", claim_addr); + dst = claim(claim_addr, uncompressed_size, 0); + if (dst != (void *)-1) break; + } + if (dst == (void *)-1) { + printf("claim error, can't allocate kernel memory\n\r"); + return; + } + + if (im[0] == 0x1f && im[1] == 0x8b) { + avail_ram = scratch; + begin_avail = avail_high = avail_ram; + end_avail = scratch + sizeof(scratch); + printf("gunzipping (0x%x <- 0x%x:0x%0x)...", + (unsigned)dst, (unsigned)im, (unsigned)im+len); + gunzip(dst, uncompressed_size, im, &len); + printf("done %u bytes\n\r", len); + printf("%u bytes of heap consumed, max in use %u\n\r", + (unsigned)(avail_high - begin_avail), heap_max); + } else { + memmove(dst, im, len); + } + + flush_cache(dst, len); + make_bi_recs((unsigned long)dst + len); + + sa = (unsigned long)dst; + printf("start address = 0x%x\n\r", (unsigned) sa); + + (*(void (*)(int, int, void *))sa)(a1, a2, prom); + + printf("returned?\n\r"); + + pause(); +} + +void make_bi_recs(unsigned long addr) +{ + struct bi_record *rec; + + rec = (struct bi_record *)_ALIGN(addr + (1<<20) -1, (1<<20)); + + rec->tag = BI_FIRST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + rec->tag = BI_BOOTLOADER_ID; + sprintf( (char *)rec->data, "chrpboot"); + rec->size = sizeof(struct bi_record) + strlen("chrpboot") + 1; + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + rec->tag = BI_MACHTYPE; + rec->data[0] = _MACH_chrp; + rec->data[1] = 1; + rec->size = sizeof(struct bi_record) + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + rec->size); +#if 0 + rec->tag = BI_SYSMAP; + rec->data[0] = (unsigned long)sysmap_data; + rec->data[1] = sysmap_len; + rec->size = sizeof(struct bi_record) + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + rec->size); +#endif + rec->tag = BI_LAST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); +} + +struct memchunk { + unsigned int size; + unsigned int pad; + struct memchunk *next; +}; + +static struct memchunk *freechunks; + +void *zalloc(void *x, unsigned items, unsigned size) +{ + void *p; + struct memchunk **mpp, *mp; + + size *= items; + size = _ALIGN(size, sizeof(struct memchunk)); + heap_use += size; + if (heap_use > heap_max) + heap_max = heap_use; + for (mpp = &freechunks; (mp = *mpp) != 0; mpp = &mp->next) { + if (mp->size == size) { + *mpp = mp->next; + return mp; + } + } + p = avail_ram; + avail_ram += size; + if (avail_ram > avail_high) + avail_high = avail_ram; + if (avail_ram > end_avail) { + printf("oops... out of memory\n\r"); + pause(); + } + return p; +} + +void zfree(void *x, void *addr, unsigned nb) +{ + struct memchunk *mp = addr; + + nb = _ALIGN(nb, sizeof(struct memchunk)); + heap_use -= nb; + if (avail_ram == addr + nb) { + avail_ram = addr; + return; + } + mp->size = nb; + mp->next = freechunks; + freechunks = mp; +} + +#define HEAD_CRC 2 +#define EXTRA_FIELD 4 +#define ORIG_NAME 8 +#define COMMENT 0x10 +#define RESERVED 0xe0 + +#define DEFLATED 8 + +void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) +{ + z_stream s; + int r, i, flags; + + /* skip header */ + i = 10; + flags = src[3]; + if (src[2] != DEFLATED || (flags & RESERVED) != 0) { + printf("bad gzipped data\n\r"); + exit(); + } + if ((flags & EXTRA_FIELD) != 0) + i = 12 + src[10] + (src[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (src[i++] != 0) + ; + if ((flags & COMMENT) != 0) + while (src[i++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + i += 2; + if (i >= *lenp) { + printf("gunzip: ran out of data in header\n\r"); + exit(); + } + + s.zalloc = zalloc; + s.zfree = zfree; + r = inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + printf("inflateInit2 returned %d\n\r", r); + exit(); + } + s.next_in = src + i; + s.avail_in = *lenp - i; + s.next_out = dst; + s.avail_out = dstlen; + r = inflate(&s, Z_FINISH); + if (r != Z_OK && r != Z_STREAM_END) { + printf("inflate returned %d msg: %s\n\r", r, s.msg); + exit(); + } + *lenp = s.next_out - (unsigned char *) dst; + inflateEnd(&s); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/boot/mknote.c linux.ac/arch/ppc64/boot/mknote.c --- linux.vanilla/arch/ppc64/boot/mknote.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/boot/mknote.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,43 @@ +/* + * Copyright (C) Cort Dougan 1999. + * + * 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. + * + * Generate a note section as per the CHRP specification. + * + */ + +#include + +#define PL(x) printf("%c%c%c%c", ((x)>>24)&0xff, ((x)>>16)&0xff, ((x)>>8)&0xff, (x)&0xff ); + +int main(void) +{ +/* header */ + /* namesz */ + PL(strlen("PowerPC")+1); + /* descrsz */ + PL(6*4); + /* type */ + PL(0x1275); + /* name */ + printf("PowerPC"); printf("%c", 0); + +/* descriptor */ + /* real-mode */ + PL(0xffffffff); + /* real-base */ + PL(0x00c00000); + /* real-size */ + PL(0xffffffff); + /* virt-base */ + PL(0xffffffff); + /* virt-size */ + PL(0xffffffff); + /* load-base */ + PL(0x4000); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/boot/no_initrd.c linux.ac/arch/ppc64/boot/no_initrd.c --- linux.vanilla/arch/ppc64/boot/no_initrd.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/boot/no_initrd.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,2 @@ +char initrd_data[1]; +int initrd_len = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/boot/piggyback.c linux.ac/arch/ppc64/boot/piggyback.c --- linux.vanilla/arch/ppc64/boot/piggyback.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/boot/piggyback.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,74 @@ +/* + * Copyright 2001 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. + */ +#include +#include + +extern long ce_exec_config[]; + +int main(int argc, char *argv[]) +{ + int i, cnt, pos, len; + unsigned int cksum, val; + unsigned char *lp; + unsigned char buf[8192]; + if (argc != 2) + { + fprintf(stderr, "usage: %s name out-file\n", + argv[0]); + exit(1); + } + fprintf(stdout, "#\n"); + fprintf(stdout, "# Miscellaneous data structures:\n"); + fprintf(stdout, "# WARNING - this file is automatically generated!\n"); + fprintf(stdout, "#\n"); + fprintf(stdout, "\n"); + fprintf(stdout, "\t.data\n"); + fprintf(stdout, "\t.globl %s_data\n", argv[1]); + fprintf(stdout, "%s_data:\n", argv[1]); + pos = 0; + cksum = 0; + while ((len = read(0, buf, sizeof(buf))) > 0) + { + cnt = 0; + lp = (unsigned char *)buf; + len = (len + 3) & ~3; /* Round up to longwords */ + for (i = 0; i < len; i += 4) + { + if (cnt == 0) + { + fprintf(stdout, "\t.long\t"); + } + fprintf(stdout, "0x%02X%02X%02X%02X", lp[0], lp[1], lp[2], lp[3]); + val = *(unsigned long *)lp; + cksum ^= val; + lp += 4; + if (++cnt == 4) + { + cnt = 0; + fprintf(stdout, " # %x \n", pos+i-12); + fflush(stdout); + } else + { + fprintf(stdout, ","); + } + } + if (cnt) + { + fprintf(stdout, "0\n"); + } + pos += len; + } + fprintf(stdout, "\t.globl %s_len\n", argv[1]); + fprintf(stdout, "%s_len:\t.long\t0x%x\n", argv[1], pos); + fflush(stdout); + fclose(stdout); + fprintf(stderr, "cksum = %x\n", cksum); + exit(0); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/boot/start.c linux.ac/arch/ppc64/boot/start.c --- linux.vanilla/arch/ppc64/boot/start.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/boot/start.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,654 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include + +#include + +int (*prom)(void *); + +void *chosen_handle; +void *stdin; +void *stdout; +void *stderr; + +void exit(void); +void *finddevice(const char *name); +int getprop(void *phandle, const char *name, void *buf, int buflen); +void chrpboot(int a1, int a2, void *prom); /* in main.c */ + +void printk(char *fmt, ...); + +void +start(int a1, int a2, void *promptr) +{ + prom = (int (*)(void *)) promptr; + chosen_handle = finddevice("/chosen"); + if (chosen_handle == (void *) -1) + exit(); + if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4) + exit(); + stderr = stdout; + if (getprop(chosen_handle, "stdin", &stdin, sizeof(stdin)) != 4) + exit(); + + chrpboot(a1, a2, promptr); + for (;;) + exit(); +} + +int +write(void *handle, void *ptr, int nb) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *ihandle; + void *addr; + int len; + int actual; + } args; + + args.service = "write"; + args.nargs = 3; + args.nret = 1; + args.ihandle = handle; + args.addr = ptr; + args.len = nb; + args.actual = -1; + (*prom)(&args); + return args.actual; +} + +int +read(void *handle, void *ptr, int nb) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *ihandle; + void *addr; + int len; + int actual; + } args; + + args.service = "read"; + args.nargs = 3; + args.nret = 1; + args.ihandle = handle; + args.addr = ptr; + args.len = nb; + args.actual = -1; + (*prom)(&args); + return args.actual; +} + +void +exit() +{ + struct prom_args { + char *service; + } args; + + for (;;) { + args.service = "exit"; + (*prom)(&args); + } +} + +void +pause(void) +{ + struct prom_args { + char *service; + } args; + + args.service = "enter"; + (*prom)(&args); +} + +void * +finddevice(const char *name) +{ + struct prom_args { + char *service; + int nargs; + int nret; + const char *devspec; + void *phandle; + } args; + + args.service = "finddevice"; + args.nargs = 1; + args.nret = 1; + args.devspec = name; + args.phandle = (void *) -1; + (*prom)(&args); + return args.phandle; +} + +void * +claim(unsigned int virt, unsigned int size, unsigned int align) +{ + struct prom_args { + char *service; + int nargs; + int nret; + unsigned int virt; + unsigned int size; + unsigned int align; + void *ret; + } args; + + args.service = "claim"; + args.nargs = 3; + args.nret = 1; + args.virt = virt; + args.size = size; + args.align = align; + (*prom)(&args); + return args.ret; +} + +int +getprop(void *phandle, const char *name, void *buf, int buflen) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *phandle; + const char *name; + void *buf; + int buflen; + int size; + } args; + + args.service = "getprop"; + args.nargs = 4; + args.nret = 1; + args.phandle = phandle; + args.name = name; + args.buf = buf; + args.buflen = buflen; + args.size = -1; + (*prom)(&args); + return args.size; +} + +int +putc(int c, void *f) +{ + char ch = c; + + if (c == '\n') + putc('\r', f); + return write(f, &ch, 1) == 1? c: -1; +} + +int +putchar(int c) +{ + return putc(c, stdout); +} + +int +fputs(char *str, void *f) +{ + int n = strlen(str); + + return write(f, str, n) == n? 0: -1; +} + +int +readchar(void) +{ + char ch; + + for (;;) { + switch (read(stdin, &ch, 1)) { + case 1: + return ch; + case -1: + printk("read(stdin) returned -1\r\n"); + return -1; + } + } +} + +static char line[256]; +static char *lineptr; +static int lineleft; + +int +getchar(void) +{ + int c; + + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + putchar('\b'); + putchar(' '); + putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + putchar('\b'); + putchar(' '); + putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + putchar('\a'); + else { + putchar(c); + *lineptr++ = c; + } + } + } + lineleft = lineptr - line; + lineptr = line; + } + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; +} + + + +/* String functions lifted from lib/vsprintf.c and lib/ctype.c */ +unsigned char _ctype[] = { +_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ +_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ +_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ +_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ +_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ +_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ +_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ +_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ +_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */ +_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */ +_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */ +_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */ +_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */ +_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */ + +size_t strnlen(const char * s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + +unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base) +{ + unsigned long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((*cp == 'x') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } + while (isxdigit(*cp) && + (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + +long simple_strtol(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -simple_strtoul(cp+1,endp,base); + return simple_strtoul(cp,endp,base); +} + +static int skip_atoi(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +static char * number(char * str, long long num, int base, int size, int precision, int type) +{ + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + + if (type & LARGE) + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return 0; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) + while(size-->0) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type & SPECIAL) { + if (base==8) + *str++ = '0'; + else if (base==16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + return str; +} + +/* Forward decl. needed for IP address printing stuff... */ +int sprintf(char * buf, const char *fmt, ...); + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + unsigned long long num; + int i, base; + char * str; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + /* 'z' support added 23/7/1999 S.H. */ + /* 'z' changed to 'Z' --davidm 1/25/99 */ + + + for (str=buf ; *fmt ; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt =='Z') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + if (!s) + s = ""; + + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else if (qualifier == 'Z') { + size_t * ip = va_arg(args, size_t *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + *str++ = '%'; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + if (qualifier == 'L') + num = va_arg(args, long long); + else if (qualifier == 'l') { + num = va_arg(args, unsigned long); + if (flags & SIGN) + num = (signed long) num; + } else if (qualifier == 'Z') { + num = va_arg(args, size_t); + } else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (signed short) num; + } else { + num = va_arg(args, unsigned int); + if (flags & SIGN) + num = (signed int) num; + } + str = number(str, num, base, field_width, precision, flags); + } + *str = '\0'; + return str-buf; +} + +int sprintf(char * buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsprintf(buf,fmt,args); + va_end(args); + return i; +} + +static char sprint_buf[1024]; + +void +printk(char *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vsprintf(sprint_buf, fmt, args); + va_end(args); + write(stdout, sprint_buf, n); +} + +int +printf(char *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vsprintf(sprint_buf, fmt, args); + va_end(args); + write(stdout, sprint_buf, n); + return n; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/boot/zImage.lds linux.ac/arch/ppc64/boot/zImage.lds --- linux.vanilla/arch/ppc64/boot/zImage.lds Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/boot/zImage.lds Wed Oct 10 01:47:34 2001 @@ -0,0 +1,74 @@ +OUTPUT_ARCH(powerpc) +SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/local/powerpc-any-elf/lib); +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .plt : { *(.plt) } + .text : + { + *(.text) + *(.fixup) + *(.got1) + } + _etext = .; + PROVIDE (etext = .); + .rodata : + { + *(.rodata) + *(.rodata1) + } + .kstrtab : { *(.kstrtab) } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.got.plt) *(.got) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + .fixup : { *(.fixup) } + + __bss_start = .; + .bss : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/boot/zlib.c linux.ac/arch/ppc64/boot/zlib.c --- linux.vanilla/arch/ppc64/boot/zlib.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/boot/zlib.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,2170 @@ +/* + * This file is derived from various .h and .c files from the zlib-0.95 + * distribution by Jean-loup Gailly and Mark Adler, with some additions + * by Paul Mackerras to aid in implementing Deflate compression and + * decompression for PPP packets. See zlib.h for conditions of + * distribution and use. + * + * Changes that have been made include: + * - changed functions not used outside this file to "local" + * - added minCompression parameter to deflateInit2 + * - added Z_PACKET_FLUSH (see zlib.h for details) + * - added inflateIncomp + * + Copyright (C) 1995 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + gzip@prep.ai.mit.edu madler@alumni.caltech.edu + + * + * + */ + +/*+++++*/ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* From: zutil.h,v 1.9 1995/05/03 17:27:12 jloup Exp */ + +#define _Z_UTIL_H + +#include "zlib.h" + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#define FAR + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern char *z_errmsg[]; /* indexed by 1-zlib_error */ + +#define ERR_RETURN(strm,err) return (strm->msg=z_errmsg[1-err], err) +/* To be used only when the state is known to be valid */ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + + /* common constants */ + +#define DEFLATED 8 + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + + /* functions */ + +#include +#define zmemcpy memcpy +#define zmemzero(dest, len) memset(dest, 0, len) + +/* Diagnostic functions */ +#ifdef DEBUG_ZLIB +# include +# ifndef verbose +# define verbose 0 +# endif +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +typedef uLong (*check_func) OF((uLong check, Bytef *buf, uInt len)); + +/* voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); */ +/* void zcfree OF((voidpf opaque, voidpf ptr)); */ + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr, size) \ + (*((strm)->zfree))((strm)->opaque, (voidpf)(addr), (size)) +#define TRY_FREE(s, p, n) {if (p) ZFREE(s, p, n);} + +/* deflate.h -- internal compression state + * Copyright (C) 1995 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/*+++++*/ +/* infblock.h -- header to use infblock.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_blocks_state; +typedef struct inflate_blocks_state FAR inflate_blocks_statef; + +local inflate_blocks_statef * inflate_blocks_new OF(( + z_stream *z, + check_func c, /* check function */ + uInt w)); /* window size */ + +local int inflate_blocks OF(( + inflate_blocks_statef *, + z_stream *, + int)); /* initial return code */ + +local void inflate_blocks_reset OF(( + inflate_blocks_statef *, + z_stream *, + uLongf *)); /* check value on output */ + +local int inflate_blocks_free OF(( + inflate_blocks_statef *, + z_stream *, + uLongf *)); /* check value on output */ + +local int inflate_addhistory OF(( + inflate_blocks_statef *, + z_stream *)); + +local int inflate_packet_flush OF(( + inflate_blocks_statef *)); + +/*+++++*/ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). */ + +typedef struct inflate_huft_s FAR inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; /* number of extra bits or operation */ + Byte Bits; /* number of bits in this code or subcode */ + } what; + uInt Nalloc; /* number of these allocated here */ + Bytef *pad; /* pad structure to a power of 2 (4 bytes for */ + } word; /* 16-bit, 8 bytes for 32-bit machines) */ + union { + uInt Base; /* literal, length base, or distance base */ + inflate_huft *Next; /* pointer to next level of table */ + } more; +}; + +#ifdef DEBUG_ZLIB + local uInt inflate_hufts; +#endif + +local int inflate_trees_bits OF(( + uIntf *, /* 19 code lengths */ + uIntf *, /* bits tree desired/actual depth */ + inflate_huft * FAR *, /* bits tree result */ + z_stream *)); /* for zalloc, zfree functions */ + +local int inflate_trees_dynamic OF(( + uInt, /* number of literal/length codes */ + uInt, /* number of distance codes */ + uIntf *, /* that many (total) code lengths */ + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *, /* distance tree result */ + z_stream *)); /* for zalloc, zfree functions */ + +local int inflate_trees_fixed OF(( + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *)); /* distance tree result */ + +local int inflate_trees_free OF(( + inflate_huft *, /* tables to free */ + z_stream *)); /* for zfree function */ + + +/*+++++*/ +/* infcodes.h -- header to use infcodes.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_codes_state; +typedef struct inflate_codes_state FAR inflate_codes_statef; + +local inflate_codes_statef *inflate_codes_new OF(( + uInt, uInt, + inflate_huft *, inflate_huft *, + z_stream *)); + +local int inflate_codes OF(( + inflate_blocks_statef *, + z_stream *, + int)); + +local void inflate_codes_free OF(( + inflate_codes_statef *, + z_stream *)); + + +/*+++++*/ +/* inflate.c -- zlib interface to inflate modules + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* inflate private state */ +struct internal_state { + + /* mode */ + enum { + METHOD, /* waiting for method byte */ + FLAG, /* waiting for flag byte */ + BLOCKS, /* decompressing blocks */ + CHECK4, /* four check bytes to go */ + CHECK3, /* three check bytes to go */ + CHECK2, /* two check bytes to go */ + CHECK1, /* one check byte to go */ + DONE, /* finished check, done */ + BAD} /* got an error--stay here */ + mode; /* current inflate mode */ + + /* mode dependent information */ + union { + uInt method; /* if FLAGS, method byte */ + struct { + uLong was; /* computed check value */ + uLong need; /* stream check value */ + } check; /* if CHECK, check values to compare */ + uInt marker; /* if BAD, inflateSync's marker bytes count */ + } sub; /* submode */ + + /* mode independent information */ + int nowrap; /* flag for no wrapper */ + uInt wbits; /* log2(window size) (8..15, defaults to 15) */ + inflate_blocks_statef + *blocks; /* current inflate_blocks state */ + +}; + + +int inflateReset(z) +z_stream *z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? BLOCKS : METHOD; + inflate_blocks_reset(z->state->blocks, z, &c); + Trace((stderr, "inflate: reset\n")); + return Z_OK; +} + + +int inflateEnd(z) +z_stream *z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z, &c); + ZFREE(z, z->state, sizeof(struct internal_state)); + z->state = Z_NULL; + Trace((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z, w) +z_stream *z; +int w; +{ + /* initialize state */ + if (z == Z_NULL) + return Z_STREAM_ERROR; +/* if (z->zalloc == Z_NULL) z->zalloc = zcalloc; */ +/* if (z->zfree == Z_NULL) z->zfree = zcfree; */ + if ((z->state = (struct internal_state FAR *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + /* handle undocumented nowrap option (no zlib header or check) */ + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + /* set window size */ + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + /* create inflate_blocks state */ + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, 1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Trace((stderr, "inflate: allocated\n")); + + /* reset state */ + inflateReset(z); + return Z_OK; +} + + +int inflateInit(z) +z_stream *z; +{ + return inflateInit2(z, DEF_WBITS); +} + + +#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;} +#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z, f) +z_stream *z; +int f; +{ + int r; + uInt b; + + if (z == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + r = Z_BUF_ERROR; + while (1) switch (z->state->mode) + { + case METHOD: + NEEDBYTE + if (((z->state->sub.method = NEXTBYTE) & 0xf) != DEFLATED) + { + z->state->mode = BAD; + z->msg = "unknown compression method"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = BAD; + z->msg = "invalid window size"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + z->state->mode = FLAG; + case FLAG: + NEEDBYTE + if ((b = NEXTBYTE) & 0x20) + { + z->state->mode = BAD; + z->msg = "invalid reserved bit"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = BAD; + z->msg = "incorrect header check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Trace((stderr, "inflate: zlib header ok\n")); + z->state->mode = BLOCKS; + case BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0) + r = inflate_packet_flush(z->state->blocks); + if (r == Z_DATA_ERROR) + { + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + break; + } + if (r != Z_STREAM_END) + return r; + r = Z_OK; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = DONE; + break; + } + z->state->mode = CHECK4; + case CHECK4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = CHECK3; + case CHECK3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = CHECK2; + case CHECK2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = CHECK1; + case CHECK1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = BAD; + z->msg = "incorrect data check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Trace((stderr, "inflate: zlib check ok\n")); + z->state->mode = DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } + + empty: + if (f != Z_PACKET_FLUSH) + return r; + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + return Z_DATA_ERROR; +} + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output (hence s->read equals s->write), and the state must + * be BLOCKS (i.e. we should be willing to see the start of a series of + * BLOCKS). On exit, the output will also be caught up, and the checksum + * will have been updated if need be. + */ + +int inflateIncomp(z) +z_stream *z; +{ + if (z->state->mode != BLOCKS) + return Z_DATA_ERROR; + return inflate_addhistory(z->state->blocks, z); +} + + +int inflateSync(z) +z_stream *z; +{ + uInt n; /* number of bytes to look at */ + Bytef *p; /* pointer to bytes */ + uInt m; /* number of marker bytes found in a row */ + uLong r, w; /* temporaries to save total_in and total_out */ + + /* set up */ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->mode != BAD) + { + z->state->mode = BAD; + z->state->sub.marker = 0; + } + if ((n = z->avail_in) == 0) + return Z_BUF_ERROR; + p = z->next_in; + m = z->state->sub.marker; + + /* search */ + while (n && m < 4) + { + if (*p == (Byte)(m < 2 ? 0 : 0xff)) + m++; + else if (*p) + m = 0; + else + m = 4 - m; + p++, n--; + } + + /* restore */ + z->total_in += p - z->next_in; + z->next_in = p; + z->avail_in = n; + z->state->sub.marker = m; + + /* return no joy or set up to restart on a new block */ + if (m != 4) + return Z_DATA_ERROR; + r = z->total_in; w = z->total_out; + inflateReset(z); + z->total_in = r; z->total_out = w; + z->state->mode = BLOCKS; + return Z_OK; +} + +#undef NEEDBYTE +#undef NEXTBYTE + +/*+++++*/ +/* infutil.h -- types and macros common to blocks and codes + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* inflate blocks semi-private state */ +struct inflate_blocks_state { + + /* mode */ + enum { + TYPE, /* get type bits (3, including end bit) */ + LENS, /* get lengths for stored */ + STORED, /* processing stored block */ + TABLE, /* get table lengths */ + BTREE, /* get bit lengths tree for a dynamic block */ + DTREE, /* get length, distance trees for a dynamic block */ + CODES, /* processing fixed or dynamic block */ + DRY, /* output remaining window bytes */ + DONEB, /* finished last block, done */ + BADB} /* got a data error--stuck here */ + mode; /* current inflate_block mode */ + + /* mode dependent information */ + union { + uInt left; /* if STORED, bytes left to copy */ + struct { + uInt table; /* table lengths (14 bits) */ + uInt index; /* index into blens (or border) */ + uIntf *blens; /* bit lengths of codes */ + uInt bb; /* bit length tree depth */ + inflate_huft *tb; /* bit length decoding tree */ + int nblens; /* # elements allocated at blens */ + } trees; /* if DTREE, decoding info for trees */ + struct { + inflate_huft *tl, *td; /* trees to free */ + inflate_codes_statef + *codes; + } decode; /* if CODES, current state */ + } sub; /* submode */ + uInt last; /* true if this block is the last block */ + + /* mode independent information */ + uInt bitk; /* bits in bit buffer */ + uLong bitb; /* bit buffer */ + Bytef *window; /* sliding window */ + Bytef *end; /* one byte after sliding window */ + Bytef *read; /* window read pointer */ + Bytef *write; /* window write pointer */ + check_func checkfn; /* check function */ + uLong check; /* check on output */ + +}; + + +/* defines for inflate input/output */ +/* update pointers and return */ +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +/* get bytes and bits */ +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +/* output bytes */ +#define WAVAIL (qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=WAVAIL;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +/* load local pointers */ +#define LOAD {LOADIN LOADOUT} + +/* + * The IBM 150 firmware munges the data right after _etext[]. This + * protects it. -- Cort + */ +local uInt protect_mask[] = {0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 ,0 ,0}; +/* And'ing with mask[n] masks the lower n bits */ +local uInt inflate_mask[] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +/* copy as much as possible from the sliding window to the output area */ +local int inflate_flush OF(( + inflate_blocks_statef *, + z_stream *, + int)); + +/*+++++*/ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +local int inflate_fast OF(( + uInt, + uInt, + inflate_huft *, + inflate_huft *, + inflate_blocks_statef *, + z_stream *)); + + +/*+++++*/ +/* infblock.c -- interpret and process block types to last block + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* Table for deflate from PKZIP's appnote.txt. */ +local uInt border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarily, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + */ + + +local void inflate_blocks_reset(s, z, c) +inflate_blocks_statef *s; +z_stream *z; +uLongf *c; +{ + if (s->checkfn != Z_NULL) + *c = s->check; + if (s->mode == BTREE || s->mode == DTREE) + ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt)); + if (s->mode == CODES) + { + inflate_codes_free(s->sub.decode.codes, z); + inflate_trees_free(s->sub.decode.td, z); + inflate_trees_free(s->sub.decode.tl, z); + } + s->mode = TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(0L, Z_NULL, 0); + Trace((stderr, "inflate: blocks reset\n")); +} + + +local inflate_blocks_statef *inflate_blocks_new(z, c, w) +z_stream *z; +check_func c; +uInt w; +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s, sizeof(struct inflate_blocks_state)); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = TYPE; + Trace((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, &s->check); + return s; +} + + +local int inflate_blocks(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt t; /* temporary storage */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input based on current state */ + while (1) switch (s->mode) + { + case TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: /* stored */ + Trace((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; /* go to byte boundary */ + DUMPBITS(t) + s->mode = LENS; /* get length of stored block */ + break; + case 1: /* fixed */ + Trace((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.tl = Z_NULL; /* don't try to free these */ + s->sub.decode.td = Z_NULL; + } + DUMPBITS(3) + s->mode = CODES; + break; + case 2: /* dynamic */ + Trace((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = TABLE; + break; + case 3: /* illegal */ + DUMPBITS(3) + s->mode = BADB; + z->msg = "invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case LENS: + NEEDBITS(32) + if (((~b) >> 16) != (b & 0xffff)) + { + s->mode = BADB; + z->msg = "invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; /* dump bits */ + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? STORED : TYPE; + break; + case STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + zmemcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? DRY : TYPE; + break; + case TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; +#ifndef PKZIP_BUG_WORKAROUND + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = BADB; + z->msg = "too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } +#endif + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (t < 19) + t = 19; + if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.trees.nblens = t; + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = BTREE; + case BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, z); + if (t != Z_OK) + { + r = t; + if (r == Z_DATA_ERROR) + s->mode = BADB; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = DTREE; + case DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->word.what.Bits; + c = h->more.Base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else /* c == 16..18 */ + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + s->mode = BADB; + z->msg = "invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + inflate_trees_free(s->sub.trees.tb, z); + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; /* must be <= 9 for lookahead assumptions */ + bd = 6; /* must be <= 9 for lookahead assumptions */ + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, z); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = BADB; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + inflate_trees_free(td, z); + inflate_trees_free(tl, z); + r = Z_MEM_ERROR; + LEAVE + } + ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt)); + s->sub.decode.codes = c; + s->sub.decode.tl = tl; + s->sub.decode.td = td; + } + s->mode = CODES; + case CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + inflate_trees_free(s->sub.decode.td, z); + inflate_trees_free(s->sub.decode.tl, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = TYPE; + break; + } + if (k > 7) /* return unused byte, if any */ + { + Assert(k < 16, "inflate_codes grabbed too many bytes") + k -= 8; + n++; + p--; /* can always return one */ + } + s->mode = DRY; + case DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = DONEB; + case DONEB: + r = Z_STREAM_END; + LEAVE + case BADB: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +local int inflate_blocks_free(s, z, c) +inflate_blocks_statef *s; +z_stream *z; +uLongf *c; +{ + inflate_blocks_reset(s, z, c); + ZFREE(z, s->window, s->end - s->window); + ZFREE(z, s, sizeof(struct inflate_blocks_state)); + Trace((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output (hence s->read equals s->write), and the state must + * be BLOCKS (i.e. we should be willing to see the start of a series of + * BLOCKS). On exit, the output will also be caught up, and the checksum + * will have been updated if need be. + */ +local int inflate_addhistory(s, z) +inflate_blocks_statef *s; +z_stream *z; +{ + uLong b; /* bit buffer */ /* NOT USED HERE */ + uInt k; /* bits in bit buffer */ /* NOT USED HERE */ + uInt t; /* temporary storage */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + if (s->read != s->write) + return Z_STREAM_ERROR; + if (s->mode != TYPE) + return Z_DATA_ERROR; + + /* we're ready to rock */ + LOAD + /* while there is input ready, copy to output buffer, moving + * pointers as needed. + */ + while (n) { + t = n; /* how many to do */ + /* is there room until end of buffer? */ + if (t > m) t = m; + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, t); + zmemcpy(q, p, t); + q += t; + p += t; + n -= t; + z->total_out += t; + s->read = q; /* drag read pointer forward */ +/* WRAP */ /* expand WRAP macro by hand to handle s->read */ + if (q == s->end) { + s->read = q = s->window; + m = WAVAIL; + } + } + UPDATE + return Z_OK; +} + + +/* + * At the end of a Deflate-compressed PPP packet, we expect to have seen + * a `stored' block type value but not the (zero) length bytes. + */ +local int inflate_packet_flush(s) + inflate_blocks_statef *s; +{ + if (s->mode != LENS) + return Z_DATA_ERROR; + s->mode = TYPE; + return Z_OK; +} + + +/*+++++*/ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + + +local int huft_build OF(( + uIntf *, /* code lengths in bits */ + uInt, /* number of codes */ + uInt, /* number of "simple" codes */ + uIntf *, /* list of base values for non-simple codes */ + uIntf *, /* list of extra bits for non-simple codes */ + inflate_huft * FAR*,/* result: starting table */ + uIntf *, /* maximum lookup bits (returns actual) */ + z_stream *)); /* for zalloc function */ + +local voidpf falloc OF(( + voidpf, /* opaque pointer (not used) */ + uInt, /* number of items */ + uInt)); /* size of item */ + +local void ffree OF(( + voidpf q, /* opaque pointer (not used) */ + voidpf p, /* what to free (not used) */ + uInt n)); /* number of bytes (not used) */ + +/* Tables for deflate from PKZIP's appnote.txt. */ +local uInt cplens[] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* actually lengths - 2; also see note #13 above about 258 */ +local uInt cplext[] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 192, 192}; /* 192==invalid */ +local uInt cpdist[] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +local uInt cpdext[] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +/* + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ +#define BMAX 15 /* maximum bit length of any code */ +#define N_MAX 288 /* maximum number of codes in any set */ + +#ifdef DEBUG_ZLIB + uInt inflate_hufts; +#endif + +local int huft_build(b, n, s, d, e, t, m, zs) +uIntf *b; /* code lengths in bits (all assumed <= BMAX) */ +uInt n; /* number of codes (assumed <= N_MAX) */ +uInt s; /* number of simple-valued codes (0..s-1) */ +uIntf *d; /* list of base values for non-simple codes */ +uIntf *e; /* list of extra bits for non-simple codes */ +inflate_huft * FAR *t; /* result: starting table */ +uIntf *m; /* maximum lookup bits, returns actual */ +z_stream *zs; /* for zalloc function */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + if the given code set is incomplete (the tables are still built in this + case), Z_DATA_ERROR if the input is invalid (all zero length codes or an + over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */ +{ + + uInt a; /* counter for codes of length k */ + uInt c[BMAX+1]; /* bit length count table */ + uInt f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register uInt i; /* counter, current code */ + register uInt j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + register uIntf *p; /* pointer into c[], b[], or v[] */ + inflate_huft *q; /* points to current table */ + struct inflate_huft_s r; /* table entry for structure assignment */ + inflate_huft *u[BMAX]; /* table stack */ + uInt v[N_MAX]; /* values in order of bit length */ + register int w; /* bits before this table == (l * h) */ + uInt x[BMAX+1]; /* bit offsets, then code stack */ + uIntf *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + uInt z; /* number of entries in current table */ + + + /* Generate counts for each bit length */ + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4 /* clear c[]--assume BMAX+1 is 16 */ + p = b; i = n; + do { + c[*p++]++; /* assume all entries <= BMAX */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((uInt)l > i) + l = i; + *m = l; + + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ + q = (inflate_huft *)Z_NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = (z = g - w) > (uInt)l ? l : z; /* table size upper limit */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + if (j < z) + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + z = 1 << j; /* table entries for j-bit table */ + + /* allocate and link in new table */ + if ((q = (inflate_huft *)ZALLOC + (zs,z + 1,sizeof(inflate_huft))) == Z_NULL) + { + if (h) + inflate_trees_free(u[0], zs); + return Z_MEM_ERROR; /* not enough memory */ + } + q->word.Nalloc = z + 1; +#ifdef DEBUG_ZLIB + inflate_hufts += z + 1; +#endif + *t = q + 1; /* link to list for huft_free() */ + *(t = &(q->next)) = Z_NULL; + u[h] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.bits = (Byte)l; /* bits to dump before this table */ + r.exop = (Byte)j; /* bits in this table */ + r.next = q; /* pointer to this table */ + j = i >> (w - l); /* (get around Turbo C bug) */ + u[h-1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; /* out of values--invalid code */ + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ + r.base = *p++; /* simple code is just the value */ + } + else + { + r.exop = (Byte)e[*p - s] + 16 + 64; /* non-simple--look up in lists */ + r.base = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + } + } + } + + + /* Return Z_BUF_ERROR if we were given an incomplete table */ + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +local int inflate_trees_bits(c, bb, tb, z) +uIntf *c; /* 19 code lengths */ +uIntf *bb; /* bits tree desired/actual depth */ +inflate_huft * FAR *tb; /* bits tree result */ +z_stream *z; /* for zfree function */ +{ + int r; + + r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z); + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR) + { + inflate_trees_free(*tb, z); + z->msg = "incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + return r; +} + + +local int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z) +uInt nl; /* number of literal/length codes */ +uInt nd; /* number of distance codes */ +uIntf *c; /* that many (total) code lengths */ +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +z_stream *z; /* for zfree function */ +{ + int r; + + /* build literal/length tree */ + if ((r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z)) != Z_OK) + { + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed literal/length tree"; + else if (r == Z_BUF_ERROR) + { + inflate_trees_free(*tl, z); + z->msg = "incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + return r; + } + + /* build distance tree */ + if ((r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z)) != Z_OK) + { + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed literal/length tree"; + else if (r == Z_BUF_ERROR) { +#ifdef PKZIP_BUG_WORKAROUND + r = Z_OK; + } +#else + inflate_trees_free(*td, z); + z->msg = "incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + inflate_trees_free(*tl, z); + return r; +#endif + } + + /* done */ + return Z_OK; +} + + +/* build fixed tables only once--keep them here */ +local int fixed_lock = 0; +local int fixed_built = 0; +#define FIXEDH 530 /* number of hufts used by fixed tables */ +local uInt fixed_left = FIXEDH; +local inflate_huft fixed_mem[FIXEDH]; +local uInt fixed_bl; +local uInt fixed_bd; +local inflate_huft *fixed_tl; +local inflate_huft *fixed_td; + + +local voidpf falloc(q, n, s) +voidpf q; /* opaque pointer (not used) */ +uInt n; /* number of items */ +uInt s; /* size of item */ +{ + Assert(s == sizeof(inflate_huft) && n <= fixed_left, + "inflate_trees falloc overflow"); + if (q) s++; /* to make some compilers happy */ + fixed_left -= n; + return (voidpf)(fixed_mem + fixed_left); +} + + +local void ffree(q, p, n) +voidpf q; +voidpf p; +uInt n; +{ + Assert(0, "inflate_trees ffree called!"); + if (q) q = p; /* to make some compilers happy */ +} + + +local int inflate_trees_fixed(bl, bd, tl, td) +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +{ + /* build fixed tables if not built already--lock out other instances */ + while (++fixed_lock > 1) + fixed_lock--; + if (!fixed_built) + { + int k; /* temporary variable */ + unsigned c[288]; /* length list for huft_build */ + z_stream z; /* for falloc function */ + + /* set up fake z_stream for memory routines */ + z.zalloc = falloc; + z.zfree = ffree; + z.opaque = Z_NULL; + + /* literal table */ + for (k = 0; k < 144; k++) + c[k] = 8; + for (; k < 256; k++) + c[k] = 9; + for (; k < 280; k++) + c[k] = 7; + for (; k < 288; k++) + c[k] = 8; + fixed_bl = 7; + huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z); + + /* distance table */ + for (k = 0; k < 30; k++) + c[k] = 5; + fixed_bd = 5; + huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z); + + /* done */ + fixed_built = 1; + } + fixed_lock--; + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +local int inflate_trees_free(t, z) +inflate_huft *t; /* table to free */ +z_stream *z; /* for zfree function */ +/* Free the malloc'ed tables built by huft_build(), which makes a linked + list of the tables it made, with the links in a dummy first entry of + each table. */ +{ + register inflate_huft *p, *q; + + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + p = t; + while (p != Z_NULL) + { + q = (--p)->next; + ZFREE(z, p, p->word.Nalloc * sizeof(inflate_huft)); + p = q; + } + return Z_OK; +} + +/*+++++*/ +/* infcodes.c -- process literals and length/distance pairs + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + +/* inflate codes private state */ +struct inflate_codes_state { + + /* mode */ + enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + START, /* x: set up for LEN */ + LEN, /* i: get length/literal/eob next */ + LENEXT, /* i: getting length extra (have base) */ + DIST, /* i: get distance next */ + DISTEXT, /* i: getting distance extra */ + COPY, /* o: copying bytes in window, waiting for space */ + LIT, /* o: got literal, waiting for output space */ + WASH, /* o: got eob, possibly still output waiting */ + END, /* x: got eob and all data flushed */ + BADCODE} /* x: got error */ + mode; /* current inflate_codes mode */ + + /* mode dependent information */ + uInt len; + union { + struct { + inflate_huft *tree; /* pointer into tree */ + uInt need; /* bits needed */ + } code; /* if LEN or DIST, where in tree */ + uInt lit; /* if LIT, literal */ + struct { + uInt get; /* bits to get for extra */ + uInt dist; /* distance back to copy from */ + } copy; /* if EXT or COPY, where and how much */ + } sub; /* submode */ + + /* mode independent information */ + Byte lbits; /* ltree bits decoded per branch */ + Byte dbits; /* dtree bits decoder per branch */ + inflate_huft *ltree; /* literal/length/eob tree */ + inflate_huft *dtree; /* distance tree */ + +}; + + +local inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z) +uInt bl, bd; +inflate_huft *tl, *td; +z_stream *z; +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +local int inflate_codes(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt j; /* temporary storage */ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + Bytef *f; /* pointer to copy strings from */ + inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input and output based on current state */ + while (1) switch (c->mode) + { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + case START: /* x: set up for LEN */ +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif /* !SLOW */ + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: /* i: get length/literal/eob next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) /* literal */ + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) /* length */ + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t->next; + break; + } + if (e & 32) /* end of block */ + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = "invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: /* i: getting length extra (have base) */ + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: /* i: get distance next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) /* distance */ + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t->next; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = "invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: /* i: getting distance extra */ + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: /* o: copying bytes in window, waiting for space */ +#ifndef __TURBOC__ /* Turbo C bug for following expression */ + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; +#else + f = q - c->sub.copy.dist; + if ((uInt)(q - s->window) < c->sub.copy.dist) + f = s->end - (c->sub.copy.dist - (q - s->window)); +#endif + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: /* o: got literal, waiting for output space */ + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: /* o: got eob, possibly more output */ + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: /* x: got error */ + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +local void inflate_codes_free(c, z) +inflate_codes_statef *c; +z_stream *z; +{ + ZFREE(z, c, sizeof(struct inflate_codes_state)); + Tracev((stderr, "inflate: codes free\n")); +} + +/*+++++*/ +/* inflate_util.c -- data and routines common to blocks and codes + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* copy as much as possible from the sliding window to the output area */ +local int inflate_flush(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt n; + Bytef *p, *q; + + /* local copies of source and destination pointers */ + p = z->next_out; + q = s->read; + + /* compute number of bytes to copy as far as end of window */ + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, n); + + /* copy as far as end of window */ + zmemcpy(p, q, n); + p += n; + q += n; + + /* see if more to copy at beginning of window */ + if (q == s->end) + { + /* wrap pointers */ + q = s->window; + if (s->write == s->end) + s->write = s->window; + + /* compute bytes to copy */ + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, n); + + /* copy */ + zmemcpy(p, q, n); + p += n; + q += n; + } + + /* update pointers */ + z->next_out = p; + s->read = q; + + /* done */ + return r; +} + + +/*+++++*/ +/* inffast.c -- process literals and length/distance pairs fast + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + +/* macros for bit input with no checking and for returning unused bytes */ +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<>3);p-=c;k&=7;} + +/* Called with number of bytes left to write in window at least 258 + (the maximum string length) and number of input bytes available + at least ten. The ten bytes are six bytes for the longest length/ + distance pair plus four bytes for overloading the bit buffer. */ + +local int inflate_fast(bl, bd, tl, td, s, z) +uInt bl, bd; +inflate_huft *tl, *td; +inflate_blocks_statef *s; +z_stream *z; +{ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + uInt ml; /* mask for literal/length tree */ + uInt md; /* mask for distance tree */ + uInt c; /* bytes to copy */ + uInt d; /* distance back to copy from */ + Bytef *r; /* copy source pointer */ + + /* load input, output, bit values */ + LOAD + + /* initialize masks */ + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + /* do until not enough input or output space for fast loop */ + do { /* assume called with m >= 258 && n >= 10 */ + /* get literal/length code */ + GRABBITS(20) /* max bits for literal/length code */ + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits for length */ + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + /* decode distance base of block to copy */ + GRABBITS(15); /* max bits for distance code */ + e = (t = td + ((uInt)b & md))->exop; + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits to add to distance base */ + e &= 15; + GRABBITS(e) /* get extra bits (up to 13) */ + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + /* do the copy */ + m -= c; + if ((uInt)(q - s->window) >= d) /* offset before dest */ + { /* just copy */ + r = q - d; + *q++ = *r++; c--; /* minimum count is three, */ + *q++ = *r++; c--; /* so unroll loop a little */ + } + else /* else offset after destination */ + { + e = d - (q - s->window); /* bytes from offset to end */ + r = s->end - e; /* pointer to offset */ + if (c > e) /* if source crosses, */ + { + c -= e; /* copy to end of window */ + do { + *q++ = *r++; + } while (--e); + r = s->window; /* copy rest from start of window */ + } + } + do { /* copy all or what's left */ + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop; + else + { + z->msg = "invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + break; + } + if ((e & 64) == 0) + { + if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = "invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + } while (m >= 258 && n >= 10); + + /* not enough input or output--restore pointers and return */ + UNGRAB + UPDATE + return Z_OK; +} + + +/*+++++*/ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: zutil.c,v 1.8 1995/05/03 17:27:12 jloup Exp */ + +char *zlib_version = ZLIB_VERSION; + +char *z_errmsg[] = { +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +""}; + + +/*+++++*/ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: adler32.c,v 1.6 1995/05/03 17:27:08 jloup Exp */ + +#define BASE 65521L /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf) {s1 += *buf++; s2 += s1;} +#define DO2(buf) DO1(buf); DO1(buf); +#define DO4(buf) DO2(buf); DO2(buf); +#define DO8(buf) DO4(buf); DO4(buf); +#define DO16(buf) DO8(buf); DO8(buf); + +/* ========================================================================= */ +uLong adler32(adler, buf, len) + uLong adler; + Bytef *buf; + uInt len; +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + k -= 16; + } + if (k != 0) do { + DO1(buf); + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/boot/zlib.h linux.ac/arch/ppc64/boot/zlib.h --- linux.vanilla/arch/ppc64/boot/zlib.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/boot/zlib.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,432 @@ +/* */ + +/* + * This file is derived from zlib.h and zconf.h from the zlib-0.95 + * distribution by Jean-loup Gailly and Mark Adler, with some additions + * by Paul Mackerras to aid in implementing Deflate compression and + * decompression for PPP packets. + */ + +/* + * ==FILEVERSION 960122== + * + * This marker is used by the Linux installation script to determine + * whether an up-to-date version of this file is already installed. + */ + +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 0.95, Aug 16th, 1995. + + Copyright (C) 1995 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + gzip@prep.ai.mit.edu madler@alumni.caltech.edu + */ + +#ifndef _ZLIB_H +#define _ZLIB_H + +/* #include "zconf.h" */ /* included directly here */ + +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: zconf.h,v 1.12 1995/05/03 17:27:12 jloup Exp */ + +/* + The library does not install any signal handler. It is recommended to + add at least a handler for SIGSEGV when decompressing; the library checks + the consistency of the input data whenever possible but may go nuts + for some forms of corrupted input. + */ + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + * Compile with -DUNALIGNED_OK if it is OK to access shorts or ints + * at addresses which are not a multiple of their size. + * Under DOS, -DFAR=far or -DFAR=__far may be needed. + */ + +#ifndef STDC +# if defined(MSDOS) || defined(__STDC__) || defined(__cplusplus) +# define STDC +# endif +#endif + +#ifdef __MWERKS__ /* Metrowerks CodeWarrior declares fileno() in unix.h */ +# include +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +#ifndef FAR +# define FAR +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2 */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + 1 << (windowBits+2) + 1 << (memLevel+9) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +typedef unsigned char Byte; /* 8 bits */ +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +typedef Byte FAR Bytef; +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +/* end of original zconf.h */ + +#define ZLIB_VERSION "0.95P" + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms may be added later and will have the same + stream interface. + + For compression the application must provide the output buffer and + may optionally provide the input buffer for optimization. For decompression, + the application must provide the input buffer and may optionally provide + the output buffer for optimization. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address, uInt nbytes)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidp opaque; /* private data object passed to zalloc and zfree */ + + Byte data_type; /* best guess about the data type: ascii or binary */ + +} z_stream; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_FULL_FLUSH 2 +#define Z_SYNC_FLUSH 3 /* experimental: partial_flush + byte align */ +#define Z_FINISH 4 +#define Z_PACKET_FLUSH 5 +/* See deflate() below for the usage of these constants */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +/* error codes for the compression/decompression functions */ + +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Used to set the data_type field */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +extern char *zlib_version; +/* The application can compare zlib_version and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + */ + + /* basic functions */ + +extern int inflateInit OF((z_stream *strm)); +/* + Initializes the internal stream state for decompression. The fields + zalloc and zfree must be initialized before by the caller. If zalloc and + zfree are set to Z_NULL, inflateInit updates them to use default allocation + functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory. msg is set to null if there is no error message. + inflateInit does not perform any decompression: this will be done by + inflate(). +*/ + + +extern int inflate OF((z_stream *strm, int flush)); +/* + Performs one or both of the following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() always provides as much output as possible + (until there is no more input data or no more space in the output buffer). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). + + If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH, + inflate flushes as much output as possible to the output buffer. The + flushing behavior of inflate is not specified for values of the flush + parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the + current implementation actually flushes as much output as possible + anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data + has been consumed, it is expecting to see the length field of a stored + block; if not, it returns Z_DATA_ERROR. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster routine + may be used for the single inflate() call. + + inflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if the end of the + compressed data has been reached and all uncompressed output has been + produced, Z_DATA_ERROR if the input data was corrupted, Z_STREAM_ERROR if + the stream structure was inconsistent (for example if next_in or next_out + was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no + progress is possible or if there was not enough room in the output buffer + when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then + call inflateSync to look for a good compression block. */ + + +extern int inflateEnd OF((z_stream *strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* advanced functions */ + +extern int inflateInit2 OF((z_stream *strm, + int windowBits)); +/* + This is another version of inflateInit with more compression options. The + fields next_out, zalloc and zfree must be initialized before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library (the value 16 will be allowed soon). The + default value is 15 if inflateInit is used instead. If a compressed stream + with a larger window size is given as input, inflate() will return with + the error code Z_DATA_ERROR instead of trying to allocate a larger window. + + If next_out is not null, the library will use this buffer for the history + buffer; the buffer must either be large enough to hold the entire output + data, or have at least 1<new(2**30) * 4; # 2**32 without going floating point + +# +# Parse the cmdline options +# +my $arg; +while (defined($arg = shift)) { + if ($arg eq '-objdump') { + $objdump = shift; + } elsif ($arg eq '-o') { + $outfile = shift; + } elsif ($arg eq '-h') { + usage(); + } elsif ($arg eq '-v') { + $verbose = 1; + } elsif ($arg eq '-dummy') { + $dummy = 1; + } elsif ($arg !~ m/^-/) { + unshift @ARGV, $arg; + last; + } else { + die "$me: unknown option $arg\n"; + } +} + +usage() unless ($#ARGV == 0 || $dummy); +$objfile = $ARGV[0]; # typically vmlinux + +if ($verbose) { + print "outfile=$outfile\n"; + print "objdump=$objdump\n"; + print "objfile=$objfile\n"; +} +( $dummy || -r $objfile ) or die "$me: cannot read $objfile: $!\n"; + + +if (!$dummy) { + # + # Read the section table from objdump -h into hash + # %sections. Each entry is [Name, Size, Addr, Name_Off, Sect_idx] and + # the hash is indexed by Name. + # + open(S, "$objdump -h $objfile 2>&1 |") or die "$me: cannot $objdump -h $objfile: $!\n"; + my $sline; + while (defined($sline = )) { + chomp($sline); + $sline =~ s/^\s*//; + next unless $sline =~ m/^[0-9]/; + my ($idx, $name, $size, $vma, $rest) = split(/\s+/, $sline, 5); + print "name=$name, size=$size, vma=$vma\n" if $verbose; + $sectlist[$nextsect] = [$name, $size, $vma, 0, 0]; + $sections{$name} = $sectlist[$nextsect++]; + } + close(S); + print "$me: processed $nextsect sections\n" if $verbose; + + # Sort by start addr for readability. This is not required. + @sectlist = sort { $a->[2] cmp $b->[2] } @sectlist; + for (my $i = 0; $i < $nextsect; $i++) { + $sectlist[$i]->[4] = $i; # Sect_Idx + } + + # + # Now read the symbol table from objdump -t + # + open(S, "$objdump -t $objfile 2>&1 |") or die "$me: cannot $objdump -t $objfile: $!\n"; + while (defined($sline = )) { + last if $sline =~ m/^SYMBOL/; + } + while (defined($sline = )) { + chomp($sline); + my ($addr, $foo, $type, $sect, $val, $name) = split(/\s+/, $sline); + # Now type might be empty. + if (!defined($name)) { + $name = $val; + $val = $sect; + $sect = $type; + $type = " "; + } + # Weed out symbols which are not useful. + next if !$name; + next if $sect eq ".rodata"; # TOC entries, I think. + next if $sect eq "*ABS*"; + next if $name =~ m/^LC?\.\./; + # print "$addr,$type,$sect,$val,$name\n" if $verbose; + my $sec_ref = $sections{$sect}; + $symbols[$nextsym++] = [$name, $sec_ref, $addr, 0]; + } + close(S); + print "$me: processed $nextsym symbols\n" if $verbose; + # Sort by addr for readability. This is not required. + @symbols = sort { $a->[2] cmp $b->[2] } @symbols; + + # + # Now that everything is sorted, we need to assign space for + # each string in the string table. + # + for (my $i = 0; $i < $nextsect; $i++) { + my $len = length($sectlist[$i]->[0]); + $sectlist[$i]->[3] = $strtablen; + $strtablen += $len+1; + } + for (my $i = 0; $i < $nextsym; $i++) { + my $len = length($symbols[$i]->[0]); + $symbols[$i]->[3] = $strtablen; + $strtablen += $len+1; + } +} + +if ($outfile) { + open(OUT, ">$outfile") or die "$me: cannot open $outfile for write: $!\n"; +} else { + open(OUT, ">&STDOUT"); +} + +if ($dummy) { + # Produce dummy output and exit. + print OUT "long __start___kallsyms = 0;\n"; + print OUT "long __stop___kallsyms = 0;\n"; + exit 0; +} + +print OUT "#include \n"; +print OUT "#include \n\n"; +print OUT "#define NUM_SECTIONS\t\t", int(keys %sections), "\n"; +print OUT "#define NUM_SYMBOLS\t\t", $nextsym, "\n"; +print OUT "#define NUM_STRING_CHARS\t", $strtablen, "\n"; +print OUT "#define FIRST_SECTION_ADDR\t0x", $sectlist[0]->[2], "UL /* section ", $sectlist[0]->[0], " */\n"; +print OUT "#define LAST_SECTION_ADDR\t0x", $sectlist[$nextsect-1]->[2], "UL + 0x", $sectlist[$nextsect-1]->[1], "UL /* section ", $sectlist[$nextsect-1]->[0], " */\n"; +print OUT <[2], "UL, 0x", $sectref->[1], ", ", $sectref->[3], "},\t /* [$i] ", $sectref->[0], " */\n"; +} + +print OUT <[1]; + print OUT "\t{SOFF(", $secref->[4], "), 0x", $symref->[2], "UL, ", $symref->[3], "}, /* ", $symref->[0], " */\n"; +} + +print OUT <[0], "\\0\"\t/* ", $sectref->[3], " */\n"; +} +for (my $i = 0; $i < $nextsym; $i++) { + my $symref = $symbols[$i]; + print OUT "\t\"", $symref->[0], "\\0\"\t/* ", $symref->[3], " */\n"; +} + + +print OUT <new(hex(substr($hexstr, 0, 8))); +# my $biglow = Math::BigInt->new(hex(substr($hexstr, 8, 8))); +# return $bighi * $shift32multiplier + $biglow; +#} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kdb/kdba_bp.c linux.ac/arch/ppc64/kdb/kdba_bp.c --- linux.vanilla/arch/ppc64/kdb/kdba_bp.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kdb/kdba_bp.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,725 @@ +/* + * Kernel Debugger Architecture Dependent Breakpoint Handling + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "privinst.h" + +static char *kdba_rwtypes[] = { "Instruction(Register)", "Data Write", + "I/O", "Data Access"}; + +/* + * Table describing processor architecture hardware + * breakpoint registers. + */ + +kdbhard_bp_t kdb_hardbreaks[KDB_MAXHARDBPT]; + +/* + * kdba_db_trap + * + * Perform breakpoint processing upon entry to the + * processor debugger fault. Determine and print + * the active breakpoint. + * + * Parameters: + * ef Exception frame containing machine register state + * error Error number passed to kdb. + * Outputs: + * None. + * Returns: + * KDB_DB_BPT Standard instruction or data breakpoint encountered + * KDB_DB_SS Single Step fault ('ss' command or end of 'ssb' command) + * KDB_DB_SSB Single Step fault, caller should continue ('ssb' command) + * KDB_DB_SSBPT Single step over breakpoint + * KDB_DB_NOBPT No existing kdb breakpoint matches this debug exception + * Locking: + * None. + * Remarks: + * Yup, there be goto's here. + * + * If multiple processors receive debug exceptions simultaneously, + * one may be waiting at the kdb fence in kdb() while the user + * issues a 'bc' command to clear the breakpoint the processor + * which is waiting has already encountered. If this is the case, + * the debug registers will no longer match any entry in the + * breakpoint table, and we'll return the value KDB_DB_NOBPT. + * This can cause a panic in die_if_kernel(). It is safer to + * disable the breakpoint (bd), go until all processors are past + * the breakpoint then clear the breakpoint (bc). This code + * recognises a breakpoint even when disabled but not when it has + * been cleared. + * + * WARNING: This routine clears the debug state. It should be called + * once per debug and the result cached. + */ + +kdb_dbtrap_t +kdba_db_trap(kdb_eframe_t ef, int error_unused) +{ + kdb_machreg_t msr,trap; + int rw, reg; + int i; + kdb_dbtrap_t rv = KDB_DB_BPT; + kdb_bp_t *bp; + + msr = get_msr(); + trap = ef->trap; + if (KDB_DEBUG(BP)) + kdb_printf("kdb: msr 0x%lx trap 0x%lx\n", msr,trap); + if (msr & MSR_SE || ((trap & 0x700) || (trap & 0xd00))) + { + if (KDB_STATE(SSBPT)) { + if (KDB_DEBUG(BP)) + kdb_printf("ssbpt\n"); + KDB_STATE_CLEAR(SSBPT); + for(i=0,bp=kdb_breakpoints; + i < KDB_MAXBPT; + i++, bp++) { + if (KDB_DEBUG(BP)) + kdb_printf("bp 0x%p enabled %d delayed %d global %d cpu %d\n", + bp, bp->bp_enabled, bp->bp_delayed, bp->bp_global, bp->bp_cpu); + if (!bp->bp_enabled) + continue; + if (!bp->bp_global && bp->bp_cpu != smp_processor_id()) + continue; + if (KDB_DEBUG(BP)) + kdb_printf("bp for this cpu\n"); + if (bp->bp_delayed) { + bp->bp_delayed = 0; + if (KDB_DEBUG(BP)) + kdb_printf("kdba_installbp\n"); + kdba_installbp(ef, bp); + if (!KDB_STATE(DOING_SS)) { + set_msr(get_msr() & ~MSR_SE); + return(KDB_DB_SSBPT); + } + break; + } + } + if (i == KDB_MAXBPT) { + kdb_printf("kdb: Unable to find delayed breakpoint\n"); + } + if (!KDB_STATE(DOING_SS)) { + set_msr(get_msr() & ~MSR_SE); + return(KDB_DB_NOBPT); + } + /* FALLTHROUGH */ + } + + /* + * KDB_STATE_DOING_SS is set when the kernel debugger is using + * the processor trap flag to single-step a processor. If a + * single step trap occurs and this flag is clear, the SS trap + * will be ignored by KDB and the kernel will be allowed to deal + * with it as necessary (e.g. for ptrace). + */ + if (!KDB_STATE(DOING_SS)) + goto unknown; + + /* single step */ + rv = KDB_DB_SS; /* Indicate single step */ + if (KDB_STATE(DOING_SSB)) { + + kdb_id1(ef->nip); + rv = KDB_DB_SSB; /* Indicate ssb - dismiss immediately */ + } else { + /* + * Print current insn + */ + kdb_printf("SS trap at "); + kdb_symbol_print(ef->nip, NULL, KDB_SP_DEFAULT|KDB_SP_NEWLINE); + kdb_id1(ef->nip); + KDB_STATE_CLEAR(DOING_SS); + } + + if (rv != KDB_DB_SSB) + set_msr(get_msr() & ~MSR_SE); + } + if (rv > 0) + goto handled; + + goto handle; + + +handle: + + /* + * Determine which breakpoint was encountered. + */ + for(i=0, bp=kdb_breakpoints; ibp_free) + && (bp->bp_global || bp->bp_cpu == smp_processor_id()) + && (bp->bp_hard) + && (bp->bp_hard->bph_reg == reg)) { + /* + * Hit this breakpoint. + */ +// kdb_printf("%s breakpoint #%d at " kdb_bfd_vma_fmt "\n", + kdb_printf("%s breakpoint #%d at 0x%ld\n", + kdba_rwtypes[rw], + i, bp->bp_addr); + + /* + * For an instruction breakpoint, disassemble + * the current instruction. + */ + if (rw == 0) { + kdb_id1(ef->nip); + } + + goto handled; + } + } + +unknown: + rv = KDB_DB_NOBPT; /* Cause kdb() to return */ + +handled: + + + return rv; +} + +/* + * kdba_bp_trap + * + * Perform breakpoint processing upon entry to the + * processor breakpoint instruction fault. Determine and print + * the active breakpoint. + * + * Parameters: + * ef Exception frame containing machine register state + * error Error number passed to kdb. + * Outputs: + * None. + * Returns: + * 0 Standard instruction or data breakpoint encountered + * 1 Single Step fault ('ss' command) + * 2 Single Step fault, caller should continue ('ssb' command) + * 3 No existing kdb breakpoint matches this debug exception + * Locking: + * None. + * Remarks: + * + * If multiple processors receive debug exceptions simultaneously, + * one may be waiting at the kdb fence in kdb() while the user + * issues a 'bc' command to clear the breakpoint the processor which + * is waiting has already encountered. If this is the case, the + * debug registers will no longer match any entry in the breakpoint + * table, and we'll return the value '3'. This can cause a panic + * in die_if_kernel(). It is safer to disable the breakpoint (bd), + * 'go' until all processors are past the breakpoint then clear the + * breakpoint (bc). This code recognises a breakpoint even when + * disabled but not when it has been cleared. + * + * WARNING: This routine resets the eip. It should be called + * once per breakpoint and the result cached. + */ + +kdb_dbtrap_t +kdba_bp_trap(kdb_eframe_t ef, int error_unused) +{ + int i; + kdb_dbtrap_t rv; + kdb_bp_t *bp; + + /* + * Determine which breakpoint was encountered. + */ + if (KDB_DEBUG(BP)) + kdb_printf("kdba_bp_trap: eip=0x%lx (not adjusted) " + "msr=0x%lx trap=0x%lx ef=0x%p esp=0x%lx\n", + ef->nip, ef->msr, ef->trap, ef, ef->gpr[1]); + + rv = KDB_DB_NOBPT; /* Cause kdb() to return */ + + for(i=0, bp=kdb_breakpoints; ibp_free) + continue; + if (!bp->bp_global && bp->bp_cpu != smp_processor_id()) + continue; + if (bp->bp_addr == (ef->nip - bp->bp_adjust)) { + /* Hit this breakpoint. */ + ef->nip -= bp->bp_adjust; + kdb_printf("Instruction(i) breakpoint #%d at 0x%lx (adjusted)\n", + i, ef->nip); + kdb_id1(ef->nip); + rv = KDB_DB_BPT; + bp->bp_delay = 1; + break; + } + } + + return rv; +} + +/* + * kdba_handle_bp + * + * Handle an instruction-breakpoint trap. Called when re-installing + * an enabled breakpoint which has has the bp_delay bit set. + * + * Parameters: + * Returns: + * Locking: + * Remarks: + * + * Ok, we really need to: + * 1) Restore the original instruction byte + * 2) Single Step + * 3) Restore breakpoint instruction + * 4) Continue. + * + * + */ + +static void +kdba_handle_bp(kdb_eframe_t ef, kdb_bp_t *bp) +{ + if (!ef) { + kdb_printf("kdba_handle_bp: ef == NULL\n"); + return; + } + + if (KDB_DEBUG(BP)) + kdb_printf("ef->eip = 0x%lx\n", ef->nip); + + /* + * Setup single step + */ + kdba_setsinglestep(ef); + + /* KDB_STATE_SSBPT is set when the kernel debugger must single step + * a task in order to re-establish an instruction breakpoint which + * uses the instruction replacement mechanism. + */ + KDB_STATE_SET(SSBPT); + + /* + * Reset delay attribute + */ + bp->bp_delay = 0; + bp->bp_delayed = 1; +} + + +/* + * kdba_bptype + * + * Return a string describing type of breakpoint. + * + * Parameters: + * bph Pointer to hardware breakpoint description + * Outputs: + * None. + * Returns: + * Character string. + * Locking: + * None. + * Remarks: + */ + +char * +kdba_bptype(kdbhard_bp_t *bph) +{ + char *mode; + + mode = kdba_rwtypes[bph->bph_mode]; + + return mode; +} + +/* + * kdba_printbpreg + * + * Print register name assigned to breakpoint + * + * Parameters: + * bph Pointer hardware breakpoint structure + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdba_printbpreg(kdbhard_bp_t *bph) +{ + kdb_printf(" in dr%ld", bph->bph_reg); +} + +/* + * kdba_printbp + * + * Print string describing hardware breakpoint. + * + * Parameters: + * bph Pointer to hardware breakpoint description + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdba_printbp(kdb_bp_t *bp) +{ + kdb_printf("\n is enabled"); + if (bp->bp_hardtype) { + kdba_printbpreg(bp->bp_hard); + if (bp->bp_hard->bph_mode != 0) { + kdb_printf(" for %d bytes", + bp->bp_hard->bph_length+1); + } + } +} + +/* + * kdba_parsebp + * + * Parse architecture dependent portion of the + * breakpoint command. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + * for Ia32 architure, data access, data write and + * I/O breakpoints are supported in addition to instruction + * breakpoints. + * + * {datar|dataw|io|inst} [length] + */ + +int +kdba_parsebp(int argc, const char **argv, int *nextargp, kdb_bp_t *bp) +{ + int nextarg = *nextargp; + int diag; + kdbhard_bp_t *bph = &bp->bp_template; + + bph->bph_mode = 0; /* Default to instruction breakpoint */ + bph->bph_length = 0; /* Length must be zero for insn bp */ + if ((argc + 1) != nextarg) { + if (strnicmp(argv[nextarg], "datar", sizeof("datar")) == 0) { + bph->bph_mode = 3; + } else if (strnicmp(argv[nextarg], "dataw", sizeof("dataw")) == 0) { + bph->bph_mode = 1; + } else if (strnicmp(argv[nextarg], "io", sizeof("io")) == 0) { + bph->bph_mode = 2; + } else if (strnicmp(argv[nextarg], "inst", sizeof("inst")) == 0) { + bph->bph_mode = 0; + } else { + return KDB_ARGCOUNT; + } + + bph->bph_length = 3; /* Default to 4 byte */ + + nextarg++; + + if ((argc + 1) != nextarg) { + unsigned long len; + + diag = kdbgetularg((char *)argv[nextarg], + &len); + if (diag) + return diag; + + + if ((len > 4) || (len == 3)) + return KDB_BADLENGTH; + + bph->bph_length = len; + bph->bph_length--; /* Normalize for debug register */ + nextarg++; + } + + if ((argc + 1) != nextarg) + return KDB_ARGCOUNT; + + /* + * Indicate to architecture independent level that + * a hardware register assignment is required to enable + * this breakpoint. + */ + + bph->bph_free = 0; + } else { + if (KDB_DEBUG(BP)) + kdb_printf("kdba_bp: no args, forcehw is %d\n", bp->bp_forcehw); + if (bp->bp_forcehw) { + /* + * We are forced to use a hardware register for this + * breakpoint because either the bph or bpha + * commands were used to establish this breakpoint. + */ + bph->bph_free = 0; + } else { + /* + * Indicate to architecture dependent level that + * the instruction replacement breakpoint technique + * should be used for this breakpoint. + */ + bph->bph_free = 1; + bp->bp_adjust = 1; /* software, int 3 is one byte */ + } + } + + *nextargp = nextarg; + return 0; +} + +/* + * kdba_allocbp + * + * Associate a hardware register with a breakpoint. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * A pointer to the allocated register kdbhard_bp_t structure for + * success, Null and a non-zero diagnostic for failure. + * Locking: + * None. + * Remarks: + */ + +kdbhard_bp_t * +kdba_allocbp(kdbhard_bp_t *bph, int *diagp) +{ + int i; + kdbhard_bp_t *newbph; + + for(i=0,newbph=kdb_hardbreaks; i < KDB_MAXHARDBPT; i++, newbph++) { + if (newbph->bph_free) { + break; + } + } + + if (i == KDB_MAXHARDBPT) { + *diagp = KDB_TOOMANYDBREGS; + return NULL; + } + + *diagp = 0; + + /* + * Copy data from template. Can't just copy the entire template + * here because the register number in kdb_hardbreaks must be + * preserved. + */ + newbph->bph_data = bph->bph_data; + newbph->bph_write = bph->bph_write; + newbph->bph_mode = bph->bph_mode; + newbph->bph_length = bph->bph_length; + + /* + * Mark entry allocated. + */ + newbph->bph_free = 0; + + return newbph; +} + +/* + * kdba_freebp + * + * Deallocate a hardware breakpoint + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + */ + +void +kdba_freebp(kdbhard_bp_t *bph) +{ + bph->bph_free = 1; +} + +/* + * kdba_initbp + * + * Initialize the breakpoint table for the hardware breakpoint + * register. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + * + * There is one entry per register. On the ia32 architecture + * all the registers are interchangeable, so no special allocation + * criteria are required. + */ + +void +kdba_initbp(void) +{ + int i; + kdbhard_bp_t *bph; + + /* + * Clear the hardware breakpoint table + */ + + memset(kdb_hardbreaks, '\0', sizeof(kdb_hardbreaks)); + + for(i=0,bph=kdb_hardbreaks; ibph_reg = i; + bph->bph_free = 1; + } +} + +/* + * kdba_installbp + * + * Install a breakpoint + * + * Parameters: + * ef Exception frame + * bp Breakpoint structure for the breakpoint to be installed + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * For hardware breakpoints, a debug register is allocated + * and assigned to the breakpoint. If no debug register is + * available, a warning message is printed and the breakpoint + * is disabled. + * + * For instruction replacement breakpoints, we must single-step + * over the replaced instruction at this point so we can re-install + * the breakpoint instruction after the single-step. + */ + +void +kdba_installbp(kdb_eframe_t ef, kdb_bp_t *bp) +{ + /* + * Install the breakpoint, if it is not already installed. + */ + + if (KDB_DEBUG(BP)) { + kdb_printf("kdba_installbp bp_installed %d\n", bp->bp_installed); + } + if (!bp->bp_installed) { + if (bp->bp_hardtype) { + //kdba_installdbreg(bp); + bp->bp_installed = 1; + if (KDB_DEBUG(BP)) { + kdb_printf("kdba_installbp hardware reg %ld at " kdb_bfd_vma_fmt "\n", + bp->bp_hard->bph_reg, bp->bp_addr); + } + } else if (bp->bp_delay) { + if (KDB_DEBUG(BP)) + kdb_printf("kdba_installbp delayed bp\n"); + kdba_handle_bp(ef, bp); + } else { + bp->bp_inst = kdba_getword(bp->bp_addr, 1); + kdba_putword(bp->bp_addr, 1, 0x7fe00008); + bp->bp_instvalid = 1; + if (KDB_DEBUG(BP)) + kdb_printf("kdba_installbp instruction 0x%x at " kdb_bfd_vma_fmt "\n", + 0x7fe00008, bp->bp_addr); + bp->bp_installed = 1; + } + } +} + +/* + * kdba_removebp + * + * Make a breakpoint ineffective. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdba_removebp(kdb_bp_t *bp) +{ + /* + * For hardware breakpoints, remove it from the active register, + * for software breakpoints, restore the instruction stream. + */ + if (KDB_DEBUG(BP)) { + kdb_printf("kdba_removebp bp_installed %d\n", bp->bp_installed); + } + if (bp->bp_installed) { + if (bp->bp_hardtype) { + if (KDB_DEBUG(BP)) { + kdb_printf("kdb: removing hardware reg %ld at " kdb_bfd_vma_fmt "\n", + bp->bp_hard->bph_reg, bp->bp_addr); + } +// kdba_removedbreg(bp); + } else if (bp->bp_instvalid) { + if (KDB_DEBUG(BP)) + kdb_printf("kdb: restoring instruction 0x%x at " kdb_bfd_vma_fmt "\n", + bp->bp_inst, bp->bp_addr); + kdba_putword(bp->bp_addr, 1, bp->bp_inst); + bp->bp_instvalid = 0; + } + bp->bp_installed = 0; + } +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kdb/kdba_bt.c linux.ac/arch/ppc64/kdb/kdba_bt.c --- linux.vanilla/arch/ppc64/kdb/kdba_bt.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kdb/kdba_bt.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,263 @@ +/* + * Minimalist Kernel Debugger - Architecture Dependent Stack Traceback + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + * Masahiro Adegawa 1999/12/01 + * 'sr' command, active flag in 'ps' + * Scott Lurndal 1999/12/12 + * Significantly restructure for linux2.3 + * Keith Owens 2000/05/23 + * KDB v1.2 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* for STACK_FRAME_OVERHEAD */ +#include +#include "privinst.h" + +void systemreset(struct pt_regs *regs) +{ + udbg_printf("Oh no!\n"); + kdb(KDB_REASON_OOPS, 0, (kdb_eframe_t) regs); + for (;;); +} + +/* Copy a block of memory using kdba_getword(). + * This is not efficient. + */ +static void kdba_getmem(unsigned long addr, void *p, int size) +{ + unsigned char *dst = (unsigned char *)p; + while (size > 0) { + *dst++ = kdba_getword(addr++, 1); + size--; + } +} + + +/* + * kdba_bt_stack_ppc + * + * kdba_bt_stack with ppc specific parameters. + * Specification as kdba_bt_stack plus :- + * + * Inputs: + * As kba_bt_stack plus + * regs_esp If 1 get esp from the registers (exception frame), if 0 + * get esp from kdba_getregcontents. + */ + +static int +kdba_bt_stack_ppc(struct pt_regs *regs, kdb_machreg_t *addr, int argcount, + struct task_struct *p, int regs_esp) +{ + kdb_machreg_t esp,eip,ebp; + kdb_symtab_t symtab, *sym; + kdbtbtable_t tbtab; + /* declare these as raw ptrs so we don't get func descriptors */ + extern void *ret_from_except, *ret_from_syscall_1, *do_bottom_half_ret; + + /* + * The caller may have supplied an address at which the + * stack traceback operation should begin. This address + * is assumed by this code to point to a return-address + * on the stack to be traced back. + * + * The end result of this will make it appear as if a function + * entitled '' was called from the function which + * contains return-address. + */ + if (addr) { + eip = 0; + esp = *addr; + ebp=0; + } else { + ebp=regs->link; + eip = regs->nip; + if (regs_esp) + esp = regs->gpr[1]; + else + kdba_getregcontents("esp", regs, &esp); + } + + kdb_printf(" SP PC Function(args)\n"); + + /* (Ref: 64-bit PowerPC ELF ABI Spplement; Ian Lance Taylor, Zembu Labs). + A PPC stack frame looks like this: + + High Address + Back Chain + FP reg save area + GP reg save area + Local var space + Parameter save area (SP+48) + TOC save area (SP+40) + link editor doubleword (SP+32) + compiler doubleword (SP+24) + LR save (SP+16) + CR save (SP+8) + Back Chain (SP+0) + + Note that the LR (ret addr) may not be saved in the *current* frame if + no functions have been called from the current function. + */ + + /* + * Run through the activation records and print them. + */ + while (1) { + kdbnearsym(eip, &symtab); + kdb_printf("0x%016lx 0x%016lx ", esp, eip); + kdba_find_tb_table(eip, &tbtab); + sym = symtab.sym_name ? &symtab : &tbtab.symtab; /* use fake symtab if necessary */ + if (sym->sym_name) { + kdb_printf("%s", sym->sym_name); + if (eip - sym->sym_start > 0) + kdb_printf("+0x%lx", eip - sym->sym_start); + } + kdb_printf("\n"); + if (eip == (kdb_machreg_t)ret_from_except || + eip == (kdb_machreg_t)ret_from_syscall_1 || + eip == (kdb_machreg_t)do_bottom_half_ret) { + /* pull exception regs from the stack */ + struct pt_regs eregs; + kdba_getmem(esp+STACK_FRAME_OVERHEAD, &eregs, sizeof(eregs)); + kdb_printf(" [exception: %lx regs 0x%lx]\n", eregs.trap, esp+STACK_FRAME_OVERHEAD); + esp = eregs.gpr[1]; + eip = eregs.nip; + if (!esp) + break; + } else { + esp = kdba_getword(esp, 8); + if (!esp) + break; + eip = kdba_getword(esp+16, 8); /* saved lr */ + } + } + + return 0; +} + +/* + * kdba_bt_stack + * + * This function implements the 'bt' command. Print a stack + * traceback. + * + * bt [] (addr-exp is for alternate stacks) + * btp (Kernel stack for ) + * + * address expression refers to a return address on the stack. It + * may be preceeded by a frame pointer. + * + * Inputs: + * regs registers at time kdb was entered. + * addr Pointer to Address provided to 'bt' command, if any. + * argcount + * p Pointer to task for 'btp' command. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * mds comes in handy when examining the stack to do a manual + * traceback. + */ + +int +kdba_bt_stack(struct pt_regs *regs, kdb_machreg_t *addr, int argcount, + struct task_struct *p) +{ + return(kdba_bt_stack_ppc(regs, addr, argcount, p, 0)); +} + +int +kdba_bt_process(struct task_struct *p, int argcount) +{ + struct pt_regs taskregs; + + memset(&taskregs, 0, sizeof(taskregs)); + if (p->thread.regs == NULL) + { + struct pt_regs regs; + asm volatile ("std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + std 3,24(%0)\n\ + std 4,32(%0)\n\ + std 5,40(%0)\n\ + std 6,48(%0)\n\ + std 7,56(%0)\n\ + std 8,64(%0)\n\ + std 9,72(%0)\n\ + std 10,80(%0)\n\ + std 11,88(%0)\n\ + std 12,96(%0)\n\ + std 13,104(%0)\n\ + std 14,112(%0)\n\ + std 15,120(%0)\n\ + std 16,128(%0)\n\ + std 17,136(%0)\n\ + std 18,144(%0)\n\ + std 19,152(%0)\n\ + std 20,160(%0)\n\ + std 21,168(%0)\n\ + std 22,176(%0)\n\ + std 23,184(%0)\n\ + std 24,192(%0)\n\ + std 25,200(%0)\n\ + std 26,208(%0)\n\ + std 27,216(%0)\n\ + std 28,224(%0)\n\ + std 29,232(%0)\n\ + std 30,240(%0)\n\ + std 31,248(%0)" : : "b" (®s)); + regs.nip = regs.link = ((unsigned long *)regs.gpr[1])[1]; + regs.msr = get_msr(); + regs.ctr = get_ctr(); + regs.xer = get_xer(); + regs.ccr = get_cr(); + regs.trap = 0; + taskregs = regs; + } + else + { + taskregs.nip = p->thread.regs->nip; + taskregs.gpr[1] = p->thread.regs->gpr[1]; + taskregs.link = p->thread.regs->link; + + } + kdb_printf("taskregs.gpr[1]=%lx\n", taskregs.gpr[1]); + if (taskregs.gpr[1] < (unsigned long)p || + taskregs.gpr[1] >= (unsigned long)p + 8192) { + kdb_printf("Stack is not in task_struct, backtrace not available\n"); + return(0); + } + kdb_printf("About to backtrace process!\n"); + return kdba_bt_stack_ppc(&taskregs, NULL, argcount, p, 1); + +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kdb/kdba_id.c linux.ac/arch/ppc64/kdb/kdba_id.c --- linux.vanilla/arch/ppc64/kdb/kdba_id.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kdb/kdba_id.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,276 @@ +/* + * Minimalist Kernel Debugger - Architecture Dependent Instruction Disassembly + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + * Masahiro Adegawa 1999/12/01 + * 'sr' command, active flag in 'ps' + * Scott Lurndal 1999/12/12 + * Significantly restructure for linux2.3 + * Keith Owens 2000/05/23 + * KDB v1.2 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * kdba_dis_getsym + * + * Get a symbol for the disassembler. + * + * Parameters: + * addr Address for which to get symbol + * dip Pointer to disassemble_info + * Returns: + * 0 + * Locking: + * Remarks: + * Not used for kdb. + */ + +/* ARGSUSED */ +static int +kdba_dis_getsym(bfd_vma addr, disassemble_info *dip) +{ + + return 0; +} + +/* + * kdba_printaddress + * + * Print (symbolically) an address. + * + * Parameters: + * addr Address for which to get symbol + * dip Pointer to disassemble_info + * flag True if a ":" sequence should follow the address + * Returns: + * number of chars printed + * Locking: + * Remarks: + * + */ + +/* ARGSUSED */ +void +kdba_printaddress(kdb_machreg_t addr, disassemble_info *dip, int flag) +{ + kdb_symtab_t symtab; + + /* + * Print a symbol name or address as necessary. + */ + kdbnearsym(addr, &symtab); + if (symtab.sym_name) { + /* Do not use kdb_symbol_print here, it always does + * kdb_printf but we want dip->fprintf_func. + */ + dip->fprintf_func(dip->stream, + "0x%0*lx %s", + 2*sizeof(addr), addr, symtab.sym_name); + /* Add offset if needed. Pad output with blanks to get + * consistent size symbols for disassembly listings. + */ + if (addr == symtab.sym_start) { + if (!flag) + dip->fprintf_func(dip->stream, " "); + } else { + int len, i; + char buf[20]; + sprintf(buf, "%lx", addr - symtab.sym_start); + dip->fprintf_func(dip->stream, "+0x%s", buf); + if (!flag) { + len = strlen(buf); + for (i = len; i < 6; i++) + dip->fprintf_func(dip->stream, " "); + } + } + + } else { + dip->fprintf_func(dip->stream, "0x%0*lx", 2*sizeof(addr), addr); + } + + if (flag) + dip->fprintf_func(dip->stream, ": "); +} + +/* + * kdba_dis_printaddr + * + * Print (symbolically) an address. Called by GNU disassembly + * code via disassemble_info structure. + * + * Parameters: + * addr Address for which to get symbol + * dip Pointer to disassemble_info + * Returns: + * number of chars printed. + * Locking: + * Remarks: + * This function will never append ":" to the printed + * symbolic address. + */ + +static void +kdba_dis_printaddr(bfd_vma addr, disassemble_info *dip) +{ + return kdba_printaddress(addr, dip, 0); +} + +/* + * kdba_dis_getmem + * + * Fetch 'length' bytes from 'addr' into 'buf'. + * + * Parameters: + * addr Address for which to get symbol + * buf Address of buffer to fill with bytes from 'addr' + * length Number of bytes to fetch + * dip Pointer to disassemble_info + * Returns: + * 0 + * Locking: + * Remarks: + * + */ + +/* ARGSUSED */ +static int +kdba_dis_getmem(bfd_vma addr, bfd_byte *buf, unsigned int length, disassemble_info *dip) +{ + bfd_byte *bp = buf; + int i; + + /* + * Fill the provided buffer with bytes from + * memory, starting at address 'addr' for 'length bytes. + * + */ + + for(i=0; iread_memory_func = kdba_dis_getmem; + dip->print_address_func = kdba_dis_printaddr; + dip->symbol_at_address_func = kdba_dis_getsym; + + dip->flavour = bfd_target_elf_flavour; + dip->arch = bfd_arch_powerpc; + dip->mach = bfd_mach_ppc_750; + dip->endian = BFD_ENDIAN_BIG; + + dip->display_endian = BFD_ENDIAN_BIG; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kdb/kdba_io.c linux.ac/arch/ppc64/kdb/kdba_io.c --- linux.vanilla/arch/ppc64/kdb/kdba_io.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kdb/kdba_io.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,162 @@ +/* + * Kernel Debugger Console I/O handler + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Chuck Fleckenstein 1999/07/20 + * Move kdb_info struct declaration to this file + * for cases where serial support is not compiled into + * the kernel. + * + * Masahiro Adegawa 1999/07/20 + * Handle some peculiarities of japanese 86/106 + * keyboards. + * + * marc@mucom.co.il 1999/07/20 + * Catch buffer overflow for serial input. + * + * Scott Foehner + * Port to ia64 + * + * Scott Lurndal 2000/01/03 + * Restructure for v1.0 + * + * Keith Owens 2000/05/23 + * KDB v1.2 + * + * Andi Kleen 2000/03/19 + * Support simultaneous input from serial line and keyboard. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#undef FILE +#include "nonstdio.h" + +int kdb_port; + +/* + * This module contains code to read characters from the keyboard or a serial + * port. + * + * It is used by the kernel debugger, and is polled, not interrupt driven. + * + */ + + +void +kdb_resetkeyboard(void) +{ +#if 0 + kdb_kbdsend(KBD_CMD_ENABLE); +#endif +} + +int inchar(void); + +#if defined(CONFIG_VT) +/* + * Check if the keyboard controller has a keypress for us. + * Some parts (Enter Release, LED change) are still blocking polled here, + * but hopefully they are all short. + */ +static int get_kbd_char(void) +{ + int keychar; + keychar = inchar(); + if (keychar == '\n') + { + kdb_printf("\n"); + } + /* + * echo the character. + */ + kdb_printf("%c", keychar); + + return keychar ; +} +#endif /* CONFIG_VT */ + + +typedef int (*get_char_func)(void); + +static get_char_func poll_funcs[] = { +#if defined(CONFIG_VT) + get_kbd_char, +#endif + NULL +}; +void flush_input(void); +char * +kdba_read(char *buffer, size_t bufsize) +{ + char *cp = buffer; + char *bufend = buffer+bufsize-2; /* Reserve space for newline and null byte */ + flush_input(); + for (;;) { + int key; + get_char_func *f; + for (f = &poll_funcs[0]; ; ++f) { + if (*f == NULL) + f = &poll_funcs[0]; + key = (*f)(); + if (key != -1) + break; + } + + /* Echo is done in the low level functions */ + switch (key) { + case '\b': /* backspace */ + if (cp > buffer) { + xmon_printf("\b \b"); + --cp; + } + break; + case '\n': /* enter */ + *cp++ = '\n'; + *cp++ = '\0'; + return buffer; + default: + if (cp < bufend) + *cp++ = key; + break; + } + } +} +static char line[256]; +static char *lineptr; +void flush_input(void) +{ + lineptr = NULL; +} +int inchar(void) +{ + if (lineptr == NULL || *lineptr == 0) + { + if (xmon_fgets(line, sizeof(line), xmon_stdin) == NULL) + { + lineptr = NULL; + return EOF; + } + lineptr = line; + } + return *lineptr++; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kdb/kdbasupport.c linux.ac/arch/ppc64/kdb/kdbasupport.c --- linux.vanilla/arch/ppc64/kdb/kdbasupport.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kdb/kdbasupport.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,1140 @@ +/* + * Kernel Debugger Architecture Independent Support Functions + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "privinst.h" +#include + +char *kdb_diemsg; + +/* + * kdba_prologue + * + * This function analyzes a gcc-generated function prototype + * with or without frame pointers to determine the amount of + * automatic storage and register save storage is used on the + * stack of the target function. It only counts instructions + * that have been executed up to but excluding the current eip. + * Inputs: + * code Start address of function code to analyze + * pc Current program counter within function + * sp Current stack pointer for function + * fp Current frame pointer for function, may not be valid + * ss Start of stack for current process. + * caller 1 if looking for data on the caller frame, 0 for callee. + * Outputs: + * ar Activation record, all fields may be set. fp and oldfp + * are 0 if they cannot be extracted. return is 0 if the + * code cannot find a valid return address. args and arg0 + * are 0 if the number of arguments cannot be safely + * calculated. + * Returns: + * 1 if prologue is valid, 0 otherwise. If pc is 0 treat it as a + * valid prologue to allow bt on wild branches. + * Locking: + * None. + * Remarks: + * + */ +int +kdba_prologue(const kdb_symtab_t *symtab, kdb_machreg_t pc, kdb_machreg_t sp, + kdb_machreg_t fp, kdb_machreg_t ss, int caller, kdb_ar_t *ar) +{ + /* We don't currently use kdb's generic activation record scanning + * code to handle backtrace. + */ + return 0; +} + + + +/* + * kdba_getregcontents + * + * Return the contents of the register specified by the + * input string argument. Return an error if the string + * does not match a machine register. + * + * The following pseudo register names are supported: + * ®s - Prints address of exception frame + * kesp - Prints kernel stack pointer at time of fault + * cesp - Prints current kernel stack pointer, inside kdb + * ceflags - Prints current flags, inside kdb + * % - Uses the value of the registers at the + * last time the user process entered kernel + * mode, instead of the registers at the time + * kdb was entered. + * + * Parameters: + * regname Pointer to string naming register + * regs Pointer to structure containing registers. + * Outputs: + * *contents Pointer to unsigned long to recieve register contents + * Returns: + * 0 Success + * KDB_BADREG Invalid register name + * Locking: + * None. + * Remarks: + * If kdb was entered via an interrupt from the kernel itself then + * ss and esp are *not* on the stack. + */ + +static struct kdbregs { + char *reg_name; + size_t reg_offset; +} kdbreglist[] = { + { "gpr0", offsetof(struct pt_regs, gpr[0]) }, + { "gpr1", offsetof(struct pt_regs, gpr[1]) }, + { "gpr2", offsetof(struct pt_regs, gpr[2]) }, + { "gpr3", offsetof(struct pt_regs, gpr[3]) }, + { "gpr4", offsetof(struct pt_regs, gpr[4]) }, + { "gpr5", offsetof(struct pt_regs, gpr[5]) }, + { "gpr6", offsetof(struct pt_regs, gpr[6]) }, + { "gpr7", offsetof(struct pt_regs, gpr[7]) }, + { "gpr8", offsetof(struct pt_regs, gpr[8]) }, + { "gpr9", offsetof(struct pt_regs, gpr[9]) }, + { "gpr10", offsetof(struct pt_regs, gpr[10]) }, + { "gpr11", offsetof(struct pt_regs, gpr[11]) }, + { "gpr12", offsetof(struct pt_regs, gpr[12]) }, + { "gpr13", offsetof(struct pt_regs, gpr[13]) }, + { "gpr14", offsetof(struct pt_regs, gpr[14]) }, + { "gpr15", offsetof(struct pt_regs, gpr[15]) }, + { "gpr16", offsetof(struct pt_regs, gpr[16]) }, + { "gpr17", offsetof(struct pt_regs, gpr[17]) }, + { "gpr18", offsetof(struct pt_regs, gpr[18]) }, + { "gpr19", offsetof(struct pt_regs, gpr[19]) }, + { "gpr20", offsetof(struct pt_regs, gpr[20]) }, + { "gpr21", offsetof(struct pt_regs, gpr[21]) }, + { "gpr22", offsetof(struct pt_regs, gpr[22]) }, + { "gpr23", offsetof(struct pt_regs, gpr[23]) }, + { "gpr24", offsetof(struct pt_regs, gpr[24]) }, + { "gpr25", offsetof(struct pt_regs, gpr[25]) }, + { "gpr26", offsetof(struct pt_regs, gpr[26]) }, + { "gpr27", offsetof(struct pt_regs, gpr[27]) }, + { "gpr28", offsetof(struct pt_regs, gpr[28]) }, + { "gpr29", offsetof(struct pt_regs, gpr[29]) }, + { "gpr30", offsetof(struct pt_regs, gpr[30]) }, + { "gpr31", offsetof(struct pt_regs, gpr[31]) }, + { "eip", offsetof(struct pt_regs, nip) }, + { "msr", offsetof(struct pt_regs, msr) }, + { "esp", offsetof(struct pt_regs, gpr[1]) }, + { "orig_gpr3", offsetof(struct pt_regs, orig_gpr3) }, + { "ctr", offsetof(struct pt_regs, ctr) }, + { "link", offsetof(struct pt_regs, link) }, + { "xer", offsetof(struct pt_regs, xer) }, + { "ccr", offsetof(struct pt_regs, ccr) }, + { "mq", offsetof(struct pt_regs, mq) }, + { "trap", offsetof(struct pt_regs, trap) }, + { "dar", offsetof(struct pt_regs, dar) }, + { "dsisr", offsetof(struct pt_regs, dsisr) }, + { "result", offsetof(struct pt_regs, result) }, + +}; + +static const int nkdbreglist = sizeof(kdbreglist) / sizeof(struct kdbregs); + +unsigned long +getsp(void) +{ + unsigned long x; + asm("mr %0,1" : "=r" (x):); + return x; +} +int +kdba_getregcontents(const char *regname, + struct pt_regs *regs, + kdb_machreg_t *contents) +{ + int i; + + if (strcmp(regname, "®s") == 0) { + *contents = (unsigned long)regs; + return 0; + } + + if (strcmp(regname, "kesp") == 0) { + *contents = (unsigned long) current->thread.ksp; + return 0; + } + + if (strcmp(regname, "cesp") == 0) { + *contents = getsp(); + return 0; + } + + if (strcmp(regname, "ceflags") == 0) { + int flags; + save_flags(flags); + *contents = flags; + return 0; + } + + if (regname[0] == '%') { + /* User registers: %%e[a-c]x, etc */ + regname++; + regs = (struct pt_regs *) + (current->thread.ksp - sizeof(struct pt_regs)); + } + + for (i=0; i + * + * Parameters: + * regname Pointer to string naming register + * regs Pointer to structure containing registers. + * contents Unsigned long containing new register contents + * Outputs: + * Returns: + * 0 Success + * KDB_BADREG Invalid register name + * Locking: + * None. + * Remarks: + */ + +int +kdba_setregcontents(const char *regname, + struct pt_regs *regs, + unsigned long contents) +{ + int i; + + if (regname[0] == '%') { + regname++; + regs = (struct pt_regs *) + (current->thread.ksp - sizeof(struct pt_regs)); + } + + for (i=0; ithread.ksp - sizeof(struct pt_regs)); + } + + if (type == NULL) { + struct kdbregs *rlp; + kdb_machreg_t contents; + + for (i=0, rlp=kdbreglist; ireg_name, regs, &contents); + kdb_printf("%-5s = 0x%p%c", rlp->reg_name, (void *)contents, (++count % 2) ? ' ' : '\n'); + } + + kdb_printf("®s = 0x%p\n", regs); + + return 0; + } + + switch (type[0]) { + case 'm': + break; + case 'r': + break; + default: + return KDB_BADREG; + } + + /* NOTREACHED */ + return 0; +} + +kdb_machreg_t +kdba_getpc(kdb_eframe_t ef) +{ + return ef->nip; +} + +int +kdba_setpc(kdb_eframe_t ef, kdb_machreg_t newpc) +{ + ef->nip = newpc; + KDB_STATE_SET(IP_ADJUSTED); + return 0; +} + +/* + * kdba_main_loop + * + * Do any architecture specific set up before entering the main kdb loop. + * The primary function of this routine is to make all processes look the + * same to kdb, kdb must be able to list a process without worrying if the + * process is running or blocked, so make all process look as though they + * are blocked. + * + * Inputs: + * reason The reason KDB was invoked + * error The hardware-defined error code + * error2 kdb's current reason code. Initially error but can change + * acording to kdb state. + * db_result Result from break or debug point. + * ef The exception frame at time of fault/breakpoint. If reason + * is KDB_REASON_SILENT then ef is NULL, otherwise it should + * always be valid. + * Returns: + * 0 KDB was invoked for an event which it wasn't responsible + * 1 KDB handled the event for which it was invoked. + * Outputs: + * Sets eip and esp in current->thread. + * Locking: + * None. + * Remarks: + * none. + */ + +int +kdba_main_loop(kdb_reason_t reason, kdb_reason_t reason2, int error, + kdb_dbtrap_t db_result, kdb_eframe_t ef) +{ + int rv; + unsigned int msr; + if (current->thread.regs == NULL) + { + struct pt_regs regs; + asm volatile ("std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + std 3,24(%0)\n\ + std 4,32(%0)\n\ + std 5,40(%0)\n\ + std 6,48(%0)\n\ + std 7,56(%0)\n\ + std 8,64(%0)\n\ + std 9,72(%0)\n\ + std 10,80(%0)\n\ + std 11,88(%0)\n\ + std 12,96(%0)\n\ + std 13,104(%0)\n\ + std 14,112(%0)\n\ + std 15,120(%0)\n\ + std 16,128(%0)\n\ + std 17,136(%0)\n\ + std 18,144(%0)\n\ + std 19,152(%0)\n\ + std 20,160(%0)\n\ + std 21,168(%0)\n\ + std 22,176(%0)\n\ + std 23,184(%0)\n\ + std 24,192(%0)\n\ + std 25,200(%0)\n\ + std 26,208(%0)\n\ + std 27,216(%0)\n\ + std 28,224(%0)\n\ + std 29,232(%0)\n\ + std 30,240(%0)\n\ + std 31,248(%0)" : : "b" (®s)); + /* Fetch the link reg for this stack frame. + NOTE: the prev kdb_printf fills in the lr. */ + regs.nip = regs.link = ((unsigned long *)regs.gpr[1])[2]; + regs.msr = get_msr(); + regs.ctr = get_ctr(); + regs.xer = get_xer(); + regs.ccr = get_cr(); + regs.trap = 0; + current->thread.regs = ®s; + } + if (ef) { + kdba_getregcontents("eip", ef, &(current->thread.regs->nip)); + kdba_getregcontents("esp", ef, &(current->thread.regs->gpr[1])); + + } + msr = get_msr(); + set_msr( msr & ~0x8000); + rv = kdb_main_loop(reason, reason2, error, db_result, ef); + set_msr(msr); + return rv; +} + +void +kdba_disableint(kdb_intstate_t *state) +{ + int *fp = (int *)state; + int flags; + + save_flags(flags); + cli(); + + *fp = flags; +} + +void +kdba_restoreint(kdb_intstate_t *state) +{ + int flags = *(int *)state; + restore_flags(flags); +} + +void +kdba_setsinglestep(struct pt_regs *regs) +{ + regs->msr |= MSR_SE; +} + +void +kdba_clearsinglestep(struct pt_regs *regs) +{ + + regs->msr &= ~MSR_SE; +} +int +kdb_getcurrentframe(struct pt_regs *regs) +{ + regs->gpr[1] = getsp(); + return 0; +} + + +#ifdef KDB_HAVE_LONGJMP +int kdb_setjmp(kdb_jmp_buf *buf) +{ + asm volatile ( + "mflr 0; std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + mfcr 0; std 0,24(%0)\n\ + std 13,32(%0)\n\ + std 14,40(%0)\n\ + std 15,48(%0)\n\ + std 16,56(%0)\n\ + std 17,64(%0)\n\ + std 18,72(%0)\n\ + std 19,80(%0)\n\ + std 20,88(%0)\n\ + std 21,96(%0)\n\ + std 22,104(%0)\n\ + std 23,112(%0)\n\ + std 24,120(%0)\n\ + std 25,128(%0)\n\ + std 26,136(%0)\n\ + std 27,144(%0)\n\ + std 28,152(%0)\n\ + std 29,160(%0)\n\ + std 30,168(%0)\n\ + std 31,176(%0)\n\ + " : : "r" (buf)); + KDB_STATE_SET(LONGJMP); + return 0; +} +void kdb_longjmp(kdb_jmp_buf *buf, int val) +{ + if (val == 0) + val = 1; + asm volatile ( + "ld 13,32(%0)\n\ + ld 14,40(%0)\n\ + ld 15,48(%0)\n\ + ld 16,56(%0)\n\ + ld 17,64(%0)\n\ + ld 18,72(%0)\n\ + ld 19,80(%0)\n\ + ld 20,88(%0)\n\ + ld 21,96(%0)\n\ + ld 22,104(%0)\n\ + ld 23,112(%0)\n\ + ld 24,120(%0)\n\ + ld 25,128(%0)\n\ + ld 26,136(%0)\n\ + ld 27,144(%0)\n\ + ld 28,152(%0)\n\ + ld 29,160(%0)\n\ + ld 30,168(%0)\n\ + ld 31,176(%0)\n\ + ld 0,24(%0)\n\ + mtcrf 0x38,0\n\ + ld 0,0(%0)\n\ + ld 1,8(%0)\n\ + ld 2,16(%0)\n\ + mtlr 0\n\ + mr 3,%1\n\ + " : : "r" (buf), "r" (val)); +} +#endif + +/* + * kdba_enable_mce + * + * This function is called once on each CPU to enable machine + * check exception handling. + * + * Inputs: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + */ + +void +kdba_enable_mce(void) +{ +} + +/* + * kdba_enable_lbr + * + * Enable last branch recording. + * + * Parameters: + * None. + * Returns: + * None + * Locking: + * None + * Remarks: + * None. + */ + +void +kdba_enable_lbr(void) +{ +} + +/* + * kdba_disable_lbr + * + * disable last branch recording. + * + * Parameters: + * None. + * Returns: + * None + * Locking: + * None + * Remarks: + * None. + */ + +void +kdba_disable_lbr(void) +{ +} + +/* + * kdba_print_lbr + * + * Print last branch and last exception addresses + * + * Parameters: + * None. + * Returns: + * None + * Locking: + * None + * Remarks: + * None. + */ + +void +kdba_print_lbr(void) +{ +} + +/* + * kdba_getword + * + * Architecture specific function to access kernel virtual + * address space. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +unsigned long +kdba_getword(unsigned long addr, size_t width) +{ + /* + * This function checks the address for validity. Any address + * in the range PAGE_OFFSET to high_memory is legal, any address + * which maps to a vmalloc region is legal, and any address which + * is a user address, we use get_user() to verify validity. + */ + + if (addr < PAGE_OFFSET) { + /* + * Usermode address. + */ + unsigned long diag; + unsigned long ulval; + + switch (width) { + case 8: + { unsigned long *lp; + + lp = (unsigned long *) addr; + diag = get_user(ulval, lp); + break; + } + case 4: + { unsigned int *ip; + + ip = (unsigned int *) addr; + diag = get_user(ulval, ip); + break; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *) addr; + diag = get_user(ulval, sp); + break; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *) addr; + diag = get_user(ulval, cp); + break; + } + default: + kdb_printf("kdbgetword: Bad width\n"); + return 0L; + } + + if (diag) { + if (!KDB_STATE(SUPPRESS)) { + kdb_printf("kdb: Bad user address 0x%lx\n", addr); + KDB_STATE_SET(SUPPRESS); + } + return 0L; + } + KDB_STATE_CLEAR(SUPPRESS); + return ulval; + } + +#if 0 + if (addr > (unsigned long)high_memory) { + if (!kdb_vmlist_check(addr, addr+width)) { + /* + * Would appear to be an illegal kernel address; + * Print a message once, and don't print again until + * a legal address is used. + */ + if (!KDB_STATE(SUPPRESS)) { + kdb_printf("kdb: Bad kernel address 0x%lx (above highmem 0x%lx)\n", addr, high_memory); + KDB_STATE_SET(SUPPRESS); + } + return 0L; + } + } +#endif + + /* + * A good address. Reset error flag. + */ + KDB_STATE_CLEAR(SUPPRESS); + + switch (width) { + case 8: + { unsigned long *lp; + + lp = (unsigned long *)(addr); + return *lp; + } + case 4: + { unsigned int *ip; + + ip = (unsigned int *)(addr); + return *ip; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *)(addr); + return *sp; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *)(addr); + return *cp; + } + } + + kdb_printf("kdbgetword: Bad width\n"); + return 0L; +} + + +/* + * kdba_putword + * + * Architecture specific function to access kernel virtual + * address space. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +unsigned long +kdba_putword(unsigned long addr, size_t size, unsigned long contents) +{ + /* + * This function checks the address for validity. Any address + * in the range PAGE_OFFSET to high_memory is legal, any address + * which maps to a vmalloc region is legal, and any address which + * is a user address, we use get_user() to verify validity. + */ + + if (addr < PAGE_OFFSET) { + /* + * Usermode address. + */ + unsigned long diag; + + switch (size) { + case 4: + { unsigned long *lp; + + lp = (unsigned long *) addr; + diag = put_user(contents, lp); + break; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *) addr; + diag = put_user(contents, sp); + break; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *) addr; + diag = put_user(contents, cp); + break; + } + default: + kdb_printf("kdba_putword: Bad width\n"); + return 0; + } + + if (diag) { + if (!KDB_STATE(SUPPRESS)) { + kdb_printf("kdb: Bad user address 0x%lx\n", addr); + KDB_STATE_SET(SUPPRESS); + } + return 0; + } + KDB_STATE_CLEAR(SUPPRESS); + return 0; + } + +#if 0 + if (addr > (unsigned long)high_memory) { + if (!kdb_vmlist_check(addr, addr+size)) { + /* + * Would appear to be an illegal kernel address; + * Print a message once, and don't print again until + * a legal address is used. + */ + if (!KDB_STATE(SUPPRESS)) { + kdb_printf("kdb: Bad kernel address 0x%lx\n", addr); + KDB_STATE_SET(SUPPRESS); + } + return 0L; + } + } +#endif + + /* + * A good address. Reset error flag. + */ + KDB_STATE_CLEAR(SUPPRESS); + + switch (size) { + case 4: + { unsigned long *lp; + + lp = (unsigned long *)(addr); + *lp = contents; + return 0; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *)(addr); + *sp = (unsigned short) contents; + return 0; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *)(addr); + *cp = (unsigned char) contents; + return 0; + } + } + + kdb_printf("kdba_putword: Bad width\n"); + return 0; +} + +/* + * kdba_callback_die + * + * Callback function for kernel 'die' function. + * + * Parameters: + * regs Register contents at time of trap + * error_code Trap-specific error code value + * trapno Trap number + * vp Pointer to die message + * Returns: + * Returns 1 if fault handled by kdb. + * Locking: + * None. + * Remarks: + * + */ +int +kdba_callback_die(struct pt_regs *regs, int error_code, long trapno, void *vp) +{ + /* + * Save a pointer to the message provided to 'die()'. + */ + kdb_diemsg = (char *)vp; + + return kdb(KDB_REASON_OOPS, error_code, (kdb_eframe_t) regs); +} + +/* + * kdba_callback_bp + * + * Callback function for kernel breakpoint trap. + * + * Parameters: + * regs Register contents at time of trap + * error_code Trap-specific error code value + * trapno Trap number + * vp Not Used. + * Returns: + * Returns 1 if fault handled by kdb. + * Locking: + * None. + * Remarks: + * + */ + +int +kdba_callback_bp(struct pt_regs *regs, int error_code, long trapno, void *vp) +{ + int diag; + + if (KDB_DEBUG(BP)) + kdb_printf("cb_bp: e_c = %d tn = %ld regs = 0x%p\n", error_code, + trapno, regs); + + diag = kdb(KDB_REASON_BREAK, error_code, (kdb_eframe_t) regs); + + if (KDB_DEBUG(BP)) + kdb_printf("cb_bp: e_c = %d tn = %ld regs = 0x%p diag = %d\n", error_code, + trapno, regs, diag); + return diag; +} + +/* + * kdba_callback_debug + * + * Callback function for kernel debug register trap. + * + * Parameters: + * regs Register contents at time of trap + * error_code Trap-specific error code value + * trapno Trap number + * vp Not used. + * Returns: + * Returns 1 if fault handled by kdb. + * Locking: + * None. + * Remarks: + * + */ + +int +kdba_callback_debug(struct pt_regs *regs, int error_code, long trapno, void *vp) +{ + return kdb(KDB_REASON_DEBUG, error_code, (kdb_eframe_t) regs); +} + +/* + * kdba_init + * + * Architecture specific initialization. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +void __init +kdba_init(void) +{ + kdba_enable_lbr(); + + return; +} + +/* + * kdba_adjust_ip + * + * Architecture specific adjustment of instruction pointer before leaving + * kdb. + * + * Parameters: + * reason The reason KDB was invoked + * error The hardware-defined error code + * ef The exception frame at time of fault/breakpoint. If reason + * is KDB_REASON_SILENT then ef is NULL, otherwise it should + * always be valid. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * noop on ix86. + */ + +void +kdba_adjust_ip(kdb_reason_t reason, int error, kdb_eframe_t ef) +{ + return; +} + + + +/* + * kdba_find_tb_table + * + * Find the traceback table (defined by the ELF64 ABI) located at + * the end of the function containing pc. + * + * Parameters: + * eip starting instruction addr. does not need to be at the start of the func. + * tab table to populate if successful + * Returns: + * non-zero if successful. unsuccessful means that a valid tb table was not found + * Locking: + * None. + * Remarks: + * None. + */ +int kdba_find_tb_table(kdb_machreg_t eip, kdbtbtable_t *tab) +{ + kdb_machreg_t codeaddr = eip; + kdb_machreg_t codeaddr_max; + kdb_machreg_t tbtab_start; + int instr; + int num_parms; + + if (tab == NULL) + return 0; + memset(tab, 0, sizeof(tab)); + + /* Scan instructions starting at codeaddr for 128k max */ + for (codeaddr_max = codeaddr + 128*1024*4; + codeaddr < codeaddr_max; + codeaddr += 4) { + instr = kdba_getword(codeaddr, 4); + if (instr == 0) { + /* table should follow. */ + int version; + unsigned long flags; + tbtab_start = codeaddr; /* save it to compute func start addr */ + codeaddr += 4; + flags = kdba_getword(codeaddr, 8); + tab->flags = flags; + version = (flags >> 56) & 0xff; + if (version != 0) + continue; /* No tb table here. */ + /* Now, like the version, some of the flags are values + that are more conveniently extracted... */ + tab->fp_saved = (flags >> 24) & 0x3f; + tab->gpr_saved = (flags >> 16) & 0x3f; + tab->fixedparms = (flags >> 8) & 0xff; + tab->floatparms = (flags >> 1) & 0x7f; + codeaddr += 8; + num_parms = tab->fixedparms + tab->floatparms; + if (num_parms) { + unsigned int parminfo; + int parm; + if (num_parms > 32) + return 1; /* incomplete */ + parminfo = kdba_getword(codeaddr, 4); + /* decode parminfo...32 bits. + A zero means fixed. A one means float and the + following bit determines single (0) or double (1). + */ + for (parm = 0; parm < num_parms; parm++) { + if (parminfo & 0x80000000) { + parminfo <<= 1; + if (parminfo & 0x80000000) + tab->parminfo[parm] = KDBTBTAB_PARMDFLOAT; + else + tab->parminfo[parm] = KDBTBTAB_PARMSFLOAT; + } else { + tab->parminfo[parm] = KDBTBTAB_PARMFIXED; + } + parminfo <<= 1; + } + codeaddr += 4; + } + if (flags & KDBTBTAB_FLAGSHASTBOFF) { + tab->tb_offset = kdba_getword(codeaddr, 4); + if (tab->tb_offset > 0) { + tab->funcstart = tbtab_start - tab->tb_offset; + } + codeaddr += 4; + } + /* hand_mask appears to be always be omitted. */ + if (flags & KDBTBTAB_FLAGSHASCTL) { + /* Assume this will never happen for C or asm */ + return 1; /* incomplete */ + } + if (flags & KDBTBTAB_FLAGSNAMEPRESENT) { + int i; + short namlen = kdba_getword(codeaddr, 2); + if (namlen >= sizeof(tab->name)) + namlen = sizeof(tab->name)-1; + codeaddr += 2; + for (i = 0; i < namlen; i++) { + tab->name[i] = kdba_getword(codeaddr++, 1); + } + tab->name[namlen] = '\0'; + } + /* Fake up a symtab entry in case the caller finds it useful */ + tab->symtab.value = tab->symtab.sym_start = tab->funcstart; + tab->symtab.sym_name = tab->name; + tab->symtab.sym_end = tbtab_start; + return 1; + } + } + return 0; /* hit max...sorry. */ +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kdb/nonstdio.h linux.ac/arch/ppc64/kdb/nonstdio.h --- linux.vanilla/arch/ppc64/kdb/nonstdio.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kdb/nonstdio.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,22 @@ +typedef int FILE; +extern FILE *xmon_stdin, *xmon_stdout; +#define EOF (-1) +#define stdin xmon_stdin +#define stdout xmon_stdout +#define printf xmon_printf +#define fprintf xmon_fprintf +#define fputs xmon_fputs +#define fgets xmon_fgets +#define putchar xmon_putchar +#define getchar xmon_getchar +#define putc xmon_putc +#define getc xmon_getc +#define fopen(n, m) NULL +#define fflush(f) do {} while (0) +#define fclose(f) do {} while (0) +extern char *fgets(char *, int, void *); +extern void xmon_printf(const char *, ...); +extern void xmon_fprintf(void *, const char *, ...); +extern void xmon_sprintf(char *, const char *, ...); + +#define perror(s) printf("%s: no files!\n", (s)) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kdb/opintl.h linux.ac/arch/ppc64/kdb/opintl.h --- linux.vanilla/arch/ppc64/kdb/opintl.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kdb/opintl.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,42 @@ +/* opintl.h - opcodes specific header for gettext code. + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + + Written by Tom Tromey + + This file is part of the opcodes library used by GAS and the GNU binutils. + + You should have received a copy of the GNU General Public License + along with GAS; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifdef ENABLE_NLS +# include +/* Note the use of dgetext() and PACKAGE here, rather than gettext(). + + This is because the code in this directory is used to build a library which + will be linked with code in other directories to form programs. We want to + maintain a seperate translation file for this directory however, rather + than being forced to merge it with that of any program linked to + libopcodes. This is a library, so it cannot depend on the catalog + currently loaded. + + In order to do this, we have to make sure that when we extract messages we + use the OPCODES domain rather than the domain of the program that included + the opcodes library, (eg OBJDUMP). Hence we use dgettext (PACKAGE, String) + and define PACKAGE to be 'opcodes'. (See the code in configure). */ +# define _(String) dgettext (PACKAGE, String) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +# define gettext(Msgid) (Msgid) +# define dgettext(Domainname, Msgid) (Msgid) +# define dcgettext(Domainname, Msgid, Category) (Msgid) +# define textdomain(Domainname) while (0) /* nothing */ +# define bindtextdomain(Domainname, Dirname) while (0) /* nothing */ +# define _(String) (String) +# define N_(String) (String) +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kdb/ppc-dis.c linux.ac/arch/ppc64/kdb/ppc-dis.c --- linux.vanilla/arch/ppc64/kdb/ppc-dis.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kdb/ppc-dis.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,275 @@ +/* ppc-dis.c -- Disassemble PowerPC instructions + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +2, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +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 file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#ifdef __KERNEL__ +#include +#include +#include +#include +#include "ppc.h" +#include +#else +#include +#include "sysdep.h" +#include "dis-asm.h" +#include "opcode/ppc.h" +#endif +bfd_vma +bfd_getb32 (addr) + register const bfd_byte *addr; +{ + unsigned long v; + + v = (unsigned long) addr[0] << 24; + v |= (unsigned long) addr[1] << 16; + v |= (unsigned long) addr[2] << 8; + v |= (unsigned long) addr[3]; + return (bfd_vma) v; +} + +bfd_vma +bfd_getl32 (addr) + register const bfd_byte *addr; +{ + unsigned long v; + + v = (unsigned long) addr[0]; + v |= (unsigned long) addr[1] << 8; + v |= (unsigned long) addr[2] << 16; + v |= (unsigned long) addr[3] << 24; + return (bfd_vma) v; +} +/* This file provides several disassembler functions, all of which use + the disassembler interface defined in dis-asm.h. Several functions + are provided because this file handles disassembly for the PowerPC + in both big and little endian mode and also for the POWER (RS/6000) + chip. */ + +static int print_insn_powerpc PARAMS ((bfd_vma, struct disassemble_info *, + int bigendian, int dialect)); + +/* Print a big endian PowerPC instruction. For convenience, also + disassemble instructions supported by the Motorola PowerPC 601 + and the Altivec vector unit. */ + +int +print_insn_big_powerpc (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + return print_insn_powerpc (memaddr, info, 1, + PPC_OPCODE_PPC | PPC_OPCODE_601 | + PPC_OPCODE_ALTIVEC); +} + +/* Print a little endian PowerPC instruction. For convenience, also + disassemble instructions supported by the Motorola PowerPC 601 + and the Altivec vector unit. */ + +int +print_insn_little_powerpc (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + return print_insn_powerpc (memaddr, info, 0, + PPC_OPCODE_PPC | PPC_OPCODE_601 | + PPC_OPCODE_ALTIVEC); +} + +/* Print a POWER (RS/6000) instruction. */ + +int +print_insn_rs6000 (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + return print_insn_powerpc (memaddr, info, 1, PPC_OPCODE_POWER); +} + +/* Print a PowerPC or POWER instruction. */ + +static int +print_insn_powerpc (memaddr, info, bigendian, dialect) + bfd_vma memaddr; + struct disassemble_info *info; + int bigendian; + int dialect; +{ + bfd_byte buffer[4]; + int status; + unsigned long insn; + const struct powerpc_opcode *opcode; + const struct powerpc_opcode *opcode_end; + unsigned long op; + + status = (*info->read_memory_func) (memaddr, buffer, 4, info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + + if (bigendian) + insn = bfd_getb32 (buffer); + else + insn = bfd_getl32 (buffer); + + /* Get the major opcode of the instruction. */ + op = PPC_OP (insn); + + /* Find the first match in the opcode table. We could speed this up + a bit by doing a binary search on the major opcode. */ + opcode_end = powerpc_opcodes + powerpc_num_opcodes; + for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++) + { + unsigned long table_op; + const unsigned char *opindex; + const struct powerpc_operand *operand; + int invalid; + int need_comma; + int need_paren; + + table_op = PPC_OP (opcode->opcode); + if (op < table_op) + break; + if (op > table_op) + continue; + + if ((insn & opcode->mask) != opcode->opcode + || (opcode->flags & dialect) == 0) + continue; + + /* Make two passes over the operands. First see if any of them + have extraction functions, and, if they do, make sure the + instruction is valid. */ + invalid = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + operand = powerpc_operands + *opindex; + if (operand->extract) + (*operand->extract) (insn, &invalid); + } + if (invalid) + continue; + + /* The instruction is valid. */ + (*info->fprintf_func) (info->stream, "%s", opcode->name); + if (opcode->operands[0] != 0) + (*info->fprintf_func) (info->stream, "\t"); + + /* Now extract and print the operands. */ + need_comma = 0; + need_paren = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + long value; + + operand = powerpc_operands + *opindex; + + /* Operands that are marked FAKE are simply ignored. We + already made sure that the extract function considered + the instruction to be valid. */ + if ((operand->flags & PPC_OPERAND_FAKE) != 0) + continue; + + /* Extract the value from the instruction. */ + if (operand->extract) + value = (*operand->extract) (insn, (int *) NULL); + else + { + value = (insn >> operand->shift) & ((1 << operand->bits) - 1); + if ((operand->flags & PPC_OPERAND_SIGNED) != 0 + && (value & (1 << (operand->bits - 1))) != 0) + value -= 1 << operand->bits; + } + + /* If the operand is optional, and the value is zero, don't + print anything. */ + if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 + && (operand->flags & PPC_OPERAND_NEXT) == 0 + && value == 0) + continue; + + if (need_comma) + { + (*info->fprintf_func) (info->stream, ","); + need_comma = 0; + } + + /* Print the operand as directed by the flags. */ + if ((operand->flags & PPC_OPERAND_GPR) != 0) + (*info->fprintf_func) (info->stream, "r%ld", value); + else if ((operand->flags & PPC_OPERAND_FPR) != 0) + (*info->fprintf_func) (info->stream, "f%ld", value); + else if ((operand->flags & PPC_OPERAND_VR) != 0) + (*info->fprintf_func) (info->stream, "v%ld", value); + else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0) + (*info->print_address_func) (memaddr + value, info); + else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) + (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info); + else if ((operand->flags & PPC_OPERAND_CR) == 0 + || (dialect & PPC_OPCODE_PPC) == 0) + (*info->fprintf_func) (info->stream, "%ld", value); + else + { + if (operand->bits == 3) + (*info->fprintf_func) (info->stream, "cr%d", value); + else + { + static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; + int cr; + int cc; + + cr = value >> 2; + if (cr != 0) + (*info->fprintf_func) (info->stream, "4*cr%d", cr); + cc = value & 3; + if (cc != 0) + { + if (cr != 0) + (*info->fprintf_func) (info->stream, "+"); + (*info->fprintf_func) (info->stream, "%s", cbnames[cc]); + } + } + } + + if (need_paren) + { + (*info->fprintf_func) (info->stream, ")"); + need_paren = 0; + } + + if ((operand->flags & PPC_OPERAND_PARENS) == 0) + need_comma = 1; + else + { + (*info->fprintf_func) (info->stream, "("); + need_paren = 1; + } + } + + /* We have found and printed an instruction; return. */ + return 4; + } + + /* We could not find a match. */ + (*info->fprintf_func) (info->stream, ".long 0x%lx", insn); + + return 4; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kdb/ppc-opc.c linux.ac/arch/ppc64/kdb/ppc-opc.c --- linux.vanilla/arch/ppc64/kdb/ppc-opc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kdb/ppc-opc.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,3491 @@ +/* ppc-opc.c -- PowerPC opcode list + Copyright (c) 1994, 95, 96, 97, 98, 99, 2000 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +2, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +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 file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. */ +#ifndef __KERNEL__ +#include +#include "sysdep.h" +#include "opcode/ppc.h" +#include "opintl.h" +#else +#include +#include +#include +#include +#include "ppc.h" +#include "opintl.h" +#endif +/* This file holds the PowerPC opcode table. The opcode table + includes almost all of the extended instruction mnemonics. This + permits the disassembler to use them, and simplifies the assembler + logic, at the cost of increasing the table size. The table is + strictly constant data, so the compiler should be able to put it in + the .text section. + + This file also holds the operand table. All knowledge about + inserting operands into instructions and vice-versa is kept in this + file. */ + +/* Local insertion and extraction functions. */ + +static unsigned long insert_bat PARAMS ((unsigned long, long, const char **)); +static long extract_bat PARAMS ((unsigned long, int *)); +static unsigned long insert_bba PARAMS ((unsigned long, long, const char **)); +static long extract_bba PARAMS ((unsigned long, int *)); +static unsigned long insert_bd PARAMS ((unsigned long, long, const char **)); +static long extract_bd PARAMS ((unsigned long, int *)); +static unsigned long insert_bdm PARAMS ((unsigned long, long, const char **)); +static long extract_bdm PARAMS ((unsigned long, int *)); +static unsigned long insert_bdp PARAMS ((unsigned long, long, const char **)); +static long extract_bdp PARAMS ((unsigned long, int *)); +static int valid_bo PARAMS ((long)); +static unsigned long insert_bo PARAMS ((unsigned long, long, const char **)); +static long extract_bo PARAMS ((unsigned long, int *)); +static unsigned long insert_boe PARAMS ((unsigned long, long, const char **)); +static long extract_boe PARAMS ((unsigned long, int *)); +static unsigned long insert_ds PARAMS ((unsigned long, long, const char **)); +static long extract_ds PARAMS ((unsigned long, int *)); +static unsigned long insert_li PARAMS ((unsigned long, long, const char **)); +static long extract_li PARAMS ((unsigned long, int *)); +static unsigned long insert_mbe PARAMS ((unsigned long, long, const char **)); +static long extract_mbe PARAMS ((unsigned long, int *)); +static unsigned long insert_mb6 PARAMS ((unsigned long, long, const char **)); +static long extract_mb6 PARAMS ((unsigned long, int *)); +static unsigned long insert_nb PARAMS ((unsigned long, long, const char **)); +static long extract_nb PARAMS ((unsigned long, int *)); +static unsigned long insert_nsi PARAMS ((unsigned long, long, const char **)); +static long extract_nsi PARAMS ((unsigned long, int *)); +static unsigned long insert_ral PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_ram PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_ras PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_rbs PARAMS ((unsigned long, long, const char **)); +static long extract_rbs PARAMS ((unsigned long, int *)); +static unsigned long insert_sh6 PARAMS ((unsigned long, long, const char **)); +static long extract_sh6 PARAMS ((unsigned long, int *)); +static unsigned long insert_spr PARAMS ((unsigned long, long, const char **)); +static long extract_spr PARAMS ((unsigned long, int *)); +static unsigned long insert_tbr PARAMS ((unsigned long, long, const char **)); +static long extract_tbr PARAMS ((unsigned long, int *)); + +/* The operands table. + + The fields are bits, shift, insert, extract, flags. + + We used to put parens around the various additions, like the one + for BA just below. However, that caused trouble with feeble + compilers with a limit on depth of a parenthesized expression, like + (reportedly) the compiler in Microsoft Developer Studio 5. So we + omit the parens, since the macros are never used in a context where + the addition will be ambiguous. */ + +const struct powerpc_operand powerpc_operands[] = +{ + /* The zero index is used to indicate the end of the list of + operands. */ +#define UNUSED 0 + { 0, 0, 0, 0, 0 }, + + /* The BA field in an XL form instruction. */ +#define BA UNUSED + 1 +#define BA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_CR }, + + /* The BA field in an XL form instruction when it must be the same + as the BT field in the same instruction. */ +#define BAT BA + 1 + { 5, 16, insert_bat, extract_bat, PPC_OPERAND_FAKE }, + + /* The BB field in an XL form instruction. */ +#define BB BAT + 1 +#define BB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_CR }, + + /* The BB field in an XL form instruction when it must be the same + as the BA field in the same instruction. */ +#define BBA BB + 1 + { 5, 11, insert_bba, extract_bba, PPC_OPERAND_FAKE }, + + /* The BD field in a B form instruction. The lower two bits are + forced to zero. */ +#define BD BBA + 1 + { 16, 0, insert_bd, extract_bd, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when absolute addressing is + used. */ +#define BDA BD + 1 + { 16, 0, insert_bd, extract_bd, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDM BDA + 1 + { 16, 0, insert_bdm, extract_bdm, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used + and absolute address is used. */ +#define BDMA BDM + 1 + { 16, 0, insert_bdm, extract_bdm, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDP BDMA + 1 + { 16, 0, insert_bdp, extract_bdp, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used + and absolute addressing is used. */ +#define BDPA BDP + 1 + { 16, 0, insert_bdp, extract_bdp, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BF field in an X or XL form instruction. */ +#define BF BDPA + 1 + { 3, 23, 0, 0, PPC_OPERAND_CR }, + + /* An optional BF field. This is used for comparison instructions, + in which an omitted BF field is taken as zero. */ +#define OBF BF + 1 + { 3, 23, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The BFA field in an X or XL form instruction. */ +#define BFA OBF + 1 + { 3, 18, 0, 0, PPC_OPERAND_CR }, + + /* The BI field in a B form or XL form instruction. */ +#define BI BFA + 1 +#define BI_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_CR }, + + /* The BO field in a B form instruction. Certain values are + illegal. */ +#define BO BI + 1 +#define BO_MASK (0x1f << 21) + { 5, 21, insert_bo, extract_bo, 0 }, + + /* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. */ +#define BOE BO + 1 + { 5, 21, insert_boe, extract_boe, 0 }, + + /* The BT field in an X or XL form instruction. */ +#define BT BOE + 1 + { 5, 21, 0, 0, PPC_OPERAND_CR }, + + /* The condition register number portion of the BI field in a B form + or XL form instruction. This is used for the extended + conditional branch mnemonics, which set the lower two bits of the + BI field. This field is optional. */ +#define CR BT + 1 + { 3, 18, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The D field in a D form instruction. This is a displacement off + a register, and implies that the next operand is a register in + parentheses. */ +#define D CR + 1 + { 16, 0, 0, 0, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ +#define DS D + 1 + { 16, 0, insert_ds, extract_ds, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The E field in a wrteei instruction. */ +#define E DS + 1 + { 1, 15, 0, 0, 0 }, + + /* The FL1 field in a POWER SC form instruction. */ +#define FL1 E + 1 + { 4, 12, 0, 0, 0 }, + + /* The FL2 field in a POWER SC form instruction. */ +#define FL2 FL1 + 1 + { 3, 2, 0, 0, 0 }, + + /* The FLM field in an XFL form instruction. */ +#define FLM FL2 + 1 + { 8, 17, 0, 0, 0 }, + + /* The FRA field in an X or A form instruction. */ +#define FRA FLM + 1 +#define FRA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_FPR }, + + /* The FRB field in an X or A form instruction. */ +#define FRB FRA + 1 +#define FRB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_FPR }, + + /* The FRC field in an A form instruction. */ +#define FRC FRB + 1 +#define FRC_MASK (0x1f << 6) + { 5, 6, 0, 0, PPC_OPERAND_FPR }, + + /* The FRS field in an X form instruction or the FRT field in a D, X + or A form instruction. */ +#define FRS FRC + 1 +#define FRT FRS + { 5, 21, 0, 0, PPC_OPERAND_FPR }, + + /* The FXM field in an XFX instruction. */ +#define FXM FRS + 1 +#define FXM_MASK (0xff << 12) + { 8, 12, 0, 0, 0 }, + + /* The L field in a D or X form instruction. */ +#define L FXM + 1 + { 1, 21, 0, 0, PPC_OPERAND_OPTIONAL }, + + /* The LEV field in a POWER SC form instruction. */ +#define LEV L + 1 + { 7, 5, 0, 0, 0 }, + + /* The LI field in an I form instruction. The lower two bits are + forced to zero. */ +#define LI LEV + 1 + { 26, 0, insert_li, extract_li, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The LI field in an I form instruction when used as an absolute + address. */ +#define LIA LI + 1 + { 26, 0, insert_li, extract_li, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The MB field in an M form instruction. */ +#define MB LIA + 1 +#define MB_MASK (0x1f << 6) + { 5, 6, 0, 0, 0 }, + + /* The ME field in an M form instruction. */ +#define ME MB + 1 +#define ME_MASK (0x1f << 1) + { 5, 1, 0, 0, 0 }, + + /* The MB and ME fields in an M form instruction expressed a single + operand which is a bitmask indicating which bits to select. This + is a two operand form using PPC_OPERAND_NEXT. See the + description in opcode/ppc.h for what this means. */ +#define MBE ME + 1 + { 5, 6, 0, 0, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT }, + { 32, 0, insert_mbe, extract_mbe, 0 }, + + /* The MB or ME field in an MD or MDS form instruction. The high + bit is wrapped to the low end. */ +#define MB6 MBE + 2 +#define ME6 MB6 +#define MB6_MASK (0x3f << 5) + { 6, 5, insert_mb6, extract_mb6, 0 }, + + /* The NB field in an X form instruction. The value 32 is stored as + 0. */ +#define NB MB6 + 1 + { 6, 11, insert_nb, extract_nb, 0 }, + + /* The NSI field in a D form instruction. This is the same as the + SI field, only negated. */ +#define NSI NB + 1 + { 16, 0, insert_nsi, extract_nsi, + PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED }, + + /* The RA field in an D, DS, X, XO, M, or MDS form instruction. */ +#define RA NSI + 1 +#define RA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_GPR }, + + /* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ +#define RAL RA + 1 + { 5, 16, insert_ral, 0, PPC_OPERAND_GPR }, + + /* The RA field in an lmw instruction, which has special value + restrictions. */ +#define RAM RAL + 1 + { 5, 16, insert_ram, 0, PPC_OPERAND_GPR }, + + /* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ +#define RAS RAM + 1 + { 5, 16, insert_ras, 0, PPC_OPERAND_GPR }, + + /* The RB field in an X, XO, M, or MDS form instruction. */ +#define RB RAS + 1 +#define RB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_GPR }, + + /* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. */ +#define RBS RB + 1 + { 5, 1, insert_rbs, extract_rbs, PPC_OPERAND_FAKE }, + + /* The RS field in a D, DS, X, XFX, XS, M, MD or MDS form + instruction or the RT field in a D, DS, X, XFX or XO form + instruction. */ +#define RS RBS + 1 +#define RT RS +#define RT_MASK (0x1f << 21) + { 5, 21, 0, 0, PPC_OPERAND_GPR }, + + /* The SH field in an X or M form instruction. */ +#define SH RS + 1 +#define SH_MASK (0x1f << 11) + { 5, 11, 0, 0, 0 }, + + /* The SH field in an MD form instruction. This is split. */ +#define SH6 SH + 1 +#define SH6_MASK ((0x1f << 11) | (1 << 1)) + { 6, 1, insert_sh6, extract_sh6, 0 }, + + /* The SI field in a D form instruction. */ +#define SI SH6 + 1 + { 16, 0, 0, 0, PPC_OPERAND_SIGNED }, + + /* The SI field in a D form instruction when we accept a wide range + of positive values. */ +#define SISIGNOPT SI + 1 + { 16, 0, 0, 0, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT }, + + /* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ +#define SPR SISIGNOPT + 1 +#define SPR_MASK (0x3ff << 11) + { 10, 11, insert_spr, extract_spr, 0 }, + + /* The BAT index number in an XFX form m[ft]ibat[lu] instruction. */ +#define SPRBAT SPR + 1 +#define SPRBAT_MASK (0x3 << 17) + { 2, 17, 0, 0, 0 }, + + /* The SPRG register number in an XFX form m[ft]sprg instruction. */ +#define SPRG SPRBAT + 1 +#define SPRG_MASK (0x3 << 16) + { 2, 16, 0, 0, 0 }, + + /* The SR field in an X form instruction. */ +#define SR SPRG + 1 + { 4, 16, 0, 0, 0 }, + + /* The SV field in a POWER SC form instruction. */ +#define SV SR + 1 + { 14, 2, 0, 0, 0 }, + + /* The TBR field in an XFX form instruction. This is like the SPR + field, but it is optional. */ +#define TBR SV + 1 + { 10, 11, insert_tbr, extract_tbr, PPC_OPERAND_OPTIONAL }, + + /* The TO field in a D or X form instruction. */ +#define TO TBR + 1 +#define TO_MASK (0x1f << 21) + { 5, 21, 0, 0, 0 }, + + /* The U field in an X form instruction. */ +#define U TO + 1 + { 4, 12, 0, 0, 0 }, + + /* The UI field in a D form instruction. */ +#define UI U + 1 + { 16, 0, 0, 0, 0 }, + + /* The VA field in a VA, VX or VXR form instruction. */ +#define VA UI + 1 +#define VA_MASK (0x1f << 16) + {5, 16, 0, 0, PPC_OPERAND_VR}, + + /* The VB field in a VA, VX or VXR form instruction. */ +#define VB VA + 1 +#define VB_MASK (0x1f << 11) + {5, 11, 0, 0, PPC_OPERAND_VR}, + + /* The VC field in a VA form instruction. */ +#define VC VB + 1 +#define VC_MASK (0x1f << 6) + {5, 6, 0, 0, PPC_OPERAND_VR}, + + /* The VD or VS field in a VA, VX, VXR or X form instruction. */ +#define VD VC + 1 +#define VS VD +#define VD_MASK (0x1f << 21) + {5, 21, 0, 0, PPC_OPERAND_VR}, + + /* The SIMM field in a VX form instruction. */ +#define SIMM VD + 1 + { 5, 16, 0, 0, PPC_OPERAND_SIGNED}, + + /* The UIMM field in a VX form instruction. */ +#define UIMM SIMM + 1 + { 5, 16, 0, 0, 0 }, + + /* The SHB field in a VA form instruction. */ +#define SHB UIMM + 1 + { 4, 6, 0, 0, 0 }, +}; + +/* The functions used to insert and extract complicated operands. */ + +/* The BA field in an XL form instruction when it must be the same as + the BT field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BT field into the BA field, + and the extraction function just checks that the fields are the + same. */ + +/*ARGSUSED*/ +static unsigned long +insert_bat (insn, value, errmsg) + unsigned long insn; + long value ATTRIBUTE_UNUSED; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | (((insn >> 21) & 0x1f) << 16); +} + +static long +extract_bat (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BB field in an XL form instruction when it must be the same as + the BA field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BA field into the BB field, + and the extraction function just checks that the fields are the + same. */ + +/*ARGSUSED*/ +static unsigned long +insert_bba (insn, value, errmsg) + unsigned long insn; + long value ATTRIBUTE_UNUSED; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | (((insn >> 16) & 0x1f) << 11); +} + +static long +extract_bba (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 16) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BD field in a B form instruction. The lower two bits are + forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_bd (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | (value & 0xfffc); +} + +/*ARGSUSED*/ +static long +extract_bd (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The BD field in a B form instruction when the - modifier is used. + This modifier means that the branch is not expected to be taken. + We must set the y bit of the BO field to 1 if the offset is + negative. When extracting, we require that the y bit be 1 and that + the offset be positive, since if the y bit is 0 we just want to + print the normal form of the instruction. */ + +/*ARGSUSED*/ +static unsigned long +insert_bdm (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + if ((value & 0x8000) != 0) + insn |= 1 << 21; + return insn | (value & 0xfffc); +} + +static long +extract_bdm (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn & (1 << 21)) == 0 + || (insn & (1 << 15)) == 0)) + *invalid = 1; + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The BD field in a B form instruction when the + modifier is used. + This is like BDM, above, except that the branch is expected to be + taken. */ + +/*ARGSUSED*/ +static unsigned long +insert_bdp (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + if ((value & 0x8000) == 0) + insn |= 1 << 21; + return insn | (value & 0xfffc); +} + +static long +extract_bdp (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn & (1 << 21)) == 0 + || (insn & (1 << 15)) != 0)) + *invalid = 1; + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* Check for legal values of a BO field. */ + +static int +valid_bo (value) + long value; +{ + /* Certain encodings have bits that are required to be zero. These + are (z must be zero, y may be anything): + 001zy + 011zy + 1z00y + 1z01y + 1z1zz + */ + switch (value & 0x14) + { + default: + case 0: + return 1; + case 0x4: + return (value & 0x2) == 0; + case 0x10: + return (value & 0x8) == 0; + case 0x14: + return value == 0x14; + } +} + +/* The BO field in a B form instruction. Warn about attempts to set + the field to an illegal value. */ + +static unsigned long +insert_bo (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (errmsg != (const char **) NULL + && ! valid_bo (value)) + *errmsg = _("invalid conditional option"); + return insn | ((value & 0x1f) << 21); +} + +static long +extract_bo (insn, invalid) + unsigned long insn; + int *invalid; +{ + long value; + + value = (insn >> 21) & 0x1f; + if (invalid != (int *) NULL + && ! valid_bo (value)) + *invalid = 1; + return value; +} + +/* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. When + extracting it, we force it to be even. */ + +static unsigned long +insert_boe (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (errmsg != (const char **) NULL) + { + if (! valid_bo (value)) + *errmsg = _("invalid conditional option"); + else if ((value & 1) != 0) + *errmsg = _("attempt to set y bit when using + or - modifier"); + } + return insn | ((value & 0x1f) << 21); +} + +static long +extract_boe (insn, invalid) + unsigned long insn; + int *invalid; +{ + long value; + + value = (insn >> 21) & 0x1f; + if (invalid != (int *) NULL + && ! valid_bo (value)) + *invalid = 1; + return value & 0x1e; +} + +/* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_ds (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | (value & 0xfffc); +} + +/*ARGSUSED*/ +static long +extract_ds (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The LI field in an I form instruction. The lower two bits are + forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_li (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if ((value & 3) != 0 && errmsg != (const char **) NULL) + *errmsg = _("ignoring least significant bits in branch offset"); + return insn | (value & 0x3fffffc); +} + +/*ARGSUSED*/ +static long +extract_li (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + if ((insn & 0x2000000) != 0) + return (insn & 0x3fffffc) - 0x4000000; + else + return insn & 0x3fffffc; +} + +/* The MB and ME fields in an M form instruction expressed as a single + operand which is itself a bitmask. The extraction function always + marks it as invalid, since we never want to recognize an + instruction which uses a field of this type. */ + +static unsigned long +insert_mbe (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + unsigned long uval, mask; + int mb, me, mx, count, last; + + uval = value; + + if (uval == 0) + { + if (errmsg != (const char **) NULL) + *errmsg = _("illegal bitmask"); + return insn; + } + + mb = 0; + me = 32; + if ((uval & 1) != 0) + last = 1; + else + last = 0; + count = 0; + + /* mb: location of last 0->1 transition */ + /* me: location of last 1->0 transition */ + /* count: # transitions */ + + for (mx = 0, mask = 1 << 31; mx < 32; ++mx, mask >>= 1) + { + if ((uval & mask) && !last) + { + ++count; + mb = mx; + last = 1; + } + else if (!(uval & mask) && last) + { + ++count; + me = mx; + last = 0; + } + } + if (me == 0) + me = 32; + + if (count != 2 && (count != 0 || ! last)) + { + if (errmsg != (const char **) NULL) + *errmsg = _("illegal bitmask"); + } + + return insn | (mb << 6) | ((me - 1) << 1); +} + +static long +extract_mbe (insn, invalid) + unsigned long insn; + int *invalid; +{ + long ret; + int mb, me; + int i; + + if (invalid != (int *) NULL) + *invalid = 1; + + mb = (insn >> 6) & 0x1f; + me = (insn >> 1) & 0x1f; + if (mb < me + 1) + { + ret = 0; + for (i = mb; i <= me; i++) + ret |= (long) 1 << (31 - i); + } + else if (mb == me + 1) + ret = ~0; + else /* (mb > me + 1) */ + { + ret = ~ (long) 0; + for (i = me + 1; i < mb; i++) + ret &= ~ ((long) 1 << (31 - i)); + } + return ret; +} + +/* The MB or ME field in an MD or MDS form instruction. The high bit + is wrapped to the low end. */ + +/*ARGSUSED*/ +static unsigned long +insert_mb6 (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | ((value & 0x1f) << 6) | (value & 0x20); +} + +/*ARGSUSED*/ +static long +extract_mb6 (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + return ((insn >> 6) & 0x1f) | (insn & 0x20); +} + +/* The NB field in an X form instruction. The value 32 is stored as + 0. */ + +static unsigned long +insert_nb (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value < 0 || value > 32) + *errmsg = _("value out of range"); + if (value == 32) + value = 0; + return insn | ((value & 0x1f) << 11); +} + +/*ARGSUSED*/ +static long +extract_nb (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + long ret; + + ret = (insn >> 11) & 0x1f; + if (ret == 0) + ret = 32; + return ret; +} + +/* The NSI field in a D form instruction. This is the same as the SI + field, only negated. The extraction function always marks it as + invalid, since we never want to recognize an instruction which uses + a field of this type. */ + +/*ARGSUSED*/ +static unsigned long +insert_nsi (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | ((- value) & 0xffff); +} + +static long +extract_nsi (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL) + *invalid = 1; + if ((insn & 0x8000) != 0) + return - ((long)(insn & 0xffff) - 0x10000); + else + return - (long)(insn & 0xffff); +} + +/* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ + +static unsigned long +insert_ral (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0 + || (unsigned long) value == ((insn >> 21) & 0x1f)) + *errmsg = "invalid register operand when updating"; + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in an lmw instruction, which has special value + restrictions. */ + +static unsigned long +insert_ram (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if ((unsigned long) value >= ((insn >> 21) & 0x1f)) + *errmsg = _("index register in load range"); + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ + +static unsigned long +insert_ras (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0) + *errmsg = _("invalid register operand when updating"); + return insn | ((value & 0x1f) << 16); +} + +/* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. This operand is marked FAKE. The insertion + function just copies the BT field into the BA field, and the + extraction function just checks that the fields are the same. */ + +/*ARGSUSED*/ +static unsigned long +insert_rbs (insn, value, errmsg) + unsigned long insn; + long value ATTRIBUTE_UNUSED; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | (((insn >> 21) & 0x1f) << 11); +} + +static long +extract_rbs (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The SH field in an MD form instruction. This is split. */ + +/*ARGSUSED*/ +static unsigned long +insert_sh6 (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4); +} + +/*ARGSUSED*/ +static long +extract_sh6 (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + return ((insn >> 11) & 0x1f) | ((insn << 4) & 0x20); +} + +/* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ + +static unsigned long +insert_spr (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_spr (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + return ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); +} + +/* The TBR field in an XFX instruction. This is just like SPR, but it + is optional. When TBR is omitted, it must be inserted as 268 (the + magic number of the TB register). These functions treat 0 + (indicating an omitted optional operand) as 268. This means that + ``mftb 4,0'' is not handled correctly. This does not matter very + much, since the architecture manual does not define mftb as + accepting any values other than 268 or 269. */ + +#define TB (268) + +static unsigned long +insert_tbr (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + if (value == 0) + value = TB; + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_tbr (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + long ret; + + ret = ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); + if (ret == TB) + ret = 0; + return ret; +} + +/* Macros used to form opcodes. */ + +/* The main opcode. */ +#define OP(x) ((((unsigned long)(x)) & 0x3f) << 26) +#define OP_MASK OP (0x3f) + +/* The main opcode combined with a trap code in the TO field of a D + form instruction. Used for extended mnemonics for the trap + instructions. */ +#define OPTO(x,to) (OP (x) | ((((unsigned long)(to)) & 0x1f) << 21)) +#define OPTO_MASK (OP_MASK | TO_MASK) + +/* The main opcode combined with a comparison size bit in the L field + of a D form or X form instruction. Used for extended mnemonics for + the comparison instructions. */ +#define OPL(x,l) (OP (x) | ((((unsigned long)(l)) & 1) << 21)) +#define OPL_MASK OPL (0x3f,1) + +/* An A form instruction. */ +#define A(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x1f) << 1) | (((unsigned long)(rc)) & 1)) +#define A_MASK A (0x3f, 0x1f, 1) + +/* An A_MASK with the FRB field fixed. */ +#define AFRB_MASK (A_MASK | FRB_MASK) + +/* An A_MASK with the FRC field fixed. */ +#define AFRC_MASK (A_MASK | FRC_MASK) + +/* An A_MASK with the FRA and FRC fields fixed. */ +#define AFRAFRC_MASK (A_MASK | FRA_MASK | FRC_MASK) + +/* A B form instruction. */ +#define B(op, aa, lk) (OP (op) | ((((unsigned long)(aa)) & 1) << 1) | ((lk) & 1)) +#define B_MASK B (0x3f, 1, 1) + +/* A B form instruction setting the BO field. */ +#define BBO(op, bo, aa, lk) (B ((op), (aa), (lk)) | ((((unsigned long)(bo)) & 0x1f) << 21)) +#define BBO_MASK BBO (0x3f, 0x1f, 1, 1) + +/* A BBO_MASK with the y bit of the BO field removed. This permits + matching a conditional branch regardless of the setting of the y + bit. */ +#define Y_MASK (((unsigned long)1) << 21) +#define BBOY_MASK (BBO_MASK &~ Y_MASK) + +/* A B form instruction setting the BO field and the condition bits of + the BI field. */ +#define BBOCB(op, bo, cb, aa, lk) \ + (BBO ((op), (bo), (aa), (lk)) | ((((unsigned long)(cb)) & 0x3) << 16)) +#define BBOCB_MASK BBOCB (0x3f, 0x1f, 0x3, 1, 1) + +/* A BBOCB_MASK with the y bit of the BO field removed. */ +#define BBOYCB_MASK (BBOCB_MASK &~ Y_MASK) + +/* A BBOYCB_MASK in which the BI field is fixed. */ +#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK) + +/* The main opcode mask with the RA field clear. */ +#define DRA_MASK (OP_MASK | RA_MASK) + +/* A DS form instruction. */ +#define DSO(op, xop) (OP (op) | ((xop) & 0x3)) +#define DS_MASK DSO (0x3f, 3) + +/* An M form instruction. */ +#define M(op, rc) (OP (op) | ((rc) & 1)) +#define M_MASK M (0x3f, 1) + +/* An M form instruction with the ME field specified. */ +#define MME(op, me, rc) (M ((op), (rc)) | ((((unsigned long)(me)) & 0x1f) << 1)) + +/* An M_MASK with the MB and ME fields fixed. */ +#define MMBME_MASK (M_MASK | MB_MASK | ME_MASK) + +/* An M_MASK with the SH and ME fields fixed. */ +#define MSHME_MASK (M_MASK | SH_MASK | ME_MASK) + +/* An MD form instruction. */ +#define MD(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x7) << 2) | ((rc) & 1)) +#define MD_MASK MD (0x3f, 0x7, 1) + +/* An MD_MASK with the MB field fixed. */ +#define MDMB_MASK (MD_MASK | MB6_MASK) + +/* An MD_MASK with the SH field fixed. */ +#define MDSH_MASK (MD_MASK | SH6_MASK) + +/* An MDS form instruction. */ +#define MDS(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0xf) << 1) | ((rc) & 1)) +#define MDS_MASK MDS (0x3f, 0xf, 1) + +/* An MDS_MASK with the MB field fixed. */ +#define MDSMB_MASK (MDS_MASK | MB6_MASK) + +/* An SC form instruction. */ +#define SC(op, sa, lk) (OP (op) | ((((unsigned long)(sa)) & 1) << 1) | ((lk) & 1)) +#define SC_MASK (OP_MASK | (((unsigned long)0x3ff) << 16) | (((unsigned long)1) << 1) | 1) + +/* An VX form instruction. */ +#define VX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x7ff)) + +/* The mask for an VX form instruction. */ +#define VX_MASK VX(0x3f, 0x7ff) + +/* An VA form instruction. */ +#define VXA(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x07f)) + +/* The mask for an VA form instruction. */ +#define VXA_MASK VXA(0x3f, 0x7f) + +/* An VXR form instruction. */ +#define VXR(op, xop, rc) (OP (op) | (((rc) & 1) << 10) | (((unsigned long)(xop)) & 0x3ff)) + +/* The mask for a VXR form instruction. */ +#define VXR_MASK VXR(0x3f, 0x3ff, 1) + +/* An X form instruction. */ +#define X(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1)) + +/* An X form instruction with the RC bit specified. */ +#define XRC(op, xop, rc) (X ((op), (xop)) | ((rc) & 1)) + +/* The mask for an X form instruction. */ +#define X_MASK XRC (0x3f, 0x3ff, 1) + +/* An X_MASK with the RA field fixed. */ +#define XRA_MASK (X_MASK | RA_MASK) + +/* An X_MASK with the RB field fixed. */ +#define XRB_MASK (X_MASK | RB_MASK) + +/* An X_MASK with the RT field fixed. */ +#define XRT_MASK (X_MASK | RT_MASK) + +/* An X_MASK with the RA and RB fields fixed. */ +#define XRARB_MASK (X_MASK | RA_MASK | RB_MASK) + +/* An X_MASK with the RT and RA fields fixed. */ +#define XRTRA_MASK (X_MASK | RT_MASK | RA_MASK) + +/* An X form comparison instruction. */ +#define XCMPL(op, xop, l) (X ((op), (xop)) | ((((unsigned long)(l)) & 1) << 21)) + +/* The mask for an X form comparison instruction. */ +#define XCMP_MASK (X_MASK | (((unsigned long)1) << 22)) + +/* The mask for an X form comparison instruction with the L field + fixed. */ +#define XCMPL_MASK (XCMP_MASK | (((unsigned long)1) << 21)) + +/* An X form trap instruction with the TO field specified. */ +#define XTO(op, xop, to) (X ((op), (xop)) | ((((unsigned long)(to)) & 0x1f) << 21)) +#define XTO_MASK (X_MASK | TO_MASK) + +/* An X form tlb instruction with the SH field specified. */ +#define XTLB(op, xop, sh) (X ((op), (xop)) | ((((unsigned long)(sh)) & 0x1f) << 11)) +#define XTLB_MASK (X_MASK | SH_MASK) + +/* An XFL form instruction. */ +#define XFL(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1) | (((unsigned long)(rc)) & 1)) +#define XFL_MASK (XFL (0x3f, 0x3ff, 1) | (((unsigned long)1) << 25) | (((unsigned long)1) << 16)) + +/* An XL form instruction with the LK field set to 0. */ +#define XL(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1)) + +/* An XL form instruction which uses the LK field. */ +#define XLLK(op, xop, lk) (XL ((op), (xop)) | ((lk) & 1)) + +/* The mask for an XL form instruction. */ +#define XL_MASK XLLK (0x3f, 0x3ff, 1) + +/* An XL form instruction which explicitly sets the BO field. */ +#define XLO(op, bo, xop, lk) \ + (XLLK ((op), (xop), (lk)) | ((((unsigned long)(bo)) & 0x1f) << 21)) +#define XLO_MASK (XL_MASK | BO_MASK) + +/* An XL form instruction which explicitly sets the y bit of the BO + field. */ +#define XLYLK(op, xop, y, lk) (XLLK ((op), (xop), (lk)) | ((((unsigned long)(y)) & 1) << 21)) +#define XLYLK_MASK (XL_MASK | Y_MASK) + +/* An XL form instruction which sets the BO field and the condition + bits of the BI field. */ +#define XLOCB(op, bo, cb, xop, lk) \ + (XLO ((op), (bo), (xop), (lk)) | ((((unsigned long)(cb)) & 3) << 16)) +#define XLOCB_MASK XLOCB (0x3f, 0x1f, 0x3, 0x3ff, 1) + +/* An XL_MASK or XLYLK_MASK or XLOCB_MASK with the BB field fixed. */ +#define XLBB_MASK (XL_MASK | BB_MASK) +#define XLYBB_MASK (XLYLK_MASK | BB_MASK) +#define XLBOCBBB_MASK (XLOCB_MASK | BB_MASK) + +/* An XL_MASK with the BO and BB fields fixed. */ +#define XLBOBB_MASK (XL_MASK | BO_MASK | BB_MASK) + +/* An XL_MASK with the BO, BI and BB fields fixed. */ +#define XLBOBIBB_MASK (XL_MASK | BO_MASK | BI_MASK | BB_MASK) + +/* An XO form instruction. */ +#define XO(op, xop, oe, rc) \ + (OP (op) | ((((unsigned long)(xop)) & 0x1ff) << 1) | ((((unsigned long)(oe)) & 1) << 10) | (((unsigned long)(rc)) & 1)) +#define XO_MASK XO (0x3f, 0x1ff, 1, 1) + +/* An XO_MASK with the RB field fixed. */ +#define XORB_MASK (XO_MASK | RB_MASK) + +/* An XS form instruction. */ +#define XS(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x1ff) << 2) | (((unsigned long)(rc)) & 1)) +#define XS_MASK XS (0x3f, 0x1ff, 1) + +/* A mask for the FXM version of an XFX form instruction. */ +#define XFXFXM_MASK (X_MASK | (((unsigned long)1) << 20) | (((unsigned long)1) << 11)) + +/* An XFX form instruction with the FXM field filled in. */ +#define XFXM(op, xop, fxm) \ + (X ((op), (xop)) | ((((unsigned long)(fxm)) & 0xff) << 12)) + +/* An XFX form instruction with the SPR field filled in. */ +#define XSPR(op, xop, spr) \ + (X ((op), (xop)) | ((((unsigned long)(spr)) & 0x1f) << 16) | ((((unsigned long)(spr)) & 0x3e0) << 6)) +#define XSPR_MASK (X_MASK | SPR_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRBAT field. */ +#define XSPRBAT_MASK (XSPR_MASK &~ SPRBAT_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRG field. */ +#define XSPRG_MASK (XSPR_MASK &~ SPRG_MASK) + +/* An X form instruction with everything filled in except the E field. */ +#define XE_MASK (0xffff7fff) + +/* The BO encodings used in extended conditional branch mnemonics. */ +#define BODNZF (0x0) +#define BODNZFP (0x1) +#define BODZF (0x2) +#define BODZFP (0x3) +#define BOF (0x4) +#define BOFP (0x5) +#define BODNZT (0x8) +#define BODNZTP (0x9) +#define BODZT (0xa) +#define BODZTP (0xb) +#define BOT (0xc) +#define BOTP (0xd) +#define BODNZ (0x10) +#define BODNZP (0x11) +#define BODZ (0x12) +#define BODZP (0x13) +#define BOU (0x14) + +/* The BI condition bit encodings used in extended conditional branch + mnemonics. */ +#define CBLT (0) +#define CBGT (1) +#define CBEQ (2) +#define CBSO (3) + +/* The TO encodings used in extended trap mnemonics. */ +#define TOLGT (0x1) +#define TOLLT (0x2) +#define TOEQ (0x4) +#define TOLGE (0x5) +#define TOLNL (0x5) +#define TOLLE (0x6) +#define TOLNG (0x6) +#define TOGT (0x8) +#define TOGE (0xc) +#define TONL (0xc) +#define TOLT (0x10) +#define TOLE (0x14) +#define TONG (0x14) +#define TONE (0x18) +#define TOU (0x1f) + +/* Smaller names for the flags so each entry in the opcodes table will + fit on a single line. */ +#undef PPC +#define PPC PPC_OPCODE_PPC | PPC_OPCODE_ANY +#define PPCCOM PPC_OPCODE_PPC | PPC_OPCODE_COMMON | PPC_OPCODE_ANY +#define PPC32 PPC_OPCODE_PPC | PPC_OPCODE_32 | PPC_OPCODE_ANY +#undef PPC64 +#define PPC64 PPC_OPCODE_PPC | PPC_OPCODE_64 | PPC_OPCODE_ANY +#define PPCONLY PPC_OPCODE_PPC +#define PPC403 PPC +#define PPC405 PPC403 +#define PPC750 PPC +#define PPC860 PPC +#define PPCVEC PPC_OPCODE_ALTIVEC | PPC_OPCODE_ANY +#define POWER PPC_OPCODE_POWER | PPC_OPCODE_ANY +#define POWER2 PPC_OPCODE_POWER | PPC_OPCODE_POWER2 | PPC_OPCODE_ANY +#define PPCPWR2 PPC_OPCODE_PPC | PPC_OPCODE_POWER | PPC_OPCODE_POWER2 | PPC_OPCODE_ANY +#define POWER32 PPC_OPCODE_POWER | PPC_OPCODE_ANY | PPC_OPCODE_32 +#define COM PPC_OPCODE_POWER | PPC_OPCODE_PPC | PPC_OPCODE_COMMON | PPC_OPCODE_ANY +#define COM32 PPC_OPCODE_POWER | PPC_OPCODE_PPC | PPC_OPCODE_COMMON | PPC_OPCODE_ANY | PPC_OPCODE_32 +#define M601 PPC_OPCODE_POWER | PPC_OPCODE_601 | PPC_OPCODE_ANY +#define PWRCOM PPC_OPCODE_POWER | PPC_OPCODE_601 | PPC_OPCODE_COMMON | PPC_OPCODE_ANY +#define MFDEC1 PPC_OPCODE_POWER +#define MFDEC2 PPC_OPCODE_PPC | PPC_OPCODE_601 + +/* The opcode table. + + The format of the opcode table is: + + NAME OPCODE MASK FLAGS { OPERANDS } + + NAME is the name of the instruction. + OPCODE is the instruction opcode. + MASK is the opcode mask; this is used to tell the disassembler + which bits in the actual opcode must match OPCODE. + FLAGS are flags indicated what processors support the instruction. + OPERANDS is the list of operands. + + The disassembler reads the table in order and prints the first + instruction which matches, so this table is sorted to put more + specific instructions before more general instructions. It is also + sorted by major opcode. */ + +const struct powerpc_opcode powerpc_opcodes[] = { +{ "tdlgti", OPTO(2,TOLGT), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdllti", OPTO(2,TOLLT), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdeqi", OPTO(2,TOEQ), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlgei", OPTO(2,TOLGE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlnli", OPTO(2,TOLNL), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdllei", OPTO(2,TOLLE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlngi", OPTO(2,TOLNG), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdgti", OPTO(2,TOGT), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdgei", OPTO(2,TOGE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdnli", OPTO(2,TONL), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlti", OPTO(2,TOLT), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlei", OPTO(2,TOLE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdngi", OPTO(2,TONG), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdnei", OPTO(2,TONE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdi", OP(2), OP_MASK, PPC64, { TO, RA, SI } }, + +{ "twlgti", OPTO(3,TOLGT), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlgti", OPTO(3,TOLGT), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twllti", OPTO(3,TOLLT), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tllti", OPTO(3,TOLLT), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "tweqi", OPTO(3,TOEQ), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "teqi", OPTO(3,TOEQ), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlgei", OPTO(3,TOLGE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlgei", OPTO(3,TOLGE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlnli", OPTO(3,TOLNL), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlnli", OPTO(3,TOLNL), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twllei", OPTO(3,TOLLE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tllei", OPTO(3,TOLLE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlngi", OPTO(3,TOLNG), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlngi", OPTO(3,TOLNG), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twgti", OPTO(3,TOGT), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tgti", OPTO(3,TOGT), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twgei", OPTO(3,TOGE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tgei", OPTO(3,TOGE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twnli", OPTO(3,TONL), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tnli", OPTO(3,TONL), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlti", OPTO(3,TOLT), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlti", OPTO(3,TOLT), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlei", OPTO(3,TOLE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlei", OPTO(3,TOLE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twngi", OPTO(3,TONG), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tngi", OPTO(3,TONG), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twnei", OPTO(3,TONE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tnei", OPTO(3,TONE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twi", OP(3), OP_MASK, PPCCOM, { TO, RA, SI } }, +{ "ti", OP(3), OP_MASK, PWRCOM, { TO, RA, SI } }, + +{ "macchw", XO(4,172,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchw.", XO(4,172,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwo", XO(4,172,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwo.", XO(4,172,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchws", XO(4,236,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchws.", XO(4,236,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwso", XO(4,236,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwso.", XO(4,236,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwsu", XO(4,204,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwsu.", XO(4,204,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwsuo", XO(4,204,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwsuo.", XO(4,204,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwu", XO(4,140,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwu.", XO(4,140,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwuo", XO(4,140,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwuo.", XO(4,140,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhw", XO(4,44,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhw.", XO(4,44,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwo", XO(4,44,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwo.", XO(4,44,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhws", XO(4,108,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhws.", XO(4,108,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwso", XO(4,108,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwso.", XO(4,108,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwsu", XO(4,76,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwsu.", XO(4,76,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwsuo", XO(4,76,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwsuo.", XO(4,76,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwu", XO(4,12,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwu.", XO(4,12,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwuo", XO(4,12,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwuo.", XO(4,12,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhw", XO(4,428,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhw.", XO(4,428,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwo", XO(4,428,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwo.", XO(4,428,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhws", XO(4,492,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhws.", XO(4,492,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwso", XO(4,492,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwso.", XO(4,492,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwsu", XO(4,460,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwsu.", XO(4,460,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwsuo", XO(4,460,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwsuo.", XO(4,460,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwu", XO(4,396,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwu.", XO(4,396,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwuo", XO(4,396,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwuo.", XO(4,396,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "mulchw", XRC(4,168,0), X_MASK, PPC405, { RT, RA, RB } }, +{ "mulchw.", XRC(4,168,1), X_MASK, PPC405, { RT, RA, RB } }, +{ "mulchwu", XRC(4,136,0), X_MASK, PPC405, { RT, RA, RB } }, +{ "mulchwu.", XRC(4,136,1), X_MASK, PPC405, { RT, RA, RB } }, +{ "mulhhw", XRC(4,40,0), X_MASK, PPC405, { RT, RA, RB } }, +{ "mulhhw.", XRC(4,40,1), X_MASK, PPC405, { RT, RA, RB } }, +{ "mulhhwu", XRC(4,8,0), X_MASK, PPC405, { RT, RA, RB } }, +{ "mulhhwu.", XRC(4,8,1), X_MASK, PPC405, { RT, RA, RB } }, +{ "mullhw", XRC(4,424,0), X_MASK, PPC405, { RT, RA, RB } }, +{ "mullhw.", XRC(4,424,1), X_MASK, PPC405, { RT, RA, RB } }, +{ "mullhwu", XRC(4,392,0), X_MASK, PPC405, { RT, RA, RB } }, +{ "mullhwu.", XRC(4,392,1), X_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchw", XO(4,174,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchw.", XO(4,174,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchwo", XO(4,174,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchwo.", XO(4,174,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchws", XO(4,238,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchws.", XO(4,238,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchwso", XO(4,238,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchwso.", XO(4,238,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhw", XO(4,46,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhw.", XO(4,46,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhwo", XO(4,46,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhwo.", XO(4,46,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhws", XO(4,110,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhws.", XO(4,110,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhwso", XO(4,110,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhwso.", XO(4,110,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhw", XO(4,430,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhw.", XO(4,430,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhwo", XO(4,430,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhwo.", XO(4,430,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhws", XO(4,494,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhws.", XO(4,494,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhwso", XO(4,494,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhwso.", XO(4,494,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "mfvscr", VX(4, 1540), VX_MASK, PPCVEC, { VD } }, +{ "mtvscr", VX(4, 1604), VX_MASK, PPCVEC, { VD } }, +{ "vaddcuw", VX(4, 384), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddfp", VX(4, 10), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddsbs", VX(4, 768), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddshs", VX(4, 832), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddsws", VX(4, 896), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddubm", VX(4, 0), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddubs", VX(4, 512), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vadduhm", VX(4, 64), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vadduhs", VX(4, 576), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vadduwm", VX(4, 128), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vadduws", VX(4, 640), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vand", VX(4, 1028), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vandc", VX(4, 1092), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavgsb", VX(4, 1282), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavgsh", VX(4, 1346), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavgsw", VX(4, 1410), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavgub", VX(4, 1026), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavguh", VX(4, 1090), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavguw", VX(4, 1154), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcfsx", VX(4, 842), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vcfux", VX(4, 778), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vcmpbfp", VXR(4, 966, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpbfp.", VXR(4, 966, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpeqfp", VXR(4, 198, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpeqfp.", VXR(4, 198, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequb", VXR(4, 6, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequb.", VXR(4, 6, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequh", VXR(4, 70, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequh.", VXR(4, 70, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequw", VXR(4, 134, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequw.", VXR(4, 134, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgefp", VXR(4, 454, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgefp.", VXR(4, 454, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtfp", VXR(4, 710, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtfp.", VXR(4, 710, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsb", VXR(4, 774, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsb.", VXR(4, 774, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsh", VXR(4, 838, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsh.", VXR(4, 838, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsw", VXR(4, 902, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsw.", VXR(4, 902, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtub", VXR(4, 518, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtub.", VXR(4, 518, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtuh", VXR(4, 582, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtuh.", VXR(4, 582, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtuw", VXR(4, 646, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtuw.", VXR(4, 646, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vctsxs", VX(4, 970), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vctuxs", VX(4, 906), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vexptefp", VX(4, 394), VX_MASK, PPCVEC, { VD, VB } }, +{ "vlogefp", VX(4, 458), VX_MASK, PPCVEC, { VD, VB } }, +{ "vmaddfp", VXA(4, 46), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmaxfp", VX(4, 1034), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxsb", VX(4, 258), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxsh", VX(4, 322), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxsw", VX(4, 386), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxub", VX(4, 2), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxuh", VX(4, 66), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxuw", VX(4, 130), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmhaddshs", VXA(4, 32), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmhraddshs", VXA(4, 33), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vminfp", VX(4, 1098), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminsb", VX(4, 770), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminsh", VX(4, 834), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminsw", VX(4, 898), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminub", VX(4, 514), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminuh", VX(4, 578), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminuw", VX(4, 642), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmladduhm", VXA(4, 34), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmrghb", VX(4, 12), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrghh", VX(4, 76), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrghw", VX(4, 140), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrglb", VX(4, 268), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrglh", VX(4, 332), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrglw", VX(4, 396), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmsummbm", VXA(4, 37), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumshm", VXA(4, 40), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumshs", VXA(4, 41), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumubm", VXA(4, 36), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumuhm", VXA(4, 38), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumuhs", VXA(4, 39), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmulesb", VX(4, 776), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmulesh", VX(4, 840), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmuleub", VX(4, 520), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmuleuh", VX(4, 584), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmulosb", VX(4, 264), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmulosh", VX(4, 328), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmuloub", VX(4, 8), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmulouh", VX(4, 72), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vnmsubfp", VXA(4, 47), VXA_MASK, PPCVEC, { VD, VA, VC, VB } }, +{ "vnor", VX(4, 1284), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vor", VX(4, 1156), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vperm", VXA(4, 43), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vpkpx", VX(4, 782), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkshss", VX(4, 398), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkshus", VX(4, 270), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkswss", VX(4, 462), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkswus", VX(4, 334), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkuhum", VX(4, 14), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkuhus", VX(4, 142), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkuwum", VX(4, 78), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkuwus", VX(4, 206), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vrefp", VX(4, 266), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrfim", VX(4, 714), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrfin", VX(4, 522), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrfip", VX(4, 650), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrfiz", VX(4, 586), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrlb", VX(4, 4), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vrlh", VX(4, 68), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vrlw", VX(4, 132), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vrsqrtefp", VX(4, 330), VX_MASK, PPCVEC, { VD, VB } }, +{ "vsel", VXA(4, 42), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vsl", VX(4, 452), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vslb", VX(4, 260), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsldoi", VXA(4, 44), VXA_MASK, PPCVEC, { VD, VA, VB, SHB } }, +{ "vslh", VX(4, 324), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vslo", VX(4, 1036), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vslw", VX(4, 388), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vspltb", VX(4, 524), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vsplth", VX(4, 588), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vspltisb", VX(4, 780), VX_MASK, PPCVEC, { VD, SIMM } }, +{ "vspltish", VX(4, 844), VX_MASK, PPCVEC, { VD, SIMM } }, +{ "vspltisw", VX(4, 908), VX_MASK, PPCVEC, { VD, SIMM } }, +{ "vspltw", VX(4, 652), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vsr", VX(4, 708), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrab", VX(4, 772), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrah", VX(4, 836), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsraw", VX(4, 900), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrb", VX(4, 516), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrh", VX(4, 580), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsro", VX(4, 1100), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrw", VX(4, 644), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubcuw", VX(4, 1408), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubfp", VX(4, 74), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubsbs", VX(4, 1792), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubshs", VX(4, 1856), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubsws", VX(4, 1920), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsububm", VX(4, 1024), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsububs", VX(4, 1536), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubuhm", VX(4, 1088), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubuhs", VX(4, 1600), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubuwm", VX(4, 1152), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubuws", VX(4, 1664), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsumsws", VX(4, 1928), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsum2sws", VX(4, 1672), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsum4sbs", VX(4, 1800), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsum4shs", VX(4, 1608), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsum4ubs", VX(4, 1544), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vupkhpx", VX(4, 846), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupkhsb", VX(4, 526), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupkhsh", VX(4, 590), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupklpx", VX(4, 974), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupklsb", VX(4, 654), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupklsh", VX(4, 718), VX_MASK, PPCVEC, { VD, VB } }, +{ "vxor", VX(4, 1220), VX_MASK, PPCVEC, { VD, VA, VB } }, + +{ "mulli", OP(7), OP_MASK, PPCCOM, { RT, RA, SI } }, +{ "muli", OP(7), OP_MASK, PWRCOM, { RT, RA, SI } }, + +{ "subfic", OP(8), OP_MASK, PPCCOM, { RT, RA, SI } }, +{ "sfi", OP(8), OP_MASK, PWRCOM, { RT, RA, SI } }, + +{ "dozi", OP(9), OP_MASK, M601, { RT, RA, SI } }, + +{ "cmplwi", OPL(10,0), OPL_MASK, PPCCOM, { OBF, RA, UI } }, +{ "cmpldi", OPL(10,1), OPL_MASK, PPC64, { OBF, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, PPCONLY, { BF, L, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, PWRCOM, { BF, RA, UI } }, + +{ "cmpwi", OPL(11,0), OPL_MASK, PPCCOM, { OBF, RA, SI } }, +{ "cmpdi", OPL(11,1), OPL_MASK, PPC64, { OBF, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, PPCONLY, { BF, L, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, PWRCOM, { BF, RA, SI } }, + +{ "addic", OP(12), OP_MASK, PPCCOM, { RT, RA, SI } }, +{ "ai", OP(12), OP_MASK, PWRCOM, { RT, RA, SI } }, +{ "subic", OP(12), OP_MASK, PPCCOM, { RT, RA, NSI } }, + +{ "addic.", OP(13), OP_MASK, PPCCOM, { RT, RA, SI } }, +{ "ai.", OP(13), OP_MASK, PWRCOM, { RT, RA, SI } }, +{ "subic.", OP(13), OP_MASK, PPCCOM, { RT, RA, NSI } }, + +{ "li", OP(14), DRA_MASK, PPCCOM, { RT, SI } }, +{ "lil", OP(14), DRA_MASK, PWRCOM, { RT, SI } }, +{ "addi", OP(14), OP_MASK, PPCCOM, { RT, RA, SI } }, +{ "cal", OP(14), OP_MASK, PWRCOM, { RT, D, RA } }, +{ "subi", OP(14), OP_MASK, PPCCOM, { RT, RA, NSI } }, +{ "la", OP(14), OP_MASK, PPCCOM, { RT, D, RA } }, + +{ "lis", OP(15), DRA_MASK, PPCCOM, { RT, SISIGNOPT } }, +{ "liu", OP(15), DRA_MASK, PWRCOM, { RT, SISIGNOPT } }, +{ "addis", OP(15), OP_MASK, PPCCOM, { RT,RA,SISIGNOPT } }, +{ "cau", OP(15), OP_MASK, PWRCOM, { RT,RA,SISIGNOPT } }, +{ "subis", OP(15), OP_MASK, PPCCOM, { RT, RA, NSI } }, + +{ "bdnz-", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPCCOM, { BDM } }, +{ "bdnz+", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPCCOM, { BDP } }, +{ "bdnz", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPCCOM, { BD } }, +{ "bdn", BBO(16,BODNZ,0,0), BBOYBI_MASK, PWRCOM, { BD } }, +{ "bdnzl-", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPCCOM, { BDM } }, +{ "bdnzl+", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPCCOM, { BDP } }, +{ "bdnzl", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPCCOM, { BD } }, +{ "bdnl", BBO(16,BODNZ,0,1), BBOYBI_MASK, PWRCOM, { BD } }, +{ "bdnza-", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPCCOM, { BDMA } }, +{ "bdnza+", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPCCOM, { BDPA } }, +{ "bdnza", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPCCOM, { BDA } }, +{ "bdna", BBO(16,BODNZ,1,0), BBOYBI_MASK, PWRCOM, { BDA } }, +{ "bdnzla-", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPCCOM, { BDMA } }, +{ "bdnzla+", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPCCOM, { BDPA } }, +{ "bdnzla", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPCCOM, { BDA } }, +{ "bdnla", BBO(16,BODNZ,1,1), BBOYBI_MASK, PWRCOM, { BDA } }, +{ "bdz-", BBO(16,BODZ,0,0), BBOYBI_MASK, PPCCOM, { BDM } }, +{ "bdz+", BBO(16,BODZ,0,0), BBOYBI_MASK, PPCCOM, { BDP } }, +{ "bdz", BBO(16,BODZ,0,0), BBOYBI_MASK, COM, { BD } }, +{ "bdzl-", BBO(16,BODZ,0,1), BBOYBI_MASK, PPCCOM, { BDM } }, +{ "bdzl+", BBO(16,BODZ,0,1), BBOYBI_MASK, PPCCOM, { BDP } }, +{ "bdzl", BBO(16,BODZ,0,1), BBOYBI_MASK, COM, { BD } }, +{ "bdza-", BBO(16,BODZ,1,0), BBOYBI_MASK, PPCCOM, { BDMA } }, +{ "bdza+", BBO(16,BODZ,1,0), BBOYBI_MASK, PPCCOM, { BDPA } }, +{ "bdza", BBO(16,BODZ,1,0), BBOYBI_MASK, COM, { BDA } }, +{ "bdzla-", BBO(16,BODZ,1,1), BBOYBI_MASK, PPCCOM, { BDMA } }, +{ "bdzla+", BBO(16,BODZ,1,1), BBOYBI_MASK, PPCCOM, { BDPA } }, +{ "bdzla", BBO(16,BODZ,1,1), BBOYBI_MASK, COM, { BDA } }, +{ "blt-", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "blt+", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "blt", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bltl-", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bltl+", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bltl", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "blta-", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "blta+", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "blta", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bltla-", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bltla+", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bltla", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bgt-", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bgt+", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bgt", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bgtl-", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bgtl+", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bgtl", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "bgta-", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bgta+", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bgta", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bgtla-", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bgtla+", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bgtla", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "beq-", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "beq+", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "beq", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "beql-", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "beql+", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "beql", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "beqa-", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "beqa+", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "beqa", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "beqla-", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "beqla+", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "beqla", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bso-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bso+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bso", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bsol-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bsol+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bsol", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "bsoa-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bsoa+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bsoa", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bsola-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bsola+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bsola", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bun-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bun+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bun", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BD } }, +{ "bunl-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bunl+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bunl", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BD } }, +{ "buna-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "buna+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "buna", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDA } }, +{ "bunla-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bunla+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bunla", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDA } }, +{ "bge-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bge+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bge", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bgel-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bgel+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bgel", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "bgea-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bgea+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bgea", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bgela-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bgela+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bgela", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bnl-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnl+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnl", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bnll-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnll+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnll", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "bnla-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnla+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnla", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bnlla-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnlla+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnlla", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "ble-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "ble+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "ble", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "blel-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "blel+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "blel", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "blea-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "blea+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "blea", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "blela-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "blela+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "blela", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bng-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bng+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bng", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bngl-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bngl+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bngl", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "bnga-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnga+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnga", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bngla-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bngla+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bngla", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bne-", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bne+", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bne", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bnel-", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnel+", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnel", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "bnea-", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnea+", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnea", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bnela-", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnela+", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnela", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bns-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bns+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bns", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bnsl-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnsl+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnsl", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "bnsa-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnsa+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnsa", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bnsla-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnsla+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnsla", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bnu-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnu+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnu", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BD } }, +{ "bnul-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnul+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnul", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BD } }, +{ "bnua-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnua+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnua", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDA } }, +{ "bnula-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnula+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnula", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDA } }, +{ "bdnzt-", BBO(16,BODNZT,0,0), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdnzt+", BBO(16,BODNZT,0,0), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdnzt", BBO(16,BODNZT,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdnztl-", BBO(16,BODNZT,0,1), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdnztl+", BBO(16,BODNZT,0,1), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdnztl", BBO(16,BODNZT,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdnzta-", BBO(16,BODNZT,1,0), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdnzta+", BBO(16,BODNZT,1,0), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdnzta", BBO(16,BODNZT,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdnztla-",BBO(16,BODNZT,1,1), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdnztla+",BBO(16,BODNZT,1,1), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdnztla", BBO(16,BODNZT,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdnzf-", BBO(16,BODNZF,0,0), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdnzf+", BBO(16,BODNZF,0,0), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdnzf", BBO(16,BODNZF,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdnzfl-", BBO(16,BODNZF,0,1), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdnzfl+", BBO(16,BODNZF,0,1), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdnzfl", BBO(16,BODNZF,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdnzfa-", BBO(16,BODNZF,1,0), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdnzfa+", BBO(16,BODNZF,1,0), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdnzfa", BBO(16,BODNZF,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdnzfla-",BBO(16,BODNZF,1,1), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdnzfla+",BBO(16,BODNZF,1,1), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdnzfla", BBO(16,BODNZF,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bt-", BBO(16,BOT,0,0), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bt+", BBO(16,BOT,0,0), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bt", BBO(16,BOT,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bbt", BBO(16,BOT,0,0), BBOY_MASK, PWRCOM, { BI, BD } }, +{ "btl-", BBO(16,BOT,0,1), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "btl+", BBO(16,BOT,0,1), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "btl", BBO(16,BOT,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bbtl", BBO(16,BOT,0,1), BBOY_MASK, PWRCOM, { BI, BD } }, +{ "bta-", BBO(16,BOT,1,0), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bta+", BBO(16,BOT,1,0), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bta", BBO(16,BOT,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bbta", BBO(16,BOT,1,0), BBOY_MASK, PWRCOM, { BI, BDA } }, +{ "btla-", BBO(16,BOT,1,1), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "btla+", BBO(16,BOT,1,1), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "btla", BBO(16,BOT,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bbtla", BBO(16,BOT,1,1), BBOY_MASK, PWRCOM, { BI, BDA } }, +{ "bf-", BBO(16,BOF,0,0), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bf+", BBO(16,BOF,0,0), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bf", BBO(16,BOF,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bbf", BBO(16,BOF,0,0), BBOY_MASK, PWRCOM, { BI, BD } }, +{ "bfl-", BBO(16,BOF,0,1), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bfl+", BBO(16,BOF,0,1), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bfl", BBO(16,BOF,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bbfl", BBO(16,BOF,0,1), BBOY_MASK, PWRCOM, { BI, BD } }, +{ "bfa-", BBO(16,BOF,1,0), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bfa+", BBO(16,BOF,1,0), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bfa", BBO(16,BOF,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bbfa", BBO(16,BOF,1,0), BBOY_MASK, PWRCOM, { BI, BDA } }, +{ "bfla-", BBO(16,BOF,1,1), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bfla+", BBO(16,BOF,1,1), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bfla", BBO(16,BOF,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bbfla", BBO(16,BOF,1,1), BBOY_MASK, PWRCOM, { BI, BDA } }, +{ "bdzt-", BBO(16,BODZT,0,0), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdzt+", BBO(16,BODZT,0,0), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdzt", BBO(16,BODZT,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdztl-", BBO(16,BODZT,0,1), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdztl+", BBO(16,BODZT,0,1), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdztl", BBO(16,BODZT,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdzta-", BBO(16,BODZT,1,0), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdzta+", BBO(16,BODZT,1,0), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdzta", BBO(16,BODZT,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdztla-", BBO(16,BODZT,1,1), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdztla+", BBO(16,BODZT,1,1), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdztla", BBO(16,BODZT,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdzf-", BBO(16,BODZF,0,0), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdzf+", BBO(16,BODZF,0,0), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdzf", BBO(16,BODZF,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdzfl-", BBO(16,BODZF,0,1), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdzfl+", BBO(16,BODZF,0,1), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdzfl", BBO(16,BODZF,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdzfa-", BBO(16,BODZF,1,0), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdzfa+", BBO(16,BODZF,1,0), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdzfa", BBO(16,BODZF,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdzfla-", BBO(16,BODZF,1,1), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdzfla+", BBO(16,BODZF,1,1), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdzfla", BBO(16,BODZF,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bc-", B(16,0,0), B_MASK, PPCCOM, { BOE, BI, BDM } }, +{ "bc+", B(16,0,0), B_MASK, PPCCOM, { BOE, BI, BDP } }, +{ "bc", B(16,0,0), B_MASK, COM, { BO, BI, BD } }, +{ "bcl-", B(16,0,1), B_MASK, PPCCOM, { BOE, BI, BDM } }, +{ "bcl+", B(16,0,1), B_MASK, PPCCOM, { BOE, BI, BDP } }, +{ "bcl", B(16,0,1), B_MASK, COM, { BO, BI, BD } }, +{ "bca-", B(16,1,0), B_MASK, PPCCOM, { BOE, BI, BDMA } }, +{ "bca+", B(16,1,0), B_MASK, PPCCOM, { BOE, BI, BDPA } }, +{ "bca", B(16,1,0), B_MASK, COM, { BO, BI, BDA } }, +{ "bcla-", B(16,1,1), B_MASK, PPCCOM, { BOE, BI, BDMA } }, +{ "bcla+", B(16,1,1), B_MASK, PPCCOM, { BOE, BI, BDPA } }, +{ "bcla", B(16,1,1), B_MASK, COM, { BO, BI, BDA } }, + +{ "sc", SC(17,1,0), 0xffffffff, PPC, { 0 } }, +{ "svc", SC(17,0,0), SC_MASK, POWER, { LEV, FL1, FL2 } }, +{ "svcl", SC(17,0,1), SC_MASK, POWER, { LEV, FL1, FL2 } }, +{ "svca", SC(17,1,0), SC_MASK, PWRCOM, { SV } }, +{ "svcla", SC(17,1,1), SC_MASK, POWER, { SV } }, + +{ "b", B(18,0,0), B_MASK, COM, { LI } }, +{ "bl", B(18,0,1), B_MASK, COM, { LI } }, +{ "ba", B(18,1,0), B_MASK, COM, { LIA } }, +{ "bla", B(18,1,1), B_MASK, COM, { LIA } }, + +{ "mcrf", XL(19,0), XLBB_MASK|(3<<21)|(3<<16), COM, { BF, BFA } }, + +{ "blr", XLO(19,BOU,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "br", XLO(19,BOU,16,0), XLBOBIBB_MASK, PWRCOM, { 0 } }, +{ "blrl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "brl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PWRCOM, { 0 } }, +{ "bdnzlr", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdnzlr-", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdnzlr+", XLO(19,BODNZP,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdnzlrl", XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdnzlrl-",XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdnzlrl+",XLO(19,BODNZP,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdzlr", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdzlr-", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdzlr+", XLO(19,BODZP,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdzlrl", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdzlrl-", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdzlrl+", XLO(19,BODZP,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bltlr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltlr-", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltlr+", XLOCB(19,BOTP,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bltlrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltlrl-", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltlrl+", XLOCB(19,BOTP,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bgtlr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtlr-", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtlr+", XLOCB(19,BOTP,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bgtlrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtlrl-", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtlrl+", XLOCB(19,BOTP,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "beqlr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqlr-", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqlr+", XLOCB(19,BOTP,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "beqlrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqlrl-", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqlrl+", XLOCB(19,BOTP,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bsolr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsolr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsolr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsor", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bsolrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsolrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsolrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsorl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bunlr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunlr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunlr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunlrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunlrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunlrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgelr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgelr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgelr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bger", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bgelrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgelrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgelrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgerl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnllr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnllr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnllr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnllrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnllrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnllrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "blelr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blelr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blelr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bler", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "blelrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blelrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blelrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blerl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnglr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnglr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnglr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnglrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnglrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnglrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnelr", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnelr-", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnelr+", XLOCB(19,BOFP,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bner", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnelrl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnelrl-", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnelrl+", XLOCB(19,BOFP,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnerl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnslr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnslr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnslr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnslrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnslrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnslrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnulr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnulr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnulr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnulrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnulrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnulrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "btlr", XLO(19,BOT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btlr-", XLO(19,BOT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btlr+", XLO(19,BOTP,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bbtr", XLO(19,BOT,16,0), XLBOBB_MASK, PWRCOM, { BI } }, +{ "btlrl", XLO(19,BOT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btlrl-", XLO(19,BOT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btlrl+", XLO(19,BOTP,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bbtrl", XLO(19,BOT,16,1), XLBOBB_MASK, PWRCOM, { BI } }, +{ "bflr", XLO(19,BOF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bflr-", XLO(19,BOF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bflr+", XLO(19,BOFP,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bbfr", XLO(19,BOF,16,0), XLBOBB_MASK, PWRCOM, { BI } }, +{ "bflrl", XLO(19,BOF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bflrl-", XLO(19,BOF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bflrl+", XLO(19,BOFP,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bbfrl", XLO(19,BOF,16,1), XLBOBB_MASK, PWRCOM, { BI } }, +{ "bdnztlr", XLO(19,BODNZT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnztlr-",XLO(19,BODNZT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnztlr+",XLO(19,BODNZTP,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnztlrl",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnztlrl-",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnztlrl+",XLO(19,BODNZTP,16,1), XLBOBB_MASK, PPCCOM,{ BI } }, +{ "bdnzflr", XLO(19,BODNZF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnzflr-",XLO(19,BODNZF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnzflr+",XLO(19,BODNZFP,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnzflrl",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnzflrl-",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnzflrl+",XLO(19,BODNZFP,16,1), XLBOBB_MASK, PPCCOM,{ BI } }, +{ "bdztlr", XLO(19,BODZT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdztlr-", XLO(19,BODZT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdztlr+", XLO(19,BODZTP,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdztlrl", XLO(19,BODZT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdztlrl-",XLO(19,BODZT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdztlrl+",XLO(19,BODZTP,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdzflr", XLO(19,BODZF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdzflr-", XLO(19,BODZF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdzflr+", XLO(19,BODZFP,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdzflrl", XLO(19,BODZF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdzflrl-",XLO(19,BODZF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdzflrl+",XLO(19,BODZFP,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bclr", XLLK(19,16,0), XLYBB_MASK, PPCCOM, { BO, BI } }, +{ "bclrl", XLLK(19,16,1), XLYBB_MASK, PPCCOM, { BO, BI } }, +{ "bclr+", XLYLK(19,16,1,0), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bclrl+", XLYLK(19,16,1,1), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bclr-", XLYLK(19,16,0,0), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bclrl-", XLYLK(19,16,0,1), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bcr", XLLK(19,16,0), XLBB_MASK, PWRCOM, { BO, BI } }, +{ "bcrl", XLLK(19,16,1), XLBB_MASK, PWRCOM, { BO, BI } }, + +{ "rfid", XL(19,18), 0xffffffff, PPC64, { 0 } }, + +{ "crnot", XL(19,33), XL_MASK, PPCCOM, { BT, BA, BBA } }, +{ "crnor", XL(19,33), XL_MASK, COM, { BT, BA, BB } }, + +{ "rfi", XL(19,50), 0xffffffff, COM, { 0 } }, +{ "rfci", XL(19,51), 0xffffffff, PPC403, { 0 } }, + +{ "rfsvc", XL(19,82), 0xffffffff, POWER, { 0 } }, + +{ "crandc", XL(19,129), XL_MASK, COM, { BT, BA, BB } }, + +{ "isync", XL(19,150), 0xffffffff, PPCCOM, { 0 } }, +{ "ics", XL(19,150), 0xffffffff, PWRCOM, { 0 } }, + +{ "crclr", XL(19,193), XL_MASK, PPCCOM, { BT, BAT, BBA } }, +{ "crxor", XL(19,193), XL_MASK, COM, { BT, BA, BB } }, + +{ "crnand", XL(19,225), XL_MASK, COM, { BT, BA, BB } }, + +{ "crand", XL(19,257), XL_MASK, COM, { BT, BA, BB } }, + +{ "crset", XL(19,289), XL_MASK, PPCCOM, { BT, BAT, BBA } }, +{ "creqv", XL(19,289), XL_MASK, COM, { BT, BA, BB } }, + +{ "crorc", XL(19,417), XL_MASK, COM, { BT, BA, BB } }, + +{ "crmove", XL(19,449), XL_MASK, PPCCOM, { BT, BA, BBA } }, +{ "cror", XL(19,449), XL_MASK, COM, { BT, BA, BB } }, + +{ "bctr", XLO(19,BOU,528,0), XLBOBIBB_MASK, COM, { 0 } }, +{ "bctrl", XLO(19,BOU,528,1), XLBOBIBB_MASK, COM, { 0 } }, +{ "bltctr", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltctr-", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltctr+", XLOCB(19,BOTP,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltctrl", XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltctrl-",XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltctrl+",XLOCB(19,BOTP,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtctr", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtctr-", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtctr+", XLOCB(19,BOTP,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtctrl", XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtctrl-",XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtctrl+",XLOCB(19,BOTP,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqctr", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqctr-", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqctr+", XLOCB(19,BOTP,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqctrl", XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqctrl-",XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqctrl+",XLOCB(19,BOTP,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsoctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsoctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsoctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsoctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsoctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsoctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgectr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgectr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgectr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgectrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgectrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgectrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlctr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlctr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlctr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlctrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlctrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlctrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blectr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blectr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blectr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blectrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blectrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blectrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngctr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngctr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngctr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngctrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngctrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngctrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnectr", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnectr-", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnectr+", XLOCB(19,BOFP,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnectrl", XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnectrl-",XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnectrl+",XLOCB(19,BOFP,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnuctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnuctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnuctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnuctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnuctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnuctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "btctr", XLO(19,BOT,528,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btctr-", XLO(19,BOT,528,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btctr+", XLO(19,BOTP,528,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btctrl", XLO(19,BOT,528,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btctrl-", XLO(19,BOT,528,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btctrl+", XLO(19,BOTP,528,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bfctr", XLO(19,BOF,528,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bfctr-", XLO(19,BOF,528,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bfctr+", XLO(19,BOFP,528,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bfctrl", XLO(19,BOF,528,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bfctrl-", XLO(19,BOF,528,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bfctrl+", XLO(19,BOFP,528,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bcctr", XLLK(19,528,0), XLYBB_MASK, PPCCOM, { BO, BI } }, +{ "bcctr-", XLYLK(19,528,0,0), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bcctr+", XLYLK(19,528,1,0), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bcctrl", XLLK(19,528,1), XLYBB_MASK, PPCCOM, { BO, BI } }, +{ "bcctrl-", XLYLK(19,528,0,1), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bcctrl+", XLYLK(19,528,1,1), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bcc", XLLK(19,528,0), XLBB_MASK, PWRCOM, { BO, BI } }, +{ "bccl", XLLK(19,528,1), XLBB_MASK, PWRCOM, { BO, BI } }, + +{ "rlwimi", M(20,0), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } }, +{ "rlimi", M(20,0), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } }, + +{ "rlwimi.", M(20,1), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } }, +{ "rlimi.", M(20,1), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } }, + +{ "rotlwi", MME(21,31,0), MMBME_MASK, PPCCOM, { RA, RS, SH } }, +{ "clrlwi", MME(21,31,0), MSHME_MASK, PPCCOM, { RA, RS, MB } }, +{ "rlwinm", M(21,0), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } }, +{ "rlinm", M(21,0), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } }, +{ "rotlwi.", MME(21,31,1), MMBME_MASK, PPCCOM, { RA,RS,SH } }, +{ "clrlwi.", MME(21,31,1), MSHME_MASK, PPCCOM, { RA, RS, MB } }, +{ "rlwinm.", M(21,1), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } }, +{ "rlinm.", M(21,1), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } }, + +{ "rlmi", M(22,0), M_MASK, M601, { RA,RS,RB,MBE,ME } }, +{ "rlmi.", M(22,1), M_MASK, M601, { RA,RS,RB,MBE,ME } }, + +{ "rotlw", MME(23,31,0), MMBME_MASK, PPCCOM, { RA, RS, RB } }, +{ "rlwnm", M(23,0), M_MASK, PPCCOM, { RA,RS,RB,MBE,ME } }, +{ "rlnm", M(23,0), M_MASK, PWRCOM, { RA,RS,RB,MBE,ME } }, +{ "rotlw.", MME(23,31,1), MMBME_MASK, PPCCOM, { RA, RS, RB } }, +{ "rlwnm.", M(23,1), M_MASK, PPCCOM, { RA,RS,RB,MBE,ME } }, +{ "rlnm.", M(23,1), M_MASK, PWRCOM, { RA,RS,RB,MBE,ME } }, + +{ "nop", OP(24), 0xffffffff, PPCCOM, { 0 } }, +{ "ori", OP(24), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "oril", OP(24), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "oris", OP(25), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "oriu", OP(25), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "xori", OP(26), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "xoril", OP(26), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "xoris", OP(27), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "xoriu", OP(27), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "andi.", OP(28), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "andil.", OP(28), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "andis.", OP(29), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "andiu.", OP(29), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "rotldi", MD(30,0,0), MDMB_MASK, PPC64, { RA, RS, SH6 } }, +{ "clrldi", MD(30,0,0), MDSH_MASK, PPC64, { RA, RS, MB6 } }, +{ "rldicl", MD(30,0,0), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, +{ "rotldi.", MD(30,0,1), MDMB_MASK, PPC64, { RA, RS, SH6 } }, +{ "clrldi.", MD(30,0,1), MDSH_MASK, PPC64, { RA, RS, MB6 } }, +{ "rldicl.", MD(30,0,1), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, + +{ "rldicr", MD(30,1,0), MD_MASK, PPC64, { RA, RS, SH6, ME6 } }, +{ "rldicr.", MD(30,1,1), MD_MASK, PPC64, { RA, RS, SH6, ME6 } }, + +{ "rldic", MD(30,2,0), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, +{ "rldic.", MD(30,2,1), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, + +{ "rldimi", MD(30,3,0), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, +{ "rldimi.", MD(30,3,1), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, + +{ "rotld", MDS(30,8,0), MDSMB_MASK, PPC64, { RA, RS, RB } }, +{ "rldcl", MDS(30,8,0), MDS_MASK, PPC64, { RA, RS, RB, MB6 } }, +{ "rotld.", MDS(30,8,1), MDSMB_MASK, PPC64, { RA, RS, RB } }, +{ "rldcl.", MDS(30,8,1), MDS_MASK, PPC64, { RA, RS, RB, MB6 } }, + +{ "rldcr", MDS(30,9,0), MDS_MASK, PPC64, { RA, RS, RB, ME6 } }, +{ "rldcr.", MDS(30,9,1), MDS_MASK, PPC64, { RA, RS, RB, ME6 } }, + +{ "cmpw", XCMPL(31,0,0), XCMPL_MASK, PPCCOM, { OBF, RA, RB } }, +{ "cmpd", XCMPL(31,0,1), XCMPL_MASK, PPC64, { OBF, RA, RB } }, +{ "cmp", X(31,0), XCMP_MASK, PPCONLY, { BF, L, RA, RB } }, +{ "cmp", X(31,0), XCMPL_MASK, PWRCOM, { BF, RA, RB } }, + +{ "twlgt", XTO(31,4,TOLGT), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlgt", XTO(31,4,TOLGT), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twllt", XTO(31,4,TOLLT), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tllt", XTO(31,4,TOLLT), XTO_MASK, PWRCOM, { RA, RB } }, +{ "tweq", XTO(31,4,TOEQ), XTO_MASK, PPCCOM, { RA, RB } }, +{ "teq", XTO(31,4,TOEQ), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlge", XTO(31,4,TOLGE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlge", XTO(31,4,TOLGE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlnl", XTO(31,4,TOLNL), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlnl", XTO(31,4,TOLNL), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlle", XTO(31,4,TOLLE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlle", XTO(31,4,TOLLE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlng", XTO(31,4,TOLNG), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlng", XTO(31,4,TOLNG), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twgt", XTO(31,4,TOGT), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tgt", XTO(31,4,TOGT), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twge", XTO(31,4,TOGE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tge", XTO(31,4,TOGE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twnl", XTO(31,4,TONL), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tnl", XTO(31,4,TONL), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlt", XTO(31,4,TOLT), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlt", XTO(31,4,TOLT), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twle", XTO(31,4,TOLE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tle", XTO(31,4,TOLE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twng", XTO(31,4,TONG), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tng", XTO(31,4,TONG), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twne", XTO(31,4,TONE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tne", XTO(31,4,TONE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "trap", XTO(31,4,TOU), 0xffffffff, PPCCOM, { 0 } }, +{ "tw", X(31,4), X_MASK, PPCCOM, { TO, RA, RB } }, +{ "t", X(31,4), X_MASK, PWRCOM, { TO, RA, RB } }, + +{ "subfc", XO(31,8,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sf", XO(31,8,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subc", XO(31,8,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfc.", XO(31,8,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sf.", XO(31,8,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subc.", XO(31,8,0,1), XO_MASK, PPCCOM, { RT, RB, RA } }, +{ "subfco", XO(31,8,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfo", XO(31,8,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subco", XO(31,8,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfco.", XO(31,8,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfo.", XO(31,8,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "mulhdu", XO(31,9,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulhdu.", XO(31,9,0,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "addc", XO(31,10,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "a", XO(31,10,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addc.", XO(31,10,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "a.", XO(31,10,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addco", XO(31,10,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "ao", XO(31,10,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addco.", XO(31,10,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "ao.", XO(31,10,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "mulhwu", XO(31,11,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhwu.", XO(31,11,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mfcr", X(31,19), XRARB_MASK, COM, { RT } }, + +{ "lwarx", X(31,20), X_MASK, PPC, { RT, RA, RB } }, + +{ "ldx", X(31,21), X_MASK, PPC64, { RT, RA, RB } }, + +{ "lwzx", X(31,23), X_MASK, PPCCOM, { RT, RA, RB } }, +{ "lx", X(31,23), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "slw", XRC(31,24,0), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sl", XRC(31,24,0), X_MASK, PWRCOM, { RA, RS, RB } }, +{ "slw.", XRC(31,24,1), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sl.", XRC(31,24,1), X_MASK, PWRCOM, { RA, RS, RB } }, + +{ "cntlzw", XRC(31,26,0), XRB_MASK, PPCCOM, { RA, RS } }, +{ "cntlz", XRC(31,26,0), XRB_MASK, PWRCOM, { RA, RS } }, +{ "cntlzw.", XRC(31,26,1), XRB_MASK, PPCCOM, { RA, RS } }, +{ "cntlz.", XRC(31,26,1), XRB_MASK, PWRCOM, { RA, RS } }, + +{ "sld", XRC(31,27,0), X_MASK, PPC64, { RA, RS, RB } }, +{ "sld.", XRC(31,27,1), X_MASK, PPC64, { RA, RS, RB } }, + +{ "and", XRC(31,28,0), X_MASK, COM, { RA, RS, RB } }, +{ "and.", XRC(31,28,1), X_MASK, COM, { RA, RS, RB } }, + +{ "maskg", XRC(31,29,0), X_MASK, M601, { RA, RS, RB } }, +{ "maskg.", XRC(31,29,1), X_MASK, M601, { RA, RS, RB } }, + +{ "cmplw", XCMPL(31,32,0), XCMPL_MASK, PPCCOM, { OBF, RA, RB } }, +{ "cmpld", XCMPL(31,32,1), XCMPL_MASK, PPC64, { OBF, RA, RB } }, +{ "cmpl", X(31,32), XCMP_MASK, PPCONLY, { BF, L, RA, RB } }, +{ "cmpl", X(31,32), XCMPL_MASK, PWRCOM, { BF, RA, RB } }, + +{ "subf", XO(31,40,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub", XO(31,40,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subf.", XO(31,40,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub.", XO(31,40,0,1), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo", XO(31,40,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo", XO(31,40,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "ldux", X(31,53), X_MASK, PPC64, { RT, RAL, RB } }, + +{ "dcbst", X(31,54), XRT_MASK, PPC, { RA, RB } }, + +{ "lwzux", X(31,55), X_MASK, PPCCOM, { RT, RAL, RB } }, +{ "lux", X(31,55), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "cntlzd", XRC(31,58,0), XRB_MASK, PPC64, { RA, RS } }, +{ "cntlzd.", XRC(31,58,1), XRB_MASK, PPC64, { RA, RS } }, + +{ "andc", XRC(31,60,0), X_MASK, COM, { RA, RS, RB } }, +{ "andc.", XRC(31,60,1), X_MASK, COM, { RA, RS, RB } }, + +{ "tdlgt", XTO(31,68,TOLGT), XTO_MASK, PPC64, { RA, RB } }, +{ "tdllt", XTO(31,68,TOLLT), XTO_MASK, PPC64, { RA, RB } }, +{ "tdeq", XTO(31,68,TOEQ), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlge", XTO(31,68,TOLGE), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlnl", XTO(31,68,TOLNL), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlle", XTO(31,68,TOLLE), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlng", XTO(31,68,TOLNG), XTO_MASK, PPC64, { RA, RB } }, +{ "tdgt", XTO(31,68,TOGT), XTO_MASK, PPC64, { RA, RB } }, +{ "tdge", XTO(31,68,TOGE), XTO_MASK, PPC64, { RA, RB } }, +{ "tdnl", XTO(31,68,TONL), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlt", XTO(31,68,TOLT), XTO_MASK, PPC64, { RA, RB } }, +{ "tdle", XTO(31,68,TOLE), XTO_MASK, PPC64, { RA, RB } }, +{ "tdng", XTO(31,68,TONG), XTO_MASK, PPC64, { RA, RB } }, +{ "tdne", XTO(31,68,TONE), XTO_MASK, PPC64, { RA, RB } }, +{ "td", X(31,68), X_MASK, PPC64, { TO, RA, RB } }, + +{ "mulhd", XO(31,73,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulhd.", XO(31,73,0,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "mulhw", XO(31,75,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhw.", XO(31,75,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mtsrd", X(31,82), XRB_MASK|(1<<20), PPC64, { SR, RS } }, + +{ "mfmsr", X(31,83), XRARB_MASK, COM, { RT } }, + +{ "ldarx", X(31,84), X_MASK, PPC64, { RT, RA, RB } }, + +{ "dcbf", X(31,86), XRT_MASK, PPC, { RA, RB } }, + +{ "lbzx", X(31,87), X_MASK, COM, { RT, RA, RB } }, + +{ "neg", XO(31,104,0,0), XORB_MASK, COM, { RT, RA } }, +{ "neg.", XO(31,104,0,1), XORB_MASK, COM, { RT, RA } }, +{ "nego", XO(31,104,1,0), XORB_MASK, COM, { RT, RA } }, +{ "nego.", XO(31,104,1,1), XORB_MASK, COM, { RT, RA } }, + +{ "mul", XO(31,107,0,0), XO_MASK, M601, { RT, RA, RB } }, +{ "mul.", XO(31,107,0,1), XO_MASK, M601, { RT, RA, RB } }, +{ "mulo", XO(31,107,1,0), XO_MASK, M601, { RT, RA, RB } }, +{ "mulo.", XO(31,107,1,1), XO_MASK, M601, { RT, RA, RB } }, + +{ "mtsrdin", X(31,114), XRA_MASK, PPC64, { RS, RB } }, + +{ "clf", X(31,118), XRB_MASK, POWER, { RT, RA } }, + +{ "lbzux", X(31,119), X_MASK, COM, { RT, RAL, RB } }, + +{ "not", XRC(31,124,0), X_MASK, COM, { RA, RS, RBS } }, +{ "nor", XRC(31,124,0), X_MASK, COM, { RA, RS, RB } }, +{ "not.", XRC(31,124,1), X_MASK, COM, { RA, RS, RBS } }, +{ "nor.", XRC(31,124,1), X_MASK, COM, { RA, RS, RB } }, + +{ "wrtee", X(31,131), XRARB_MASK, PPC403, { RS } }, + +{ "subfe", XO(31,136,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfe", XO(31,136,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subfe.", XO(31,136,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfe.", XO(31,136,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subfeo", XO(31,136,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfeo", XO(31,136,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subfeo.", XO(31,136,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfeo.", XO(31,136,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "adde", XO(31,138,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "ae", XO(31,138,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "adde.", XO(31,138,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "ae.", XO(31,138,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addeo", XO(31,138,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "aeo", XO(31,138,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addeo.", XO(31,138,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "aeo.", XO(31,138,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "mtcr", XFXM(31,144,0xff), XFXFXM_MASK|FXM_MASK, COM, { RS }}, +{ "mtcrf", X(31,144), XFXFXM_MASK, COM, { FXM, RS } }, + +{ "mtmsr", X(31,146), XRARB_MASK, COM, { RS } }, + +{ "stdx", X(31,149), X_MASK, PPC64, { RS, RA, RB } }, + +{ "stwcx.", XRC(31,150,1), X_MASK, PPC, { RS, RA, RB } }, + +{ "stwx", X(31,151), X_MASK, PPCCOM, { RS, RA, RB } }, +{ "stx", X(31,151), X_MASK, PWRCOM, { RS, RA, RB } }, + +{ "slq", XRC(31,152,0), X_MASK, M601, { RA, RS, RB } }, +{ "slq.", XRC(31,152,1), X_MASK, M601, { RA, RS, RB } }, + +{ "sle", XRC(31,153,0), X_MASK, M601, { RA, RS, RB } }, +{ "sle.", XRC(31,153,1), X_MASK, M601, { RA, RS, RB } }, + +{ "wrteei", X(31,163), XE_MASK, PPC403, { E } }, + +{ "mtmsrd", X(31,178), XRARB_MASK, PPC64, { RS } }, + +{ "stdux", X(31,181), X_MASK, PPC64, { RS, RAS, RB } }, + +{ "stwux", X(31,183), X_MASK, PPCCOM, { RS, RAS, RB } }, +{ "stux", X(31,183), X_MASK, PWRCOM, { RS, RA, RB } }, + +{ "sliq", XRC(31,184,0), X_MASK, M601, { RA, RS, SH } }, +{ "sliq.", XRC(31,184,1), X_MASK, M601, { RA, RS, SH } }, + +{ "subfze", XO(31,200,0,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfze", XO(31,200,0,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfze.", XO(31,200,0,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfze.", XO(31,200,0,1), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfzeo", XO(31,200,1,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfzeo", XO(31,200,1,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfzeo.",XO(31,200,1,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfzeo.", XO(31,200,1,1), XORB_MASK, PWRCOM, { RT, RA } }, + +{ "addze", XO(31,202,0,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "aze", XO(31,202,0,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addze.", XO(31,202,0,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "aze.", XO(31,202,0,1), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addzeo", XO(31,202,1,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "azeo", XO(31,202,1,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addzeo.", XO(31,202,1,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "azeo.", XO(31,202,1,1), XORB_MASK, PWRCOM, { RT, RA } }, + +{ "mtsr", X(31,210), XRB_MASK|(1<<20), COM32, { SR, RS } }, + +{ "stdcx.", XRC(31,214,1), X_MASK, PPC64, { RS, RA, RB } }, + +{ "stbx", X(31,215), X_MASK, COM, { RS, RA, RB } }, + +{ "sllq", XRC(31,216,0), X_MASK, M601, { RA, RS, RB } }, +{ "sllq.", XRC(31,216,1), X_MASK, M601, { RA, RS, RB } }, + +{ "sleq", XRC(31,217,0), X_MASK, M601, { RA, RS, RB } }, +{ "sleq.", XRC(31,217,1), X_MASK, M601, { RA, RS, RB } }, + +{ "subfme", XO(31,232,0,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfme", XO(31,232,0,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfme.", XO(31,232,0,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfme.", XO(31,232,0,1), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfmeo", XO(31,232,1,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfmeo", XO(31,232,1,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfmeo.",XO(31,232,1,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfmeo.", XO(31,232,1,1), XORB_MASK, PWRCOM, { RT, RA } }, + +{ "mulld", XO(31,233,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulld.", XO(31,233,0,1), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulldo", XO(31,233,1,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulldo.", XO(31,233,1,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "addme", XO(31,234,0,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "ame", XO(31,234,0,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addme.", XO(31,234,0,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "ame.", XO(31,234,0,1), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addmeo", XO(31,234,1,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "ameo", XO(31,234,1,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addmeo.", XO(31,234,1,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "ameo.", XO(31,234,1,1), XORB_MASK, PWRCOM, { RT, RA } }, + +{ "mullw", XO(31,235,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "muls", XO(31,235,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "mullw.", XO(31,235,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "muls.", XO(31,235,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "mullwo", XO(31,235,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "mulso", XO(31,235,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "mullwo.", XO(31,235,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "mulso.", XO(31,235,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "mtsrin", X(31,242), XRA_MASK, PPC32, { RS, RB } }, +{ "mtsri", X(31,242), XRA_MASK, POWER32, { RS, RB } }, + +{ "dcbtst", X(31,246), XRT_MASK, PPC, { RA, RB } }, + +{ "stbux", X(31,247), X_MASK, COM, { RS, RAS, RB } }, + +{ "slliq", XRC(31,248,0), X_MASK, M601, { RA, RS, SH } }, +{ "slliq.", XRC(31,248,1), X_MASK, M601, { RA, RS, SH } }, + +{ "doz", XO(31,264,0,0), XO_MASK, M601, { RT, RA, RB } }, +{ "doz.", XO(31,264,0,1), XO_MASK, M601, { RT, RA, RB } }, +{ "dozo", XO(31,264,1,0), XO_MASK, M601, { RT, RA, RB } }, +{ "dozo.", XO(31,264,1,1), XO_MASK, M601, { RT, RA, RB } }, + +{ "add", XO(31,266,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "cax", XO(31,266,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "add.", XO(31,266,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "cax.", XO(31,266,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addo", XO(31,266,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "caxo", XO(31,266,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addo.", XO(31,266,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "caxo.", XO(31,266,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "lscbx", XRC(31,277,0), X_MASK, M601, { RT, RA, RB } }, +{ "lscbx.", XRC(31,277,1), X_MASK, M601, { RT, RA, RB } }, + +{ "dcbt", X(31,278), XRT_MASK, PPC, { RA, RB } }, + +{ "lhzx", X(31,279), X_MASK, COM, { RT, RA, RB } }, + +{ "icbt", X(31,262), XRT_MASK, PPC403, { RA, RB } }, + +{ "eqv", XRC(31,284,0), X_MASK, COM, { RA, RS, RB } }, +{ "eqv.", XRC(31,284,1), X_MASK, COM, { RA, RS, RB } }, + +{ "tlbie", X(31,306), XRTRA_MASK, PPC, { RB } }, +{ "tlbi", X(31,306), XRT_MASK, POWER, { RA, RB } }, + +{ "eciwx", X(31,310), X_MASK, PPC, { RT, RA, RB } }, + +{ "lhzux", X(31,311), X_MASK, COM, { RT, RAL, RB } }, + +{ "xor", XRC(31,316,0), X_MASK, COM, { RA, RS, RB } }, +{ "xor.", XRC(31,316,1), X_MASK, COM, { RA, RS, RB } }, + +{ "mfexisr", XSPR(31,323,64), XSPR_MASK, PPC403, { RT } }, +{ "mfexier", XSPR(31,323,66), XSPR_MASK, PPC403, { RT } }, +{ "mfbr0", XSPR(31,323,128), XSPR_MASK, PPC403, { RT } }, +{ "mfbr1", XSPR(31,323,129), XSPR_MASK, PPC403, { RT } }, +{ "mfbr2", XSPR(31,323,130), XSPR_MASK, PPC403, { RT } }, +{ "mfbr3", XSPR(31,323,131), XSPR_MASK, PPC403, { RT } }, +{ "mfbr4", XSPR(31,323,132), XSPR_MASK, PPC403, { RT } }, +{ "mfbr5", XSPR(31,323,133), XSPR_MASK, PPC403, { RT } }, +{ "mfbr6", XSPR(31,323,134), XSPR_MASK, PPC403, { RT } }, +{ "mfbr7", XSPR(31,323,135), XSPR_MASK, PPC403, { RT } }, +{ "mfbear", XSPR(31,323,144), XSPR_MASK, PPC403, { RT } }, +{ "mfbesr", XSPR(31,323,145), XSPR_MASK, PPC403, { RT } }, +{ "mfiocr", XSPR(31,323,160), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacr0", XSPR(31,323,192), XSPR_MASK, PPC403, { RT } }, +{ "mfdmact0", XSPR(31,323,193), XSPR_MASK, PPC403, { RT } }, +{ "mfdmada0", XSPR(31,323,194), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasa0", XSPR(31,323,195), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacc0", XSPR(31,323,196), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacr1", XSPR(31,323,200), XSPR_MASK, PPC403, { RT } }, +{ "mfdmact1", XSPR(31,323,201), XSPR_MASK, PPC403, { RT } }, +{ "mfdmada1", XSPR(31,323,202), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasa1", XSPR(31,323,203), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacc1", XSPR(31,323,204), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacr2", XSPR(31,323,208), XSPR_MASK, PPC403, { RT } }, +{ "mfdmact2", XSPR(31,323,209), XSPR_MASK, PPC403, { RT } }, +{ "mfdmada2", XSPR(31,323,210), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasa2", XSPR(31,323,211), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacc2", XSPR(31,323,212), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacr3", XSPR(31,323,216), XSPR_MASK, PPC403, { RT } }, +{ "mfdmact3", XSPR(31,323,217), XSPR_MASK, PPC403, { RT } }, +{ "mfdmada3", XSPR(31,323,218), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasa3", XSPR(31,323,219), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacc3", XSPR(31,323,220), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasr", XSPR(31,323,224), XSPR_MASK, PPC403, { RT } }, +{ "mfdcr", X(31,323), X_MASK, PPC403, { RT, SPR } }, + +{ "div", XO(31,331,0,0), XO_MASK, M601, { RT, RA, RB } }, +{ "div.", XO(31,331,0,1), XO_MASK, M601, { RT, RA, RB } }, +{ "divo", XO(31,331,1,0), XO_MASK, M601, { RT, RA, RB } }, +{ "divo.", XO(31,331,1,1), XO_MASK, M601, { RT, RA, RB } }, + +{ "mfmq", XSPR(31,339,0), XSPR_MASK, M601, { RT } }, +{ "mfxer", XSPR(31,339,1), XSPR_MASK, COM, { RT } }, +{ "mfrtcu", XSPR(31,339,4), XSPR_MASK, COM, { RT } }, +{ "mfrtcl", XSPR(31,339,5), XSPR_MASK, COM, { RT } }, +{ "mfdec", XSPR(31,339,6), XSPR_MASK, MFDEC1, { RT } }, +{ "mflr", XSPR(31,339,8), XSPR_MASK, COM, { RT } }, +{ "mfctr", XSPR(31,339,9), XSPR_MASK, COM, { RT } }, +{ "mftid", XSPR(31,339,17), XSPR_MASK, POWER, { RT } }, +{ "mfdsisr", XSPR(31,339,18), XSPR_MASK, COM, { RT } }, +{ "mfdar", XSPR(31,339,19), XSPR_MASK, COM, { RT } }, +{ "mfdec", XSPR(31,339,22), XSPR_MASK, MFDEC2, { RT } }, +{ "mfsdr0", XSPR(31,339,24), XSPR_MASK, POWER, { RT } }, +{ "mfsdr1", XSPR(31,339,25), XSPR_MASK, COM, { RT } }, +{ "mfsrr0", XSPR(31,339,26), XSPR_MASK, COM, { RT } }, +{ "mfsrr1", XSPR(31,339,27), XSPR_MASK, COM, { RT } }, +{ "mfcmpa", XSPR(31,339,144), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpb", XSPR(31,339,145), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpc", XSPR(31,339,146), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpd", XSPR(31,339,147), XSPR_MASK, PPC860, { RT } }, +{ "mficr", XSPR(31,339,148), XSPR_MASK, PPC860, { RT } }, +{ "mfder", XSPR(31,339,149), XSPR_MASK, PPC860, { RT } }, +{ "mfcounta", XSPR(31,339,150), XSPR_MASK, PPC860, { RT } }, +{ "mfcountb", XSPR(31,339,151), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpe", XSPR(31,339,152), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpf", XSPR(31,339,153), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpg", XSPR(31,339,154), XSPR_MASK, PPC860, { RT } }, +{ "mfcmph", XSPR(31,339,155), XSPR_MASK, PPC860, { RT } }, +{ "mflctrl1", XSPR(31,339,156), XSPR_MASK, PPC860, { RT } }, +{ "mflctrl2", XSPR(31,339,157), XSPR_MASK, PPC860, { RT } }, +{ "mfictrl", XSPR(31,339,158), XSPR_MASK, PPC860, { RT } }, +{ "mfbar", XSPR(31,339,159), XSPR_MASK, PPC860, { RT } }, +{ "mfsprg4", XSPR(31,339,260), XSPR_MASK, PPC405, { RT } }, +{ "mfsprg5", XSPR(31,339,261), XSPR_MASK, PPC405, { RT } }, +{ "mfsprg6", XSPR(31,339,262), XSPR_MASK, PPC405, { RT } }, +{ "mfsprg7", XSPR(31,339,263), XSPR_MASK, PPC405, { RT } }, +{ "mfsprg", XSPR(31,339,272), XSPRG_MASK, PPC, { RT, SPRG } }, +{ "mfsprg0", XSPR(31,339,272), XSPR_MASK, PPC, { RT } }, +{ "mfsprg1", XSPR(31,339,273), XSPR_MASK, PPC, { RT } }, +{ "mfsprg2", XSPR(31,339,274), XSPR_MASK, PPC, { RT } }, +{ "mfsprg3", XSPR(31,339,275), XSPR_MASK, PPC, { RT } }, +{ "mfasr", XSPR(31,339,280), XSPR_MASK, PPC64, { RT } }, +{ "mfear", XSPR(31,339,282), XSPR_MASK, PPC, { RT } }, +{ "mfpvr", XSPR(31,339,287), XSPR_MASK, PPC, { RT } }, +{ "mfibatu", XSPR(31,339,528), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfibatl", XSPR(31,339,529), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatu", XSPR(31,339,536), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatl", XSPR(31,339,537), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfic_cst", XSPR(31,339,560), XSPR_MASK, PPC860, { RT } }, +{ "mfic_adr", XSPR(31,339,561), XSPR_MASK, PPC860, { RT } }, +{ "mfic_dat", XSPR(31,339,562), XSPR_MASK, PPC860, { RT } }, +{ "mfdc_cst", XSPR(31,339,568), XSPR_MASK, PPC860, { RT } }, +{ "mfdc_adr", XSPR(31,339,569), XSPR_MASK, PPC860, { RT } }, +{ "mfdc_dat", XSPR(31,339,570), XSPR_MASK, PPC860, { RT } }, +{ "mfdpdr", XSPR(31,339,630), XSPR_MASK, PPC860, { RT } }, +{ "mfdpir", XSPR(31,339,631), XSPR_MASK, PPC860, { RT } }, +{ "mfimmr", XSPR(31,339,638), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_ctr", XSPR(31,339,784), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_ap", XSPR(31,339,786), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_epn", XSPR(31,339,787), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_twc", XSPR(31,339,789), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_rpn", XSPR(31,339,790), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_ctr", XSPR(31,339,792), XSPR_MASK, PPC860, { RT } }, +{ "mfm_casid",XSPR(31,339,793), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_ap", XSPR(31,339,794), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_epn", XSPR(31,339,795), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_twb", XSPR(31,339,796), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_twc", XSPR(31,339,797), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_rpn", XSPR(31,339,798), XSPR_MASK, PPC860, { RT } }, +{ "mfm_tw", XSPR(31,339,799), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_dbcam",XSPR(31,339,816), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_dbram0",XSPR(31,339,817), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_dbram1",XSPR(31,339,818), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_dbcam", XSPR(31,339,824), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_dbram0",XSPR(31,339,825), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_dbram1",XSPR(31,339,826), XSPR_MASK, PPC860, { RT } }, +{ "mfzpr", XSPR(31,339,944), XSPR_MASK, PPC403, { RT } }, +{ "mfpid", XSPR(31,339,945), XSPR_MASK, PPC403, { RT } }, +{ "mfccr0", XSPR(31,339,947), XSPR_MASK, PPC405, { RT } }, +{ "mficdbdr", XSPR(31,339,979), XSPR_MASK, PPC403, { RT } }, +{ "mfummcr0", XSPR(31,339,936), XSPR_MASK, PPC750, { RT } }, +{ "mfupmc1", XSPR(31,339,937), XSPR_MASK, PPC750, { RT } }, +{ "mfupmc2", XSPR(31,339,938), XSPR_MASK, PPC750, { RT } }, +{ "mfusia", XSPR(31,339,939), XSPR_MASK, PPC750, { RT } }, +{ "mfummcr1", XSPR(31,339,940), XSPR_MASK, PPC750, { RT } }, +{ "mfupmc3", XSPR(31,339,941), XSPR_MASK, PPC750, { RT } }, +{ "mfupmc4", XSPR(31,339,942), XSPR_MASK, PPC750, { RT } }, +{ "mfiac3", XSPR(31,339,948), XSPR_MASK, PPC405, { RT } }, +{ "mfiac4", XSPR(31,339,949), XSPR_MASK, PPC405, { RT } }, +{ "mfdvc1", XSPR(31,339,950), XSPR_MASK, PPC405, { RT } }, +{ "mfdvc2", XSPR(31,339,951), XSPR_MASK, PPC405, { RT } }, +{ "mfmmcr0", XSPR(31,339,952), XSPR_MASK, PPC750, { RT } }, +{ "mfpmc1", XSPR(31,339,953), XSPR_MASK, PPC750, { RT } }, +{ "mfsgr", XSPR(31,339,953), XSPR_MASK, PPC403, { RT } }, +{ "mfpmc2", XSPR(31,339,954), XSPR_MASK, PPC750, { RT } }, +{ "mfdcwr", XSPR(31,339,954), XSPR_MASK, PPC403, { RT } }, +{ "mfsia", XSPR(31,339,955), XSPR_MASK, PPC750, { RT } }, +{ "mfsler", XSPR(31,339,955), XSPR_MASK, PPC405, { RT } }, +{ "mfmmcr1", XSPR(31,339,956), XSPR_MASK, PPC750, { RT } }, +{ "mfsu0r", XSPR(31,339,956), XSPR_MASK, PPC405, { RT } }, +{ "mfpmc3", XSPR(31,339,957), XSPR_MASK, PPC750, { RT } }, +{ "mfdbcr1", XSPR(31,339,957), XSPR_MASK, PPC405, { RT } }, +{ "mfpmc4", XSPR(31,339,958), XSPR_MASK, PPC750, { RT } }, +{ "mfesr", XSPR(31,339,980), XSPR_MASK, PPC403, { RT } }, +{ "mfdear", XSPR(31,339,981), XSPR_MASK, PPC403, { RT } }, +{ "mfevpr", XSPR(31,339,982), XSPR_MASK, PPC403, { RT } }, +{ "mfcdbcr", XSPR(31,339,983), XSPR_MASK, PPC403, { RT } }, +{ "mftsr", XSPR(31,339,984), XSPR_MASK, PPC403, { RT } }, +{ "mftcr", XSPR(31,339,986), XSPR_MASK, PPC403, { RT } }, +{ "mfpit", XSPR(31,339,987), XSPR_MASK, PPC403, { RT } }, +{ "mftbhi", XSPR(31,339,988), XSPR_MASK, PPC403, { RT } }, +{ "mftblo", XSPR(31,339,989), XSPR_MASK, PPC403, { RT } }, +{ "mfsrr2", XSPR(31,339,990), XSPR_MASK, PPC403, { RT } }, +{ "mfsrr3", XSPR(31,339,991), XSPR_MASK, PPC403, { RT } }, +{ "mfdbsr", XSPR(31,339,1008), XSPR_MASK, PPC403, { RT } }, +{ "mfdbcr0", XSPR(31,339,1010), XSPR_MASK, PPC405, { RT } }, +{ "mfiac1", XSPR(31,339,1012), XSPR_MASK, PPC403, { RT } }, +{ "mfiac2", XSPR(31,339,1013), XSPR_MASK, PPC403, { RT } }, +{ "mfdac1", XSPR(31,339,1014), XSPR_MASK, PPC403, { RT } }, +{ "mfdac2", XSPR(31,339,1015), XSPR_MASK, PPC403, { RT } }, +{ "mfdccr", XSPR(31,339,1018), XSPR_MASK, PPC403, { RT } }, +{ "mficcr", XSPR(31,339,1019), XSPR_MASK, PPC403, { RT } }, +{ "mfpbl1", XSPR(31,339,1020), XSPR_MASK, PPC403, { RT } }, +{ "mfpbu1", XSPR(31,339,1021), XSPR_MASK, PPC403, { RT } }, +{ "mfpbl2", XSPR(31,339,1022), XSPR_MASK, PPC403, { RT } }, +{ "mfpbu2", XSPR(31,339,1023), XSPR_MASK, PPC403, { RT } }, +{ "mfl2cr", XSPR(31,339,1017), XSPR_MASK, PPC750, { RT } }, +{ "mfictc", XSPR(31,339,1019), XSPR_MASK, PPC750, { RT } }, +{ "mfthrm1", XSPR(31,339,1020), XSPR_MASK, PPC750, { RT } }, +{ "mfthrm2", XSPR(31,339,1021), XSPR_MASK, PPC750, { RT } }, +{ "mfthrm3", XSPR(31,339,1022), XSPR_MASK, PPC750, { RT } }, +{ "mfspr", X(31,339), X_MASK, COM, { RT, SPR } }, + +{ "lwax", X(31,341), X_MASK, PPC64, { RT, RA, RB } }, + +{ "lhax", X(31,343), X_MASK, COM, { RT, RA, RB } }, + +{ "dccci", X(31,454), XRT_MASK, PPC403, { RA, RB } }, + +{ "abs", XO(31,360,0,0), XORB_MASK, M601, { RT, RA } }, +{ "abs.", XO(31,360,0,1), XORB_MASK, M601, { RT, RA } }, +{ "abso", XO(31,360,1,0), XORB_MASK, M601, { RT, RA } }, +{ "abso.", XO(31,360,1,1), XORB_MASK, M601, { RT, RA } }, + +{ "divs", XO(31,363,0,0), XO_MASK, M601, { RT, RA, RB } }, +{ "divs.", XO(31,363,0,1), XO_MASK, M601, { RT, RA, RB } }, +{ "divso", XO(31,363,1,0), XO_MASK, M601, { RT, RA, RB } }, +{ "divso.", XO(31,363,1,1), XO_MASK, M601, { RT, RA, RB } }, + +{ "tlbia", X(31,370), 0xffffffff, PPC, { 0 } }, + +{ "mftbl", XSPR(31,371,268), XSPR_MASK, PPC, { RT } }, +{ "mftbu", XSPR(31,371,269), XSPR_MASK, PPC, { RT } }, +{ "mftb", X(31,371), X_MASK, PPC, { RT, TBR } }, + +{ "lwaux", X(31,373), X_MASK, PPC64, { RT, RAL, RB } }, + +{ "lhaux", X(31,375), X_MASK, COM, { RT, RAL, RB } }, + +{ "sthx", X(31,407), X_MASK, COM, { RS, RA, RB } }, + +{ "lfqx", X(31,791), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "lfqux", X(31,823), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "stfqx", X(31,919), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "stfqux", X(31,951), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "orc", XRC(31,412,0), X_MASK, COM, { RA, RS, RB } }, +{ "orc.", XRC(31,412,1), X_MASK, COM, { RA, RS, RB } }, + +{ "sradi", XS(31,413,0), XS_MASK, PPC64, { RA, RS, SH6 } }, +{ "sradi.", XS(31,413,1), XS_MASK, PPC64, { RA, RS, SH6 } }, + +{ "slbie", X(31,434), XRTRA_MASK, PPC64, { RB } }, + +{ "ecowx", X(31,438), X_MASK, PPC, { RT, RA, RB } }, + +{ "sthux", X(31,439), X_MASK, COM, { RS, RAS, RB } }, + +{ "mr", XRC(31,444,0), X_MASK, COM, { RA, RS, RBS } }, +{ "or", XRC(31,444,0), X_MASK, COM, { RA, RS, RB } }, +{ "mr.", XRC(31,444,1), X_MASK, COM, { RA, RS, RBS } }, +{ "or.", XRC(31,444,1), X_MASK, COM, { RA, RS, RB } }, + +{ "mtexisr", XSPR(31,451,64), XSPR_MASK, PPC403, { RT } }, +{ "mtexier", XSPR(31,451,66), XSPR_MASK, PPC403, { RT } }, +{ "mtbr0", XSPR(31,451,128), XSPR_MASK, PPC403, { RT } }, +{ "mtbr1", XSPR(31,451,129), XSPR_MASK, PPC403, { RT } }, +{ "mtbr2", XSPR(31,451,130), XSPR_MASK, PPC403, { RT } }, +{ "mtbr3", XSPR(31,451,131), XSPR_MASK, PPC403, { RT } }, +{ "mtbr4", XSPR(31,451,132), XSPR_MASK, PPC403, { RT } }, +{ "mtbr5", XSPR(31,451,133), XSPR_MASK, PPC403, { RT } }, +{ "mtbr6", XSPR(31,451,134), XSPR_MASK, PPC403, { RT } }, +{ "mtbr7", XSPR(31,451,135), XSPR_MASK, PPC403, { RT } }, +{ "mtbear", XSPR(31,451,144), XSPR_MASK, PPC403, { RT } }, +{ "mtbesr", XSPR(31,451,145), XSPR_MASK, PPC403, { RT } }, +{ "mtiocr", XSPR(31,451,160), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacr0", XSPR(31,451,192), XSPR_MASK, PPC403, { RT } }, +{ "mtdmact0", XSPR(31,451,193), XSPR_MASK, PPC403, { RT } }, +{ "mtdmada0", XSPR(31,451,194), XSPR_MASK, PPC403, { RT } }, +{ "mtdmasa0", XSPR(31,451,195), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacc0", XSPR(31,451,196), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacr1", XSPR(31,451,200), XSPR_MASK, PPC403, { RT } }, +{ "mtdmact1", XSPR(31,451,201), XSPR_MASK, PPC403, { RT } }, +{ "mtdmada1", XSPR(31,451,202), XSPR_MASK, PPC403, { RT } }, +{ "mtdmasa1", XSPR(31,451,203), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacc1", XSPR(31,451,204), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacr2", XSPR(31,451,208), XSPR_MASK, PPC403, { RT } }, +{ "mtdmact2", XSPR(31,451,209), XSPR_MASK, PPC403, { RT } }, +{ "mtdmada2", XSPR(31,451,210), XSPR_MASK, PPC403, { RT } }, +{ "mtdmasa2", XSPR(31,451,211), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacc2", XSPR(31,451,212), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacr3", XSPR(31,451,216), XSPR_MASK, PPC403, { RT } }, +{ "mtdmact3", XSPR(31,451,217), XSPR_MASK, PPC403, { RT } }, +{ "mtdmada3", XSPR(31,451,218), XSPR_MASK, PPC403, { RT } }, +{ "mtdmasa3", XSPR(31,451,219), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacc3", XSPR(31,451,220), XSPR_MASK, PPC403, { RT } }, +{ "mtdmasr", XSPR(31,451,224), XSPR_MASK, PPC403, { RT } }, +{ "mtummcr0", XSPR(31,451,936), XSPR_MASK, PPC750, { RT } }, +{ "mtupmc1", XSPR(31,451,937), XSPR_MASK, PPC750, { RT } }, +{ "mtupmc2", XSPR(31,451,938), XSPR_MASK, PPC750, { RT } }, +{ "mtusia", XSPR(31,451,939), XSPR_MASK, PPC750, { RT } }, +{ "mtummcr1", XSPR(31,451,940), XSPR_MASK, PPC750, { RT } }, +{ "mtupmc3", XSPR(31,451,941), XSPR_MASK, PPC750, { RT } }, +{ "mtupmc4", XSPR(31,451,942), XSPR_MASK, PPC750, { RT } }, +{ "mtmmcr0", XSPR(31,451,952), XSPR_MASK, PPC750, { RT } }, +{ "mtpmc1", XSPR(31,451,953), XSPR_MASK, PPC750, { RT } }, +{ "mtpmc2", XSPR(31,451,954), XSPR_MASK, PPC750, { RT } }, +{ "mtsia", XSPR(31,451,955), XSPR_MASK, PPC750, { RT } }, +{ "mtmmcr1", XSPR(31,451,956), XSPR_MASK, PPC750, { RT } }, +{ "mtpmc3", XSPR(31,451,957), XSPR_MASK, PPC750, { RT } }, +{ "mtpmc4", XSPR(31,451,958), XSPR_MASK, PPC750, { RT } }, +{ "mtl2cr", XSPR(31,451,1017), XSPR_MASK, PPC750, { RT } }, +{ "mtictc", XSPR(31,451,1019), XSPR_MASK, PPC750, { RT } }, +{ "mtthrm1", XSPR(31,451,1020), XSPR_MASK, PPC750, { RT } }, +{ "mtthrm2", XSPR(31,451,1021), XSPR_MASK, PPC750, { RT } }, +{ "mtthrm3", XSPR(31,451,1022), XSPR_MASK, PPC750, { RT } }, +{ "mtdcr", X(31,451), X_MASK, PPC403, { SPR, RS } }, + +{ "divdu", XO(31,457,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divdu.", XO(31,457,0,1), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divduo", XO(31,457,1,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divduo.", XO(31,457,1,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "divwu", XO(31,459,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwu.", XO(31,459,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo", XO(31,459,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo.", XO(31,459,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mtmq", XSPR(31,467,0), XSPR_MASK, M601, { RS } }, +{ "mtxer", XSPR(31,467,1), XSPR_MASK, COM, { RS } }, +{ "mtlr", XSPR(31,467,8), XSPR_MASK, COM, { RS } }, +{ "mtctr", XSPR(31,467,9), XSPR_MASK, COM, { RS } }, +{ "mttid", XSPR(31,467,17), XSPR_MASK, POWER, { RS } }, +{ "mtdsisr", XSPR(31,467,18), XSPR_MASK, COM, { RS } }, +{ "mtdar", XSPR(31,467,19), XSPR_MASK, COM, { RS } }, +{ "mtrtcu", XSPR(31,467,20), XSPR_MASK, COM, { RS } }, +{ "mtrtcl", XSPR(31,467,21), XSPR_MASK, COM, { RS } }, +{ "mtdec", XSPR(31,467,22), XSPR_MASK, COM, { RS } }, +{ "mtsdr0", XSPR(31,467,24), XSPR_MASK, POWER, { RS } }, +{ "mtsdr1", XSPR(31,467,25), XSPR_MASK, COM, { RS } }, +{ "mtsrr0", XSPR(31,467,26), XSPR_MASK, COM, { RS } }, +{ "mtsrr1", XSPR(31,467,27), XSPR_MASK, COM, { RS } }, +{ "mtcmpa", XSPR(31,467,144), XSPR_MASK, PPC860, { RT } }, +{ "mtcmpb", XSPR(31,467,145), XSPR_MASK, PPC860, { RT } }, +{ "mtcmpc", XSPR(31,467,146), XSPR_MASK, PPC860, { RT } }, +{ "mtcmpd", XSPR(31,467,147), XSPR_MASK, PPC860, { RT } }, +{ "mticr", XSPR(31,467,148), XSPR_MASK, PPC860, { RT } }, +{ "mtder", XSPR(31,467,149), XSPR_MASK, PPC860, { RT } }, +{ "mtcounta", XSPR(31,467,150), XSPR_MASK, PPC860, { RT } }, +{ "mtcountb", XSPR(31,467,151), XSPR_MASK, PPC860, { RT } }, +{ "mtcmpe", XSPR(31,467,152), XSPR_MASK, PPC860, { RT } }, +{ "mtcmpf", XSPR(31,467,153), XSPR_MASK, PPC860, { RT } }, +{ "mtcmpg", XSPR(31,467,154), XSPR_MASK, PPC860, { RT } }, +{ "mtcmph", XSPR(31,467,155), XSPR_MASK, PPC860, { RT } }, +{ "mtlctrl1", XSPR(31,467,156), XSPR_MASK, PPC860, { RT } }, +{ "mtlctrl2", XSPR(31,467,157), XSPR_MASK, PPC860, { RT } }, +{ "mtictrl", XSPR(31,467,158), XSPR_MASK, PPC860, { RT } }, +{ "mtbar", XSPR(31,467,159), XSPR_MASK, PPC860, { RT } }, +{ "mtsprg", XSPR(31,467,272), XSPRG_MASK, PPC, { SPRG, RS } }, +{ "mtsprg0", XSPR(31,467,272), XSPR_MASK, PPC, { RT } }, +{ "mtsprg1", XSPR(31,467,273), XSPR_MASK, PPC, { RT } }, +{ "mtsprg2", XSPR(31,467,274), XSPR_MASK, PPC, { RT } }, +{ "mtsprg3", XSPR(31,467,275), XSPR_MASK, PPC, { RT } }, +{ "mtsprg4", XSPR(31,467,276), XSPR_MASK, PPC405, { RT } }, +{ "mtsprg5", XSPR(31,467,277), XSPR_MASK, PPC405, { RT } }, +{ "mtsprg6", XSPR(31,467,278), XSPR_MASK, PPC405, { RT } }, +{ "mtsprg7", XSPR(31,467,279), XSPR_MASK, PPC405, { RT } }, +{ "mtasr", XSPR(31,467,280), XSPR_MASK, PPC64, { RS } }, +{ "mtear", XSPR(31,467,282), XSPR_MASK, PPC, { RS } }, +{ "mttbl", XSPR(31,467,284), XSPR_MASK, PPC, { RS } }, +{ "mttbu", XSPR(31,467,285), XSPR_MASK, PPC, { RS } }, +{ "mtibatu", XSPR(31,467,528), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtibatl", XSPR(31,467,529), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatu", XSPR(31,467,536), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatl", XSPR(31,467,537), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtzpr", XSPR(31,467,944), XSPR_MASK, PPC403, { RT } }, +{ "mtpid", XSPR(31,467,945), XSPR_MASK, PPC403, { RT } }, +{ "mtccr0", XSPR(31,467,947), XSPR_MASK, PPC405, { RT } }, +{ "mtiac3", XSPR(31,467,948), XSPR_MASK, PPC405, { RT } }, +{ "mtiac4", XSPR(31,467,949), XSPR_MASK, PPC405, { RT } }, +{ "mtdvc1", XSPR(31,467,950), XSPR_MASK, PPC405, { RT } }, +{ "mtdvc2", XSPR(31,467,951), XSPR_MASK, PPC405, { RT } }, +{ "mtsgr", XSPR(31,467,953), XSPR_MASK, PPC403, { RT } }, +{ "mtdcwr", XSPR(31,467,954), XSPR_MASK, PPC403, { RT } }, +{ "mtsler", XSPR(31,467,955), XSPR_MASK, PPC405, { RT } }, +{ "mtsu0r", XSPR(31,467,956), XSPR_MASK, PPC405, { RT } }, +{ "mtdbcr1", XSPR(31,467,957), XSPR_MASK, PPC405, { RT } }, +{ "mticdbdr",XSPR(31,467,979), XSPR_MASK, PPC403, { RT } }, +{ "mtesr", XSPR(31,467,980), XSPR_MASK, PPC403, { RT } }, +{ "mtdear", XSPR(31,467,981), XSPR_MASK, PPC403, { RT } }, +{ "mtevpr", XSPR(31,467,982), XSPR_MASK, PPC403, { RT } }, +{ "mtcdbcr", XSPR(31,467,983), XSPR_MASK, PPC403, { RT } }, +{ "mttsr", XSPR(31,467,984), XSPR_MASK, PPC403, { RT } }, +{ "mttcr", XSPR(31,467,986), XSPR_MASK, PPC403, { RT } }, +{ "mtpit", XSPR(31,467,987), XSPR_MASK, PPC403, { RT } }, +{ "mttbhi", XSPR(31,467,988), XSPR_MASK, PPC403, { RT } }, +{ "mttblo", XSPR(31,467,989), XSPR_MASK, PPC403, { RT } }, +{ "mtsrr2", XSPR(31,467,990), XSPR_MASK, PPC403, { RT } }, +{ "mtsrr3", XSPR(31,467,991), XSPR_MASK, PPC403, { RT } }, +{ "mtdbsr", XSPR(31,467,1008), XSPR_MASK, PPC403, { RT } }, +{ "mtdbcr0", XSPR(31,467,1010), XSPR_MASK, PPC405, { RT } }, +{ "mtiac1", XSPR(31,467,1012), XSPR_MASK, PPC403, { RT } }, +{ "mtiac2", XSPR(31,467,1013), XSPR_MASK, PPC403, { RT } }, +{ "mtdac1", XSPR(31,467,1014), XSPR_MASK, PPC403, { RT } }, +{ "mtdac2", XSPR(31,467,1015), XSPR_MASK, PPC403, { RT } }, +{ "mtdccr", XSPR(31,467,1018), XSPR_MASK, PPC403, { RT } }, +{ "mticcr", XSPR(31,467,1019), XSPR_MASK, PPC403, { RT } }, +{ "mtpbl1", XSPR(31,467,1020), XSPR_MASK, PPC403, { RT } }, +{ "mtpbu1", XSPR(31,467,1021), XSPR_MASK, PPC403, { RT } }, +{ "mtpbl2", XSPR(31,467,1022), XSPR_MASK, PPC403, { RT } }, +{ "mtpbu2", XSPR(31,467,1023), XSPR_MASK, PPC403, { RT } }, +{ "mtspr", X(31,467), X_MASK, COM, { SPR, RS } }, + +{ "dcbi", X(31,470), XRT_MASK, PPC, { RA, RB } }, + +{ "nand", XRC(31,476,0), X_MASK, COM, { RA, RS, RB } }, +{ "nand.", XRC(31,476,1), X_MASK, COM, { RA, RS, RB } }, + +{ "dcread", X(31,486), X_MASK, PPC403, { RT, RA, RB }}, + +{ "nabs", XO(31,488,0,0), XORB_MASK, M601, { RT, RA } }, +{ "nabs.", XO(31,488,0,1), XORB_MASK, M601, { RT, RA } }, +{ "nabso", XO(31,488,1,0), XORB_MASK, M601, { RT, RA } }, +{ "nabso.", XO(31,488,1,1), XORB_MASK, M601, { RT, RA } }, + +{ "divd", XO(31,489,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divd.", XO(31,489,0,1), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divdo", XO(31,489,1,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divdo.", XO(31,489,1,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "divw", XO(31,491,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divw.", XO(31,491,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo", XO(31,491,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo.", XO(31,491,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "slbia", X(31,498), 0xffffffff, PPC64, { 0 } }, + +{ "cli", X(31,502), XRB_MASK, POWER, { RT, RA } }, + +{ "mcrxr", X(31,512), XRARB_MASK|(3<<21), COM, { BF } }, + +{ "clcs", X(31,531), XRB_MASK, M601, { RT, RA } }, + +{ "lswx", X(31,533), X_MASK, PPCCOM, { RT, RA, RB } }, +{ "lsx", X(31,533), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "lwbrx", X(31,534), X_MASK, PPCCOM, { RT, RA, RB } }, +{ "lbrx", X(31,534), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "lfsx", X(31,535), X_MASK, COM, { FRT, RA, RB } }, + +{ "srw", XRC(31,536,0), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sr", XRC(31,536,0), X_MASK, PWRCOM, { RA, RS, RB } }, +{ "srw.", XRC(31,536,1), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sr.", XRC(31,536,1), X_MASK, PWRCOM, { RA, RS, RB } }, + +{ "rrib", XRC(31,537,0), X_MASK, M601, { RA, RS, RB } }, +{ "rrib.", XRC(31,537,1), X_MASK, M601, { RA, RS, RB } }, + +{ "srd", XRC(31,539,0), X_MASK, PPC64, { RA, RS, RB } }, +{ "srd.", XRC(31,539,1), X_MASK, PPC64, { RA, RS, RB } }, + +{ "maskir", XRC(31,541,0), X_MASK, M601, { RA, RS, RB } }, +{ "maskir.", XRC(31,541,1), X_MASK, M601, { RA, RS, RB } }, + +{ "tlbsync", X(31,566), 0xffffffff, PPC, { 0 } }, + +{ "lfsux", X(31,567), X_MASK, COM, { FRT, RAS, RB } }, + +{ "mfsr", X(31,595), XRB_MASK|(1<<20), COM32, { RT, SR } }, + +{ "lswi", X(31,597), X_MASK, PPCCOM, { RT, RA, NB } }, +{ "lsi", X(31,597), X_MASK, PWRCOM, { RT, RA, NB } }, + +{ "sync", X(31,598), 0xffffffff, PPCCOM, { 0 } }, +{ "dcs", X(31,598), 0xffffffff, PWRCOM, { 0 } }, + +{ "lfdx", X(31,599), X_MASK, COM, { FRT, RA, RB } }, + +{ "mfsri", X(31,627), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "dclst", X(31,630), XRB_MASK, PWRCOM, { RS, RA } }, + +{ "lfdux", X(31,631), X_MASK, COM, { FRT, RAS, RB } }, + +{ "mfsrin", X(31,659), XRA_MASK, PPC32, { RT, RB } }, + +{ "stswx", X(31,661), X_MASK, PPCCOM, { RS, RA, RB } }, +{ "stsx", X(31,661), X_MASK, PWRCOM, { RS, RA, RB } }, + +{ "stwbrx", X(31,662), X_MASK, PPCCOM, { RS, RA, RB } }, +{ "stbrx", X(31,662), X_MASK, PWRCOM, { RS, RA, RB } }, + +{ "stfsx", X(31,663), X_MASK, COM, { FRS, RA, RB } }, + +{ "srq", XRC(31,664,0), X_MASK, M601, { RA, RS, RB } }, +{ "srq.", XRC(31,664,1), X_MASK, M601, { RA, RS, RB } }, + +{ "sre", XRC(31,665,0), X_MASK, M601, { RA, RS, RB } }, +{ "sre.", XRC(31,665,1), X_MASK, M601, { RA, RS, RB } }, + +{ "stfsux", X(31,695), X_MASK, COM, { FRS, RAS, RB } }, + +{ "sriq", XRC(31,696,0), X_MASK, M601, { RA, RS, SH } }, +{ "sriq.", XRC(31,696,1), X_MASK, M601, { RA, RS, SH } }, + +{ "stswi", X(31,725), X_MASK, PPCCOM, { RS, RA, NB } }, +{ "stsi", X(31,725), X_MASK, PWRCOM, { RS, RA, NB } }, + +{ "stfdx", X(31,727), X_MASK, COM, { FRS, RA, RB } }, + +{ "srlq", XRC(31,728,0), X_MASK, M601, { RA, RS, RB } }, +{ "srlq.", XRC(31,728,1), X_MASK, M601, { RA, RS, RB } }, + +{ "sreq", XRC(31,729,0), X_MASK, M601, { RA, RS, RB } }, +{ "sreq.", XRC(31,729,1), X_MASK, M601, { RA, RS, RB } }, + +{ "dcba", X(31,758), XRT_MASK, PPC405, { RA, RB } }, + +{ "stfdux", X(31,759), X_MASK, COM, { FRS, RAS, RB } }, + +{ "srliq", XRC(31,760,0), X_MASK, M601, { RA, RS, SH } }, +{ "srliq.", XRC(31,760,1), X_MASK, M601, { RA, RS, SH } }, + +{ "lhbrx", X(31,790), X_MASK, COM, { RT, RA, RB } }, + +{ "sraw", XRC(31,792,0), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sra", XRC(31,792,0), X_MASK, PWRCOM, { RA, RS, RB } }, +{ "sraw.", XRC(31,792,1), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sra.", XRC(31,792,1), X_MASK, PWRCOM, { RA, RS, RB } }, + +{ "srad", XRC(31,794,0), X_MASK, PPC64, { RA, RS, RB } }, +{ "srad.", XRC(31,794,1), X_MASK, PPC64, { RA, RS, RB } }, + +{ "rac", X(31,818), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "srawi", XRC(31,824,0), X_MASK, PPCCOM, { RA, RS, SH } }, +{ "srai", XRC(31,824,0), X_MASK, PWRCOM, { RA, RS, SH } }, +{ "srawi.", XRC(31,824,1), X_MASK, PPCCOM, { RA, RS, SH } }, +{ "srai.", XRC(31,824,1), X_MASK, PWRCOM, { RA, RS, SH } }, + +{ "eieio", X(31,854), 0xffffffff, PPC, { 0 } }, + +{ "tlbsx", XRC(31,914,0), X_MASK, PPC403, { RT, RA, RB } }, +{ "tlbsx.", XRC(31,914,1), X_MASK, PPC403, { RT, RA, RB } }, + +{ "sthbrx", X(31,918), X_MASK, COM, { RS, RA, RB } }, + +{ "sraq", XRC(31,920,0), X_MASK, M601, { RA, RS, RB } }, +{ "sraq.", XRC(31,920,1), X_MASK, M601, { RA, RS, RB } }, + +{ "srea", XRC(31,921,0), X_MASK, M601, { RA, RS, RB } }, +{ "srea.", XRC(31,921,1), X_MASK, M601, { RA, RS, RB } }, + +{ "extsh", XRC(31,922,0), XRB_MASK, PPCCOM, { RA, RS } }, +{ "exts", XRC(31,922,0), XRB_MASK, PWRCOM, { RA, RS } }, +{ "extsh.", XRC(31,922,1), XRB_MASK, PPCCOM, { RA, RS } }, +{ "exts.", XRC(31,922,1), XRB_MASK, PWRCOM, { RA, RS } }, + +{ "tlbrehi", XTLB(31,946,0), XTLB_MASK, PPC403, { RT, RA } }, +{ "tlbrelo", XTLB(31,946,1), XTLB_MASK, PPC403, { RT, RA } }, +{ "tlbre", X(31,946), X_MASK, PPC403, { RT, RA, SH } }, + +{ "sraiq", XRC(31,952,0), X_MASK, M601, { RA, RS, SH } }, +{ "sraiq.", XRC(31,952,1), X_MASK, M601, { RA, RS, SH } }, + +{ "extsb", XRC(31,954,0), XRB_MASK, PPC, { RA, RS} }, +{ "extsb.", XRC(31,954,1), XRB_MASK, PPC, { RA, RS} }, + +{ "iccci", X(31,966), XRT_MASK, PPC403, { RA, RB } }, + +{ "tlbld", X(31,978), XRTRA_MASK, PPC, { RB } }, + +{ "tlbwehi", XTLB(31,978,0), XTLB_MASK, PPC403, { RT, RA } }, +{ "tlbwelo", XTLB(31,978,1), XTLB_MASK, PPC403, { RT, RA } }, +{ "tlbwe", X(31,978), X_MASK, PPC403, { RS, RA, SH } }, + +{ "icbi", X(31,982), XRT_MASK, PPC, { RA, RB } }, + +{ "stfiwx", X(31,983), X_MASK, PPC, { FRS, RA, RB } }, + +{ "extsw", XRC(31,986,0), XRB_MASK, PPC, { RA, RS } }, +{ "extsw.", XRC(31,986,1), XRB_MASK, PPC, { RA, RS } }, + +{ "icread", X(31,998), XRT_MASK, PPC403, { RA, RB } }, + +{ "tlbli", X(31,1010), XRTRA_MASK, PPC, { RB } }, + +{ "dcbz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, +{ "dclz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, + +{ "lvebx", X(31, 7), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvehx", X(31, 39), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvewx", X(31, 71), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvsl", X(31, 6), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvsr", X(31, 38), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvx", X(31, 103), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvxl", X(31, 359), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "stvebx", X(31, 135), X_MASK, PPCVEC, { VS, RA, RB } }, +{ "stvehx", X(31, 167), X_MASK, PPCVEC, { VS, RA, RB } }, +{ "stvewx", X(31, 199), X_MASK, PPCVEC, { VS, RA, RB } }, +{ "stvx", X(31, 231), X_MASK, PPCVEC, { VS, RA, RB } }, +{ "stvxl", X(31, 487), X_MASK, PPCVEC, { VS, RA, RB } }, + +{ "lwz", OP(32), OP_MASK, PPCCOM, { RT, D, RA } }, +{ "l", OP(32), OP_MASK, PWRCOM, { RT, D, RA } }, + +{ "lwzu", OP(33), OP_MASK, PPCCOM, { RT, D, RAL } }, +{ "lu", OP(33), OP_MASK, PWRCOM, { RT, D, RA } }, + +{ "lbz", OP(34), OP_MASK, COM, { RT, D, RA } }, + +{ "lbzu", OP(35), OP_MASK, COM, { RT, D, RAL } }, + +{ "stw", OP(36), OP_MASK, PPCCOM, { RS, D, RA } }, +{ "st", OP(36), OP_MASK, PWRCOM, { RS, D, RA } }, + +{ "stwu", OP(37), OP_MASK, PPCCOM, { RS, D, RAS } }, +{ "stu", OP(37), OP_MASK, PWRCOM, { RS, D, RA } }, + +{ "stb", OP(38), OP_MASK, COM, { RS, D, RA } }, + +{ "stbu", OP(39), OP_MASK, COM, { RS, D, RAS } }, + +{ "lhz", OP(40), OP_MASK, COM, { RT, D, RA } }, + +{ "lhzu", OP(41), OP_MASK, COM, { RT, D, RAL } }, + +{ "lha", OP(42), OP_MASK, COM, { RT, D, RA } }, + +{ "lhau", OP(43), OP_MASK, COM, { RT, D, RAL } }, + +{ "sth", OP(44), OP_MASK, COM, { RS, D, RA } }, + +{ "sthu", OP(45), OP_MASK, COM, { RS, D, RAS } }, + +{ "lmw", OP(46), OP_MASK, PPCCOM, { RT, D, RAM } }, +{ "lm", OP(46), OP_MASK, PWRCOM, { RT, D, RA } }, + +{ "stmw", OP(47), OP_MASK, PPCCOM, { RS, D, RA } }, +{ "stm", OP(47), OP_MASK, PWRCOM, { RS, D, RA } }, + +{ "lfs", OP(48), OP_MASK, COM, { FRT, D, RA } }, + +{ "lfsu", OP(49), OP_MASK, COM, { FRT, D, RAS } }, + +{ "lfd", OP(50), OP_MASK, COM, { FRT, D, RA } }, + +{ "lfdu", OP(51), OP_MASK, COM, { FRT, D, RAS } }, + +{ "stfs", OP(52), OP_MASK, COM, { FRS, D, RA } }, + +{ "stfsu", OP(53), OP_MASK, COM, { FRS, D, RAS } }, + +{ "stfd", OP(54), OP_MASK, COM, { FRS, D, RA } }, + +{ "stfdu", OP(55), OP_MASK, COM, { FRS, D, RAS } }, + +{ "lfq", OP(56), OP_MASK, POWER2, { FRT, D, RA } }, + +{ "lfqu", OP(57), OP_MASK, POWER2, { FRT, D, RA } }, + +{ "ld", DSO(58,0), DS_MASK, PPC64, { RT, DS, RA } }, + +{ "ldu", DSO(58,1), DS_MASK, PPC64, { RT, DS, RAL } }, + +{ "lwa", DSO(58,2), DS_MASK, PPC64, { RT, DS, RA } }, + +{ "fdivs", A(59,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fdivs.", A(59,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsubs", A(59,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fsubs.", A(59,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fadds", A(59,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fadds.", A(59,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsqrts", A(59,22,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fsqrts.", A(59,22,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fres", A(59,24,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fres.", A(59,24,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fmuls", A(59,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fmuls.", A(59,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } }, + +{ "fmsubs", A(59,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmsubs.", A(59,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmadds", A(59,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmadds.", A(59,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmsubs", A(59,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmsubs.",A(59,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmadds", A(59,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmadds.",A(59,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "stfq", OP(60), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "stfqu", OP(61), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "std", DSO(62,0), DS_MASK, PPC64, { RS, DS, RA } }, + +{ "stdu", DSO(62,1), DS_MASK, PPC64, { RS, DS, RAS } }, + +{ "fcmpu", X(63,0), X_MASK|(3<<21), COM, { BF, FRA, FRB } }, + +{ "frsp", XRC(63,12,0), XRA_MASK, COM, { FRT, FRB } }, +{ "frsp.", XRC(63,12,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "fctiw", XRC(63,14,0), XRA_MASK, PPCCOM, { FRT, FRB } }, +{ "fcir", XRC(63,14,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiw.", XRC(63,14,1), XRA_MASK, PPCCOM, { FRT, FRB } }, +{ "fcir.", XRC(63,14,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fctiwz", XRC(63,15,0), XRA_MASK, PPCCOM, { FRT, FRB } }, +{ "fcirz", XRC(63,15,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiwz.", XRC(63,15,1), XRA_MASK, PPCCOM, { FRT, FRB } }, +{ "fcirz.", XRC(63,15,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fdiv", A(63,18,0), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fd", A(63,18,0), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, +{ "fdiv.", A(63,18,1), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fd.", A(63,18,1), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, + +{ "fsub", A(63,20,0), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fs", A(63,20,0), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, +{ "fsub.", A(63,20,1), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fs.", A(63,20,1), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, + +{ "fadd", A(63,21,0), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fa", A(63,21,0), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, +{ "fadd.", A(63,21,1), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fa.", A(63,21,1), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, + +{ "fsqrt", A(63,22,0), AFRAFRC_MASK, PPCPWR2, { FRT, FRB } }, +{ "fsqrt.", A(63,22,1), AFRAFRC_MASK, PPCPWR2, { FRT, FRB } }, + +{ "fsel", A(63,23,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fsel.", A(63,23,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmul", A(63,25,0), AFRB_MASK, PPCCOM, { FRT, FRA, FRC } }, +{ "fm", A(63,25,0), AFRB_MASK, PWRCOM, { FRT, FRA, FRC } }, +{ "fmul.", A(63,25,1), AFRB_MASK, PPCCOM, { FRT, FRA, FRC } }, +{ "fm.", A(63,25,1), AFRB_MASK, PWRCOM, { FRT, FRA, FRC } }, + +{ "frsqrte", A(63,26,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "frsqrte.",A(63,26,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fmsub", A(63,28,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fms", A(63,28,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, +{ "fmsub.", A(63,28,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fms.", A(63,28,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, + +{ "fmadd", A(63,29,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fma", A(63,29,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, +{ "fmadd.", A(63,29,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fma.", A(63,29,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, + +{ "fnmsub", A(63,30,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fnms", A(63,30,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, +{ "fnmsub.", A(63,30,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fnms.", A(63,30,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, + +{ "fnmadd", A(63,31,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fnma", A(63,31,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, +{ "fnmadd.", A(63,31,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fnma.", A(63,31,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, + +{ "fcmpo", X(63,32), X_MASK|(3<<21), COM, { BF, FRA, FRB } }, + +{ "mtfsb1", XRC(63,38,0), XRARB_MASK, COM, { BT } }, +{ "mtfsb1.", XRC(63,38,1), XRARB_MASK, COM, { BT } }, + +{ "fneg", XRC(63,40,0), XRA_MASK, COM, { FRT, FRB } }, +{ "fneg.", XRC(63,40,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "mcrfs", X(63,64), XRB_MASK|(3<<21)|(3<<16), COM, { BF, BFA } }, + +{ "mtfsb0", XRC(63,70,0), XRARB_MASK, COM, { BT } }, +{ "mtfsb0.", XRC(63,70,1), XRARB_MASK, COM, { BT } }, + +{ "fmr", XRC(63,72,0), XRA_MASK, COM, { FRT, FRB } }, +{ "fmr.", XRC(63,72,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "mtfsfi", XRC(63,134,0), XRA_MASK|(3<<21)|(1<<11), COM, { BF, U } }, +{ "mtfsfi.", XRC(63,134,1), XRA_MASK|(3<<21)|(1<<11), COM, { BF, U } }, + +{ "fnabs", XRC(63,136,0), XRA_MASK, COM, { FRT, FRB } }, +{ "fnabs.", XRC(63,136,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "fabs", XRC(63,264,0), XRA_MASK, COM, { FRT, FRB } }, +{ "fabs.", XRC(63,264,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "mffs", XRC(63,583,0), XRARB_MASK, COM, { FRT } }, +{ "mffs.", XRC(63,583,1), XRARB_MASK, COM, { FRT } }, + +{ "mtfsf", XFL(63,711,0), XFL_MASK, COM, { FLM, FRB } }, +{ "mtfsf.", XFL(63,711,1), XFL_MASK, COM, { FLM, FRB } }, + +{ "fctid", XRC(63,814,0), XRA_MASK, PPC64, { FRT, FRB } }, +{ "fctid.", XRC(63,814,1), XRA_MASK, PPC64, { FRT, FRB } }, + +{ "fctidz", XRC(63,815,0), XRA_MASK, PPC64, { FRT, FRB } }, +{ "fctidz.", XRC(63,815,1), XRA_MASK, PPC64, { FRT, FRB } }, + +{ "fcfid", XRC(63,846,0), XRA_MASK, PPC64, { FRT, FRB } }, +{ "fcfid.", XRC(63,846,1), XRA_MASK, PPC64, { FRT, FRB } }, + +}; + +const int powerpc_num_opcodes = + sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]); + +/* The macro table. This is only used by the assembler. */ + +/* The expressions of the form (-x ! 31) & (x | 31) have the value 0 + when x=0; 32-x when x is between 1 and 31; are negative if x is + negative; and are 32 or more otherwise. This is what you want + when, for instance, you are emulating a right shift by a + rotate-left-and-mask, because the underlying instructions support + shifts of size 0 but not shifts of size 32. By comparison, when + extracting x bits from some word you want to use just 32-x, because + the underlying instructions don't support extracting 0 bits but do + support extracting the whole word (32 bits in this case). */ + +const struct powerpc_macro powerpc_macros[] = { +{ "extldi", 4, PPC64, "rldicr %0,%1,%3,(%2)-1" }, +{ "extldi.", 4, PPC64, "rldicr. %0,%1,%3,(%2)-1" }, +{ "extrdi", 4, PPC64, "rldicl %0,%1,(%2)+(%3),64-(%2)" }, +{ "extrdi.", 4, PPC64, "rldicl. %0,%1,(%2)+(%3),64-(%2)" }, +{ "insrdi", 4, PPC64, "rldimi %0,%1,64-((%2)+(%3)),%3" }, +{ "insrdi.", 4, PPC64, "rldimi. %0,%1,64-((%2)+(%3)),%3" }, +{ "rotrdi", 3, PPC64, "rldicl %0,%1,(-(%2)!63)&((%2)|63),0" }, +{ "rotrdi.", 3, PPC64, "rldicl. %0,%1,(-(%2)!63)&((%2)|63),0" }, +{ "sldi", 3, PPC64, "rldicr %0,%1,%2,63-(%2)" }, +{ "sldi.", 3, PPC64, "rldicr. %0,%1,%2,63-(%2)" }, +{ "srdi", 3, PPC64, "rldicl %0,%1,(-(%2)!63)&((%2)|63),%2" }, +{ "srdi.", 3, PPC64, "rldicl. %0,%1,(-(%2)!63)&((%2)|63),%2" }, +{ "clrrdi", 3, PPC64, "rldicr %0,%1,0,63-(%2)" }, +{ "clrrdi.", 3, PPC64, "rldicr. %0,%1,0,63-(%2)" }, +{ "clrlsldi",4, PPC64, "rldic %0,%1,%3,(%2)-(%3)" }, +{ "clrlsldi.",4, PPC64, "rldic. %0,%1,%3,(%2)-(%3)" }, + +{ "extlwi", 4, PPCCOM, "rlwinm %0,%1,%3,0,(%2)-1" }, +{ "extlwi.", 4, PPCCOM, "rlwinm. %0,%1,%3,0,(%2)-1" }, +{ "extrwi", 4, PPCCOM, "rlwinm %0,%1,(%2)+(%3),32-(%2),31" }, +{ "extrwi.", 4, PPCCOM, "rlwinm. %0,%1,(%2)+(%3),32-(%2),31" }, +{ "inslwi", 4, PPCCOM, "rlwimi %0,%1,(-(%3)!31)&((%3)|31),%3,(%2)+(%3)-1" }, +{ "inslwi.", 4, PPCCOM, "rlwimi. %0,%1,(-(%3)!31)&((%3)|31),%3,(%2)+(%3)-1"}, +{ "insrwi", 4, PPCCOM, "rlwimi %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1" }, +{ "insrwi.", 4, PPCCOM, "rlwimi. %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1"}, +{ "rotrwi", 3, PPCCOM, "rlwinm %0,%1,(-(%2)!31)&((%2)|31),0,31" }, +{ "rotrwi.", 3, PPCCOM, "rlwinm. %0,%1,(-(%2)!31)&((%2)|31),0,31" }, +{ "slwi", 3, PPCCOM, "rlwinm %0,%1,%2,0,31-(%2)" }, +{ "sli", 3, PWRCOM, "rlinm %0,%1,%2,0,31-(%2)" }, +{ "slwi.", 3, PPCCOM, "rlwinm. %0,%1,%2,0,31-(%2)" }, +{ "sli.", 3, PWRCOM, "rlinm. %0,%1,%2,0,31-(%2)" }, +{ "srwi", 3, PPCCOM, "rlwinm %0,%1,(-(%2)!31)&((%2)|31),%2,31" }, +{ "sri", 3, PWRCOM, "rlinm %0,%1,(-(%2)!31)&((%2)|31),%2,31" }, +{ "srwi.", 3, PPCCOM, "rlwinm. %0,%1,(-(%2)!31)&((%2)|31),%2,31" }, +{ "sri.", 3, PWRCOM, "rlinm. %0,%1,(-(%2)!31)&((%2)|31),%2,31" }, +{ "clrrwi", 3, PPCCOM, "rlwinm %0,%1,0,0,31-(%2)" }, +{ "clrrwi.", 3, PPCCOM, "rlwinm. %0,%1,0,0,31-(%2)" }, +{ "clrlslwi",4, PPCCOM, "rlwinm %0,%1,%3,(%2)-(%3),31-(%3)" }, +{ "clrlslwi.",4, PPCCOM, "rlwinm. %0,%1,%3,(%2)-(%3),31-(%3)" }, + +}; + +const int powerpc_num_macros = + sizeof (powerpc_macros) / sizeof (powerpc_macros[0]); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kdb/ppc.h linux.ac/arch/ppc64/kdb/ppc.h --- linux.vanilla/arch/ppc64/kdb/ppc.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kdb/ppc.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,259 @@ +/* ppc.h -- Header file for PowerPC opcode table + Copyright 1994, 1995 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +1, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +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 file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef PPC_H +#define PPC_H + +/* The opcode table is an array of struct powerpc_opcode. */ + +struct powerpc_opcode +{ + /* The opcode name. */ + const char *name; + + /* The opcode itself. Those bits which will be filled in with + operands are zeroes. */ + unsigned long opcode; + + /* The opcode mask. This is used by the disassembler. This is a + mask containing ones indicating those bits which must match the + opcode field, and zeroes indicating those bits which need not + match (and are presumably filled in by operands). */ + unsigned long mask; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The defined values + are listed below. */ + unsigned long flags; + + /* An array of operand codes. Each code is an index into the + operand table. They appear in the order which the operands must + appear in assembly code, and are terminated by a zero. */ + unsigned char operands[8]; +}; + +/* The table itself is sorted by major opcode number, and is otherwise + in the order in which the disassembler should consider + instructions. */ +extern const struct powerpc_opcode powerpc_opcodes[]; +extern const int powerpc_num_opcodes; + +/* Values defined for the flags field of a struct powerpc_opcode. */ + +/* Opcode is defined for the PowerPC architecture. */ +#define PPC_OPCODE_PPC (01) + +/* Opcode is defined for the POWER (RS/6000) architecture. */ +#define PPC_OPCODE_POWER (02) + +/* Opcode is defined for the POWER2 (Rios 2) architecture. */ +#define PPC_OPCODE_POWER2 (04) + +/* Opcode is only defined on 32 bit architectures. */ +#define PPC_OPCODE_32 (010) + +/* Opcode is only defined on 64 bit architectures. */ +#define PPC_OPCODE_64 (020) + +/* Opcode is supported by the Motorola PowerPC 601 processor. The 601 + is assumed to support all PowerPC (PPC_OPCODE_PPC) instructions, + but it also supports many additional POWER instructions. */ +#define PPC_OPCODE_601 (040) + +/* Opcode is supported in both the Power and PowerPC architectures + (ie, compiler's -mcpu=common or assembler's -mcom). */ +#define PPC_OPCODE_COMMON (0100) + +/* Opcode is supported for any Power or PowerPC platform (this is + for the assembler's -many option, and it eliminates duplicates). */ +#define PPC_OPCODE_ANY (0200) + +/* Opcode is supported as part of the 64-bit bridge. */ +#define PPC_OPCODE_64_BRIDGE (0400) + +/* Opcode is supported by Altivec Vector Unit */ +#define PPC_OPCODE_ALTIVEC (01000) + +/* A macro to extract the major opcode from an instruction. */ +#define PPC_OP(i) (((i) >> 26) & 0x3f) + +/* The operands table is an array of struct powerpc_operand. */ + +struct powerpc_operand +{ + /* The number of bits in the operand. */ + int bits; + + /* How far the operand is left shifted in the instruction. */ + int shift; + + /* Insertion function. This is used by the assembler. To insert an + operand value into an instruction, check this field. + + If it is NULL, execute + i |= (op & ((1 << o->bits) - 1)) << o->shift; + (i is the instruction which we are filling in, o is a pointer to + this structure, and op is the opcode value; this assumes twos + complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction and the operand value. It will return the new value + of the instruction. If the ERRMSG argument is not NULL, then if + the operand value is illegal, *ERRMSG will be set to a warning + string (the operand will be inserted in any case). If the + operand value is legal, *ERRMSG will be unchanged (most operands + can accept any value). */ + unsigned long (*insert) PARAMS ((unsigned long instruction, long op, + const char **errmsg)); + + /* Extraction function. This is used by the disassembler. To + extract this operand type from an instruction, check this field. + + If it is NULL, compute + op = ((i) >> o->shift) & ((1 << o->bits) - 1); + if ((o->flags & PPC_OPERAND_SIGNED) != 0 + && (op & (1 << (o->bits - 1))) != 0) + op -= 1 << o->bits; + (i is the instruction, o is a pointer to this structure, and op + is the result; this assumes twos complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction value. It will return the value of the operand. If + the INVALID argument is not NULL, *INVALID will be set to + non-zero if this operand type can not actually be extracted from + this operand (i.e., the instruction does not match). If the + operand is valid, *INVALID will not be changed. */ + long (*extract) PARAMS ((unsigned long instruction, int *invalid)); + + /* One bit syntax flags. */ + unsigned long flags; +}; + +/* Elements in the table are retrieved by indexing with values from + the operands field of the powerpc_opcodes table. */ + +extern const struct powerpc_operand powerpc_operands[]; + +/* Values defined for the flags field of a struct powerpc_operand. */ + +/* This operand takes signed values. */ +#define PPC_OPERAND_SIGNED (01) + +/* This operand takes signed values, but also accepts a full positive + range of values when running in 32 bit mode. That is, if bits is + 16, it takes any value from -0x8000 to 0xffff. In 64 bit mode, + this flag is ignored. */ +#define PPC_OPERAND_SIGNOPT (02) + +/* This operand does not actually exist in the assembler input. This + is used to support extended mnemonics such as mr, for which two + operands fields are identical. The assembler should call the + insert function with any op value. The disassembler should call + the extract function, ignore the return value, and check the value + placed in the valid argument. */ +#define PPC_OPERAND_FAKE (04) + +/* The next operand should be wrapped in parentheses rather than + separated from this one by a comma. This is used for the load and + store instructions which want their operands to look like + reg,displacement(reg) + */ +#define PPC_OPERAND_PARENS (010) + +/* This operand may use the symbolic names for the CR fields, which + are + lt 0 gt 1 eq 2 so 3 un 3 + cr0 0 cr1 1 cr2 2 cr3 3 + cr4 4 cr5 5 cr6 6 cr7 7 + These may be combined arithmetically, as in cr2*4+gt. These are + only supported on the PowerPC, not the POWER. */ +#define PPC_OPERAND_CR (020) + +/* This operand names a register. The disassembler uses this to print + register names with a leading 'r'. */ +#define PPC_OPERAND_GPR (040) + +/* This operand names a floating point register. The disassembler + prints these with a leading 'f'. */ +#define PPC_OPERAND_FPR (0100) + +/* This operand is a relative branch displacement. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_RELATIVE (0200) + +/* This operand is an absolute branch address. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_ABSOLUTE (0400) + +/* This operand is optional, and is zero if omitted. This is used for + the optional BF and L fields in the comparison instructions. The + assembler must count the number of operands remaining on the line, + and the number of operands remaining for the opcode, and decide + whether this operand is present or not. The disassembler should + print this operand out only if it is not zero. */ +#define PPC_OPERAND_OPTIONAL (01000) + +/* This flag is only used with PPC_OPERAND_OPTIONAL. If this operand + is omitted, then for the next operand use this operand value plus + 1, ignoring the next operand field for the opcode. This wretched + hack is needed because the Power rotate instructions can take + either 4 or 5 operands. The disassembler should print this operand + out regardless of the PPC_OPERAND_OPTIONAL field. */ +#define PPC_OPERAND_NEXT (02000) + +/* This operand should be regarded as a negative number for the + purposes of overflow checking (i.e., the normal most negative + number is disallowed and one more than the normal most positive + number is allowed). This flag will only be set for a signed + operand. */ +#define PPC_OPERAND_NEGATIVE (04000) + +/* This operand names a vector unit register. The disassembler + prints these with a leading 'v'. */ +#define PPC_OPERAND_VR (010000) + + +/* The POWER and PowerPC assemblers use a few macros. We keep them + with the operands table for simplicity. The macro table is an + array of struct powerpc_macro. */ + +struct powerpc_macro +{ + /* The macro name. */ + const char *name; + + /* The number of operands the macro takes. */ + unsigned int operands; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The values are the + same as those for the struct powerpc_opcode flags field. */ + unsigned long flags; + + /* A format string to turn the macro into a normal instruction. + Each %N in the string is replaced with operand number N (zero + based). */ + const char *format; +}; + +extern const struct powerpc_macro powerpc_macros[]; +extern const int powerpc_num_macros; + +#endif /* PPC_H */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kdb/privinst.h linux.ac/arch/ppc64/kdb/privinst.h --- linux.vanilla/arch/ppc64/kdb/privinst.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kdb/privinst.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,103 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include + +#define GETREG(reg) \ + static inline unsigned long get_ ## reg (void) \ + { unsigned long ret; asm volatile ("mf" #reg " %0" : "=r" (ret) :); return ret; } + +#define SETREG(reg) \ + static inline void set_ ## reg (unsigned long val) \ + { asm volatile ("mt" #reg " %0" : : "r" (val)); } + +GETREG(msr) +SETREG(msr) +SETREG(msrd) +GETREG(cr) + +#define GSETSPR(n, name) \ + static inline long get_ ## name (void) \ + { long ret; asm volatile ("mfspr %0," #n : "=r" (ret) : ); return ret; } \ + static inline void set_ ## name (long val) \ + { asm volatile ("mtspr " #n ",%0" : : "r" (val)); } + +GSETSPR(0, mq) +GSETSPR(1, xer) +GSETSPR(4, rtcu) +GSETSPR(5, rtcl) +GSETSPR(8, lr) +GSETSPR(9, ctr) +GSETSPR(18, dsisr) +GSETSPR(19, dar) +GSETSPR(22, dec) +GSETSPR(25, sdr1) +GSETSPR(26, srr0) +GSETSPR(27, srr1) +GSETSPR(272, sprg0) +GSETSPR(273, sprg1) +GSETSPR(274, sprg2) +GSETSPR(275, sprg3) +GSETSPR(282, ear) +GSETSPR(287, pvr) +#ifndef CONFIG_8xx +GSETSPR(528, bat0u) +GSETSPR(529, bat0l) +GSETSPR(530, bat1u) +GSETSPR(531, bat1l) +GSETSPR(532, bat2u) +GSETSPR(533, bat2l) +GSETSPR(534, bat3u) +GSETSPR(535, bat3l) +GSETSPR(1008, hid0) +GSETSPR(1009, hid1) +GSETSPR(1010, iabr) +GSETSPR(1013, dabr) +GSETSPR(1023, pir) +#else +GSETSPR(144, cmpa) +GSETSPR(145, cmpb) +GSETSPR(146, cmpc) +GSETSPR(147, cmpd) +GSETSPR(158, ictrl) +#endif + +static inline int get_sr(int n) +{ + int ret; + +#if 0 +// DRENG does not assemble + asm (" mfsrin %0,%1" : "=r" (ret) : "r" (n << 28)); +#endif + return ret; +} + +static inline void set_sr(int n, int val) +{ +#if 0 +// DRENG does not assemble + asm ("mtsrin %0,%1" : : "r" (val), "r" (n << 28)); +#endif +} + +static inline void store_inst(void *p) +{ + asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p)); +} + +static inline void cflush(void *p) +{ + asm volatile ("dcbf 0,%0; icbi 0,%0" : : "r" (p)); +} + +static inline void cinval(void *p) +{ + asm volatile ("dcbi 0,%0; icbi 0,%0" : : "r" (p)); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kdb/start.c linux.ac/arch/ppc64/kdb/start.c --- linux.vanilla/arch/ppc64/kdb/start.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kdb/start.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,267 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SMP +#include +#endif + +#define TXRDY 0x20 +#define RXRDY 0x01 + +static volatile unsigned char *sccc, * volatile sccd; +extern void xmon_printf(const char *fmt, ...); + + +extern void *comport1; +void +xmon_map_scc(void) +{ + /* should already be mapped by the kernel boot */ + sccd = (volatile unsigned char *) (((unsigned long)comport1)); + sccc = (volatile unsigned char *) (((unsigned long)comport1)+5); +} + +static int scc_initialized = 0; + +void xmon_init_scc(void); + +int +xmon_write(void *handle, void *ptr, int nb) +{ + char *p = ptr; + int i, c, ct; + +#ifdef CONFIG_SMP + static unsigned long xmon_write_lock; + int lock_wait = 1000000; + int locked; + + while ((locked = test_and_set_bit(0, &xmon_write_lock)) != 0) + if (--lock_wait == 0) + break; +#endif + + if (!scc_initialized) + xmon_init_scc(); + ct = 0; + for (i = 0; i < nb; ++i) { + while ((*sccc & TXRDY) == 0) + /* spin for ready */; + c = p[i]; + if (c == '\n' && !ct) { + c = '\r'; + ct = 1; + --i; + } else { + ct = 0; + } + sccd[3] &= ~0x80; /* reset DLAB */ + eieio(); + *sccd = c; + eieio(); + } + +#ifdef CONFIG_SMP + if (!locked) + clear_bit(0, &xmon_write_lock); +#endif + return nb; +} + + +int +xmon_read(void *handle, void *ptr, int nb) +{ + char *p = ptr; + int i; + + if (!scc_initialized) + xmon_init_scc(); + for (i = 0; i < nb; ++i) { + while ((*sccc & RXRDY) == 0) + /* spin for ready */; + sccd[3] &= ~0x80; /* reset DLAB */ + eieio(); + *p++ = *sccd; + } + return i; +} + +int +xmon_read_poll(void) +{ + if ((*sccc & RXRDY) == 0) + return -1; + sccd[3] &= ~0x80; /* reset DLAB */ + eieio(); + return *sccd; +} + +void +xmon_init_scc() +{ + sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */ + sccd[0] = 12; eieio(); /* DLL = 9600 baud */ + sccd[1] = 0; eieio(); + sccd[2] = 0; eieio(); /* FCR = 0 */ + sccd[3] = 3; eieio(); /* LCR = 8N1 */ + sccd[1] = 0; eieio(); /* IER = 0 */ + scc_initialized = 1; +} + + +void *xmon_stdin; +void *xmon_stdout; +void *xmon_stderr; + +void +xmon_init(void) +{ +} + +int +xmon_putc(int c, void *f) +{ + char ch = c; + + if (c == '\n') + xmon_putc('\r', f); + return xmon_write(f, &ch, 1) == 1? c: -1; +} + +int +xmon_putchar(int c) +{ + return xmon_putc(c, xmon_stdout); +} + +int +xmon_fputs(char *str, void *f) +{ + int n = strlen(str); + + return xmon_write(f, str, n) == n? 0: -1; +} + +int +xmon_readchar(void) +{ + char ch; + + for (;;) { + switch (xmon_read(xmon_stdin, &ch, 1)) { + case 1: + return ch; + case -1: + xmon_printf("read(stdin) returned -1\r\n", 0, 0); + return -1; + } + } +} + +static char line[256]; +static char *lineptr; +static int lineleft; + + +int +xmon_getchar(void) +{ + int c; + + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = xmon_readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + xmon_putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + xmon_putchar('\a'); + else { + xmon_putchar(c); + *lineptr++ = c; + } + } + } + lineleft = lineptr - line; + lineptr = line; + } + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; +} + +char * +xmon_fgets(char *str, int nb, void *f) +{ + char *p; + int c; + + for (p = str; p < str + nb - 1; ) { + c = xmon_getchar(); + if (c == -1) { + if (p == str) + return 0; + break; + } + *p++ = c; + if (c == '\n') + break; + } + *p = 0; + return str; +} + +void +xmon_enter(void) +{ +#ifdef CONFIG_ADB_PMU + pmu_suspend(); +#endif +} + +void +xmon_leave(void) +{ +#ifdef CONFIG_ADB_PMU + pmu_resume(); +#endif +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kdb/subr_prf.c linux.ac/arch/ppc64/kdb/subr_prf.c --- linux.vanilla/arch/ppc64/kdb/subr_prf.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kdb/subr_prf.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,50 @@ +/* + * Written by Cort Dougan to replace the version originally used + * by Paul Mackerras, which came from NetBSD and thus had copyright + * conflicts with Linux. + * + * This file makes liberal use of the standard linux utility + * routines to reduce the size of the binary. We assume we can + * trust some parts of Linux inside the debugger. + * -- Cort (cort@cs.nmt.edu) + * + * Copyright (C) 1999 Cort Dougan. + */ + +#include +#include +#include +#include "nonstdio.h" + +extern int xmon_write(void *, void *, int); + +void +xmon_vfprintf(void *f, const char *fmt, va_list ap) +{ + static char xmon_buf[2048]; + int n; + + n = vsprintf(xmon_buf, fmt, ap); + xmon_write(f, xmon_buf, n); +} + +void +xmon_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xmon_vfprintf(stdout, fmt, ap); + va_end(ap); +} + +void +xmon_fprintf(void *f, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xmon_vfprintf(f, fmt, ap); + va_end(ap); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/HvCall.c linux.ac/arch/ppc64/kernel/HvCall.c --- linux.vanilla/arch/ppc64/kernel/HvCall.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/HvCall.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,121 @@ +/* + * HvCall.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. + */ + +#include +#include +#include +#include +#include +#include +#include +#ifndef _HVCALLSC_H +#include +#endif +#include + +#ifndef _HVTYPES_H +#include +#endif + + +/*===================================================================== + * Note that this call takes at MOST one page worth of data + */ +int HvCall_readLogBuffer(HvLpIndex lpIndex, void *buffer, u64 bufLen) +{ + struct HvLpBufferList *bufList; + u64 bytesLeft = bufLen; + u64 leftThisPage; + u64 curPtr = virt_to_absolute( (unsigned long) buffer ); + u64 retVal; + int npages; + int i; + + npages = 0; + while (bytesLeft) + { + npages++; + leftThisPage = ((curPtr & PAGE_MASK) + PAGE_SIZE) - curPtr; + + if (leftThisPage > bytesLeft) + bytesLeft = 0; + else + bytesLeft -= leftThisPage; + + curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE; + } + + if (npages == 0) + return 0; + + bufList = (struct HvLpBufferList *)kmalloc(npages * sizeof(struct HvLpBufferList), GFP_ATOMIC); + bytesLeft = bufLen; + curPtr = virt_to_absolute( (unsigned long) buffer ); + for(i=0; i bytesLeft) + { + bufList[i].len = bytesLeft; + bytesLeft = 0; + } + else + { + bufList[i].len = leftThisPage; + bytesLeft -= leftThisPage; + } + + curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE; + } + + + retVal = HvCall3(HvCallBaseReadLogBuffer,lpIndex, virt_to_absolute((unsigned long)bufList), bufLen); + + kfree(bufList); + + return (int)retVal; +} + +/*===================================================================== + */ +void HvCall_writeLogBuffer(const void *buffer, u64 bufLen) +{ + struct HvLpBufferList bufList; + u64 bytesLeft = bufLen; + u64 leftThisPage; + u64 curPtr = virt_to_absolute( (unsigned long) buffer ); + + while (bytesLeft) + { + bufList.addr = curPtr; + + leftThisPage = ((curPtr & PAGE_MASK) + PAGE_SIZE) - curPtr; + + if (leftThisPage > bytesLeft) + { + bufList.len = bytesLeft; + bytesLeft = 0; + } + else + { + bufList.len = leftThisPage; + bytesLeft -= leftThisPage; + } + + curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE; + } + + + HvCall2(HvCallBaseWriteLogBuffer, virt_to_absolute((unsigned long)&bufList), bufLen); + +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/HvLpConfig.c linux.ac/arch/ppc64/kernel/HvLpConfig.c --- linux.vanilla/arch/ppc64/kernel/HvLpConfig.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/HvLpConfig.c Wed Oct 10 01:47:34 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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/HvLpEvent.c linux.ac/arch/ppc64/kernel/HvLpEvent.c --- linux.vanilla/arch/ppc64/kernel/HvLpEvent.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/HvLpEvent.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,77 @@ +/* + * Copyright 2001 Mike Corrigan 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. + */ +#include +#include +#include +#include +#include +#include + +/* Array of LpEvent handler functions */ +LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes]; +unsigned lpEventHandlerPaths[HvLpEvent_Type_NumTypes]; + +/* Register a handler for an LpEvent type */ + +int HvLpEvent_registerHandler( HvLpEvent_Type eventType, LpEventHandler handler ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes ) { + lpEventHandler[eventType] = handler; + rc = 0; + } + return rc; + +} + +int HvLpEvent_unregisterHandler( HvLpEvent_Type eventType ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes ) { + if ( !lpEventHandlerPaths[eventType] ) { + lpEventHandler[eventType] = NULL; + rc = 0; + } + } + return rc; +} + +/* (lpIndex is the partition index of the target partition. + * needed only for VirtualIo, VirtualLan and SessionMgr. Zero + * indicates to use our partition index - for the other types) + */ +int HvLpEvent_openPath( HvLpEvent_Type eventType, HvLpIndex lpIndex ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes && + lpEventHandler[eventType] ) { + if ( lpIndex == 0 ) + lpIndex = itLpNaca.xLpIndex; + HvCallEvent_openLpEventPath( lpIndex, eventType ); + ++lpEventHandlerPaths[eventType]; + rc = 0; + } + return rc; +} + +int HvLpEvent_closePath( HvLpEvent_Type eventType, HvLpIndex lpIndex ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes && + lpEventHandler[eventType] && + lpEventHandlerPaths[eventType] ) { + if ( lpIndex == 0 ) + lpIndex = itLpNaca.xLpIndex; + HvCallEvent_closeLpEventPath( lpIndex, eventType ); + --lpEventHandlerPaths[eventType]; + rc = 0; + } + return rc; +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/ItLpQueue.c linux.ac/arch/ppc64/kernel/ItLpQueue.c --- linux.vanilla/arch/ppc64/kernel/ItLpQueue.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/ItLpQueue.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,123 @@ +/* + * ItLpQueue.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. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Array of LpEvent handler functions */ +extern LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes]; + +struct HvLpEvent * ItLpQueue_getNextLpEvent( struct ItLpQueue * lpQueue ) +{ + struct HvLpEvent * nextLpEvent = + (struct HvLpEvent *)lpQueue->xSlicCurEventPtr; + if ( nextLpEvent->xFlags.xValid ) { + /* Set pointer to next potential event */ + lpQueue->xSlicCurEventPtr += ((nextLpEvent->xSizeMinus1 + + LpEventAlign ) / + LpEventAlign ) * + LpEventAlign; + /* Wrap to beginning if no room at end */ + if (lpQueue->xSlicCurEventPtr > lpQueue->xSlicLastValidEventPtr) + lpQueue->xSlicCurEventPtr = lpQueue->xSlicEventStackPtr; + } + else + nextLpEvent = NULL; + + return nextLpEvent; +} + +int ItLpQueue_isLpIntPending( struct ItLpQueue * lpQueue ) +{ + int retval = 0; + struct HvLpEvent * nextLpEvent; + if ( lpQueue ) { + nextLpEvent = (struct HvLpEvent *)lpQueue->xSlicCurEventPtr; + retval = nextLpEvent->xFlags.xValid | lpQueue->xPlicOverflowIntPending; + } + return retval; +} + +void ItLpQueue_clearValid( struct HvLpEvent * event ) +{ + /* Clear the valid bit of the event + * Also clear bits within this event that might + * look like valid bits (on 64-byte boundaries) + */ + unsigned extra = (( event->xSizeMinus1 + LpEventAlign ) / + LpEventAlign ) - 1; + switch ( extra ) { + case 3: + ((struct HvLpEvent*)((char*)event+3*LpEventAlign))->xFlags.xValid=0; + case 2: + ((struct HvLpEvent*)((char*)event+2*LpEventAlign))->xFlags.xValid=0; + case 1: + ((struct HvLpEvent*)((char*)event+1*LpEventAlign))->xFlags.xValid=0; + case 0: + ; + } + mb(); + event->xFlags.xValid = 0; +} + +unsigned ItLpQueue_process( struct ItLpQueue * lpQueue, struct pt_regs *regs ) +{ + unsigned numIntsProcessed = 0; + struct HvLpEvent * nextLpEvent; + + for (;;) { + nextLpEvent = ItLpQueue_getNextLpEvent( lpQueue ); + if ( nextLpEvent ) { + /* Count events to return to caller + * and count processed events in lpQueue + */ + ++numIntsProcessed; + lpQueue->xLpIntCount++; + /* Call appropriate handler here, passing + * a pointer to the LpEvent. The handler + * must make a copy of the LpEvent if it + * needs it in a bottom half. (perhaps for + * an ACK) + * + * Handlers are responsible for ACK processing + * + * The Hypervisor guarantees that LpEvents will + * only be delivered with types that we have + * registered for, so no type check is necessary + * here! + */ + if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes ) + lpQueue->xLpIntCountByType[nextLpEvent->xType]++; + if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes && + lpEventHandler[nextLpEvent->xType] ) + lpEventHandler[nextLpEvent->xType](nextLpEvent, regs); + else + printk(KERN_INFO "Unexpected Lp Event type=%d\n", nextLpEvent->xType ); + + ItLpQueue_clearValid( nextLpEvent ); + } + else /* No more valid events + * If overflow events are pending + * process them + */ + if ( lpQueue->xPlicOverflowIntPending ) { + HvCallEvent_getOverflowLpEvents( + lpQueue->xIndex); + } + else /* If nothing left then we are done */ + break; + } + return numIntsProcessed; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/LparData.c linux.ac/arch/ppc64/kernel/LparData.c --- linux.vanilla/arch/ppc64/kernel/LparData.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/LparData.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,249 @@ +/* + * Copyright 2001 Mike Corrigan, 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. + */ +#define __KERNEL__ 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char _start_boltedStacks[]; + +/* The LparMap data is now located at offset 0x6000 in head.S + * It was put there so that the HvReleaseData could address it + * with a 32-bit offset as required by the iSeries hypervisor + * + * The Naca has a pointer to the ItVpdAreas. The hypervisor finds + * the Naca via the HvReleaseData area. The HvReleaseData has the + * offset into the Naca of the pointer to the ItVpdAreas. + */ + +extern struct ItVpdAreas itVpdAreas; + +/* The LpQueue is used to pass event data from the hypervisor to + * the partition. This is where I/O interrupt events are communicated. + * The ItLpQueue must be initialized (even though only to all zeros) + * If it were uninitialized (in .bss) it would get zeroed after the + * kernel gets control. The hypervisor will have filled in some fields + * before the kernel gets control. By initializing it we keep it out + * of the .bss + */ + +struct ItLpQueue xItLpQueue = {}; + + +/* The HvReleaseData is the root of the information shared between + * the hypervisor and Linux. + */ + +struct HvReleaseData + hvReleaseData = { 0xc8a5d9c4, /* desc = "HvRD" ebcdic */ + sizeof(struct HvReleaseData), + offsetof(struct Naca, xItVpdAreas), + (struct Naca *)(KERNELBASE+0x4000), /* 64-bit Naca address */ + 0x6000, /* offset of LparMap within loadarea (see head.S) */ + 0, + 1, /* tags inactive */ + 0, /* 64 bit */ + 0, /* shared processors */ + 0, /* HMT allowed */ + 6, /* TEMP: This allows non-GA driver */ + 3, /* We are v5r1m0 */ + 3, /* Min supported PLIC = v5r1m0 */ + 3, /* Min usuable PLIC = v5r1m0 */ + { 0xd3, 0x89, 0x95, 0xa4, /* "Linux 2.4 "*/ + 0xa7, 0x40, 0xf2, 0x4b, + 0xf4, 0x4b, 0xf6, 0xf4 }, + {0} + }; + +extern void SystemReset_Iseries(void); +extern void MachineCheck_Iseries(void); +extern void DataAccess_Iseries(void); +extern void InstructionAccess_Iseries(void); +extern void HardwareInterrupt_Iseries(void); +extern void Alignment_Iseries(void); +extern void ProgramCheck_Iseries(void); +extern void FPUnavailable_Iseries(void); +extern void Decrementer_Iseries(void); +extern void Trap_0a_Iseries(void); +extern void Trap_0b_Iseries(void); +extern void SystemCall_Iseries(void); +extern void SingleStep_Iseries(void); +extern void Trap_0e_Iseries(void); +extern void PerformanceMonitor_Iseries(void); + +struct ItLpNaca itLpNaca = { 0xd397d581, /* desc = "LpNa" ebcdic */ + 0x0400, /* size of ItLpNaca */ + 0x0300, 19, /* offset to int array, # ents */ + 0, 0, 0, /* Part # of primary, serv, me */ + 0, 0x100, /* # of LP queues, offset */ + 0, 0, 0, /* Piranha stuff */ + { 0,0,0,0,0 }, /* reserved */ + 0,0,0,0,0,0,0, /* stuff */ + { 0,0,0,0,0 }, /* reserved */ + 0, /* reserved */ + 0, /* VRM index of PLIC */ + 0, 0, /* min supported, compat SLIC */ + 0, /* 64-bit addr of load area */ + 0, /* chunks for load area */ + 0, 0, /* PASE mask, seg table */ + { 0 }, /* 64 reserved bytes */ + { 0 }, /* 128 reserved bytes */ + { 0 }, /* Old LP Queue */ + { 0 }, /* 384 reserved bytes */ + { + (u64)SystemReset_Iseries, /* 0x100 System Reset */ + (u64)MachineCheck_Iseries, /* 0x200 Machine Check */ + (u64)DataAccess_Iseries, /* 0x300 Data Access */ + (u64)InstructionAccess_Iseries, /* 0x400 Instruction Access */ + (u64)HardwareInterrupt_Iseries, /* 0x500 External */ + (u64)Alignment_Iseries, /* 0x600 Alignment */ + (u64)ProgramCheck_Iseries, /* 0x700 Program Check */ + (u64)FPUnavailable_Iseries, /* 0x800 FP Unavailable */ + (u64)Decrementer_Iseries, /* 0x900 Decrementer */ + (u64)Trap_0a_Iseries, /* 0xa00 Trap 0A */ + (u64)Trap_0b_Iseries, /* 0xb00 Trap 0B */ + (u64)SystemCall_Iseries, /* 0xc00 System Call */ + (u64)SingleStep_Iseries, /* 0xd00 Single Step */ + (u64)Trap_0e_Iseries, /* 0xe00 Trap 0E */ + (u64)PerformanceMonitor_Iseries,/* 0xf00 Performance Monitor */ + 0xc0001000, /* int 0x1000 */ + 0xc0001010, /* int 0x1010 */ + 0xc0001020, /* int 0x1020 CPU ctls */ + (u64)HardwareInterrupt_Iseries /* SC Ret Hdlr */ + /* 0x380 D-SLB */ + /* 0x480 I-SLB */ + } + }; + +struct ItIplParmsReal xItIplParmsReal = {}; + +struct IoHriProcessorVpd xIoHriProcessorVpd[maxProcessors] = { + { + xInstCacheOperandSize: 32, + xDataCacheOperandSize: 32, + xProcFreq: 50000000, + xTimeBaseFreq: 50000000, + xPVR: 0x3600 + } +}; + + +u64 xMsVpd[3400] = {}; /* Space for Main Store Vpd 27,200 bytes */ + +u64 xRecoveryLogBuffer[32] = {}; /* Space for Recovery Log Buffer */ + +struct SpCommArea xSpCommArea = { + 0xE2D7C3C2, + 1, + {0}, + 0, 0, 0, 0, {0} +}; + +struct ItVpdAreas itVpdAreas = { + 0xc9a3e5c1, /* "ItVA" */ + sizeof( struct ItVpdAreas ), + 0, 0, + 26, /* # VPD array entries */ + 10, /* # DMA array entries */ + maxProcessors*2, maxProcessors, /* Max logical, physical procs */ + offsetof(struct ItVpdAreas,xPlicDmaToks),/* offset to DMA toks */ + offsetof(struct ItVpdAreas,xSlicVpdAdrs),/* offset to VPD addrs */ + offsetof(struct ItVpdAreas,xPlicDmaLens),/* offset to DMA lens */ + offsetof(struct ItVpdAreas,xSlicVpdLens),/* offset to VPD lens */ + 0, /* max slot labels */ + 1, /* max LP queues */ + {0}, {0}, /* reserved */ + {0}, /* DMA lengths */ + {0}, /* DMA tokens */ + { /* VPD lengths */ + 0,0,0,0, /* 0 - 3 */ + sizeof(struct Paca), /* 4 length of Paca */ + 0, /* 5 */ + sizeof(struct ItIplParmsReal),/* 6 length of IPL parms */ + 26992, /* 7 length of MS VPD */ + 0, /* 8 */ + sizeof(struct ItLpNaca),/* 9 length of LP Naca */ + sizeof(struct SpCommArea), /* 10 */ + 256, /* 11 length of Recovery Log Buf */ + 0,0,0,0, /* 12 - 15 */ + sizeof(struct IoHriProcessorVpd),/* 16 length of Proc Vpd */ + 0,0,0,0,0,0, /* 17 - 22 */ + sizeof(struct ItLpQueue),/* 23 length of Lp Queue */ + 0,0 /* 24 - 25 */ + }, + { /* VPD addresses */ + 0,0,0,0, /* 0 - 3 */ + &xPaca[0], /* 4 first Paca */ + 0, /* 5 */ + &xItIplParmsReal, /* 6 IPL parms */ + &xMsVpd, /* 7 MS Vpd */ + 0, /* 8 */ + &itLpNaca, /* 9 LpNaca */ + &xSpCommArea, /* 10 */ + &xRecoveryLogBuffer, /* 11 Recovery Log Buffer */ + 0,0,0,0, /* 12 - 15 */ + &xIoHriProcessorVpd, /* 16 Proc Vpd */ + 0,0,0,0,0,0, /* 17 - 22 */ + &xItLpQueue, /* 23 Lp Queue */ + 0,0 + } + +}; + + +/* Data area used in flush_hash_page */ +long long flush_hash_page_hpte[2]; + +struct msChunks msChunks = {0, 0, 0, 0, NULL}; + +/* Depending on whether this is called from iSeries or pSeries setup + * code, the location of the msChunks struct may or may not have + * to be reloc'd, so we force the caller to do that for us by passing + * in a pointer to the structure. + */ +unsigned long +msChunks_alloc(unsigned long mem, unsigned long num_chunks, unsigned long chunk_size) +{ + unsigned long offset = reloc_offset(); + struct msChunks *_msChunks = PTRRELOC(&msChunks); + + _msChunks->num_chunks = num_chunks; + _msChunks->chunk_size = chunk_size; + _msChunks->chunk_shift = __ilog2(chunk_size); + _msChunks->chunk_mask = (1UL<<_msChunks->chunk_shift)-1; + + mem = _ALIGN(mem, sizeof(msChunks_entry)); + _msChunks->abs = (msChunks_entry *)(mem + offset); + mem += num_chunks * sizeof(msChunks_entry); + + return mem; +} + + + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/Makefile linux.ac/arch/ppc64/kernel/Makefile --- linux.vanilla/arch/ppc64/kernel/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/Makefile Wed Oct 10 01:47:34 2001 @@ -0,0 +1,75 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +USE_STANDARD_AS_RULE := true + +EXTRA_CFLAGS = -mno-minimal-toc + +KHEAD := head.o + +all: $(KHEAD) kernel.o + +O_TARGET := kernel.o + +export-objs := ppc_ksyms.o setup.o + +obj-y := ppc_ksyms.o setup.o entry.o traps.o irq.o idle.o \ + time.o process.o signal.o syscalls.o misc.o ptrace.o \ + align.o semaphore.o bitops.o stab.o htab.o pacaData.o \ + LparData.o udbg.o binfmt_elf32.o sys_ppc32.o sys32.o \ + ioctl32.o ptrace32.o signal32.o open_pic.o xics.o \ + pmc.o mf_proc.o proc_pmc.o proc_pcifr.o iSeries_setup.o \ + ItLpQueue.o hvCall.o mf.o viopath.o HvLpEvent.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 +else +obj-$(CONFIG_PCI) += pci_dma.o +endif + +obj-$(CONFIG_KGDB) += ppc-stub.o + +obj-$(CONFIG_SMP) += smp.o + +# tibit: for matrox_init2() +ifeq ($(CONFIG_NVRAM),y) + obj-$(CONFIG_NVRAM) += pmac_nvram.o +endif + +ifeq ($(CONFIG_ALL_PPC),y) + obj-y += prom.o lmb.o rtas.o rtas-eventscan.o rtas-proc.o chrp_setup.o \ + chrp_time.o pSeries_pci.o i8259.o +endif + +include $(TOPDIR)/Rules.make + +# +# This is just to get the dependencies... +# + +head.o: head.S ppc_defs.h + +ppc_defs.h: mk_defs.c ppc_defs.head \ + $(TOPDIR)/include/asm/mmu.h \ + $(TOPDIR)/include/asm/processor.h \ + $(TOPDIR)/include/asm/pgtable.h \ + $(TOPDIR)/include/asm/ptrace.h + $(CC) $(CFLAGS) -S mk_defs.c + cp ppc_defs.head ppc_defs.h +# for bk, this way we can write to the file even if it's not checked out + chmod u+w ppc_defs.h + grep '^#define' mk_defs.s >> ppc_defs.h + rm mk_defs.s + +checks: checks.c + $(HOSTCC) -I$(HPATH) $(HOSTCFLAGS) -D__KERNEL__ -fno-builtin -o checks checks.c + ./checks diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/align.c linux.ac/arch/ppc64/kernel/align.c --- linux.vanilla/arch/ppc64/kernel/align.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/align.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,368 @@ +/* + * align.c - handle alignment exceptions for the Power PC. + * + * Copyright (c) 1996 Paul Mackerras + * Copyright (c) 1998-1999 TiVo, Inc. + * PowerPC 403GCX modifications. + * Copyright (c) 1999 Grant Erickson + * PowerPC 403GCX/405GP modifications. + * Copyright (c) 2001 PPC64 team, 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct aligninfo { + unsigned char len; + unsigned char flags; +}; + +#define OPCD(inst) (((inst) & 0xFC000000) >> 26) +#define RS(inst) (((inst) & 0x03E00000) >> 21) +#define RA(inst) (((inst) & 0x001F0000) >> 16) +#define IS_DFORM(code) ((code) >= 32 && (code) <= 47) + +#define INVALID { 0, 0 } + +#define LD 1 /* load */ +#define ST 2 /* store */ +#define SE 4 /* sign-extend value */ +#define F 8 /* to/from fp regs */ +#define U 0x10 /* update index register */ +#define M 0x20 /* multiple load/store */ +#define S 0x40 /* single-precision fp, or byte-swap value */ +#define HARD 0x80 /* string, stwcx. */ +#define D 0x100 /* double-word load/store */ + +#define DCBZ 0x5f /* 8xx/82xx dcbz faults when cache not enabled */ + +/* + * The PowerPC stores certain bits of the instruction that caused the + * alignment exception in the DSISR register. This array maps those + * bits to information about the operand length and what the + * instruction would do. + */ +static struct aligninfo aligninfo[128] = { + { 4, LD }, /* 00 0 0000: lwz / lwarx */ + INVALID, /* 00 0 0001 */ + { 4, ST }, /* 00 0 0010: stw */ + INVALID, /* 00 0 0011 */ + { 2, LD }, /* 00 0 0100: lhz */ + { 2, LD+SE }, /* 00 0 0101: lha */ + { 2, ST }, /* 00 0 0110: sth */ + { 4, LD+M }, /* 00 0 0111: lmw */ + { 4, LD+F+S }, /* 00 0 1000: lfs */ + { 8, LD+F }, /* 00 0 1001: lfd */ + { 4, ST+F+S }, /* 00 0 1010: stfs */ + { 8, ST+F }, /* 00 0 1011: stfd */ + INVALID, /* 00 0 1100 */ + { 8, LD }, /* 00 0 1101: ld */ + INVALID, /* 00 0 1110 */ + { 8, ST }, /* 00 0 1111: std */ + { 4, LD+U }, /* 00 1 0000: lwzu */ + INVALID, /* 00 1 0001 */ + { 4, ST+U }, /* 00 1 0010: stwu */ + INVALID, /* 00 1 0011 */ + { 2, LD+U }, /* 00 1 0100: lhzu */ + { 2, LD+SE+U }, /* 00 1 0101: lhau */ + { 2, ST+U }, /* 00 1 0110: sthu */ + { 4, ST+M }, /* 00 1 0111: stmw */ + { 4, LD+F+S+U }, /* 00 1 1000: lfsu */ + { 8, LD+F+U }, /* 00 1 1001: lfdu */ + { 4, ST+F+S+U }, /* 00 1 1010: stfsu */ + { 8, ST+F+U }, /* 00 1 1011: stfdu */ + INVALID, /* 00 1 1100 */ + { 8, ST }, /* 00 1 1101: std */ + INVALID, /* 00 1 1110 */ + INVALID, /* 00 1 1111 */ + { 8, LD }, /* 01 0 0000: ldx */ + INVALID, /* 01 0 0001 */ + { 8, ST }, /* 01 0 0010: stdx */ + INVALID, /* 01 0 0011 */ + INVALID, /* 01 0 0100 */ + INVALID, /* 01 0 0101: lwax?? */ + INVALID, /* 01 0 0110 */ + INVALID, /* 01 0 0111 */ + { 0, LD+HARD }, /* 01 0 1000: lswx */ + { 0, LD+HARD }, /* 01 0 1001: lswi */ + { 0, ST+HARD }, /* 01 0 1010: stswx */ + { 0, ST+HARD }, /* 01 0 1011: stswi */ + INVALID, /* 01 0 1100 */ + { 8, LD+U }, /* 01 0 1101: ldu */ + INVALID, /* 01 0 1110 */ + { 8, ST+U }, /* 01 0 1111: stdu */ + { 8, LD+U }, /* 01 1 0000: ldux */ + INVALID, /* 01 1 0001 */ + { 8, ST+U }, /* 01 1 0010: stdux */ + INVALID, /* 01 1 0011 */ + INVALID, /* 01 1 0100 */ + INVALID, /* 01 1 0101: lwaux?? */ + INVALID, /* 01 1 0110 */ + INVALID, /* 01 1 0111 */ + INVALID, /* 01 1 1000 */ + INVALID, /* 01 1 1001 */ + INVALID, /* 01 1 1010 */ + INVALID, /* 01 1 1011 */ + INVALID, /* 01 1 1100 */ + INVALID, /* 01 1 1101 */ + INVALID, /* 01 1 1110 */ + INVALID, /* 01 1 1111 */ + INVALID, /* 10 0 0000 */ + INVALID, /* 10 0 0001 */ + { 0, ST+HARD }, /* 10 0 0010: stwcx. */ + INVALID, /* 10 0 0011 */ + INVALID, /* 10 0 0100 */ + INVALID, /* 10 0 0101 */ + INVALID, /* 10 0 0110 */ + INVALID, /* 10 0 0111 */ + { 4, LD+S }, /* 10 0 1000: lwbrx */ + INVALID, /* 10 0 1001 */ + { 4, ST+S }, /* 10 0 1010: stwbrx */ + INVALID, /* 10 0 1011 */ + { 2, LD+S }, /* 10 0 1100: lhbrx */ + INVALID, /* 10 0 1101 */ + { 2, ST+S }, /* 10 0 1110: sthbrx */ + INVALID, /* 10 0 1111 */ + INVALID, /* 10 1 0000 */ + INVALID, /* 10 1 0001 */ + INVALID, /* 10 1 0010 */ + INVALID, /* 10 1 0011 */ + INVALID, /* 10 1 0100 */ + INVALID, /* 10 1 0101 */ + INVALID, /* 10 1 0110 */ + INVALID, /* 10 1 0111 */ + INVALID, /* 10 1 1000 */ + INVALID, /* 10 1 1001 */ + INVALID, /* 10 1 1010 */ + INVALID, /* 10 1 1011 */ + INVALID, /* 10 1 1100 */ + INVALID, /* 10 1 1101 */ + INVALID, /* 10 1 1110 */ + { 0, ST+HARD }, /* 10 1 1111: dcbz */ + { 4, LD }, /* 11 0 0000: lwzx */ + INVALID, /* 11 0 0001 */ + { 4, ST }, /* 11 0 0010: stwx */ + INVALID, /* 11 0 0011 */ + { 2, LD }, /* 11 0 0100: lhzx */ + { 2, LD+SE }, /* 11 0 0101: lhax */ + { 2, ST }, /* 11 0 0110: sthx */ + INVALID, /* 11 0 0111 */ + { 4, LD+F+S }, /* 11 0 1000: lfsx */ + { 8, LD+F }, /* 11 0 1001: lfdx */ + { 4, ST+F+S }, /* 11 0 1010: stfsx */ + { 8, ST+F }, /* 11 0 1011: stfdx */ + INVALID, /* 11 0 1100 */ + INVALID, /* 11 0 1101 */ + INVALID, /* 11 0 1110 */ + INVALID, /* 11 0 1111 */ + { 4, LD+U }, /* 11 1 0000: lwzux */ + INVALID, /* 11 1 0001 */ + { 4, ST+U }, /* 11 1 0010: stwux */ + INVALID, /* 11 1 0011 */ + { 2, LD+U }, /* 11 1 0100: lhzux */ + { 2, LD+SE+U }, /* 11 1 0101: lhaux */ + { 2, ST+U }, /* 11 1 0110: sthux */ + INVALID, /* 11 1 0111 */ + { 4, LD+F+S+U }, /* 11 1 1000: lfsux */ + { 8, LD+F+U }, /* 11 1 1001: lfdux */ + { 4, ST+F+S+U }, /* 11 1 1010: stfsux */ + { 8, ST+F+U }, /* 11 1 1011: stfdux */ + INVALID, /* 11 1 1100 */ + INVALID, /* 11 1 1101 */ + INVALID, /* 11 1 1110 */ + INVALID, /* 11 1 1111 */ +}; + +#define SWAP(a, b) (t = (a), (a) = (b), (b) = t) + +int +fix_alignment(struct pt_regs *regs) +{ + int instr, nb, flags; + int opcode, f1, f2, f3; + int i, t; + int reg, areg; + unsigned char *addr; + union { + int l; + long ll; + float f; + double d; + unsigned char v[8]; + } data; + + if (__is_processor(PV_POWER4)) { + /* + * The POWER4 has a DSISR register but doesn't set it on + * an alignment fault. -- paulus + */ + + instr = *((unsigned int *)regs->nip); + opcode = OPCD(instr); + reg = RS(instr); + areg = RA(instr); + + if (IS_DFORM(opcode)) { + f1 = 0; + f2 = (instr & 0x04000000) >> 26; + f3 = (instr & 0x78000000) >> 27; + } else { + f1 = (instr & 0x00000006) >> 1; + f2 = (instr & 0x00000040) >> 6; + f3 = (instr & 0x00000780) >> 7; + } + + instr = ((f1 << 5) | (f2 << 4) | f3); + } else { + reg = (regs->dsisr >> 5) & 0x1f; /* source/dest register */ + areg = regs->dsisr & 0x1f; /* register to update */ + instr = (regs->dsisr >> 10) & 0x7f; + instr |= (regs->dsisr >> 13) & 0x60; + } + + nb = aligninfo[instr].len; + if (nb == 0) { + long *p; + int i; + + if (instr != DCBZ) + return 0; /* too hard or invalid instruction */ + /* + * The dcbz (data cache block zero) instruction + * gives an alignment fault if used on non-cacheable + * memory. We handle the fault mainly for the + * case when we are running with the cache disabled + * for debugging. + */ + p = (long *) (regs->dar & -L1_CACHE_BYTES); + for (i = 0; i < L1_CACHE_BYTES / sizeof(long); ++i) + p[i] = 0; + return 1; + } + + flags = aligninfo[instr].flags; + + /* For the 4xx-family processors, the 'dar' field of the + * pt_regs structure is overloaded and is really from the DEAR. + */ + + addr = (unsigned char *)regs->dar; + + /* Verify the address of the operand */ + if (user_mode(regs)) { + if (verify_area((flags & ST? VERIFY_WRITE: VERIFY_READ), addr, nb)) + return -EFAULT; /* bad address */ + } + + if ((flags & F) && (regs->msr & MSR_FP)) + giveup_fpu(current); + if (flags & M) + return 0; /* too hard for now */ + + /* If we read the operand, copy it in */ + if (flags & LD) { + if (nb == 2) { + data.v[0] = data.v[1] = 0; + if (__get_user(data.v[2], addr) + || __get_user(data.v[3], addr+1)) + return -EFAULT; + } else { + for (i = 0; i < nb; ++i) + if (__get_user(data.v[i], addr+i)) + return -EFAULT; + } + } + /* Unfortunately D (== 0x100) doesn't fit in the aligninfo[n].flags + field. So synthesize it here. */ + if ((flags & F) == 0 && nb == 8) + flags |= D; + + switch (flags & ~U) { + case LD+SE: + if (data.v[2] >= 0x80) + data.v[0] = data.v[1] = -1; + /* fall through */ + case LD: + regs->gpr[reg] = data.l; + break; + case LD+D: + regs->gpr[reg] = data.ll; + break; + case LD+S: + if (nb == 2) { + SWAP(data.v[2], data.v[3]); + } else { + SWAP(data.v[0], data.v[3]); + SWAP(data.v[1], data.v[2]); + } + regs->gpr[reg] = data.l; + break; + case ST: + data.l = regs->gpr[reg]; + break; + case ST+D: + data.ll = regs->gpr[reg]; + break; + case ST+S: + data.l = regs->gpr[reg]; + if (nb == 2) { + SWAP(data.v[2], data.v[3]); + } else { + SWAP(data.v[0], data.v[3]); + SWAP(data.v[1], data.v[2]); + } + break; + case LD+F: + current->thread.fpr[reg] = data.d; + break; + case ST+F: + data.d = current->thread.fpr[reg]; + break; + /* these require some floating point conversions... */ + /* we'd like to use the assignment, but we have to compile + * the kernel with -msoft-float so it doesn't use the + * fp regs for copying 8-byte objects. */ + case LD+F+S: + enable_kernel_fp(); + cvt_fd(&data.f, ¤t->thread.fpr[reg], ¤t->thread.fpscr); + /* current->thread.fpr[reg] = data.f; */ + break; + case ST+F+S: + enable_kernel_fp(); + cvt_df(¤t->thread.fpr[reg], &data.f, ¤t->thread.fpscr); + /* data.f = current->thread.fpr[reg]; */ + break; + default: + printk("align: can't handle flags=%x\n", flags); + return 0; + } + + if (flags & ST) { + if (nb == 2) { + if (__put_user(data.v[2], addr) + || __put_user(data.v[3], addr+1)) + return -EFAULT; + } else { + for (i = 0; i < nb; ++i) + if (__put_user(data.v[i], addr+i)) + return -EFAULT; + } + } + + if (flags & U) { + regs->gpr[areg] = regs->dar; + } + + return 1; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/binfmt_elf32.c linux.ac/arch/ppc64/kernel/binfmt_elf32.c --- linux.vanilla/arch/ppc64/kernel/binfmt_elf32.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/binfmt_elf32.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,112 @@ +/* + * binfmt_elf32.c: Support 32-bit PPC ELF binaries on Power3 and followons. + * based on the SPARC64 version. + * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com) + * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) + * + * Copyright (C) 2000,2001 Ken Aaker (kdaaker@rchland.vnet.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. + */ + +#define ELF_ARCH EM_PPC +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2MSB; + +#include +#include +#include +#include + +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +#define elf_prstatus elf_prstatus32 +struct elf_prstatus32 +{ + struct elf_siginfo pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + unsigned int pr_sigpend; /* Set of pending signals */ + unsigned int pr_sighold; /* Set of held signals */ + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct timeval32 pr_utime; /* User time */ + struct timeval32 pr_stime; /* System time */ + struct timeval32 pr_cutime; /* Cumulative user time */ + struct timeval32 pr_cstime; /* Cumulative system time */ + elf_gregset_t pr_reg; /* General purpose registers. */ + int pr_fpvalid; /* True if math co-processor being used. */ +}; + + +#define elf_prpsinfo elf_prpsinfo32 +struct elf_prpsinfo32 +{ + char pr_state; /* numeric process state */ + char pr_sname; /* char for pr_state */ + char pr_zomb; /* zombie */ + char pr_nice; /* nice val */ + char pr_pad[4]; /* pad to put pr_flag on 8 byte boundary */ + unsigned int pr_flag; /* flags */ + u16 pr_uid; + u16 pr_gid; + pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; + /* Lots missing */ + char pr_fname[16]; /* filename of executable */ + char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ +}; + +#include + +#undef NEW_TO_OLD_UID +#undef NEW_TO_OLD_GID +#define NEW_TO_OLD_UID(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid) +#define NEW_TO_OLD_GID(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid) + +#undef start_thread +#define start_thread start_thread32 + +#define elf_debug elf_debug_elf32 +#define set_brk set_brk_elf32 +#define padzero padzero_elf32 +#define create_elf_tables create_elf_tables_elf32 +// #define elf_map elf_map_elf32 +#define load_elf_interp load_elf_interp_elf32 +#define load_aout_interp load_aout_interp_elf32 +#define elf_format elf_format_elf32 +#define load_elf_binary load_elf_binary_elf32 +#define load_elf_library load_elf_library_elf32 +#define dump_write dump_write_elf32 +#define dump_seek dump_seek_elf32 +#define maydump maydump_elf32 +#define notesize notesize_elf32 +#define dump_regs dump_regs_elf32 +#define writenote writenote_elf32 +#define elf_core_dump elf_core_dump_elf32 +#define init_elf_binfmt init_elf_binfmt_elf32 +#define exit_elf_binfmt exit_elf_binfmt_elf32 + + +#undef CONFIG_BINFMT_ELF +#ifdef CONFIG_BINFMT_ELF32 +#define CONFIG_BINFMT_ELF CONFIG_BINFMT_ELF32 +#endif +#undef CONFIG_BINFMT_ELF_MODULE +#ifdef CONFIG_BINFMT_ELF32_MODULE +#define CONFIG_BINFMT_ELF_MODULE CONFIG_BINFMT_ELF32_MODULE +#endif + +MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit Linux PPC binaries on the Power3+"); +MODULE_AUTHOR("Eric Youngdale, David S. Miller, Jakub Jelinek, Ken Aaker"); + +#undef MODULE_DESCRIPTION +#undef MODULE_AUTHOR + +#include "../../../fs/binfmt_elf.c" diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/bitops.c linux.ac/arch/ppc64/kernel/bitops.c --- linux.vanilla/arch/ppc64/kernel/bitops.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/bitops.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,90 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + * Adapted for ppc64 - Todd Inglett, Anton Blanchard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG_BITOPS + +/* + * Bitops are weird when viewed on big-endian systems. They were designed + * on little endian so the size of the bitset doesn't matter (low order bytes + * come first) as long as the bit in question is valid. + * + * Bits are "tested" often using the C expression (val & (1<> 6); + unsigned long result = offset & ~63UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 63UL; + + if (offset) { + tmp = *p++; + tmp |= ~0UL >> (64-offset); + if (size < 64) + goto found_first; + if (~tmp) + goto found_middle; + size -= 64; + result += 64; + } + while (size & ~63UL) { + if (~(tmp = *(p++))) + goto found_middle; + result += 64; + size -= 64; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp |= ~0UL << size; + if (tmp == ~0UL) + return result+size; +found_middle: + return result + ffz(tmp); +} + +void BUG_OUTLINE(char* file, unsigned line) +{ + udbg_printf("BUG - kernel BUG at %s:%d! \n", __FILE__, __LINE__); + PPCDBG_ENTER_DEBUGGER(); + printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); + __asm__ __volatile__(".long " BUG_ILLEGAL_INSTR); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/checks.c linux.ac/arch/ppc64/kernel/checks.c --- linux.vanilla/arch/ppc64/kernel/checks.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/checks.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,67 @@ +/* + * Copyright 2001 PPC 64 Team, 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Do various before compile checks of data structures + * + * This is invoked when you do a make checks + * Is this enough or are there more things that we would like to do here? + * -- tgall + */ +int main(void) +{ + int ret = 0; +#if 0 + if ( sizeof(struct thread_struct) % 16 ) + { + printf("Thread struct is not modulo 16 bytes: " + "%d bytes total, %d bytes off\n", + sizeof(struct thread_struct), + sizeof(struct thread_struct)%16); + ret = -1; + } +#endif + + if ( sizeof(struct pt_regs) % 16 ) + { + printf("pt_regs struct is not modulo 16 bytes: " + "%d bytes total, %d bytes off\n", + sizeof(struct pt_regs), + sizeof(struct pt_regs)%16); + ret = -1; + + } + + printf("Task size : %d bytes\n" + "Tss size : %d bytes\n" + "pt_regs size : %d bytes\n" + "Kernel stack size: %d bytes\n", + sizeof(struct task_struct), sizeof(struct thread_struct), + sizeof(struct pt_regs), + sizeof(union task_union) - sizeof(struct task_struct)); + return ret; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/chrp_setup.c linux.ac/arch/ppc64/kernel/chrp_setup.c --- linux.vanilla/arch/ppc64/kernel/chrp_setup.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/chrp_setup.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,394 @@ +/* + * linux/arch/ppc/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * Modified by PPC64 Team, 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. + */ + +/* + * bootup setup stuff.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*#include "time.h" */ +#include "local_irq.h" +#include "i8259.h" +#include "open_pic.h" +#include "xics.h" +#include + +extern volatile unsigned char *chrp_int_ack_special; +extern struct Naca *naca; + +unsigned long chrp_get_rtc_time(void); +int chrp_set_rtc_time(unsigned long nowtime); +void chrp_calibrate_decr(void); +long chrp_time_init(void); + +void chrp_setup_pci_ptrs(void); +void chrp_progress(char *, unsigned short); +void chrp_request_regions(void); + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern int pckbd_rate(struct kbd_repeat *rep); +extern void pckbd_init_hw(void); +extern unsigned char pckbd_sysrq_xlate[128]; +extern void openpic_init_IRQ(void); + +extern void find_and_init_phbs(void); +extern void pSeries_pcibios_fixup(void); +extern void iSeries_pcibios_fixup(void); + +kdev_t boot_dev; +unsigned long virtPython0Facilities = 0; // python0 facility area (memory mapped io) (64-bit format) VIRTUAL address. + +extern HPTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +extern int probingmem; +extern unsigned long loops_per_jiffy; + +extern void *comport1; + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ +#endif + +int __chrp +chrp_get_cpuinfo(char *buffer) +{ + long len; /* i --Unused */ + /* unsigned int t; --Unused */ + struct device_node *root; + const char *model = ""; + + root = find_path_device("/"); + if (root) + model = get_property(root, "model", NULL); + len = sprintf(buffer,"machine\t\t: CHRP %s\n", model); + + return len; +} + +/* + * Fixes for the National Semiconductor PC78308VUL SuperI/O + * + * Some versions of Open Firmware incorrectly initialize the IRQ settings + * for keyboard and mouse + */ +static inline void __init sio_write(u8 val, u8 index) +{ + outb(index, 0x15c); + outb(val, 0x15d); +} + +static inline u8 __init sio_read(u8 index) +{ + outb(index, 0x15c); + return inb(0x15d); +} + +static void __init sio_fixup_irq(const char *name, u8 device, u8 level, + u8 type) +{ + u8 level0, type0, active; + struct device_node *root; + + root = find_path_device("/"); + if (root && + !strncmp(get_property(root, "model", NULL), "IBM,LongTrail", 13 ) ) + { + /* select logical device */ + sio_write(device, 0x07); + active = sio_read(0x30); + level0 = sio_read(0x70); + type0 = sio_read(0x71); + printk("sio: %s irq level %d, type %d, %sactive: ", name, level0, type0, + !active ? "in" : ""); + if (level0 == level && type0 == type && active) + printk("OK\n"); + else { + printk("remapping to level %d, type %d, active\n", level, type); + sio_write(0x01, 0x30); + sio_write(level, 0x70); + sio_write(type, 0x71); + } + } + +} + +void __init chrp_request_regions(void) { + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); + request_region(0x00,0x20,"dma1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xc0,0x20,"dma2"); +} + +void __init +chrp_setup_arch(void) +{ + extern char cmd_line[]; + struct device_node *root; + unsigned int *opprop; + + /* openpic global configuration register (64-bit format). */ + /* openpic Interrupt Source Unit pointer (64-bit format). */ + /* python0 facility area (mmio) (64-bit format) REAL address. */ + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000; + +#ifdef CONFIG_BLK_DEV_INITRD + /* this is fine for chrp */ + initrd_below_start_ok = 1; + + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + else +#endif + ROOT_DEV = to_kdev_t(0x0802); /* sda2 (sda1 is for the kernel) */ + + printk("Boot arguments: %s\n", cmd_line); + + /* Find and initialize PCI host bridges */ + /* iSeries needs to be done much later. */ + #ifndef CONFIG_PPC_ISERIES + find_and_init_phbs(); + #endif + + /* Find the Open PIC if present */ + root = find_path_device("/"); + opprop = (unsigned int *) get_property(root, + "platform-open-pic", NULL); + if (opprop != 0) { + int n = prom_n_addr_cells(root); + unsigned long openpic; + + for (openpic = 0; n > 0; --n) + openpic = (openpic << 32) + *opprop++; + printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic); + udbg_printf("OpenPIC addr: %lx\n", openpic); + OpenPIC_Addr = ioremap(openpic, 0x40000); + } + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + /* Get the event scan rate for the rtas so we know how + * often it expects a heartbeat. -- Cort + */ + if (rtas.event_scan.rate) { + ppc_md.heartbeat = rtas_event_scan; + ppc_md.heartbeat_reset = (HZ/rtas.event_scan.rate*60)-1; + ppc_md.heartbeat_count = 1; + printk("RTAS Event Scan Rate: %lu (%lu jiffies)\n", + rtas.event_scan.rate, ppc_md.heartbeat_reset); + } +} + +void __init +chrp_init2(void) +{ + /* + * It is sensitive, when this is called (not too earlu) + * -- tibit + */ + chrp_request_regions(); + ppc_md.progress(UTS_RELEASE, 0x7777); +} + +void __init +chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if ( r6 ) + { + initrd_start = __va(r6); + initrd_end = __va(r6 + r7); + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* pci_dram_offset/isa_io_base/isa_mem_base set by setup_pci_ptrs() */ + + ppc_md.ppc_machine = _machine; + + ppc_md.setup_arch = chrp_setup_arch; + ppc_md.setup_residual = NULL; + ppc_md.get_cpuinfo = chrp_get_cpuinfo; + if(naca->interrupt_controller == IC_OPEN_PIC) { + ppc_md.init_IRQ = openpic_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + ppc_md.post_irq = NULL; + } else { + ppc_md.init_IRQ = xics_init_IRQ; + ppc_md.get_irq = xics_get_irq; + ppc_md.post_irq = NULL; + } + + #ifndef CONFIG_PPC_ISERIES + ppc_md.pcibios_fixup = pSeries_pcibios_fixup; + #else + ppc_md.pcibios_fixup = NULL; + // ppc_md.pcibios_fixup = iSeries_pcibios_fixup; + #endif + + + ppc_md.init = chrp_init2; + + ppc_md.restart = rtas_restart; + ppc_md.power_off = rtas_power_off; + ppc_md.halt = rtas_halt; + + ppc_md.time_init = chrp_time_init; + ppc_md.set_rtc_time = chrp_set_rtc_time; + ppc_md.get_rtc_time = chrp_get_rtc_time; + ppc_md.calibrate_decr = chrp_calibrate_decr; + +#ifdef CONFIG_VT + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; + ppc_md.kbd_rate = pckbd_rate; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; + SYSRQ_KEY = 0x63; /* Print Screen */ +#endif /* CONFIG_MAGIC_SYSRQ */ +#endif /* CONFIG_VT */ + + ppc_md.progress = chrp_progress; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.insw = chrp_ide_insw; + ppc_ide_md.outsw = chrp_ide_outsw; + ppc_ide_md.default_irq = chrp_ide_default_irq; + ppc_ide_md.default_io_base = chrp_ide_default_io_base; + ppc_ide_md.ide_check_region = chrp_ide_check_region; + ppc_ide_md.ide_request_region = chrp_ide_request_region; + ppc_ide_md.ide_release_region = chrp_ide_release_region; + ppc_ide_md.fix_driveid = chrp_ide_fix_driveid; + ppc_ide_md.ide_init_hwif = chrp_ide_init_hwif_ports; + + ppc_ide_md.io_base = _IO_BASE; +#endif + ppc_md.progress("Linux ppc64\n", 0x0); +} + +void __chrp +chrp_progress(char *s, unsigned short hex) +{ + struct device_node *root; + int width, *p; + char *os; + static int max_width; + + if ( (_machine != _MACH_chrp) || !rtas.base ) + return; + + if (hex) + udbg_printf(" %s\n", s); + + if (max_width == 0) { + if ( (root = find_path_device("/rtas")) && + (p = (unsigned int *)get_property(root, + "ibm,display-line-length", + NULL)) ) + max_width = *p; + else + max_width = 0x10; + } + + if ( call_rtas( "display-character", 1, 1, NULL, '\r' ) ) + { + /* assume no display-character RTAS method - use hex display */ + call_rtas("set-indicator", 3, 1, NULL, 6, 0, hex); + return; + } + + width = max_width; + os = s; + while ( *os ) + { + if ( (*os == '\n') || (*os == '\r') ) + width = max_width; + else + width--; + call_rtas( "display-character", 1, 1, NULL, *os++ ); + /* if we overwrite the screen length */ + if ( width == 0 ) + while ( (*os != 0) && (*os != '\n') && (*os != '\r') ) + os++; + } + + /* Blank to end of line. */ + while ( width-- > 0 ) + call_rtas( "display-character", 1, 1, NULL, ' ' ); +} + +void chrp_init_map_io_space(void) +{ + /* naca->serialPortAddr is initialized earlier in prom.c */ + comport1 = (void *)ioremap(naca->serialPortAddr, PAGE_SIZE); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/chrp_time.c linux.ac/arch/ppc64/kernel/chrp_time.c --- linux.vanilla/arch/ppc64/kernel/chrp_time.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/chrp_time.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,203 @@ +/* + * linux/arch/i386/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * + * Adapted for PowerPC (PreP) by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * copied and modified from intel version + * Modified for PPC64 by PPC64 Team, Copyright 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +/*#include "time.h" */ + +static int nvram_as1 = NVRAM_AS1; +static int nvram_as0 = NVRAM_AS0; +static int nvram_data = NVRAM_DATA; + +long __init chrp_time_init(void) +{ + struct device_node *rtcs; + int base; + + rtcs = find_compatible_devices("rtc", "pnpPNP,b00"); + if (rtcs == NULL || rtcs->addrs == NULL) { + return 0; + } + base = rtcs->addrs[0].address; + nvram_as1 = 0; + nvram_as0 = base; + nvram_data = base + 1; + + return 0; +} + +int __chrp chrp_cmos_clock_read(int addr) +{ + int retval; + +#if 1 /* DRENG -- see chrp_time_init, chrp_cmos_clock_write + * I think this is broken in general on 64b -- + * nvram_as0 = -1 in 64b format + */ + if (nvram_as1 != 0) { + outb(addr>>8, nvram_as1); + } + outb(addr, nvram_as0); + retval = inb(nvram_data); + return retval; +#else + return(-1); +#endif +} + +void __chrp chrp_cmos_clock_write(unsigned long val, int addr) +{ +#if 1 /* DRENG -- see chrp_cmos_clock_read */ + if (nvram_as1 != 0) + outb(addr>>8, nvram_as1); + outb(addr, nvram_as0); + outb(val, nvram_data); + return; +#else + return; +#endif +} + +/* + * Set the hardware clock. -- Cort + */ +int __chrp chrp_set_rtc_time(unsigned long nowtime) +{ + unsigned char save_control, save_freq_select; + struct rtc_time tm; + + to_tm(nowtime, &tm); + + save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ + + chrp_cmos_clock_write((save_control|RTC_SET), RTC_CONTROL); + + save_freq_select = chrp_cmos_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */ + + chrp_cmos_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + tm.tm_year -= 1900; + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); + } + chrp_cmos_clock_write(tm.tm_sec,RTC_SECONDS); + chrp_cmos_clock_write(tm.tm_min,RTC_MINUTES); + chrp_cmos_clock_write(tm.tm_hour,RTC_HOURS); + chrp_cmos_clock_write(tm.tm_mon,RTC_MONTH); + chrp_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH); + chrp_cmos_clock_write(tm.tm_year,RTC_YEAR); + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + chrp_cmos_clock_write(save_control, RTC_CONTROL); + chrp_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT); + + if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) ) + time_state = TIME_OK; + return 0; +} + +unsigned long __chrp chrp_get_rtc_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + int i; + + /* The Linux interpretation of the CMOS clock register contents: + * When the Update-In-Progress (UIP) flag goes from 1 to 0, the + * RTC registers show the second which has precisely just started. + * Let's hope other operating systems interpret the RTC the same way. + */ + /* read RTC exactly on falling edge of update flag */ + for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ + if (chrp_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP) + break; + for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ + if (!(chrp_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP)) + break; + do { /* Isn't this overkill ? UIP above should guarantee consistency */ + sec = chrp_cmos_clock_read(RTC_SECONDS); + min = chrp_cmos_clock_read(RTC_MINUTES); + hour = chrp_cmos_clock_read(RTC_HOURS); + day = chrp_cmos_clock_read(RTC_DAY_OF_MONTH); + mon = chrp_cmos_clock_read(RTC_MONTH); + year = chrp_cmos_clock_read(RTC_YEAR); + } while (sec != chrp_cmos_clock_read(RTC_SECONDS)); + if (!(chrp_cmos_clock_read(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + if ((year += 1900) < 1970) + year += 100; + return mktime(year, mon, day, hour, min, sec); +} + +extern void setup_default_decr(void); + +void __init chrp_calibrate_decr(void) +{ + struct device_node *cpu; + int *fp; + unsigned long freq; + + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + freq = 16666000; /* hardcoded default */ + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (int *) get_property(cpu, "timebase-frequency", NULL); + if (fp != 0) + freq = *fp; + } + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000 ); + + tb_ticks_per_jiffy = freq / HZ; + tb_ticks_per_usec = freq / 1000000; + setup_default_decr(); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/entry.S linux.ac/arch/ppc64/kernel/entry.S --- linux.vanilla/arch/ppc64/kernel/entry.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/entry.S Wed Oct 10 01:47:34 2001 @@ -0,0 +1,675 @@ +/* + * arch/ppc/kernel/entry.S + * + * + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Copyright (C) 1996 Cort Dougan + * Adapted for Power Macintosh by Paul Mackerras. + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net). + * + * This file contains the system call entry code, context switch + * code, and exception/interrupt return code for PowerPC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + + +#include "ppc_asm.h" +#include +#include +#include +#include +#include +#include + +#undef SHOW_SYSCALLS +#undef SHOW_SYSCALLS_TASK + +#ifdef SHOW_SYSCALLS_TASK + .data +show_syscalls_task: + .long -1 +#endif + +/* + * Handle a system call. + */ + .text +_GLOBAL(DoSyscall) + mfspr r30,SPRG3 /* Get PACA */ + ld r29,PACACURRENT(r30) + std r0,THREAD+LAST_SYSCALL(r29) + ld r11,_CCR(r1) /* Clear SO bit in CR */ + lis r10,0x1000 + andc r11,r11,r10 + std r11,_CCR(r1) +#ifdef SHOW_SYSCALLS +#ifdef SHOW_SYSCALLS_TASK + LOADBASE(r31,show_syscalls_task) + ld r31,show_syscalls_task@l(r31) + cmp 0,r29,r31 + bne 1f +#endif + LOADADDR(r3,7f) + ld r4,GPR0(r1) + ld r5,GPR3(r1) + ld r6,GPR4(r1) + ld r7,GPR5(r1) + ld r8,GPR6(r1) + ld r9,GPR7(r1) + bl .printk + LOADADDR(r3,77f) + ld r4,GPR8(r1) + ld r5,GPR9(r1) + mr r6,r29 + bl .printk + ld r0,GPR0(r1) + ld r3,GPR3(r1) + ld r4,GPR4(r1) + ld r5,GPR5(r1) + ld r6,GPR6(r1) + ld r7,GPR7(r1) + ld r8,GPR8(r1) +1: +#endif /* SHOW_SYSCALLS */ + cmpi 0,r0,0x7777 /* Special case for 'sys_sigreturn' */ + beq- 10f + cmpi 0,r0,0x6666 /* Special case for 'sys_rt_sigreturn' */ + beq- 16f + + /* MIKEC: I changed TASK_PTRACE to 64-bit here. Should I have? + * TASK_PTRACE is aka task_struct.ptrace (in linux/sched.h) + * which is an unsigned long + */ + ld r10,TASK_PTRACE(r29) + andi. r10,r10,PT_TRACESYS + bne- 50f + cmpli 0,r0,NR_syscalls + bge- 66f +/* Ken Aaker: Need to vector to 32 Bit or default sys_call_table here, + * based on caller's run-mode / personality. + * + */ +#ifdef CONFIG_BINFMT_ELF32 + ld r10,THREAD+THREAD_FLAGS(r29) + andi. r10,r10,PPC_FLAG_32BIT + beq+ 15f + LOADADDR(r10,.sys_call_table32) +/* Now mung the first 4 parameters into shape, by making certain that + * the high bits (most significant 32 bits in 64 bit reg) are 0 + * for the first 4 parameter regs(3-6). + */ + clrldi r3,r3,32 + clrldi r4,r4,32 + clrldi r5,r5,32 + clrldi r6,r6,32 + b 17f +15: +#endif + LOADADDR(r10,.sys_call_table) +17: + slwi r0,r0,3 + ldx r10,r10,r0 /* Fetch system call handler [ptr] */ + cmpi 0,r10,0 + beq- 66f + mtlr r10 + addi r9,r1,STACK_FRAME_OVERHEAD + blrl /* Call handler */ +_GLOBAL(ret_from_syscall_1) +20: std r3,RESULT(r1) /* Save result */ +#ifdef SHOW_SYSCALLS +#ifdef SHOW_SYSCALLS_TASK + /* MIKEC: Will r29 (current) be preserved through any method of getting + to ret_from_syscall_1 ?? */ + cmp 0,r29,r31 + bne 91f +#endif + mr r4,r3 + LOADADDR(r3,79f) + bl .printk + ld r3,RESULT(r1) +91: +#endif + li r10,-_LAST_ERRNO + cmpl 0,r3,r10 + blt 30f + neg r3,r3 + cmpi 0,r3,ERESTARTNOHAND + bne 22f + li r3,EINTR +22: ld r10,_CCR(r1) /* Set SO bit in CR */ + oris r10,r10,0x1000 + std r10,_CCR(r1) +30: std r3,GPR3(r1) /* Update return value */ + b .ret_from_except +66: li r3,ENOSYS + b 22b +/* sys_sigreturn */ +10: addi r3,r1,STACK_FRAME_OVERHEAD + bl .sys_sigreturn + cmpi 0,r3,0 /* Check for restarted system call */ + bge .ret_from_except + b 20b +/* sys_rt_sigreturn */ +16: addi r3,r1,STACK_FRAME_OVERHEAD + bl .sys_rt_sigreturn + cmpi 0,r3,0 /* Check for restarted system call */ + bge .ret_from_except + b 20b + +/* Traced system call support */ +50: bl .syscall_trace + ld r0,GPR0(r1) /* Restore original registers */ + ld r3,GPR3(r1) + ld r4,GPR4(r1) + ld r5,GPR5(r1) + ld r6,GPR6(r1) + ld r7,GPR7(r1) + ld r8,GPR8(r1) + ld r9,GPR9(r1) + cmpli 0,r0,NR_syscalls + bge- 66f +#ifdef CONFIG_BINFMT_ELF32 + ld r10,THREAD+THREAD_FLAGS(r29) + andi. r10,r10,PPC_FLAG_32BIT + beq+ 55f + LOADADDR(r10,.sys_call_table32) +/* Now mung the first 4 parameters into shape, by making certain that + * the high bits (most significant 32 bits in 64 bit reg) are 0 + * for the first 4 parameter regs(3-6). + */ + clrldi r3,r3,32 + clrldi r4,r4,32 + clrldi r5,r5,32 + clrldi r6,r6,32 + b 57f +55: +#endif + LOADADDR(r10,.sys_call_table) +57: + slwi r0,r0,3 + ldx r10,r10,r0 /* Fetch system call handler [ptr] */ + cmpi 0,r10,0 + beq- 66f + mtlr r10 + addi r9,r1,STACK_FRAME_OVERHEAD + blrl /* Call handler */ +_GLOBAL(ret_from_syscall_2) + std r3,RESULT(r1) /* Save result */ + std r3,GPR0(r1) /* temporary gross hack to make strace work */ + li r10,-_LAST_ERRNO + cmpl 0,r3,r10 + blt 60f + neg r3,r3 + cmpi 0,r3,ERESTARTNOHAND + bne 57f + li r3,EINTR +57: ld r10,_CCR(r1) /* Set SO bit in CR */ + oris r10,r10,0x1000 + std r10,_CCR(r1) +60: std r3,GPR3(r1) /* Update return value */ + bl .syscall_trace + b .ret_from_except +66: li r3,ENOSYS + b 57b +#ifdef SHOW_SYSCALLS +7: .string "syscall %d(%x, %x, %x, %x, %x, " +77: .string "%x, %x), current=%p\n" +79: .string " -> %x\n" + .align 2,0 +#endif + +/* + * This routine switches between two different tasks. The process + * state of one is saved on its kernel stack. Then the state + * of the other is restored from its kernel stack. The memory + * management hardware is updated to the second process's state. + * Finally, we can return to the second process, via ret_from_except. + * On entry, r3 points to the THREAD for the current task, r4 + * points to the THREAD for the new task. + * + * Note: there are two ways to get to the "going out" portion + * of this code; either by coming in via the entry (_switch) + * or via "fork" which must set up an environment equivalent + * to the "_switch" path. If you change this (or in particular, the + * SAVE_REGS macro), you'll have to change the fork code also. + * + * The code which creates the new task context is in 'copy_thread' + * in arch/ppc/kernel/process.c + */ +_GLOBAL(_switch) + stdu r1,-INT_FRAME_SIZE(r1) + ld r6,0(r1) + std r6,GPR1(r1) + /* r3-r13 are caller saved -- Cort */ + SAVE_GPR(2, r1) + SAVE_8GPRS(14, r1) + SAVE_10GPRS(22, r1) + mflr r20 /* Return to switch caller */ + mfmsr r22 + li r6,MSR_FP /* Disable floating-point */ + andc r22,r22,r6 + std r20,_NIP(r1) + std r22,_MSR(r1) + std r20,_LINK(r1) + mfcr r20 + mfctr r22 + mfspr r23,XER + std r20,_CCR(r1) + std r22,_CTR(r1) + std r23,_XER(r1) + li r6,0x0ff0 + std r6,TRAP(r1) + std r1,KSP(r3) /* Set old stack pointer */ + + mfspr r5,SPRG3 /* Get Paca */ + ld r3,PACACURRENT(r5) /* Get old 'current' for return value */ + + addi r6,r4,-THREAD /* Convert THREAD to 'current' */ + std r6,PACACURRENT(r5) /* Set new 'current' */ + + ld r1,KSP(r4) /* Load new stack pointer */ + ld r9,_MSR(r1) /* Returning to user mode? */ + andi. r9,r9,MSR_PR + beq+ 10f /* if not, don't adjust kernel stack */ +8: addi r7,r1,INT_FRAME_SIZE /* size of frame */ + std r7,KSP(r4) /* save kernel stack pointer */ + std r1,PACAKSAVE(r5) /* save exception stack pointer */ +10: ld r7,_CTR(r1) + ld r6,_LINK(r1) + mtctr r7 + mtlr r6 + ld r7,_XER(r1) + ld r6,_CCR(r1) + mtspr XER,r7 + mtcrf 0xFF,r6 + /* r3-r13 are destroyed -- Cort */ + REST_GPR(14, r1) + REST_8GPRS(15, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + ld r7,_NIP(r1) /* Restore environment */ + + mfmsr r6 /* Get current interrupt state */ + rldicl r6,r6,48,1 + rldicl r6,r6,16,0 /* clear MSR_EE */ + mtmsrd r6 /* Update machine state */ + + ld r6,_MSR(r1) + 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. */ + SYNC + rfid + +#ifdef CONFIG_SMP +_GLOBAL(ret_from_smpfork) + bl .schedule_tail + b .ret_from_except +#endif + +_GLOBAL(ret_from_except) +#ifdef CONFIG_PPC_ISERIES + ld r5,_MSR(r1) + andi. r5,r5,MSR_EE + beq 4f +irq_recheck: + mfmsr r5 + andi. r5,r5,MSR_EE + bne 4f + /* + * Check for pending interrupts (iSeries) + */ + CHECKANYINT(r4,r5) + beq+ 4f /* skip do_IRQ if no interrupts */ + + addi r3,r1,STACK_FRAME_OVERHEAD + bl .do_IRQ + b irq_recheck /* loop back and handle more */ +4: +#endif +_GLOBAL(do_bottom_half_ret) + ld r3,_MSR(r1) /* Returning to user mode? */ + andi. r3,r3,MSR_PR + beq+ restore /* if so, check need_resched and signals */ +_GLOBAL(ret_to_user_hook) + nop + /* 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 + bl .schedule + /* SIGPENDING is an int (32-bits) */ +7: + mfspr r4,SPRG3 /* current task's PACA */ + ld r4,PACACURRENT(r4) /* Get 'current' */ + lwz r5,SIGPENDING(r4) /* Check for pending unblocked signals */ + cmpwi 0,r5,0 + beq+ restore + li r3,0 + addi r4,r1,STACK_FRAME_OVERHEAD + bl .do_signal +_GLOBAL(do_signal_ret) +restore: + ld r3,_CTR(r1) + ld r0,_LINK(r1) + mtctr r3 + mtlr r0 + ld r3,_XER(r1) + mtspr XER,r3 + REST_8GPRS(5, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + + /* make sure we hard disable here, even if rtl is active, to protect + * SRR[01] and SPRG2 -- Cort + */ + mfmsr r0 /* Get current interrupt state */ + rldicl r0,r0,48,1 + rldicl r0,r0,16,0 /* clear MSR_EE */ + mtmsrd r0 /* Update machine state */ +#ifdef CONFIG_PPC_ISERIES + ld r0,_MSR(r1) + andi. r3,r0,MSR_EE + beq+ 1f + + CHECKANYINT(r4,r3) + bne- irq_recheck + +1: +#endif + 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 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 + ld r2,_CCR(r1) + mtcrf 0xFF,r2 + ld r2,_NIP(r1) + mtspr SRR0,r2 + ld r0,GPR0(r1) + ld r2,GPR2(r1) + ld r3,GPR3(r1) + ld r4,GPR4(r1) + ld r1,GPR1(r1) + + /* sync required to force memory operations on this processor */ + /* to complete before the current thread gives up control. */ + SYNC + rfid + +#ifdef CONFIG_PPC_ISERIES +/* + * Fake an interrupt from kernel mode. + * This is used when enable_irq loses an interrupt. + * We only fill in the stack frame minimally. + */ +_GLOBAL(fake_interrupt) + mflr r0 + std r0,16(r1) + stdu r1,-(INT_FRAME_SIZE)(r1) + std r0,_NIP(r1) + std r0,_LINK(r1) + mfmsr r3 + std r3,_MSR(r1) + li r0,0x0fac + std r0,TRAP(r1) + addi r3,r1,STACK_FRAME_OVERHEAD + li r4,1 + bl .do_IRQ + addi r1,r1,INT_FRAME_SIZE + ld r0,16(r1) + mtlr r0 + blr + +/* + * Fake a decrementer from kernel mode. + * This is used when the decrementer pops in + * the hypervisor. We only fill in the stack + * frame minimally + */ +_GLOBAL(fake_decrementer) + mflr r0 + std r0,16(r1) + stdu r1,-(INT_FRAME_SIZE)(r1) + std r0,_NIP(r1) + std r0,_LINK(r1) + mfmsr r3 + std r3,_MSR(r1) + li r0,0x0fac + std r0,TRAP(r1) + addi r3,r1,STACK_FRAME_OVERHEAD + bl .timer_interrupt + addi r1,r1,INT_FRAME_SIZE + ld r0,16(r1) + mtlr r0 + blr +#endif +/* + * On CHRP, the Run-Time Abstraction Services (RTAS) have to be + * called with the MMU off. + * + * In addition, we need to be in 32b mode, at least for now. + * + * Note: r3 is an input parameter to rtas, so don't trash it... + */ +_GLOBAL(enter_rtas) + mflr r0 + std r0,16(r1) + stdu r1,-INT_FRAME_SIZE(r1) // Save SP and create stack space + + /* Because RTAS is running in 32b mode, it clobbers the high order half + * of all registers that it saves. We therefore save those registers + * RTAS might touch to the stack. (r0, r3-r13 are caller saved) + */ + SAVE_GPR(2, r1) // Save the TOC + SAVE_8GPRS(14, r1) // Save the non-volatiles + SAVE_10GPRS(22, r1) // ditto + + mfcr r4 + std r4,_CCR(r1) + mfctr r5 + std r5,_CTR(r1) + mfspr r6,XER + std r6,_XER(r1) + mfdar r7 + std r7,_DAR(r1) + mfdsisr r8 + std r8,_DSISR(r1) + mfsrr0 r9 + std r9,_DSISR(r1) + + /* Unfortunatly, the stack pointer and the MSR are also clobbered, so they + * are saved in the PACA (SPRG3) which allows us to restore our original + * state after RTAS returns. + */ + mfspr r13,SPRG3 /* Get PACA */ + std r1,PACAR1(r13) + mfmsr r6 + std r6,PACASAVEDMSR(r13) + + /* Setup our real return addr */ + SET_REG_TO_LABEL(r4,.rtas_return_loc) + SET_REG_TO_CONST(r9,KERNELBASE) + sub r4,r4,r9 + mtlr r4 + + li r0,0 + ori r0,r0,MSR_EE|MSR_SE|MSR_BE + andc r0,r6,r0 + + li r9,1 + rldicr r9,r9,MSR_SF_LG,(63-MSR_SF_LG) + ori r9,r9,MSR_IR|MSR_DR|MSR_FE0|MSR_FE1|MSR_FP + andc r6,r0,r9 + sync /* disable interrupts so SRR0/1 */ + mtmsrd r0 /* don't get trashed */ + + SET_REG_TO_LABEL(r4,rtas) + ld r5,RTASENTRY(r4) /* get the rtas->entry value */ + ld r4,RTASBASE(r4) /* get the rtas->base value */ + + mtspr SRR0,r5 + mtspr SRR1,r6 + rfid + +_STATIC(rtas_return_loc) + /* relocation is off at this point */ + mfspr r4,SPRG3 /* Get PACA */ + SET_REG_TO_CONST(r5, KERNELBASE) + sub r4,r4,r5 /* RELOC the PACA base pointer */ + + ld r1,PACAR1(r4) /* Restore our SP */ + LOADADDR(r3,.rtas_restore_regs) + ld r4,PACASAVEDMSR(r4) /* Restore our MSR */ + + mtspr SRR0,r3 + mtspr SRR1,r4 + rfid + +_STATIC(rtas_restore_regs) + /* relocation is on at this point */ + REST_GPR(2, r1) // Restore the TOC + REST_8GPRS(14, r1) // Restore the non-volatiles + REST_10GPRS(22, r1) // ditto + + ld r4,_CCR(r1) + mtcr r4 + + ld r5,_CTR(r1) + mtctr r5 + + ld r6,_XER(r1) + mtspr XER,r6 + + ld r7,_DAR(r1) + mtdar r7 + + ld r8,_DSISR(r1) + mtdsisr r8 + + ld r9,_DSISR(r1) + mtsrr0 r9 + + addi r1,r1,INT_FRAME_SIZE + ld r0,16(r1) /* get return address */ + + mtlr r0 + blr /* return to caller */ + + +_GLOBAL(enter_prom) + mflr r0 + std r0,16(r1) + stdu r1,-INT_FRAME_SIZE(r1) // Save SP and create stack space + + /* Because PROM is running in 32b mode, it clobbers the high order half + * of all registers that it saves. We therefore save those registers + * PROM might touch to the stack. (r0, r3-r13 are caller saved) + */ + SAVE_8GPRS(2, r1) // Save the TOC & incoming param(s) + SAVE_8GPRS(14, r1) // Save the non-volatiles + SAVE_10GPRS(22, r1) // ditto + + mfcr r4 + std r4,_CCR(r1) + mfctr r5 + std r5,_CTR(r1) + mfspr r6,XER + std r6,_XER(r1) + mfdar r7 + std r7,_DAR(r1) + mfdsisr r8 + std r8,_DSISR(r1) + mfsrr0 r9 + std r9,_DSISR(r1) + mfmsr r10 + std r10,_MSR(r1) + + /* Unfortunatly, the stack pointer is also clobbered, so it is saved + * in the SPRG2 which allows us to restore our original state after + * PROM returns. + */ + mtspr SPRG2,r1 + + /* put a relocation offset into r3 */ + bl .reloc_offset + LOADADDR(r12,prom) + sub r12,r12,r3 + ld r12,PROMENTRY(r12) /* get the prom->entry value */ + mtlr r12 + + mfmsr r11 /* grab the current MSR */ + li r12,1 + rldicr r12,r12,MSR_SF_LG,(63-MSR_SF_LG) + andc r11,r11,r12 + li r12,1 + rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG) + andc r11,r11,r12 + mtmsrd r11 + SYNC + + REST_8GPRS(2, r1) // Restore the TOC & incoming param(s) + REST_8GPRS(14, r1) // Restore the non-volatiles + REST_10GPRS(22, r1) // ditto + blrl + + mfspr r1,SPRG2 + ld r6,_MSR(r1) + mtmsrd r6 + SYNC + + REST_GPR(2, r1) // Restore the TOC + REST_8GPRS(14, r1) // Restore the non-volatiles + REST_10GPRS(22, r1) // ditto + + ld r4,_CCR(r1) + mtcr r4 + + ld r5,_CTR(r1) + mtctr r5 + + ld r6,_XER(r1) + mtspr XER,r6 + + ld r7,_DAR(r1) + mtdar r7 + + ld r8,_DSISR(r1) + mtdsisr r8 + + ld r9,_DSISR(r1) + mtsrr0 r9 + + addi r1,r1,INT_FRAME_SIZE + ld r0,16(r1) /* get return address */ + + mtlr r0 + blr /* return to caller */ + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/flight_recorder.c linux.ac/arch/ppc64/kernel/flight_recorder.c --- linux.vanilla/arch/ppc64/kernel/flight_recorder.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/flight_recorder.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,174 @@ +/************************************************************************ + * flight_recorder.c + ************************************************************************ + * This code supports the a generic flight recorder. * + * 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 * + ************************************************************************ + * This is a simple text based flight recorder. Useful for logging + * information the you may want to retreive at a latter time. Errors or + * debug inforamtion are good examples. A good method to dump the + * information is via the proc file system. + * + * To use. + * 1. Create the flight recorder object. Passing a NULL pointer will + * kmalloc the space for you. If it is too early for kmalloc, create + * space for the object. Beware, don't lie about the size, you will + * pay for that later. + * FlightRecorder* TestFr = alloc_Flight_Recorder(NULL,"TestFr",4096); + * + * 2. Log any notable events, initialzation, error conditions, etc. + * LOGFR(TestFr,"5. Stack Variable(10) %d",StackVariable); + * + * 3. Dump the information to a buffer. + * fr_Dump(TestFr, proc_file_buffer, proc_file_buffer_size); + * + ************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +static char LogText[512]; +static int LogTextIndex; +static spinlock_t Fr_Lock; + +/************************************************************************ + * Build the log time prefix based on Flags. + * 00 = No time prefix + * 01 = Date(mmddyy) Time(hhmmss) prefix + * 02 = Day(dd) Time(hhmmss) prefix + * 03 = Time(hhmmss) prefix + ************************************************************************/ +static void fr_Log_Time(FlightRecorder* Fr) { + struct timeval TimeClock; + struct rtc_time LogTime; + + do_gettimeofday(&TimeClock); + to_tm(TimeClock.tv_sec, &LogTime); + + if( Fr->Flags == 1) { + LogTextIndex = sprintf(LogText,"%02d%02d%02d %02d%02d%02d ", + LogTime.tm_mon, LogTime.tm_mday, LogTime.tm_year-2000, + LogTime.tm_hour,LogTime.tm_min, LogTime.tm_sec); + } + else if( Fr->Flags == 2) { + LogTextIndex = sprintf(LogText,"%02d %02d%02d%02d ", + LogTime.tm_mday, + LogTime.tm_hour,LogTime.tm_min, LogTime.tm_sec); + } + + else if( Fr->Flags == 3) { + LogTextIndex = sprintf(LogText,"%02d%02d%02d ", + LogTime.tm_hour,LogTime.tm_min, LogTime.tm_sec); + } + else { + LogTextIndex = 0; + } +} +/************************************************************************/ +/* Log entry into buffer, */ +/* ->If entry is going to wrap, log "WRAP" and start at the top. */ +/************************************************************************/ +static void fr_Log_Data(FlightRecorder* Fr) { + int TextLen = strlen(LogText); + int Residual = ( Fr->EndPointer - Fr->NextPointer)-15; + if(TextLen > Residual) { + strcpy(Fr->NextPointer,"WRAP"); + Fr->WrapPointer = Fr->NextPointer + 5; + Fr->NextPointer = Fr->StartPointer; + } + strcpy(Fr->NextPointer,LogText); + Fr->NextPointer += TextLen+1; + strcpy(Fr->NextPointer,"<="); +} +/************************************************************************ + * Build the log text, support variable args. + ************************************************************************/ +void fr_Log_Entry(struct flightRecorder* LogFr, const char *fmt, ...) { + va_list arg_ptr; + spin_lock(&Fr_Lock); + + fr_Log_Time(LogFr); + va_start(arg_ptr, fmt); + vsprintf(LogText+LogTextIndex, fmt, arg_ptr); + va_end(arg_ptr); + fr_Log_Data(LogFr); + + spin_unlock(&Fr_Lock); + +} +/************************************************************************ + * Dump Flight Recorder into buffer. + * -> Handles the buffer wrapping. + ************************************************************************/ +int fr_Dump(FlightRecorder* Fr, char *Buffer, int BufferLen) { + int LineLen = 0; + char* StartEntry; + char* EndEntry; + spin_lock(&Fr_Lock); + /******************************************************************* + * If Buffer has wrapped, find last usable entry to start with. + *******************************************************************/ + if(Fr->WrapPointer != NULL) { + StartEntry = Fr->NextPointer+3; + StartEntry += strlen(StartEntry)+1; + EndEntry = Fr->WrapPointer; + + while(EndEntry > StartEntry && LineLen < BufferLen) { + LineLen += sprintf(Buffer+LineLen,"%s\n",StartEntry); + StartEntry += strlen(StartEntry) + 1; + } + } + + /******************************************************************* + * Dump from the beginning to the last logged entry + *******************************************************************/ + StartEntry = Fr->StartPointer; + EndEntry = Fr->NextPointer; + while(EndEntry > StartEntry && LineLen < BufferLen) { + LineLen += sprintf(Buffer+LineLen,"%s\n",StartEntry); + StartEntry += strlen(StartEntry) + 1; + } + spin_unlock(&Fr_Lock); + return LineLen; +} + +/************************************************************************ + * Allocate and Initialized the Flight Recorder + * -> If no FlightRecorder pointer is passed, the space is kmalloc. + ************************************************************************/ +FlightRecorder* alloc_Flight_Recorder(FlightRecorder* FrPtr, char* Signature, int SizeOfFr) { + FlightRecorder* Fr = FrPtr; /* Pointer to Object */ + int FrSize = (SizeOfFr/16)*16; /* Could be static */ + if(Fr == NULL) Fr = (FlightRecorder*)kmalloc(SizeOfFr, GFP_KERNEL); + memset(Fr,SizeOfFr,0); + strcpy(Fr->Signature,Signature); + Fr->Size = FrSize; + Fr->Flags = 2; + Fr->StartPointer = (char*)&Fr->Buffer; + Fr->EndPointer = (char*)Fr + Fr->Size; + Fr->NextPointer = Fr->StartPointer; + + fr_Log_Entry(Fr,"Initialized."); + return Fr; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/head.S linux.ac/arch/ppc64/kernel/head.S --- linux.vanilla/arch/ppc64/kernel/head.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/head.S Wed Oct 10 01:47:34 2001 @@ -0,0 +1,1528 @@ +/* + * arch/ppc64/kernel/head.S + * + * + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Copyright (C) 1996 Cort Dougan + * Adapted for Power Macintosh by Paul Mackerras. + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * + * Adapted for 64bit PowerPC by Dave Engebretsen, Peter Bergner, and + * Mike Corrigan {engebret|bergner|mikejc}@us.ibm.com + * + * This file contains the low-level support and setup for the + * PowerPC-64 platform, including trap and interrupt dispatch. + * + * 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. + * + */ + +#define SECONDARY_PROCESSORS + +#include "ppc_asm.h" +#include "ppc_defs.h" +#include +#include +#include +#include + +/* + * We layout physical memory as follows: + * 0x0000 - 0x00ff : Secondary processor spin code + * 0x0100 - 0x2fff : pSeries Interrupt prologs + * 0x3000 - 0x3fff : Interrupt support + * 0x4000 - 0x4fff : NACA + * 0x5000 - 0x5fff : Initial segment table + * 0x6000 : iSeries and common interrupt prologs + * + */ + +/* + * Entering into this code we make the following assumptions: + * For pSeries: + * 1. The MMU is off & open firmware is running in real mode. + * 2. The kernel is entered at __start + * + * For iSeries: + * 1. The MMU is on (as it always is for iSeries) + * 2. The kernel is entered at SystemReset_Iseries + */ + + .text + .globl _stext +_stext: +_STATIC(__start) + b .__start_initialization_pSeries + + /* At offset 0x20, there is a pointer to iSeries LPAR data. + * This is required by the hypervisor */ + . = 0x20 + .llong hvReleaseData-KERNELBASE + + /* At offset 0x28 and 0x30 are offsets to the msChunks + * array (used by the iSeries LPAR debugger to do translation + * between physical addresses and absolute addresses) and + * to the pidhash table (also used by the debugger) */ + .llong msChunks-KERNELBASE + .llong pidhash-KERNELBASE + + /* Secondary processors spin on this value until it goes to 1. */ + .globl __secondary_hold_spinloop +__secondary_hold_spinloop: + .llong 0x0 + + /* Secondary processors write this value with their cpu # */ + /* after they enter the spin loop immediatly below. */ + .globl __secondary_hold_acknowledge +__secondary_hold_acknowledge: + .llong 0x0 + + . = 0x60 +/* + * The following code is used on pSeries to hold secondary processors + * in a spin loop after they have been freed from OpenFirmware, but + * before the bulk of the kernel has been relocated. This code + * is relocated to physical address 0x60 before prom_init is run. + * All of it must fit below the first exception vector at 0x100. + */ +_GLOBAL(__secondary_hold) + /* Grab our cpu# */ + mr r24,r3 + + /* Tell the master cpu we're here */ + /* Relocation is off & we are located at an address less */ + /* than 0x100, so only need to grab low order offset. */ + li r3,__secondary_hold_acknowledge@l + std r24,0(r3) + + /* Get the address on which we are going to spin. */ + li r3,__secondary_hold_spinloop@l + + /* All secondary cpu's wait here until told to start. */ +100: ld r4,0(r3) + cmpdi 0,r4,1 + bne 100b + +#ifdef CONFIG_SMP + mr r3,r24 + b .pseries_secondary_smp_init +#else + BUG_OPCODE +#endif + +/* + * The following macros define the code that appears as + * the prologue to each of the exception handlers. They + * are split into two parts to allow a single kernel binary + * to be used for pSeries, and iSeries. + */ + +/* + * We make as much of the exception code common between native Pseries + * and Iseries LPAR implementations as possible. + */ + +/* + * This is the start of the interrupt handlers for Pseries + * This code runs with relocation off. + */ + +#define EXCEPTION_PROLOG_PSERIES(label) \ + mtspr SPRG2,r20; /* use SPRG2 as scratch reg */ \ + mfspr r20,SPRG3; /* get Paca virt addr */ \ + clrldi r20,r20,12; /* convert virt to real addr */ \ + std r21,PACAR21(r20); /* Save GPR21 in Paca */ \ + mfspr r21,SRR0; /* EA of interrupted instr */ \ + std r21,LPPACA+LPPACASRR0(r20); /* Set SRR0 in ItLpPaca */ \ + mfspr r21,SRR1; /* machine state at interrupt */ \ + std r21,LPPACA+LPPACASRR1(r20); /* Set SRR1 in ItLpPaca */ \ + SET_REG_TO_LABEL(r21,label); /* Get addr to branch to */ \ + mtspr SRR0,r21; \ + li r20,0x5; /* Turn on 64b, 64bints */ \ + sldi r20,r20,61; \ + ori r20,r20,0x30; /* for generic handlers */ \ + mfmsr r21; /* Turn on IR & DR */ \ + or r21,r21,r20; /* for generic handlers */ \ + mtspr SRR1,r21; \ + rfid /* Jump to generic handlers */ +/* + * This is the start of the interrupt handlers for i_series + * This code runs with relocation on. + */ + +#define EXCEPTION_PROLOG_ISERIES \ + mtspr SPRG2,r20; /* use SPRG2 as scratch reg */\ + mfspr r20,SPRG3; /* get Paca */\ + std r21,PACAR21(r20) /* Save GPR21 in Paca */ + +#define EXCEPTION_PROLOG_COMMON \ + mfspr r20,SPRG3; /* get Paca virt addr */ \ + std r22,PACAR22(r20); /* Save GPR22 in Paca */\ + mfcr r22; \ + ld r21,LPPACA+LPPACASRR1(r20); /* Get SRR1 from ItLpPaca */ \ + andi. r21,r21,MSR_PR; /* Set CR for later branch */ \ + beq+ 1f; \ + ld r21,PACAKSAVE(r20); /* exception stack to use */\ + b 2f; \ +1: \ + subi r21,r1,INT_FRAME_SIZE;/* alloc exc. frame */ \ +2: \ + std r22,_CCR(r21); /* save CR in stackframe */\ + ld r22,PACAR21(r20); /* Get GPR21 from Paca */\ + std r22,GPR21(r21); /* Save GPR21 in stackframe */\ + ld r22,PACAR22(r20); /* Get GPR22 from Paca */\ + std r22,GPR22(r21); /* Save GPR22 in stackframe */\ + std r23,GPR23(r21); /* Save GPR23 in stackframe */\ + mfspr r22,SPRG2; /* Get GPR20 from SPRG2 */\ + std r22,GPR20(r21); /* Save GPR20 in stackframe */\ + mflr r22; \ + std r22,_LINK(r21); \ + mfctr r22; \ + std r22,_CTR(r21); \ + mfspr r22,XER; \ + std r22,_XER(r21); \ + ld r22,LPPACA+LPPACASRR0(r20); /* Get SRR0 from ItLpPaca */\ + ld r23,LPPACA+LPPACASRR1(r20); /* Get SRR1 from ItLpPaca */\ + SAVE_8GPRS(0, r21); \ + SAVE_4GPRS(8, r21); \ + SAVE_2GPRS(12, r21); \ + ld r2,PACATOC(r20); \ + std r1,0(r21); \ + mr r1,r21 +/* + * Note: code which follows this uses cr0.eq (set if from kernel), + * r21, r22 (SRR0), and r23 (SRR1). + */ + +/* + * Exception vectors. + */ +#define STD_EXCEPTION_PSERIES(n, label ) \ + . = n; \ + .globl label##_Pseries; \ +label##_Pseries: \ + EXCEPTION_PROLOG_PSERIES( label##_common ) + +#define STD_EXCEPTION_ISERIES( label ) \ + .globl label##_Iseries; \ +label##_Iseries: \ + EXCEPTION_PROLOG_ISERIES; \ + b label##_common + +#define STD_EXCEPTION_COMMON( trap, label, hdlr ) \ + .globl label##_common; \ +label##_common: \ + EXCEPTION_PROLOG_COMMON; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + SET_REG_TO_CONST(r20, MSR_KERNEL); \ + li r6,trap; \ + bl .transfer_to_handler; \ + .llong hdlr; \ + .llong .ret_from_except + +/* + * Start of pSeries system interrupt routines + */ + . = 0x100 + .globl __start_interupts +__start_interupts: + + STD_EXCEPTION_PSERIES( 0x100, SystemReset ) + STD_EXCEPTION_PSERIES( 0x200, MachineCheck ) + STD_EXCEPTION_PSERIES( 0x300, DataAccess ) + STD_EXCEPTION_PSERIES( 0x380, DataAccessSLB ) + STD_EXCEPTION_PSERIES( 0x400, InstructionAccess ) + STD_EXCEPTION_PSERIES( 0x480, InstructionAccessSLB ) + STD_EXCEPTION_PSERIES( 0x500, HardwareInterrupt ) + STD_EXCEPTION_PSERIES( 0x600, Alignment ) + STD_EXCEPTION_PSERIES( 0x700, ProgramCheck ) + STD_EXCEPTION_PSERIES( 0x800, FPUnavailable ) + STD_EXCEPTION_PSERIES( 0x900, Decrementer ) + STD_EXCEPTION_PSERIES( 0xa00, Trap_0a ) + STD_EXCEPTION_PSERIES( 0xb00, Trap_0b ) + STD_EXCEPTION_PSERIES( 0xc00, SystemCall ) + STD_EXCEPTION_PSERIES( 0xd00, SingleStep ) + STD_EXCEPTION_PSERIES( 0xe00, Trap_0e ) + STD_EXCEPTION_PSERIES( 0xf00, PerformanceMonitor ) + + . = 0x4000 + .globl __end_interupts + .globl __start_naca +__end_interupts: +__start_naca: + .globl xNaca + /* Save space for naca. + * The first dword of the Naca is required by iSeries LPAR to + * point to itVpdAreas. On pSeries native, this value is not used. + */ + .llong itVpdAreas + .llong 0x0 + .llong 0x0 + .llong xPaca + + /* + * Space for the initial segment table + * For LPAR, the hypervisor must fill in at least one entry + * before we get control (with relocate on) + */ + + . = 0x5000 + .globl __end_naca + .globl __start_stab +__end_naca: +__start_stab: + + + . = 0x6000 + .globl __end_stab +__end_stab: + + /* + * The iSeries LPAR map is at this fixed address + * so that the HvReleaseData structure can address + * it with a 32-bit offset. + * + * The VSID values below are dependent on the + * VSID generation algorithm. See include/asm/mmu_context.h. + */ + + .llong 1 /* # ESIDs to be mapped by hypervisor */ + .llong 1 /* # memory ranges to be mapped by hypervisor */ + .llong 5 /* Page # of segment table within load area */ + .llong 0 /* Reserved */ + .llong 0 /* Reserved */ + .llong 0 /* Reserved */ + .llong 0 /* Reserved */ + .llong 0 /* Reserved */ + .llong 0x0c00000000 /* ESID to map (Kernel at EA = 0xC000000000000000) */ + .llong 0x06a99b4b14 /* VSID to map (Kernel at VA = 0x6a99b4b140000000) */ + .llong 8192 /* # pages to map (32 MB) */ + .llong 0 /* Offset from start of loadarea to start of map */ + .llong 0x0006a99b4b140000 /* VPN of first page to map */ + + . = 0x6100 + +/*** ISeries-LPAR interrupt handlers ***/ + + STD_EXCEPTION_ISERIES( MachineCheck ) + STD_EXCEPTION_ISERIES( DataAccess ) + STD_EXCEPTION_ISERIES( DataAccessSLB ) + STD_EXCEPTION_ISERIES( InstructionAccess ) + STD_EXCEPTION_ISERIES( InstructionAccessSLB ) + STD_EXCEPTION_ISERIES( HardwareInterrupt ) + STD_EXCEPTION_ISERIES( Alignment ) + STD_EXCEPTION_ISERIES( ProgramCheck ) + STD_EXCEPTION_ISERIES( FPUnavailable ) + STD_EXCEPTION_ISERIES( Decrementer ) + STD_EXCEPTION_ISERIES( Trap_0a ) + STD_EXCEPTION_ISERIES( Trap_0b ) + STD_EXCEPTION_ISERIES( SystemCall ) + STD_EXCEPTION_ISERIES( SingleStep ) + STD_EXCEPTION_ISERIES( Trap_0e ) + STD_EXCEPTION_ISERIES( PerformanceMonitor ) + + .globl SystemReset_Iseries +SystemReset_Iseries: + 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(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 */ + lis r3,0x8002 + rldicr r0,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */ + rldicl r3,r3,0,48 /* r3 = r3 & 0x000000000000ffff */ + or r3,r3,r0 /* r3 = r3 | r0 */ +#else /* CONFIG_SMP */ + /* Yield the processor. This is required for non-SMP kernels + which are running on multi-threaded machines. */ + lis r3,0x8000 + rldicr r3,r3,32,15 /* r3 = (r3 << 32) & 0xffff000000000000 */ + 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 r25,SPRG3 /* Put r25 back ???? */ + b 1b /* If SMP not configured, secondaries + * loop forever */ + +/*** Common interrupt handlers ***/ + + STD_EXCEPTION_COMMON( 0x100, SystemReset, .SystemResetException ) + STD_EXCEPTION_COMMON( 0x200, MachineCheck, .MachineCheckException ) + STD_EXCEPTION_COMMON( 0x900, Decrementer, .timer_interrupt ) + STD_EXCEPTION_COMMON( 0xa00, Trap_0a, .UnknownException ) + STD_EXCEPTION_COMMON( 0xb00, Trap_0b, .UnknownException ) + STD_EXCEPTION_COMMON( 0xd00, SingleStep, .SingleStepException ) + 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) + andis. r0,r20,0xa450 /* weird error? */ + bne 1f /* if not, try to put a PTE */ + mfspr r3,DAR /* into the hash table */ + std r3,_DAR(r21) + rlwinm r4,r20,32-23,29,29 /* DSISR_STORE -> _PAGE_RW */ + + andis. r0,r20,0x0020 /* Is it a page table fault? */ + beq 2f /* If so handle it */ + li r4,0x300 /* Trap number */ + bl .do_stab_SI + b 1f + +2: bl .do_hash_page_DSI /* Try to handle as hpte fault */ +1: + ld r4,_DAR(r21) + ld r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + + SET_REG_TO_CONST(r20, MSR_KERNEL) + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + li r6,0x300 + bl .transfer_to_handler + .llong .do_page_fault + .llong .ret_from_except + + .globl DataAccessSLB_common +DataAccessSLB_common: + EXCEPTION_PROLOG_COMMON + mfspr r3,DAR + li r4,0x380 /* Exception vector */ + bl .ste_allocate + or. r3,r3,r3 /* Check return code */ + bne 1f /* Branch on failure */ + + /* SLB Allocation success - Return. */ + ld r3,_CCR(r21) + ld r4,_LINK(r21) + ld r5,_CTR(r21) + ld r6,_XER(r21) + + mtcrf 0xff,r3 + mtlr r4 + mtctr r5 + mtspr XER,r6 + REST_8GPRS(0,r21) + REST_4GPRS(8,r21) + REST_2GPRS(12,r21) + mtspr SRR1,r23 + mtspr SRR0,r22 + ld r20,GPR20(r21) + ld r22,GPR22(r21) + ld r23,GPR23(r21) + ld r21,GPR21(r21) + RFID + +1: + mfspr r4,DAR + std r4,_DAR(r21) + mfspr r5,DSISR + std r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + + SET_REG_TO_CONST(r20, MSR_KERNEL) + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + li r6,0x380 + bl .transfer_to_handler + .llong .do_page_fault + .llong .ret_from_except + + .globl InstructionAccess_common +InstructionAccess_common: + EXCEPTION_PROLOG_COMMON + + andis. r0,r23,0x0020 /* no ste found? */ + beq 2f + mr r3,r22 /* SRR0 at interrupt */ + li r4,0x400 /* Trap number */ + bl .do_stab_SI + b 1f + +2: andis. r0,r23,0x4000 /* no pte found? */ + beq 1f /* if so, try to put a PTE */ + mr r3,r22 /* into the hash table */ + bl .do_hash_page_ISI /* Try to handle as hpte fault */ +1: + mr r4,r22 + mr r5,r23 + addi r3,r1,STACK_FRAME_OVERHEAD + SET_REG_TO_CONST(r20, MSR_KERNEL) + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + li r6,0x400 + bl .transfer_to_handler + .llong .do_page_fault + .llong .ret_from_except + + .globl InstructionAccessSLB_common +InstructionAccessSLB_common: + EXCEPTION_PROLOG_COMMON + mr r3,r22 /* SRR0 = NIA */ + li r4,0x480 /* Exception vector */ + bl .ste_allocate + or. r3,r3,r3 /* Check return code */ + bne 1f /* Branch on failure */ + + /* SLB Allocation success - Return. */ + ld r3,_CCR(r21) + ld r4,_LINK(r21) + ld r5,_CTR(r21) + ld r6,_XER(r21) + + mtcrf 0xff,r3 + mtlr r4 + mtctr r5 + mtspr XER,r6 + REST_8GPRS(0,r21) + REST_4GPRS(8,r21) + REST_2GPRS(12,r21) + mtspr SRR1,r23 + mtspr SRR0,r22 + ld r20,GPR20(r21) + ld r22,GPR22(r21) + ld r23,GPR23(r21) + ld r21,GPR21(r21) + RFID + +1: + mfspr r4,DAR + std r4,_DAR(r21) + mfspr r5,DSISR + std r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + + SET_REG_TO_CONST(r20, MSR_KERNEL) + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + li r6,0x380 + bl .transfer_to_handler + .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 + mfspr r4,DAR + std r4,_DAR(r21) + mfspr r5,DSISR + std r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + SET_REG_TO_CONST(r20, MSR_KERNEL) + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + li r6,0x600 + bl .transfer_to_handler + .llong .AlignmentException + .llong .ret_from_except + + .globl ProgramCheck_common +ProgramCheck_common: + EXCEPTION_PROLOG_COMMON + addi r3,r1,STACK_FRAME_OVERHEAD + SET_REG_TO_CONST(r20, MSR_KERNEL) + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + li r6,0x700 + bl .transfer_to_handler + .llong .ProgramCheckException + .llong .ret_from_except + + .globl FPUnavailable_common +FPUnavailable_common: + EXCEPTION_PROLOG_COMMON + bne .load_up_fpu /* if from user, just load it up */ + SET_REG_TO_CONST(r20, MSR_KERNEL) + li r6,0x800 + bl .transfer_to_handler /* if from kernel, take a trap */ + .llong .KernelFP + .llong .ret_from_except + + .globl SystemCall_common +SystemCall_common: + EXCEPTION_PROLOG_COMMON +#ifdef CONFIG_PPC_ISERIES + 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: +#endif + std r3,ORIG_GPR3(r21) + SET_REG_TO_CONST(r20, MSR_KERNEL) + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + li r6,0xC00 + bl .transfer_to_handler + .llong .DoSyscall + .llong .ret_from_except + +_GLOBAL(do_hash_page_ISI) + li r4,0 +_GLOBAL(do_hash_page_DSI) + rlwimi r4,r23,32-13,30,30 /* Insert MSR_PR as _PAGE_USER */ + ori r4,r4,1 /* add _PAGE_PRESENT */ + + mflr r21 /* Save LR in r21 */ + /* r21 restored later from r1 */ + /* + * r3 contains the faulting address + * r4 contains the required access permissions + * + * at return r3 = 0 for success + */ + + bl .hash_page /* build HPTE if possible */ + mtlr r21 /* restore LR */ + mr r21,r1 /* restore r21 */ + + or. r3,r3,r3 /* Check return code */ + bnelr /* Return to DSI or ISI on failure */ + + /* + * The HPTE has been created/updated. Restore and retry the faulting inst + */ + + ld r3,_CCR(r21) + ld r4,_LINK(r21) + ld r5,_CTR(r21) + ld r6,_XER(r21) + mtcrf 0xff,r3 + mtlr r4 + mtctr r5 + mtspr XER,r6 + REST_8GPRS(0,r21) + REST_4GPRS(8,r21) + REST_2GPRS(12,r21) + mtspr SRR1,r23 + mtspr SRR0,r22 + ld r20,GPR20(r21) + ld r22,GPR22(r21) + ld r23,GPR23(r21) + 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 */ + /* + * r3 contains the faulting address + * r4 contains the required access permissions + * + * at return r3 = 0 for success + */ + + bl .ste_allocate /* build STE if possible */ + mtlr r21 /* restore LR */ + mr r21,r1 /* restore r21 */ + + or. r3,r3,r3 /* Check return code */ + bnelr /* Return to DSI or ISI on failure */ + + /* + * The STE has been created/updated. Restore and retry the faulting inst + */ + + ld r3,_CCR(r21) + ld r4,_LINK(r21) + ld r5,_CTR(r21) + ld r6,_XER(r21) + + mtcrf 0xff,r3 + mtlr r4 + mtctr r5 + mtspr XER,r6 + REST_8GPRS(0,r21) + REST_4GPRS(8,r21) + REST_2GPRS(12,r21) + mtspr SRR1,r23 + mtspr SRR0,r22 + ld r20,GPR20(r21) + ld r22,GPR22(r21) + ld r23,GPR23(r21) + ld r21,GPR21(r21) + rfid + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. Address + * translation is already on. + */ +_GLOBAL(transfer_to_handler) +/* + * Save the rest of the registers into the pt_regs structure + */ + std r22,_NIP(r21) + std r23,_MSR(r21) + std r6,TRAP(r21) + ld r6,GPR6(r21) + SAVE_2GPRS(14, r21) + SAVE_4GPRS(16, r21) + SAVE_8GPRS(24, r21) +/* + * Clear the RESULT field + */ + 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 */ +/* + * Indicate that r1 contains the kernel stack and + * get the Kernel TOC and CURRENT pointers from the Paca + */ + mfspr r23,SPRG3 /* Get PACA */ + std r22,PACAKSAVE(r23) /* r1 is now kernel sp */ + ld r2,PACATOC(r23) /* Get Kernel TOC pointer */ + ld r22,PACACURRENT(r23) /* Get CURRENT */ +/* + * If from user state, update THREAD.regs + */ + beq 2f /* Modify THREAD.regs if from user */ + 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 + * set here because it could have been changed by the user. + */ + +/* + * Check for kernel stack overflow + */ + addi r24,r22,TASK_STRUCT_SIZE /* check for kernel stack overflow */ + cmpld 0,r1,r22 + cmpld 1,r1,r24 + crand 1,1,4 + bgt- .stack_ovf /* if r22 < r1 < r22+TASK_STRUCT_SIZE */ + +/* + * Get the handler address and its return address and + * rfid to the handler + */ +/* MIKEC: Do we really need to rfid here? Since we are already running + with relocate on, could we just branch to the handler? + We also will be turning on MSR.EE for some interrupts here +*/ + mflr r23 + ld r24,0(r23) /* virtual address of handler */ + ld r23,8(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + rfid /* jump to handler */ + +#ifdef CONFIG_SMP +/* + * On pSeries, secondary processors spin in the following code. + * 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. */ + + mtspr SPRG3,r25 /* Save vaddr of Paca in SPRG3 */ + mr r24,r3 /* __secondary_start needs cpu# */ + +1: + HMT_LOW + 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 SECONDARY_PROCESSORS + bne .__secondary_start +#endif + b 1b /* Loop until told to go */ +#endif /* CONFIG_SMP */ + +_GLOBAL(__start_initialization_iSeries) + + LOADADDR(r1,init_task_union) + addi r1,r1,TASK_UNION_SIZE + li r0,0 + stdu r0,-STACK_FRAME_OVERHEAD(r1) + + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + + LOADADDR(r9,naca) + SET_REG_TO_CONST(r4, KERNELBASE) + addi r4,r4,0x4000 + std r4,0(r9) /* set the naca pointer */ + + /* Get the pointer to the segment table */ + ld r6,PACA(r4) /* Get the base Paca pointer */ + ld r4,PACASTABVIRT(r6) + + li r3,1 /* Indicate to skip adding esid C to table */ + bl .stab_initialize + + b .start_here_common + +_GLOBAL(__start_initialization_pSeries) + mr r31,r3 /* save parameters */ + mr r30,r4 + mr r29,r5 + mr r28,r6 + mr r27,r7 + mr r26,r8 /* YABOOT: debug_print() routine */ + mr r25,r9 /* YABOOT: debug_delay() routine */ + mr r24,r10 /* YABOOT: debug_prom() routine */ + + bl .enable_64b_mode + + /* put a relocation offset into r3 */ + bl .reloc_offset + + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + + /* Relocate the TOC from a virt addr to a real addr */ + sub r2,r2,r3 + + /* setup the naca pointer which is needed by prom_init */ + LOADADDR(r9,naca) + sub r9,r9,r3 /* addr of the variable naca */ + + SET_REG_TO_CONST(r4, KERNELBASE) + sub r4,r4,r3 + addi r4,r4,0x4000 + std r4,0(r9) /* set the value of naca */ + + /* DRENG / PPPBBB Fix the following comment!!! -Peter */ + /* The following copies the first 0x100 bytes of code from the */ + /* load addr to physical addr 0x0. This code causes secondary */ + /* processors to spin until a flag in the PACA is set. This */ + /* is done at this time rather than with the entire kernel */ + /* relocation which is done below because we need to cause the */ + /* processors to spin on code that is not going to move while OF */ + /* is still alive. Although the spin code is not actually run on */ + /* a uniprocessor, we always do this copy. */ + SET_REG_TO_CONST(r4, KERNELBASE)/* Src addr */ + sub r4,r4,r3 /* current address of __start */ + /* the source addr */ + li r3,0 /* Dest addr */ + li r5,0x100 /* # bytes of memory to copy */ + li r6,0 /* Destination offset */ + bl .copy_and_flush /* copy the first 0x100 bytes */ + + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + mr r8,r26 + mr r9,r25 + mr r10,r24 + + bl .prom_init + + li r24,0 /* cpu # */ + +/* + * At this point, r3 contains the physical address we are running at, + * returned by prom_init() + */ +_STATIC(__after_prom_start) + +/* + * We need to run with __start at physical address 0. + * This will leave some code in the first 256B of + * real memory, which are reserved for software use. + * The remainder of the first page is loaded with the fixed + * interrupt vectors. The next two pages are filled with + * unknown exception placeholders. + * + * Note: This process overwrites the OF exception vectors. + * r26 == relocation offset + * r27 == KERNELBASE + */ + bl .reloc_offset + mr r26,r3 + SET_REG_TO_CONST(r27,KERNELBASE) + + li r3,0 /* target addr */ + + sub r4,r27,r26 /* source addr */ + /* current address of _start */ + /* i.e. where we are running */ + /* the source addr */ + + LOADADDR(r5,copy_to_here) /* # bytes of memory to copy */ + sub r5,r5,r27 + + li r6,0x100 /* Start offset, the first 0x100 */ + /* bytes were copied earlier. */ + + bl .copy_and_flush /* copy the first n bytes */ + /* this includes the code being */ + /* executed here. */ + + li r0,4f@l /* Jump to the copy of this code */ + mtctr r0 /* that we just made */ + bctr + +4: LOADADDR(r9,rtas) + sub r9,r9,r26 + ld r5,RTASBASE(r9) /* get the value of rtas->base */ + ld r9,RTASSIZE(r9) /* get the value of rtas->size */ + bl .copy_and_flush /* copy upto rtas->base */ + add r6,r6,r9 /* then skip over rtas->size bytes */ + + LOADADDR(r5,klimit) + sub r5,r5,r26 + ld r5,0(r5) /* get the value of klimit */ + sub r5,r5,r27 + bl .copy_and_flush /* copy the rest */ + b .start_here_pSeries + +/* + * Copy routine used to copy the kernel to start at physical address 0 + * and flush and invalidate the caches as needed. + * r3 = dest addr, r4 = source addr, r5 = copy limit, r6 = start offset + * on exit, r3, r4, r5 are unchanged, r6 is updated to be >= r5. + * + * Note: this routine *only* clobbers r0, r6 and lr + */ +_STATIC(copy_and_flush) + addi r5,r5,-8 + addi r6,r6,-8 +4: li r0,16 /* Use the least common */ + /* denominator cache line */ + /* size. This results in */ + /* extra cache line flushes */ + /* but operation is correct. */ + /* Can't get cache line size */ + /* from NACA as it is being */ + /* moved too. */ + + mtctr r0 /* put # words/line in ctr */ +3: addi r6,r6,8 /* copy a cache line */ + ldx r0,r6,r4 + stdx r0,r6,r3 + bdnz 3b + dcbst r6,r3 /* write it to memory */ + sync + icbi r6,r3 /* flush the icache line */ + cmpld 0,r6,r5 + blt 4b + sync + addi r5,r5,8 + addi r6,r6,8 + blr + +.align 8 +copy_to_here: + +/* + * On kernel stack overflow, load up an initial stack pointer + * and call StackOverflow(regs), which should not return. + */ +_STATIC(stack_ovf) + addi r3,r1,STACK_FRAME_OVERHEAD + LOADADDR(r1,init_task_union) + addi r1,r1,TASK_UNION_SIZE-STACK_FRAME_OVERHEAD + LOADADDR(r24,StackOverflow) + SET_REG_TO_CONST(r20, MSR_KERNEL); + mtspr SRR0,r24 + mtspr SRR1,r20 + rfid + +/* + * Disable FP for the task which had the FPU previously, + * and save its floating-point registers in its thread_struct. + * Enables the FPU for use in the kernel on return. + * On SMP we know the fpu is free, since we give it up every + * switch. -- Cort + */ +_STATIC(load_up_fpu) + mfmsr r5 /* grab the current MSR */ + ori r5,r5,MSR_FP + mtmsrd r5 /* enable use of fpu now */ + SYNC +/* + * For SMP, we don't do lazy FPU switching because it just gets too + * horrendously complex, especially when a task switches from one CPU + * to another. Instead we call giveup_fpu in switch_to. + * + */ +#ifndef CONFIG_SMP + LOADBASE(r3,last_task_used_math) + ld r4,last_task_used_math@l(r3) + cmpi 0,r4,0 + beq 1f + addi r4,r4,THREAD /* want THREAD of last_task_used_math */ + SAVE_32FPRS(0, r4) + mffs fr0 + stfd fr0,THREAD_FPSCR-4(r4) + ld r5,PT_REGS(r4) + ld r4,_MSR-STACK_FRAME_OVERHEAD(r5) + li r20,MSR_FP|MSR_FE0|MSR_FE1 + andc r4,r4,r20 /* disable FP for previous task */ + std r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: +#endif /* CONFIG_SMP */ + /* enable use of FP after return */ + ori r23,r23,MSR_FP|MSR_FE0|MSR_FE1 + mfspr r5,SPRG3 /* current task's PACA */ + ld r5,PACACURRENT(r5) /* Get 'current' */ + addi r5,r5,THREAD /* Get THREAD */ + lfd fr0,THREAD_FPSCR-4(r5) + mtfsf 0xff,fr0 + REST_32FPRS(0, r5) +#ifndef CONFIG_SMP + subi r4,r5,THREAD /* Back to 'current' */ + std r4,last_task_used_math@l(r3) +#endif /* CONFIG_SMP */ + /* restore registers and return */ + ld r3,_CCR(r21) + ld r4,_LINK(r21) + mtcrf 0xff,r3 + mtlr r4 + REST_2GPRS(1, r21) + REST_4GPRS(3, r21) + /* we haven't used ctr or xer */ + mtspr SRR1,r23 + mtspr SRR0,r22 + REST_GPR(20, r21) + REST_2GPRS(22, r21) + ld r21,GPR21(r21) + rfid + +/* + * FP unavailable trap from kernel - print a message, but let + * the task use FP in the kernel until it returns to user mode. + */ +_GLOBAL(KernelFP) + ld r3,_MSR(r1) + ori r3,r3,MSR_FP + std r3,_MSR(r1) /* enable use of FP after return */ + LOADADDR(r3,86f) + mfspr r4,SPRG3 /* Get PACA */ + ld r4,PACACURRENT(r4) /* current */ + ld r5,_NIP(r1) + b .ret_from_except +86: .string "floating point used in kernel (task=%p, pc=%x)\n" + .align 4 + +/* + * giveup_fpu(tsk) + * Disable FP for the task given as the argument, + * and save the floating-point registers in its thread_struct. + * Enables the FPU for use in the kernel on return. + */ +_GLOBAL(giveup_fpu) + mfmsr r5 + ori r5,r5,MSR_FP + mtmsrd r5 /* enable use of fpu now */ + SYNC + cmpi 0,r3,0 + beqlr- /* if no previous owner, done */ + addi r3,r3,THREAD /* want THREAD of task */ + ld r5,PT_REGS(r3) + cmpi 0,r5,0 + SAVE_32FPRS(0, r3) + mffs fr0 + stfd fr0,THREAD_FPSCR-4(r3) + beq 1f + ld r4,_MSR-STACK_FRAME_OVERHEAD(r5) + li r3,MSR_FP|MSR_FE0|MSR_FE1 + andc r4,r4,r3 /* disable FP for previous task */ + std r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: +#ifndef CONFIG_SMP + li r5,0 + LOADBASE(r4,last_task_used_math) + std r5,last_task_used_math@l(r4) +#endif /* CONFIG_SMP */ + blr + + + +#ifdef CONFIG_SMP +/* + * This function is called after the master CPU has released the + * secondary processors. The execution environment is relocation off. + * The Paca for this processor has the following fields initialized at + * this point: + * 1. Processor number + * 2. Segment table pointer (virtual address) + * On entry the following are set: + * r24 = cpu# (in Linux terms) + * r25 = Paca virtual address + * SPRG3 = Paca virtual address + */ +_GLOBAL(__secondary_start) + + HMT_MEDIUM /* Set thread priority to MEDIUM */ + + /* set up the TOC (virtual address) */ + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + + std r2,PACATOC(r25) + li r6,0 + std r6,PACAKSAVE(r25) + +#ifndef CONFIG_PPC_ISERIES + /* Initialize the page table pointer register. */ + 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,PACASTABVIRT(r25) /* get addr of segment table */ + li r3,0 /* 0 -> include esid 0xC00000000 */ + std r3,0(r1) /* Zero the stack frame pointer */ + bl .stab_initialize + + LOADADDR(r3,current_set) + 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 + + 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 + mtlr r7 + + /* enable MMU and jump to start_secondary */ + LOADADDR(r3,.start_secondary) + SET_REG_TO_CONST(r4, MSR_KERNEL) + mtspr SRR0,r3 + mtspr SRR1,r4 + rfid +#endif /* CONFIG_SMP */ + +/* + * This subroutine clobbers r11, r12 and the LR + */ +_GLOBAL(enable_64b_mode) + mfmsr r11 /* grab the current MSR */ + li r12,1 + rldicr r12,r12,MSR_SF_LG,(63-MSR_SF_LG) + or r11,r11,r12 + li r12,1 + rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG) + or r11,r11,r12 + mtmsrd r11 + SYNC + blr + +/* + * This subroutine clobbers r11, r12 and the LR + */ +_GLOBAL(enable_32b_mode) + mfmsr r11 /* grab the current MSR */ + li r12,1 + rldicr r12,r12,MSR_SF_LG,(63-MSR_SF_LG) + andc r11,r11,r12 + li r12,1 + rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG) + andc r11,r11,r12 + mtmsrd r11 + SYNC + blr + + +/* + * This is where the main kernel code starts. + */ +_STATIC(start_here_pSeries) + +#ifdef CONFIG_SMP + /* All secondary cpus are now spinning on a common + * spinloop, release them all now so they can start + * to spin on their individual Paca spinloops. + * For non SMP kernels, the secondary cpus never + * get out of the common spinloop. + */ + li r3,1 + LOADADDR(r5,__secondary_hold_spinloop) + tophys(r4,r5) + std r3,0(r4) +#endif + + /* get a new offset, now that the kernel has moved. */ + bl .reloc_offset + mr r26,r3 + + /* The following gets the stack and TOC set up with the regs */ + /* pointing to the real addr of the kernel stack. This is */ + /* all done to support the C function call below which sets */ + /* up the htab. This is done because we have relocated the */ + /* kernel but are still running in real mode. */ + + /* real ptr to current */ + LOADADDR(r3,init_task_union) + sub r3,r3,r26 + + /* set up a stack pointer (physical address) */ + addi r1,r3,TASK_UNION_SIZE + li r0,0 + stdu r0,-STACK_FRAME_OVERHEAD(r1) + + /* set up the TOC (physical address) */ + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + sub r2,r2,r26 + + /* setup the naca pointer which is needed by *tab_initialize */ + LOADADDR(r6,naca) + sub r6,r6,r26 /* addr of the variable naca */ + li r27,0x4000 + std r27,0(r6) /* set the value of naca */ + + /* Init naca->debug_switch so it can be used in stab & htab init. */ + bl .ppcdbg_initialize + + /* Get the pointer to the segment table which is used by */ + /* stab_initialize */ + ld r6,PACA(r27) /* Get the base Paca pointer */ + sub r6,r6,r26 /* convert to physical addr */ + mtspr SPRG3,r6 /* PPPBBB: Temp... -Peter */ + ld r4,PACASTABREAL(r6) + ori r3,r4,1 /* turn on valid bit */ + mtasr r3 /* set the stab location */ + + /* Initialize an initial memory mapping and turn on relocation. */ + li r3,0 /* 0 -> include esid 0xC00000000 */ + slbia + bl .stab_initialize + bl .htab_initialize + + LOADADDR(r6,_SDR1) + sub r6,r6,r26 + ld r6,0(r6) /* get the value of _SDR1 */ + mtspr SDR1,r6 /* set the htab location */ + + LOADADDR(r3,.start_here_common) + SET_REG_TO_CONST(r4, MSR_KERNEL) + mtspr SRR0,r3 + mtspr SRR1,r4 + rfid + + /* This is where all platforms converge execution */ +_STATIC(start_here_common) + /* relocation is on at this point */ + + /* Clear out the BSS */ + LOADADDR(r11,_end) + + LOADADDR(r8,__bss_start) + + sub r11,r11,r8 /* bss size */ + addi r11,r11,7 /* round up to an even double word */ + rldicl. r11,r11,61,3 /* shift right by 3 */ + beq 4f + addi r8,r8,-8 + li r0,0 + mtctr r11 /* zero this many doublewords */ +3: stdu r0,8(r8) + bdnz 3b +4: + + /* The following code sets up the SP and TOC now that we are */ + /* running with translation enabled. */ + + /* ptr to current */ + LOADADDR(r3,init_task_union) + + /* set up the stack */ + addi r1,r3,TASK_UNION_SIZE + li r0,0 + stdu r0,-STACK_FRAME_OVERHEAD(r1) + + /* set up the TOC */ + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + + /* setup the naca pointer */ + LOADADDR(r9,naca) + + SET_REG_TO_CONST(r8, KERNELBASE) + addi r8,r8,0x4000 + std r8,0(r9) /* set the value of the naca ptr */ + + bl .naca_init /* Set initial values in the naca */ + + LOADADDR(r4,naca) /* Get Naca ptr address */ + ld r4,0(r4) /* Get the location of the naca */ + ld r4,PACA(r4) /* Get the base Paca pointer */ + mtspr SPRG3,r4 + + /* ptr to current */ + LOADADDR(r3,init_task_union) + std r3,PACACURRENT(r4) + + std r2,PACATOC(r4) + li r5,0 + std r0,PACAKSAVE(r4) +/* + * Register old definition new definition + * + * SPRG0 temp - used to save gpr reserved for hypervisor + * SPRG1 temp - used to save gpr unused + * SPRG2 0 or kernel stack frame temp - used to save gpr + * SPRG3 Linux thread virt addr of Paca + */ + + /* + * Restore the parms passed in from the bootloader. + */ + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + + bl .setup_system + + /* Load up the kernel context */ +5: + bl .start_kernel + +/* + * We put a few things here that have to be page-aligned. + * This stuff goes at the beginning of the data segment, + * which is page-aligned. + */ + .data + .align 12 + .globl sdata +sdata: + .globl empty_zero_page +empty_zero_page: + .space 4096 + + .globl swapper_pg_dir +swapper_pg_dir: + .space 4096 + + .globl ioremap_dir +ioremap_dir: + .space 4096 + + .globl bolted_dir +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. + */ + .globl cmd_line +cmd_line: + .space 512 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/htab.c linux.ac/arch/ppc64/kernel/htab.c --- linux.vanilla/arch/ppc64/kernel/htab.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/htab.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,1167 @@ +/* + * + * + * PowerPC64 port by Mike Corrigan and Dave Engebretsen + * {mikejc|engebret}@us.ibm.com + * + * Copyright (c) 2000 Mike Corrigan + * + * Module name: htab.c + * + * Description: + * PowerPC Hashed Page Table functions + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* For iSeries */ +#include + +/* Note: pte --> Linux PTE + * HPTE --> PowerPC Hashed Page Table Entry + */ + +HTAB htab_data = {NULL, NULL, 0, 0, 0}; + +static ssize_t ppc_htab_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_htab_write(struct file * file, const char * buffer, + size_t count, loff_t *ppos); +static long ppc_htab_lseek(struct file * file, loff_t offset, int orig); +int proc_dol2crvec(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp); + +void htab_initialize(void); +void make_pte(HPTE * htab, unsigned long va, unsigned long pa, + int mode, unsigned long hash_mask); +extern unsigned long reloc_offset(void); +extern unsigned long get_kernel_vsid( unsigned long ea ); +extern void cacheable_memzero( void *, unsigned int ); + +extern unsigned long _SDR1; +extern unsigned long klimit; +extern struct Naca *naca; + +extern unsigned long _ASR; +extern inline void make_ste(unsigned long stab, + unsigned long esid, unsigned long vsid); + +extern pgd_t * bolted_pgd; + +extern char _stext[], _etext[], __start_naca[], __end_stab[]; + +static spinlock_t hash_table_lock = SPIN_LOCK_UNLOCKED; + +#define PTRRELOC(x) ((typeof(x))((unsigned long)(x) - offset)) +#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) +{ + unsigned long table, htab_size_bytes; + unsigned long pteg_count; + unsigned long mode_ro, mode_rw, mask; + unsigned long offset = reloc_offset(); + struct Naca *_naca = RELOC(naca); + HTAB *_htab_data = PTRRELOC(&htab_data); + + /* + * Calculate the required size of the htab. We want the number of + * PTEGs to equal one half the number of real pages. + */ + htab_size_bytes = htab_size(_naca->physicalMemorySize); + pteg_count = htab_size_bytes >> 7; + _htab_data->htab_num_ptegs = pteg_count; + _htab_data->htab_hash_mask = pteg_count - 1; + + /* Find storage for the HPT. Must be contiguous in + * the absolute address space. + */ + table = lmb_alloc(htab_size_bytes, htab_size_bytes); + if ( !table ) + panic("ERROR, cannot find space for HPTE\n"); + _htab_data->htab = (HPTE *)__a2v(table); + + /* htab absolute addr + encoded htabsize */ + RELOC(_SDR1) = table + __ilog2(pteg_count) - 11; + + /* Initialize the HPT with no entries */ + cacheable_memzero((void *)table, htab_size_bytes); + + mode_ro = _PAGE_ACCESSED | _PAGE_COHERENT | PP_RXRX; + mode_rw = _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX; + mask = pteg_count-1; + + /* Create PTE's for the kernel text and data sections plus + * the HPT and HPTX arrays. Make the assumption that + * (addr & KERNELBASE) == 0 (ie they are disjoint). + * We also assume that the va is <= 64 bits. + */ +#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 ) +{ + unsigned long vsid = vpn >> 16; + unsigned long page = vpn & 0xffff; + + return (vsid & 0x7fffffffff) ^ page; +} + +/* + * Create a pte. Used during initialization only. + * We assume the PTE will fit in the primary PTEG. + */ +void make_pte(HPTE *htab, + unsigned long va, unsigned long pa, int mode, + unsigned long hash_mask) +{ + HPTE *hptep; + unsigned long hash, i; + volatile unsigned long x = 1; + + hash = hpt_hash( va >> 12 ); + + hptep = htab + ((hash & hash_mask)*HPTES_PER_GROUP); + + for (i = 0; i < 8; ++i, ++hptep) { + if ( hptep->dw0.dw0.v == 0 ) { /* !valid */ + hptep->dw1.dword1 = pa | mode; + hptep->dw0.dword0 = 0; + hptep->dw0.dw0.avpn = va >> 23; + hptep->dw0.dw0.bolted = 1; /* bolted */ + hptep->dw0.dw0.v = 1; /* make valid */ + return; + } + } + + /* We should _never_ get here and too early to call xmon. */ + for(;x;x|=1); +} + +void invalidate_hpte( unsigned long slot ) +{ + + /* Invalidate the HPTE */ + + if ( _machine == _MACH_iSeries ) { + HvCallHpt_invalidateSetSwBitsGet( slot, 0, 0 ); + } + else { + /* Local copy of the first doubleword of the HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + + /* Locate the HPTE */ + HPTE * hptep = htab_data.htab + slot; + + /* Get the first doubleword of the HPTE */ + hpte_dw0.d = hptep->dw0.dword0; + + /* Invalidate the hpte */ + hptep->dw0.dword0 = 0; + + /* Invalidate the tlb */ + { + unsigned long vsid, group, pi, pi_high; + + vsid = hpte_dw0.h.avpn >> 5; + group = slot >> 3; + if(hpte_dw0.h.h) { + group = ~group; + } + pi = (vsid ^ group) & 0x7ff; + pi_high = (hpte_dw0.h.avpn & 0x1f) << 11; + pi |= pi_high; + _tlbie(pi << 12); + } + } +} + + +/* Select an available HPT slot for a new HPTE + * return slot index (if in primary group) + * return -slot index (if in secondary group) + */ +long select_hpte_slot( unsigned long vpn ) +{ + +/****** Add code for iSeries ******/ + + HPTE * hptep; + HPTE hpte; + unsigned long primary_hash; + unsigned long hpteg_slot; + long ret_slot, orig_slot; + unsigned i, k; + + if ( _machine == _MACH_iSeries ) { + ret_slot = orig_slot = HvCallHpt_findValid( &hpte, vpn ); + if ( hpte.dw0.dw0.v ) { /* If valid ...what do we do now? */ + udbg_printf( "select_hpte_slot: vpn 0x%016lx already valid at slot 0x%016lx\n", vpn, ret_slot ); + udbg_printf( "select_hpte_slot: returned hpte 0x%016lx 0x%016lx\n", hpte.dw0.dword0, hpte.dw1.dword1 ); + + return (0x8000000000000000); +/* panic("select_hpte_slot found entry already valid\n"); */ + } + if ( ret_slot == -1 ) { /* -1 indicates no available slots */ + /* + * Eventually we will have a hypervisor call which will allow us to conditionally + * invalidate an hpte depending on whether it is bolted. For now, we need to look + * each entry to see if it is bolted before we invalidate it. + */ + + + /* ADD CODE HERE */ + /* add code to cast out a non-bolted hpte */ + } + else { + if ( ret_slot < 0 ) { + ret_slot &= 0x7fffffffffffffff; + ret_slot = -ret_slot; + } + } + PPCDBG(PPCDBG_MM, "select_hpte_slot: vpn=0x%016lx, orig_slot=0x%016lx, ret_slot=0x%016lx \n", + vpn, orig_slot, ret_slot ); + return ret_slot; + } + /* Search the primary group for an available slot */ + + primary_hash = hpt_hash( vpn ); + hpteg_slot = ( primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + hptep = htab_data.htab + hpteg_slot; + + for (i=0; idw0.dw0.v == 0 ) { + /* If an available slot found, return it */ + return hpteg_slot + i; + } + hptep++; + } + + /* No available entry found in primary group */ + + PMC_SW_SYSTEM(htab_primary_overflows); + + /* Search the secondary group */ + + hpteg_slot = ( ~primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + hptep = htab_data.htab + hpteg_slot; + + for (i=0; idw0.dw0.v == 0 ) { + /* If an available slot found, return it */ + return -(hpteg_slot + i); + } + hptep++; + } + + /* No available entry found in secondary group */ + + PMC_SW_SYSTEM(htab_capacity_castouts); + + /* Select an entry in the primary group to replace */ + + hpteg_slot = ( primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + hptep = htab_data.htab + hpteg_slot; + k = htab_data.next_round_robin++ & 0x7; + + for (i=0; idw0.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 ) +{ + unsigned long vpn, flags; + long hpte_slot; + unsigned hash; + pte_t pte; + + vpn = ((vsid << 28) | ( ea & 0xffff000 )) >> 12; + + spin_lock_irqsave( &hash_table_lock, flags ); + + hpte_slot = select_hpte_slot( vpn ); + hash = 0; + if ( hpte_slot < 0 ) { + if ( hpte_slot == 0x8000000000000000 ) { + udbg_printf("hash_page: ptep = 0x%016lx\n", (unsigned long)ptep ); + udbg_printf("hash_page: ea = 0x%016lx\n", ea ); + udbg_printf("hash_page: vpn = 0x%016lx\n", vpn ); + + panic("hash_page: hpte already exists\n"); + } + hash = 1; + hpte_slot = -hpte_slot; + } + create_valid_hpte( hpte_slot, vpn, pa >> 12, hash, ptep, + hpteflags, bolted ); + + if ( ptep ) { + /* Get existing pte flags */ + pte = *ptep; + pte_val(pte) &= ~_PAGE_HPTEFLAGS; + + /* Add in the has hpte flag */ + pte_val(pte) |= _PAGE_HASHPTE; + + /* Add in the _PAGE_SECONDARY flag */ + pte_val(pte) |= hash << 15; + + /* Add in the hpte slot */ + pte_val(pte) |= (hpte_slot << 12) & _PAGE_GROUP_IX; + + /* Save the new pte. */ + *ptep = pte; + + } + + spin_unlock_irqrestore( &hash_table_lock, flags ); + +} + +/* Create an HPTE and validate it + * It is assumed that the HPT slot currently is invalid. + * The HPTE is set with the vpn, rpn (converted to absolute) + * and flags + */ +void create_valid_hpte(unsigned long slot, unsigned long vpn, + unsigned long prpn, unsigned hash, + void * ptep, unsigned hpteflags, + unsigned bolted ) +{ + /* Local copy of HPTE */ + struct { + /* Local copy of first doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } dw0; + /* Local copy of second doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword1 h; + Hpte_dword1_flags f; + } dw1; + } lhpte; + + unsigned long avpn = vpn >> 11; + unsigned long arpn = physRpn_to_absRpn( prpn ); + + /* Fill in the local HPTE with absolute rpn, avpn and flags */ + lhpte.dw1.d = 0; + lhpte.dw1.h.rpn = arpn; + lhpte.dw1.f.flags = hpteflags; + + lhpte.dw0.d = 0; + lhpte.dw0.h.avpn = avpn; + lhpte.dw0.h.h = hash; + lhpte.dw0.h.bolted = bolted; + lhpte.dw0.h.v = 1; + + /* Now fill in the actual HPTE */ + + PPCDBG(PPCDBG_MM, "create_valid_hpte: slot=0x%lx, hash=%d, hpte=0x%016lx 0x%016lx\n", slot, hash, lhpte.dw0.d, lhpte.dw1.d ); + + if ( _machine == _MACH_iSeries ) { + HvCallHpt_addValidate( slot, hash, (HPTE *)&lhpte ); + } + else { + HPTE * hptep = htab_data.htab + slot; + + /* Set the second dword first so that the valid bit + * is the last thing set + */ + + hptep->dw1.dword1 = lhpte.dw1.d; + + /* Guarantee the second dword is visible before + * the valid bit + */ + + __asm__ __volatile__ ("eieio" : : : "memory"); + + /* Now set the first dword including the valid bit */ + hptep->dw0.dword0 = lhpte.dw0.d; + + __asm__ __volatile__ ("ptesync" : : : "memory"); + + } +} + +/* find_linux_pte returns the address of a linux pte for a given + * effective address and directory. If not found, it returns zero. + */ + +pte_t * find_linux_pte( pgd_t * pgdir, unsigned long ea ) +{ + pgd_t *pg; + pmd_t *pm; + pte_t *pt = NULL; + pte_t pte; + pg = pgdir + pgd_index( ea ); + if ( ! pgd_none( *pg ) ) { + + pm = pmd_offset( pg, ea ); + if ( ! pmd_none( *pm ) ) { + pt = pte_offset( pm, ea ); + pte = *pt; + if ( ! pte_present( pte ) ) + pt = NULL; + } + } + + return pt; + +} + +static inline unsigned long computeHptePP( unsigned long pte ) +{ + return ( pte & _PAGE_USER ) | + ( ( ( pte & _PAGE_USER ) >> 1 ) & + ( ( ~( ( pte >> 2 ) & /* _PAGE_RW */ + ( pte >> 7 ) ) ) & /* _PAGE_DIRTY */ + 1 ) ); +} + +static void updateHptePP( long slot, unsigned long newpp, unsigned long va ) +{ + if ( _machine == _MACH_iSeries ) { + HvCallHpt_setPp( slot, newpp ); + } + else { + /* Local copy of first doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + + /* Local copy of second doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword1 h; + Hpte_dword1_flags f; + } hpte_dw1; + + HPTE * hptep = htab_data.htab + slot; + + /* Turn off valid bit in HPTE */ + hpte_dw0.d = hptep->dw0.dword0; + hpte_dw0.h.v = 0; + hptep->dw0.dword0 = hpte_dw0.d; + + /* Ensure it is out of the tlb too */ + _tlbie( va ); + + /* Insert the new pp bits into the HPTE */ + hpte_dw1.d = hptep->dw1.dword1; + hpte_dw1.h.pp = newpp; + hptep->dw1.dword1 = hpte_dw1.d; + + /* Ensure it is visible before validating */ + __asm__ __volatile__ ("eieio" : : : "memory"); + + /* Turn the valid bit back on in HPTE */ + hpte_dw0.h.v = 1; + hptep->dw0.dword0 = hpte_dw0.d; + + __asm__ __volatile__ ("ptesync" : : : "memory"); + } +} + +/* Handle a fault by adding an HPTE + * If the address can't be determined to be valid + * via Linux page tables, return 1. If handled + * return 0 + */ + +int hash_page( unsigned long ea, unsigned long access ) +{ + int rc = 1; + void * pgdir = NULL; + unsigned long va, vsid, vpn; + unsigned long newpp, hash_ind, prpn; + unsigned long hpteflags, regionid; + 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)) { + return 1; + } + + 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"); +#if defined(CONFIG_XMON) + xmon(0); +#endif + for(;x;x|=1); + + vsid = get_kernel_vsid( ea ); + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + vpn = va >> PAGE_SHIFT; + ptep = 0; + 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_HPTENOIX; + spin_lock( &hash_table_lock ); + break; + case VMALLOC_REGION_ID: + mm = &init_mm; + vsid = get_kernel_vsid( ea ); + break; + case IO_REGION_ID: + mm = &ioremap_mm; + vsid = get_kernel_vsid( ea ); + break; + case USER_REGION_ID: + mm = current->mm; + if ( mm == NULL ) { + PPCDBG(PPCDBG_MM, "hash_page returning; mm = 0\n"); + return 1; + } + vsid = get_vsid(mm->context, ea ); + break; + case BOLTED_REGION_ID: + default: + /* Not a valid range, send the problem up to do_page_fault */ + return 1; + break; + } + + if ( regionid != KERNEL_REGION_ID ) { + + /* Search the Linux page table for a match with va */ + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + vpn = va >> PAGE_SHIFT; + pgdir = mm->pgd; + PPCDBG(PPCDBG_MM, "hash_page ea = 0x%16.16lx, va = 0x%16.16lx\n current = 0x%16.16lx, access = %lx\n", ea, va, current, access); + if ( pgdir == NULL ) { + return 1; + } + + /* Lock the Linux page table to prevent mmap and kswapd + * from modifying entries while we search and update + */ + + spin_lock( &mm->page_table_lock ); + + ptep = find_linux_pte( pgdir, ea ); + /* If no pte found, send the problem up to do_page_fault */ + if ( ! ptep ) { + spin_unlock( &mm->page_table_lock ); + return 1; + } + + /* Acquire the hash table lock to guarantee that the linux + * pte we fetch will not change + */ + spin_lock( &hash_table_lock ); + + old_pte = *ptep; + + /* If the pte is not "present" (valid), send the problem + * up to do_page_fault. + */ + if ( ! pte_present( old_pte ) ) { + spin_unlock( &hash_table_lock ); + spin_unlock( &mm->page_table_lock ); + return 1; + } + + /* At this point we have found a pte (which was present). + * The spinlocks prevent this status from changing + * The hash_table_lock prevents the _PAGE_HASHPTE status + * from changing (RPN, DIRTY and ACCESSED too) + * The page_table_lock prevents the pte from being + * invalidated or modified + */ + + } + +/* At this point, we have a pte (old_pte) which can be used to build or update + * an HPTE. There are 5 cases: + * + * 1. There is a valid (present) pte with no associated HPTE (this is + * the most common case) + * 2. There is a valid (present) pte with an associated HPTE. The + * current values of the pp bits in the HPTE prevent access because the + * user doesn't have appropriate access rights. + * 3. There is a valid (present) pte with an associated HPTE. The + * current values of the pp bits in the HPTE prevent access because we are + * doing software DIRTY bit management and the page is currently not DIRTY. + * 4. This is a Kernel address (0xC---) for which there is no page directory. + * There is an HPTE for this page, but the pp bits prevent access. + * Since we always set up kernel pages with R/W access for the kernel + * this case only comes about for users trying to access the kernel. + * This case is always an error and is not dealt with further here. + * 5. This is a Kernel address (0xC---) for which there is no page directory. + * There is no HPTE for this page. + + * Check the user's access rights to the page. If access should be prevented + * then send the problem up to do_page_fault. + */ + + 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 */ + if ( access & _PAGE_RW ) + pte_val(new_pte) |= _PAGE_ACCESSED | + _PAGE_DIRTY; + else + pte_val(new_pte) |= _PAGE_ACCESSED; + + /* Only cases 1, 3 and 5 still in play */ + + newpp = computeHptePP( pte_val(new_pte) ); + + /* Check if pte already has an hpte (case 3) */ + if ( pte_val(old_pte) & _PAGE_HASHPTE ) { + /* There MIGHT be an HPTE for this pte */ + unsigned long hash, slot, secondary; + /* Local copy of first doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + hash = hpt_hash( vpn ); + secondary = (pte_val(old_pte) & _PAGE_SECONDARY) >> 15; + if ( secondary ) + hash = ~hash; + slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; + slot += (pte_val(old_pte) & _PAGE_GROUP_IX) >> 12; + /* If there is an HPTE for this page it is indexed by slot */ + hpte_dw0.d = get_hpte0( slot ); + if ( (hpte_dw0.h.avpn == (vpn >> 11) ) && + (hpte_dw0.h.v) && + (hpte_dw0.h.h == secondary ) ){ + /* HPTE matches */ + updateHptePP( slot, newpp, va ); + if ( !pte_same( old_pte, new_pte ) ) + *ptep = new_pte; + } + else { + /* HPTE is not for this pte */ + pte_val(old_pte) &= ~_PAGE_HPTEFLAGS; + } + } + if ( !( pte_val(old_pte) & _PAGE_HASHPTE ) ) { + /* Cases 1 and 5 */ + /* For these cases we need to create a new + * HPTE and update the linux pte (for + * case 1). For case 5 there is no linux pte. + * + * Find an available HPTE slot + */ + slot = select_hpte_slot( vpn ); + + /* Debug code */ + if ( slot == 0x8000000000000000 ) { + unsigned long xold_pte = pte_val(old_pte); + unsigned long xnew_pte = pte_val(new_pte); + + udbg_printf("hash_page: ptep = 0x%016lx\n", (unsigned long)ptep ); + udbg_printf("hash_page: old_pte = 0x%016lx\n", xold_pte ); + udbg_printf("hash_page: new_pte = 0x%016lx\n", xnew_pte ); + udbg_printf("hash_page: ea = 0x%016lx\n", ea ); + udbg_printf("hash_page: va = 0x%016lx\n", va ); + udbg_printf("hash_page: access = 0x%016lx\n", access ); + + panic("hash_page: hpte already exists\n"); + } + + hash_ind = 0; + if ( slot < 0 ) { + slot = -slot; + hash_ind = 1; + } + + /* Set the physical address */ + prpn = pte_val(old_pte) >> PTE_SHIFT; + + if ( ptep ) { + /* Update the linux pte with the HPTE slot */ + pte_val(new_pte) &= ~_PAGE_HPTEFLAGS; + pte_val(new_pte) |= hash_ind << 15; + pte_val(new_pte) |= (slot<<12) & _PAGE_GROUP_IX; + pte_val(new_pte) |= _PAGE_HASHPTE; + /* No need to use ldarx/stdcx here because all + * who might be updating the pte will hold the page_table_lock + * or the hash_table_lock (we hold both) + */ + *ptep = new_pte; + } + + /* copy appropriate flags from linux pte */ + hpteflags = (pte_val(new_pte) & 0x1f8) | newpp; + + /* Create the HPTE */ + create_valid_hpte( slot, vpn, prpn, hash_ind, ptep, hpteflags, 0 ); + + } + + + /* Indicate success */ + rc = 0; + + } + + spin_unlock( &hash_table_lock ); + if (ptep) + spin_unlock( &mm->page_table_lock ); + + return rc; +} + +unsigned long ___pa(unsigned long ea) +{ + unsigned long pa = 0; + struct mm_struct *mm; + void *pgdir = NULL; + pte_t *ptep; + + switch( REGION_ID(ea) ) { + case KERNEL_REGION_ID: + pa = ea - PAGE_OFFSET; + break; + case VMALLOC_REGION_ID: + mm = &init_mm; + pgdir = mm->pgd; + ptep = find_linux_pte(pgdir, ea); + pa = pte_pagenr(*ptep) << PAGE_SHIFT; + break; + case IO_REGION_ID: + mm = &ioremap_mm; + pgdir = mm->pgd; + ptep = find_linux_pte(pgdir, ea); + pa = pte_pagenr(*ptep) << PAGE_SHIFT; + break; + case BOLTED_REGION_ID: + pgdir = bolted_pgd; + ptep = find_linux_pte(pgdir, ea); + pa = pte_pagenr(*ptep) << PAGE_SHIFT; + break; + default: + break; + } + + pa |= (ea & (PAGE_SIZE-1)); + + return(pa); +} + +void flush_hash_page( unsigned long context, unsigned long ea, pte_t pte ) +{ + unsigned long vsid, vpn, va, hash, secondary, slot, flags; + /* Local copy of first doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + + if ( (ea >= USER_START ) && ( ea <= USER_END ) ) + vsid = get_vsid( context, ea ); + else + vsid = get_kernel_vsid( ea ); + va = (vsid << 28) | (ea & 0x0fffffff); + vpn = va >> PAGE_SHIFT; + hash = hpt_hash( vpn ); + secondary = (pte_val(pte) & _PAGE_SECONDARY) >> 15; + if ( secondary ) + hash = ~hash; + slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; + slot += (pte_val(pte) & _PAGE_GROUP_IX) >> 12; + /* If there is an HPTE for this page it is indexed by slot */ + + spin_lock_irqsave( &hash_table_lock, flags); + hpte_dw0.d = get_hpte0( slot ); + if ( (hpte_dw0.h.avpn == (vpn >> 11) ) && + (hpte_dw0.h.v) && + (hpte_dw0.h.h == secondary ) ){ + /* 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 ); +} + +int proc_dol2crvec(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + int vleft, first=1, len, left, val; +#define TMPBUFLEN 256 + char buf[TMPBUFLEN], *p; + static const char *sizestrings[4] = { + "2MB", "256KB", "512KB", "1MB" + }; + static const char *clockstrings[8] = { + "clock disabled", "+1 clock", "+1.5 clock", "reserved(3)", + "+2 clock", "+2.5 clock", "+3 clock", "reserved(7)" + }; + static const char *typestrings[4] = { + "flow-through burst SRAM", "reserved SRAM", + "pipelined burst SRAM", "pipelined late-write SRAM" + }; + static const char *holdstrings[4] = { + "0.5", "1.0", "(reserved2)", "(reserved3)" + }; + + if ( ((_get_PVR() >> 16) != 8) && ((_get_PVR() >> 16) != 12)) + return -EFAULT; + + if ( /*!table->maxlen ||*/ (filp->f_pos && !write)) { + *lenp = 0; + return 0; + } + + vleft = table->maxlen / sizeof(int); + left = *lenp; + + for (; left /*&& vleft--*/; first=0) { + if (write) { + while (left) { + char c; + if(get_user(c,(char *) buffer)) + return -EFAULT; + if (!isspace(c)) + break; + left--; + ((char *) buffer)++; + } + if (!left) + break; + len = left; + if (len > TMPBUFLEN-1) + len = TMPBUFLEN-1; + if(copy_from_user(buf, buffer, len)) + return -EFAULT; + buf[len] = 0; + p = buf; + if (*p < '0' || *p > '9') + break; + val = simple_strtoul(p, &p, 0); + len = p-buf; + if ((len < left) && *p && !isspace(*p)) + break; + buffer += len; + left -= len; +#if 0 + /* DRENG need a def */ + _set_L2CR(0); + _set_L2CR(val); + while ( _get_L2CR() & 0x1 ) + /* wait for invalidate to finish */; +#endif + + } else { + p = buf; + if (!first) + *p++ = '\t'; +#if 0 + /* DRENG need a def */ + val = _get_L2CR(); +#endif + p += sprintf(p, "0x%08x: ", val); + p += sprintf(p, " %s", (val >> 31) & 1 ? "enabled" : + "disabled"); + p += sprintf(p, ", %sparity", (val>>30)&1 ? "" : "no "); + p += sprintf(p, ", %s", sizestrings[(val >> 28) & 3]); + p += sprintf(p, ", %s", clockstrings[(val >> 25) & 7]); + p += sprintf(p, ", %s", typestrings[(val >> 23) & 2]); + p += sprintf(p, "%s", (val>>22)&1 ? ", data only" : ""); + p += sprintf(p, "%s", (val>>20)&1 ? ", ZZ enabled": ""); + p += sprintf(p, ", %s", (val>>19)&1 ? "write-through" : + "copy-back"); + p += sprintf(p, "%s", (val>>18)&1 ? ", testing" : ""); + p += sprintf(p, ", %sns hold",holdstrings[(val>>16)&3]); + p += sprintf(p, "%s", (val>>15)&1 ? ", DLL slow" : ""); + p += sprintf(p, "%s", (val>>14)&1 ? ", diff clock" :""); + p += sprintf(p, "%s", (val>>13)&1 ? ", DLL bypass" :""); + + p += sprintf(p,"\n"); + + len = strlen(buf); + if (len > left) + len = left; + if(copy_to_user(buffer, buf, len)) + return -EFAULT; + left -= len; + buffer += len; + break; + } + } + + if (!write && !first && left) { + if(put_user('\n', (char *) buffer)) + return -EFAULT; + left--, buffer++; + } + if (write) { + p = (char *) buffer; + while (left) { + char c; + if(get_user(c, p++)) + return -EFAULT; + if (!isspace(c)) + break; + left--; + } + } + if (write && first) + return -EINVAL; + *lenp -= left; + filp->f_pos += *lenp; + return 0; +} + +static long +ppc_htab_lseek(struct file * file, loff_t offset, int orig) +{ + switch (orig) { + case 0: + file->f_pos = offset; + return(file->f_pos); + case 1: + file->f_pos += offset; + return(file->f_pos); + case 2: + return(-EINVAL); + default: + return(-EINVAL); + } +} + +/* + * Allow user to define performance counters and resize the hash table + */ +static ssize_t ppc_htab_write(struct file * file, const char * buffer, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +/* + * print some useful info about the hash table. This function + * is _REALLY_ slow (see the nested for loops below) but nothing + * in here should be really timing critical. -- Cort + */ +static ssize_t ppc_htab_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +/* round mem size up to next power of 2 -- htab size must be a power of 2 */ +unsigned long +rounded_mem_size(unsigned long mem_size) +{ + unsigned long rnd_mem_size; + + rnd_mem_size = 1UL << (unsigned long)__ilog2(mem_size); + if ( rnd_mem_size < mem_size ) + rnd_mem_size <<= 1; + + return rnd_mem_size; +} + +unsigned long +htab_size(unsigned long mem_size) +{ + unsigned long offset = reloc_offset(); + struct Naca *_naca = RELOC(naca); + unsigned long rnd_mem_size = rounded_mem_size(mem_size); + unsigned long pteg_count = (rnd_mem_size >> (12 + 1)); /* #pages/2 */ + /* For debug, make the HTAB 1/8 as big as it normally would be. */ + if(_naca->debug_switch & PPCDBG_HTABSIZE) { + pteg_count >>= 3; + } + return (pteg_count << 7); /* pteg_count*128B/PTEG */ +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/hvCall.S linux.ac/arch/ppc64/kernel/hvCall.S --- linux.vanilla/arch/ppc64/kernel/hvCall.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/hvCall.S Wed Oct 10 01:47:34 2001 @@ -0,0 +1,99 @@ +/* + * arch/ppc64/kernel/hvCall.S + * + * + * This file contains the code to perform calls to the + * iSeries LPAR hypervisor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "ppc_asm.h" +#include +#include + + .text + +/* + * Hypervisor call + * + * Invoke the iSeries hypervisor via the System Call instruction + * Parameters are passed to this routine in registers r3 - r10 + * + * r3 contains the HV function to be called + * r4-r10 contain the operands to the hypervisor function + * + */ + +_GLOBAL(HvCall) +_GLOBAL(HvCall0) +_GLOBAL(HvCall1) +_GLOBAL(HvCall2) +_GLOBAL(HvCall3) +_GLOBAL(HvCall4) +_GLOBAL(HvCall5) +_GLOBAL(HvCall6) +_GLOBAL(HvCall7) + + + mfcr r0 + std r0,-8(r1) + stdu r1,-(STACK_FRAME_OVERHEAD+16)(r1) + + /* r0 = 0xffffffffffffffff indicates a hypervisor call */ + + li r0,-1 + + /* Invoke the hypervisor */ + + sc + + ld r1,0(r1) + ld r0,-8(r1) + mtcrf 0xff,r0 + + /* return to caller, return value in r3 */ + + blr + +_GLOBAL(HvCall0Ret16) +_GLOBAL(HvCall1Ret16) +_GLOBAL(HvCall2Ret16) +_GLOBAL(HvCall3Ret16) +_GLOBAL(HvCall4Ret16) +_GLOBAL(HvCall5Ret16) +_GLOBAL(HvCall6Ret16) +_GLOBAL(HvCall7Ret16) + + mfcr r0 + std r0,-8(r1) + std r31,-16(r1) + stdu r1,-(STACK_FRAME_OVERHEAD+32)(r1) + + mr r31,r4 + li r0,-1 + mr r4,r5 + mr r5,r6 + mr r6,r7 + mr r7,r8 + mr r8,r9 + mr r9,r10 + + sc + + std r3,0(r31) + std r4,8(r31) + + mr r3,r5 + + ld r1,0(r1) + ld r0,-8(r1) + mtcrf 0xff,r0 + ld r31,-16(r1) + + blr + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/i8259.c linux.ac/arch/ppc64/kernel/i8259.c --- linux.vanilla/arch/ppc64/kernel/i8259.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/i8259.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,163 @@ +/* + * c 2001 PPC64 Team, 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. + */ +#include +#include +#include +#include +#include +#include "i8259.h" +#include +#include + +unsigned char cached_8259[2] = { 0xff, 0xff }; +#define cached_A1 (cached_8259[0]) +#define cached_21 (cached_8259[1]) + +static spinlock_t i8259_lock = SPIN_LOCK_UNLOCKED; + +int i8259_pic_irq_offset; + +int i8259_irq(int cpu) +{ + int irq; + + spin_lock/*_irqsave*/(&i8259_lock/*, flags*/); + /* + * Perform an interrupt acknowledge cycle on controller 1 + */ + outb(0x0C, 0x20); + irq = inb(0x20) & 7; + if (irq == 2) + { + /* + * Interrupt is cascaded so perform interrupt + * acknowledge on controller 2 + */ + outb(0x0C, 0xA0); + irq = (inb(0xA0) & 7) + 8; + } + else if (irq==7) + { + /* + * This may be a spurious interrupt + * + * Read the interrupt status register. If the most + * significant bit is not set then there is no valid + * interrupt + */ + outb(0x0b, 0x20); + if(~inb(0x20)&0x80) { + spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/); + return -1; + } + } + spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/); + return irq; +} + +static void i8259_mask_and_ack_irq(unsigned int irq_nr) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; + + if (irq_nr > 7) { + cached_A1 |= 1 << (irq_nr-8); + inb(0xA1); /* DUMMY */ + outb(cached_A1,0xA1); + outb(0x20,0xA0); /* Non-specific EOI */ + outb(0x20,0x20); /* Non-specific EOI to cascade */ + } else { + cached_21 |= 1 << irq_nr; + inb(0x21); /* DUMMY */ + outb(cached_21,0x21); + outb(0x20,0x20); /* Non-specific EOI */ + } + spin_unlock_irqrestore(&i8259_lock, flags); +} + +static void i8259_set_irq_mask(int irq_nr) +{ + outb(cached_A1,0xA1); + outb(cached_21,0x21); +} + +static void i8259_mask_irq(unsigned int irq_nr) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; + if ( irq_nr < 8 ) + cached_21 |= 1 << irq_nr; + else + cached_A1 |= 1 << (irq_nr-8); + i8259_set_irq_mask(irq_nr); + spin_unlock_irqrestore(&i8259_lock, flags); +} + +static void i8259_unmask_irq(unsigned int irq_nr) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; + if ( irq_nr < 8 ) + cached_21 &= ~(1 << irq_nr); + else + cached_A1 &= ~(1 << (irq_nr-8)); + i8259_set_irq_mask(irq_nr); + spin_unlock_irqrestore(&i8259_lock, flags); +} + +static void i8259_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + i8259_unmask_irq(irq); +} + +struct hw_interrupt_type i8259_pic = { + " i8259 ", + NULL, + NULL, + i8259_unmask_irq, + i8259_mask_irq, + i8259_mask_and_ack_irq, + i8259_end_irq, + NULL +}; + +void __init i8259_init(void) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + /* init master interrupt controller */ + outb(0x11, 0x20); /* Start init sequence */ + outb(0x00, 0x21); /* Vector base */ + outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ + outb(0x01, 0x21); /* Select 8086 mode */ + outb(0xFF, 0x21); /* Mask all */ + /* init slave interrupt controller */ + outb(0x11, 0xA0); /* Start init sequence */ + outb(0x08, 0xA1); /* Vector base */ + outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ + outb(0x01, 0xA1); /* Select 8086 mode */ + outb(0xFF, 0xA1); /* Mask all */ + outb(cached_A1, 0xA1); + outb(cached_21, 0x21); + spin_unlock_irqrestore(&i8259_lock, flags); + request_irq( i8259_pic_irq_offset + 2, no_action, SA_INTERRUPT, + "82c59 secondary cascade", NULL ); + +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/i8259.h linux.ac/arch/ppc64/kernel/i8259.h --- linux.vanilla/arch/ppc64/kernel/i8259.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/i8259.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,19 @@ +/* + * c 2001 PPC 64 Team, 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. + */ +#ifndef _PPC_KERNEL_i8259_H +#define _PPC_KERNEL_i8259_H + +#include "local_irq.h" + +extern struct hw_interrupt_type i8259_pic; + +void i8259_init(void); +int i8259_irq(int); + +#endif /* _PPC_KERNEL_i8259_H */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/iSeries_IoMmTable.c linux.ac/arch/ppc64/kernel/iSeries_IoMmTable.c --- linux.vanilla/arch/ppc64/kernel/iSeries_IoMmTable.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/iSeries_IoMmTable.c Wed Oct 10 01:47:34 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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/iSeries_IoMmTable.h linux.ac/arch/ppc64/kernel/iSeries_IoMmTable.h --- linux.vanilla/arch/ppc64/kernel/iSeries_IoMmTable.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/iSeries_IoMmTable.h Wed Oct 10 01:47:34 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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/iSeries_dma.c linux.ac/arch/ppc64/kernel/iSeries_dma.c --- linux.vanilla/arch/ppc64/kernel/iSeries_dma.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/iSeries_dma.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,1121 @@ +/* + * iSeries_dma.c + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * Dynamic DMA mapping support. + * + * Manages the TCE space assigned to this partition + * + * modeled from pci-dma.c + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*#define DEBUG_TCE 1 */ + +/*HACK HACK HACK */ + +u8 iSeries_Get_Bus( struct pci_dev * dv ) +{ + return 0; +} + +/* HACK HACK HACK */ + +unsigned long phb_tce_table_init( void * x ) +{ + return (unsigned long)x; +} + + +struct TceTable virtBusTceTable; /* Tce table for virtual bus */ + +struct TceTable * tceTables[256]; /* Tce tables for 256 busses + * Bus 255 is the virtual bus + * zero indicates no bus defined + */ + /* allocates a contiguous range of tces (power-of-2 size) */ +static long alloc_tce_range( struct TceTable *, + unsigned order ); + /* allocates a contiguous range of tces (power-of-2 size) + * assumes lock already held + */ +static long alloc_tce_range_nolock( struct TceTable *, + unsigned order ); + /* frees a contiguous range of tces (power-of-2 size) */ +static void free_tce_range( struct TceTable *, + long tcenum, + unsigned order ); + /* frees a contiguous rnage of tces (power-of-2 size) + * assumes lock already held + */ +static void free_tce_range_nolock( struct TceTable *, + long tcenum, + unsigned order ); + /* allocates a range of tces and sets them to the pages */ +static dma_addr_t get_tces( struct TceTable *, + unsigned order, + void *page, + unsigned numPages, + int tceType, + int direction ); +static void free_tces( struct TceTable *, + dma_addr_t tce, + unsigned order, + unsigned numPages ); +static long test_tce_range( struct TceTable *, + long tcenum, + unsigned order ); + +static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents, + dma_addr_t dma_addr, unsigned long numTces ); + +static unsigned long num_tces_sg( struct scatterlist *sg, + int nents ); + +static dma_addr_t create_tces_sg( struct TceTable *tbl, + struct scatterlist *sg, + int nents, + unsigned numTces, + int tceType, + int direction ); + +static unsigned __inline__ count_leading_zeros32( unsigned long x ) +{ + unsigned lz; + asm("cntlzw %0,%1" : "=r"(lz) : "r"(x)); + return lz; +} + +static void __inline__ build_tce( struct TceTable * tbl, long tcenum, + unsigned long uaddr, int tceType, int direction ) +{ + u64 setTceRc; + union Tce tce; + + tce.wholeTce = 0; + tce.tceBits.rpn = (virt_to_absolute(uaddr)) >> PAGE_SHIFT; + /* If for virtual bus */ + if ( tceType == TCE_VB ) { + tce.tceBits.valid = 1; + tce.tceBits.allIo = 1; + if ( direction != PCI_DMA_TODEVICE ) + tce.tceBits.readWrite = 1; + } + /* If for PCI bus */ + else { + tce.tceBits.readWrite = 1; // Read allowed + if ( direction != PCI_DMA_TODEVICE ) + tce.tceBits.pciWrite = 1; + } + setTceRc = HvCallXm_setTce( (u64)tbl->index, (u64)tcenum, tce.wholeTce ); + if ( setTceRc ) { + printk("build_tce: HvCallXm_setTce failed, rc=%ld, index=%ld, tcenum=%0lx, tce=%016lx\n", + setTceRc, (u64)tbl->index, (u64)tcenum, tce.wholeTce ); + } + +} + + + +/* Build a TceTable structure. This contains a multi-level bit map which + * is used to manage allocation of the tce space. + */ + +struct TceTable * build_tce_table( struct HvTceTableManagerCB * tceTableParms, + struct TceTable * tbl ) +{ + unsigned long bits, bytes, totalBytes; + unsigned long numBits[NUM_TCE_LEVELS], numBytes[NUM_TCE_LEVELS]; + unsigned i, k, m; + unsigned char * pos, * p, b; + + tbl->size = tceTableParms->size; + tbl->busNumber = tceTableParms->busNumber; + tbl->startOffset = tceTableParms->startOffset; + tbl->index = tceTableParms->index; + spin_lock_init( &(tbl->lock) ); + + tbl->mlbm.maxLevel = 0; + + /* Compute number of bits and bytes for each level of the + * multi-level bit map + */ + totalBytes = 0; + bits = tbl->size * (PAGE_SIZE / sizeof( union Tce )); + + for ( i=0; imlbm.level[i].map = pos; + tbl->mlbm.maxLevel = i; + + if ( numBits[i] & 1 ) { + p = pos + numBytes[i] - 1; + m = (( numBits[i] % 8) - 1) & 7; + *p = 0x80 >> m; +#ifdef DEBUG_TCE + printk("build_tce_table: level %d last bit %x\n", i, 0x80>>m ); +#endif + } + } + else + tbl->mlbm.level[i].map = 0; + pos += numBytes[i]; + /* see the comment up above on the totalBytes calculation + * for why we do this. */ + pos += ((numBytes[i] + 7) / 8) * 8; + + tbl->mlbm.level[i].numBits = numBits[i]; + tbl->mlbm.level[i].numBytes = numBytes[i]; + + } + + /* For the highest level, turn on all the bits */ + + i = tbl->mlbm.maxLevel; + p = tbl->mlbm.level[i].map; + m = numBits[i]; +#ifdef DEBUG_TCE + printk("build_tce_table: highest level (%d) has all bits set\n", i); +#endif + for (k=0; k= 8 ) { + /* handle full bytes */ + *p++ = 0xff; + m -= 8; + } + else { + /* handle the last partial byte */ + b = 0x80; + *p = 0; + while (m) { + *p |= b; + b >>= 1; + --m; + } + } + } + + return tbl; + +} + +static long alloc_tce_range( struct TceTable *tbl, unsigned order ) +{ + long retval; + unsigned long flags; + + /* Lock the tce allocation bitmap */ + spin_lock_irqsave( &(tbl->lock), flags ); + + /* Do the actual work */ + retval = alloc_tce_range_nolock( tbl, order ); + + /* Unlock the tce allocation bitmap */ + spin_unlock_irqrestore( &(tbl->lock), flags ); + + return retval; +} + +static long alloc_tce_range_nolock( struct TceTable *tbl, unsigned order ) +{ + unsigned long numBits, numBytes; + unsigned long i, bit, block, mask; + long tcenum; + u32 * map; + + /* If the order (power of 2 size) requested is larger than our + * biggest, indicate failure + */ + if ( order > tbl->mlbm.maxLevel ) { + printk("alloc_tce_range_nolock: invalid order requested, order = %d\n", order ); + return -1; + } + + numBits = tbl->mlbm.level[order].numBits; + numBytes = tbl->mlbm.level[order].numBytes; + map = (u32 *)(tbl->mlbm.level[order].map); + + /* Initialize return value to -1 (failure) */ + tcenum = -1; + + /* Loop through the bytes of the bitmap */ + for (i=0; i> bit); + *map &= mask; + /* compute the index into our tce table for + * the first tce in the block + */ +#ifdef DEBUG_TCE + printk("alloc_tce_range_nolock: allocating block %ld, (byte=%ld, bit=%ld) order %d\n", block, i, bit, order ); +#endif + tcenum = block << order; + break; + } + ++map; + } + +#ifdef DEBUG_TCE + if ( tcenum == -1 ) { + printk("alloc_tce_range_nolock: no available blocks of order = %d\n", order ); + if ( order < tbl->mlbm.maxLevel ) + printk("alloc_tce_range_nolock: trying next bigger size\n" ); + else + printk("alloc_tce_range_nolock: maximum size reached...failing\n"); + } +#endif + + /* If no block of the requested size was found, try the next + * size bigger. If one of those is found, return the second + * half of the block to freespace and keep the first half + */ + if ( ( tcenum == -1 ) && ( order < tbl->mlbm.maxLevel ) ) { + tcenum = alloc_tce_range_nolock( tbl, order+1 ); + if ( tcenum != -1 ) { + free_tce_range_nolock( tbl, tcenum+(1<lock), flags ); + + /* Do the actual work */ + free_tce_range_nolock( tbl, tcenum, order ); + + /* Unlock the tce allocation bitmap */ + spin_unlock_irqrestore( &(tbl->lock), flags ); + +} + +static void free_tce_range_nolock( struct TceTable *tbl, long tcenum, unsigned order ) +{ + unsigned long block; + unsigned byte, bit, mask, b; + unsigned char * map, * bytep; + + if ( order > tbl->mlbm.maxLevel ) { + printk("free_tce_range: order too large, order = %d, tcenum = %ld\n", order, tcenum ); + return; + } + + block = tcenum >> order; + if ( tcenum != (block << order ) ) { + printk("free_tce_range: tcenum %lx is not on appropriate boundary for order %x\n", tcenum, order ); + return; + } + if ( block >= tbl->mlbm.level[order].numBits ) { + printk("free_tce_range: tcenum %lx is outside the range of this map (order %x, numBits %lx\n", tcenum, order, tbl->mlbm.level[order].numBits ); + return; + } +#ifdef DEBUG_TCE + if ( test_tce_range( tbl, tcenum, order ) ) { + printk("free_tce_range: freeing range not completely allocated.\n"); + printk("free_tce_range: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order ); + } +#endif + map = tbl->mlbm.level[order].map; + byte = block / 8; + bit = block % 8; + mask = 0x80 >> bit; + bytep = map + byte; +#ifdef DEBUG_TCE + printk("free_tce_range_nolock: freeing block %ld (byte=%d, bit=%d) of order %d\n",block, byte, bit, order); + if ( *bytep & mask ) + printk("free_tce_range: already free: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order ); +#endif + *bytep |= mask; + + /* If there is a higher level in the bit map than this we may be + * able to buddy up this block with its partner. + * If this is the highest level we can't buddy up + * If this level has an odd number of bits and + * we are freeing the last block we can't buddy up + */ + if ( ( order < tbl->mlbm.maxLevel ) && + ( ( 0 == ( tbl->mlbm.level[order].numBits & 1 ) ) || + ( block < tbl->mlbm.level[order].numBits-1 ) ) ) { + + /* See if we can buddy up the block we just freed */ + bit &= 6; /* get to the first of the buddy bits */ + mask = 0xc0 >> bit; /* build two bit mask */ + b = *bytep & mask; /* Get the two bits */ + if ( 0 == (b ^ mask) ) { /* If both bits are on */ + /* both of the buddy blocks are free we can combine them */ + *bytep ^= mask; /* turn off the two bits */ + block = ( byte * 8 ) + bit; /* block of first of buddies */ + tcenum = block << order; + /* free the buddied block */ +#ifdef DEBUG_TCE + printk("free_tce_range: buddying up block %ld and block %ld\n", block, block+1); +#endif + free_tce_range_nolock( tbl, tcenum, order+1 ); + } + } +} + +static long test_tce_range( struct TceTable *tbl, long tcenum, unsigned order ) +{ + unsigned long block; + unsigned byte, bit, mask, b; + long retval, retLeft, retRight; + unsigned char * map; + + map = tbl->mlbm.level[order].map; + block = tcenum >> order; + byte = block / 8; /* Byte within bitmap */ + bit = block % 8; /* Bit within byte */ + mask = 0x80 >> bit; + b = (*(map+byte) & mask ); /* 0 if block is allocated, else free */ + if ( b ) + retval = 1; /* 1 == block is free */ + else + retval = 0; /* 0 == block is allocated */ + /* Test bits at all levels below this to ensure that all agree */ + + if (order) { + retLeft = test_tce_range( tbl, tcenum, order-1 ); + retRight = test_tce_range( tbl, tcenum+(1<<(order-1)), order-1 ); + if ( retLeft || retRight ) { + retval = 2; + } + } + + /* Test bits at all levels above this to ensure that all agree */ + + return retval; +} + +static dma_addr_t get_tces( struct TceTable *tbl, unsigned order, void *page, unsigned numPages, int tceType, int direction ) +{ + long tcenum; + unsigned long uaddr; + unsigned i; + dma_addr_t retTce = NO_TCE; + + uaddr = (unsigned long)page & PAGE_MASK; + + /* Allocate a range of tces */ + tcenum = alloc_tce_range( tbl, order ); + if ( tcenum != -1 ) { + /* We got the tces we wanted */ + tcenum += tbl->startOffset; /* Offset into real TCE table */ + retTce = tcenum << PAGE_SHIFT; /* Set the return dma address */ + /* Setup a tce for each page */ + for (i=0; isize * (PAGE_SIZE / sizeof(union Tce))) - 1; + + tcenum = dma_addr >> PAGE_SHIFT; + tcenum -= tbl->startOffset; + + if ( tcenum > maxTcenum ) { + printk("free_tces: tcenum > maxTcenum, tcenum = %ld, maxTcenum = %ld\n", + tcenum, maxTcenum ); + printk("free_tces: TCE Table at %16lx\n", (unsigned long)tbl ); + printk("free_tces: bus# %lu\n", (unsigned long)tbl->busNumber ); + printk("free_tces: size %lu\n", (unsigned long)tbl->size ); + printk("free_tces: startOff %lu\n", (unsigned long)tbl->startOffset ); + printk("free_tces: index %lu\n", (unsigned long)tbl->index ); + return; + } + + freeTce = tcenum; + + for (i=0; iindex, (u64)tcenum, tce.wholeTce ); + if ( setTceRc ) { + printk("free_tces: HvCallXm_setTce failed, rc=%ld, index=%ld, tcenum=%0lx, tce=%016lx\n", + setTceRc, (u64)tbl->index, (u64)tcenum, tce.wholeTce ); + } + + ++tcenum; + } + + free_tce_range( tbl, freeTce, order ); + +} + +void __init create_virtual_bus_tce_table(void) +{ + struct TceTable * t; + struct HvTceTableManagerCB virtBusTceTableParms; + u64 absParmsPtr; + + virtBusTceTableParms.busNumber = 255; /* Bus 255 is the virtual bus */ + virtBusTceTableParms.virtualBusFlag = 0xff; /* Ask for virtual bus */ + + absParmsPtr = virt_to_absolute( (u64)&virtBusTceTableParms ); + HvCallXm_getTceTableParms( absParmsPtr ); + + t = build_tce_table( &virtBusTceTableParms, &virtBusTceTable ); + if ( t ) { + tceTables[255] = t; + printk("Virtual Bus TCE table built successfully.\n"); + printk(" TCE table size = %ld entries\n", + (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); + printk(" TCE table token = %d\n", + (unsigned)t->index ); + printk(" TCE table start entry = 0x%lx\n", + (unsigned long)t->startOffset ); + } + else + printk("Virtual Bus TCE table failed.\n"); +} + +void __init create_pci_bus_tce_table( unsigned busNumber ) +{ + struct TceTable * t; + struct TceTable * newTceTable; + struct HvTceTableManagerCB pciBusTceTableParms; + u64 absParmsPtr; + unsigned i; + + if ( busNumber > 254 ) { + printk("PCI Bus TCE table failed.\n"); + printk(" Invalid bus number %u\n", busNumber ); + return; + } + + newTceTable = kmalloc( sizeof(struct TceTable), GFP_KERNEL ); + + pciBusTceTableParms.busNumber = busNumber; + pciBusTceTableParms.virtualBusFlag = 0; + + absParmsPtr = virt_to_absolute( (u64)&pciBusTceTableParms ); + HvCallXm_getTceTableParms( absParmsPtr ); + + /* Determine if the table identified by the index and startOffset + * returned by the hypervisor for this bus has already been created. + */ + + for ( i=0; i<255; ++i ) { + t = tceTables[i]; + if ( t ) { + if ( ( t->index == pciBusTceTableParms.index ) && + ( t->startOffset == pciBusTceTableParms.startOffset ) ) { + if ( t->size != pciBusTceTableParms.size ) + printk("PCI Bus %d Shares a TCE table with Bus %d, but sizes differ\n", busNumber, i ); + else + printk("PCI Bus %d Shares a TCE table with Bus %d\n", busNumber, i ); + tceTables[busNumber] = t; + break; + } + } + } + + if ( ! tceTables[busNumber] ) { + t = build_tce_table( &pciBusTceTableParms, newTceTable ); + if ( t ) { + tceTables[busNumber] = t; + printk("PCI Bus TCE table built successfully.\n"); + printk(" TCE table size = %ld entries\n", + (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); + printk(" TCE table token = %d\n", + (unsigned)t->index ); + printk(" TCE table start entry = 0x%lx\n", + (unsigned long)t->startOffset ); + } + else { + kfree( newTceTable ); + printk("PCI Bus TCE table failed.\n"); + } + } +} + + +/* Allocates a contiguous real buffer and creates TCEs over it. + * Returns the virtual address of the buffer and sets dma_handle + * to the dma address (tce) of the first page. + */ +void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + struct TceTable * tbl; + void *ret = NULL; + unsigned order, nPages, bus; + dma_addr_t tce; + int tceType; + + size = PAGE_ALIGN(size); + order = get_order(size); + nPages = 1 << order; + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return NULL; +#endif /* CONFIG_PCI */ + } + + tbl = tceTables[bus]; + if ( tbl ) { + /* Alloc enough pages (and possibly more) */ + ret = (void *)__get_free_pages( GFP_ATOMIC, order ); + if ( ret ) { + /* Page allocation succeeded */ + memset(ret, 0, nPages << PAGE_SHIFT); + /* Set up tces to cover the allocated range */ + tce = get_tces( tbl, order, ret, nPages, tceType, + PCI_DMA_BIDIRECTIONAL ); + if ( tce == NO_TCE ) { +/*#ifdef DEBUG_TCE */ + printk("pci_alloc_consistent: get_tces failed\n" ); +/*#endif */ + free_pages( (unsigned long)ret, order ); + ret = NULL; + } + else + { + *dma_handle = tce; + } + } +/*#ifdef DEBUG_TCE */ + else + printk("pci_alloc_consistent: __get_free_pages failed for order = %d\n", order); +/*#endif*/ + } + + return ret; +} + +void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + struct TceTable * tbl; + unsigned order, nPages, bus; + + size = PAGE_ALIGN(size); + order = get_order(size); + nPages = 1 << order; + + if ( order > 10 ) + printk("pci_free_consistent: order=%d, size=%ld, nPages=%d, dma_handle=%016lx, vaddr=%016lx\n", + order, size, nPages, (unsigned long)dma_handle, (unsigned long)vaddr ); + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + printk("pci_free_consistent: invalid bus # %d\n", bus ); + printk("pci_free_consistent: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + tbl = tceTables[bus]; + + if ( tbl ) { + free_tces( tbl, dma_handle, order, nPages ); + free_pages( (unsigned long)vaddr, order ); + } +} + +/* Creates TCEs for a user provided buffer. The user buffer must be + * contiguous real kernel storage (not vmalloc). The address of the buffer + * passed here is the kernel (virtual) address of the buffer. The buffer + * need not be page aligned, the dma_addr_t returned will point to the same + * byte within the page as vaddr. + */ +dma_addr_t pci_map_single( struct pci_dev *hwdev, void *vaddr, size_t size, int direction ) +{ + struct TceTable * tbl; + dma_addr_t dma_handle; + unsigned long uaddr; + unsigned order, nPages, bus; + int tceType; + + if ( direction == PCI_DMA_NONE ) + BUG(); + + dma_handle = NO_TCE; + + uaddr = (unsigned long)vaddr; + nPages = PAGE_ALIGN( uaddr + size ) - ( uaddr & PAGE_MASK ); + order = get_order( nPages & PAGE_MASK ); + nPages >>= PAGE_SHIFT; + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return NO_TCE; +#endif /* CONFIG_PCI */ + + } + + tbl = tceTables[bus]; + + if ( tbl ) { + dma_handle = get_tces( tbl, order, vaddr, nPages, tceType, + direction ); + dma_handle |= ( uaddr & ~PAGE_MASK ); + } + + return dma_handle; +} + +void pci_unmap_single( struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction ) +{ + struct TceTable * tbl; + unsigned order, nPages, bus; + + if ( direction == PCI_DMA_NONE ) + BUG(); + + nPages = PAGE_ALIGN( dma_handle + size ) - ( dma_handle & PAGE_MASK ); + order = get_order( nPages & PAGE_MASK ); + nPages >>= PAGE_SHIFT; + + if ( order > 10 ) + printk("pci_unmap_single: order=%d, size=%ld, nPages=%d, dma_handle=%016lx\n", + order, size, nPages, (unsigned long)dma_handle ); + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + printk("pci_unmap_single: invalid bus # %d\n", bus ); + printk("pci_unmap_single: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + tbl = tceTables[bus]; + + if ( tbl ) + free_tces( tbl, dma_handle, order, nPages ); + +} + +/* Figure out how many TCEs are actually going to be required + * to map this scatterlist. This code is not optimal. It + * takes into account the case where entry n ends in the same + * page in which entry n+1 starts. It does not handle the + * general case of entry n ending in the same page in which + * entry m starts. + */ +static unsigned long num_tces_sg( struct scatterlist *sg, int nents ) +{ + unsigned long nTces, numPages, startPage, endPage, prevEndPage; + unsigned i; + + prevEndPage = 0; + nTces = 0; + + for (i=0; iaddress >> PAGE_SHIFT; + endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT; + numPages = endPage - startPage + 1; + /* Simple optimization: if the previous entry ended + * in the same page in which this entry starts + * then we can reduce the required pages by one. + * This matches assumptions in fill_scatterlist_sg and + * create_tces_sg + */ + if ( startPage == prevEndPage ) + --numPages; + nTces += numPages; + prevEndPage = endPage; + sg++; + } + return nTces; +} + +/* Fill in the dma data in the scatterlist + * return the number of dma sg entries created + */ +static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents, + dma_addr_t dma_addr , unsigned long numTces) +{ + struct scatterlist *dma_sg; + u32 cur_start_dma; + unsigned long cur_len_dma, cur_end_virt, uaddr; + unsigned num_dma_ents; + + dma_sg = sg; + num_dma_ents = 1; + + /* Process the first sg entry */ + cur_start_dma = dma_addr + ((unsigned long)sg->address & (~PAGE_MASK)); + cur_len_dma = sg->length; + /* cur_end_virt holds the address of the byte immediately after the + * end of the current buffer. + */ + cur_end_virt = (unsigned long)sg->address + cur_len_dma; + /* Later code assumes that unused sg->dma_address and sg->dma_length + * fields will be zero. Other archs seem to assume that the user + * (device driver) guarantees that...I don't want to depend on that + */ + sg->dma_address = sg->dma_length = 0; + + /* Process the rest of the sg entries */ + while (--nents) { + ++sg; + /* Clear possibly unused fields. Note: sg >= dma_sg so + * this can't be clearing a field we've already set + */ + sg->dma_address = sg->dma_length = 0; + + /* Check if it is possible to make this next entry + * contiguous (in dma space) with the previous entry. + */ + + /* The entries can be contiguous in dma space if + * the previous entry ends immediately before the + * start of the current entry (in virtual space) + * or if the previous entry ends at a page boundary + * and the current entry starts at a page boundary. + */ + uaddr = (unsigned long)sg->address; + if ( ( uaddr != cur_end_virt ) && + ( ( ( uaddr | cur_end_virt ) & (~PAGE_MASK) ) || + ( ( uaddr & PAGE_MASK ) == ( ( cur_end_virt-1 ) & PAGE_MASK ) ) ) ) { + /* This entry can not be contiguous in dma space. + * save the previous dma entry and start a new one + */ + dma_sg->dma_address = cur_start_dma; + dma_sg->dma_length = cur_len_dma; + + ++dma_sg; + ++num_dma_ents; + + cur_start_dma += cur_len_dma-1; + /* If the previous entry ends and this entry starts + * in the same page then they share a tce. In that + * case don't bump cur_start_dma to the next page + * in dma space. This matches assumptions made in + * num_tces_sg and create_tces_sg. + */ + if ((uaddr & PAGE_MASK) == ((cur_end_virt-1) & PAGE_MASK)) + cur_start_dma &= PAGE_MASK; + else + cur_start_dma = PAGE_ALIGN(cur_start_dma+1); + cur_start_dma += ( uaddr & (~PAGE_MASK) ); + cur_len_dma = 0; + } + /* Accumulate the length of this entry for the next + * dma entry + */ + cur_len_dma += sg->length; + cur_end_virt = uaddr + sg->length; + } + /* Fill in the last dma entry */ + dma_sg->dma_address = cur_start_dma; + dma_sg->dma_length = cur_len_dma; + + if ((((cur_start_dma +cur_len_dma - 1)>> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1) != numTces) + { + printk("fill_scatterlist_sg: numTces %ld, used tces %d\n", + numTces, + (unsigned)(((cur_start_dma + cur_len_dma - 1) >> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1)); + } + + + return num_dma_ents; +} + +/* Call the hypervisor to create the TCE entries. + * return the number of TCEs created + */ +static dma_addr_t create_tces_sg( struct TceTable *tbl, struct scatterlist *sg, + int nents, unsigned numTces, int tceType, int direction ) +{ + unsigned order, i, j; + unsigned long startPage, endPage, prevEndPage, numPages, uaddr; + long tcenum, starttcenum; + dma_addr_t dmaAddr; + + dmaAddr = NO_TCE; + + order = get_order( numTces << PAGE_SHIFT ); + /* allocate a block of tces */ + tcenum = alloc_tce_range( tbl, order ); + if ( tcenum != -1 ) { + tcenum += tbl->startOffset; + starttcenum = tcenum; + dmaAddr = tcenum << PAGE_SHIFT; + prevEndPage = 0; + for (j=0; jaddress >> PAGE_SHIFT; + endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT; + numPages = endPage - startPage + 1; + + uaddr = (unsigned long)sg->address; + + /* If the previous entry ended in the same page that + * the current page starts then they share that + * tce and we reduce the number of tces we need + * by one. This matches assumptions made in + * num_tces_sg and fill_scatterlist_sg + */ + if ( startPage == prevEndPage ) { + --numPages; + uaddr += PAGE_SIZE; + } + + for (i=0; idma_address = pci_map_single( hwdev, sg->address, + sg->length, direction ); + sg->dma_length = sg->length; + return 1; + } + + if ( direction == PCI_DMA_NONE ) + BUG(); + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return 0; +#endif /* CONFIG_PCI */ + } + + tbl = tceTables[bus]; + + if ( tbl ) { + /* Compute the number of tces required */ + numTces = num_tces_sg( sg, nents ); + /* Create the tces and get the dma address */ + dma_handle = create_tces_sg( tbl, sg, nents, numTces, + tceType, direction ); + + /* Fill in the dma scatterlist */ + num_dma = fill_scatterlist_sg( sg, nents, dma_handle, numTces ); + } + + return num_dma; +} + +void pci_unmap_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nelms, int direction ) +{ + struct TceTable * tbl; + unsigned order, numTces, bus, i; + dma_addr_t dma_end_page, dma_start_page; + + if ( direction == PCI_DMA_NONE ) + BUG(); + + dma_start_page = sg->dma_address & PAGE_MASK; + for ( i=nelms; i>0; --i ) { + unsigned k = i - 1; + if ( sg[k].dma_length ) { + dma_end_page = ( sg[k].dma_address + + sg[k].dma_length - 1 ) & PAGE_MASK; + break; + } + } + + numTces = ((dma_end_page - dma_start_page ) >> PAGE_SHIFT) + 1; + order = get_order( numTces << PAGE_SHIFT ); + + if ( order > 10 ) + printk("pci_unmap_sg: order=%d, numTces=%d, nelms=%d, dma_start_page=%016lx, dma_end_page=%016lx\n", + order, numTces, nelms, (unsigned long)dma_start_page, (unsigned long)dma_end_page ); + + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + printk("pci_unmap_sg: invalid bus # %d\n", bus ); + printk("pci_unmap_sg: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + + tbl = tceTables[bus]; + + if ( tbl ) + free_tces( tbl, dma_start_page, order, numTces ); + +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/iSeries_pci.c linux.ac/arch/ppc64/kernel/iSeries_pci.c --- linux.vanilla/arch/ppc64/kernel/iSeries_pci.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/iSeries_pci.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,431 @@ +/* + * iSeries_pci.c + * + * Copyright (C) 2001 Allan Trautman, IBM Corporation + * + * iSeries specific routines for PCI. + * + * Based on code from pci.c and iSeries_pci.c 32bit + * + * 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 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +//#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "pci.h" + +extern struct pci_controller* hose_head; +extern struct pci_controller** hose_tail; +extern struct Naca *naca; +extern struct device_node *allnodes; +extern unsigned long phb_tce_table_init(struct pci_controller *phb); + +extern struct pci_ops iSeries_pci_ops; +extern struct flightRecorder* PciFr; + +int PciTraceFlag = 0; + +struct pci_dev; + +struct i_device_node { + struct list_head iDevice_Chain; + char* Name; + char* Type; + struct pci_dev* PciDev; + int BusNumber; + int SubBus; + int ReturnCode; + int DevFn; +}; + +LIST_HEAD(iDevice_List); + +/******************************************************************* + * Forward declares of prototypes. + *******************************************************************/ +unsigned long find_and_init_phbs(void); +void fixup_resources(struct pci_dev *dev); +void iSeries_pcibios_fixup(void); +struct pci_controller* alloc_phb(struct device_node *dev, char *model, unsigned int addr_size_words) ; + +void iSeries_Scan_PHBs_Slots(struct pci_controller* Phb); +void iSeries_Scan_EADs_Bridge(HvBusNumber Bus, HvSubBusNumber SubBus, int IdSel); +int iSeries_Scan_Bridge_Slot(HvBusNumber Bus, HvSubBusNumber SubBus, int MaxAgents); + +/********************************************************************************** + * + * + **********************************************************************************/ +#define ISERIES_PCI_READ_OP(size, call, type) \ +/***********************************************************************************/ \ +int iSeries_pci_read_config_##size(struct pci_dev* PciDev, int PciOffset, type ReadValue) { \ + struct i_device_node* DeviceNode = PciDev->sysdata; \ + if(DeviceNode->BusNumber == 0xFF) DeviceNode->ReturnCode = 0x301; return DeviceNode->ReturnCode; \ + DeviceNode->ReturnCode = HvCallPci_config##call(DeviceNode->BusNumber, DeviceNode->SubBus, \ + DeviceNode->DevFn, PciOffset, ReadValue); \ + if(DeviceNode->ReturnCode != 0 ) { \ + PCIFR("RC##size: %02X,%02X,%02X,%04X Rtn: %04X", \ + DeviceNode->BusNumber, DeviceNode->SubBus, DeviceNode->DevFn, PciOffset,DeviceNode->ReturnCode); \ + } \ + return DeviceNode->ReturnCode; \ +} +/***********************************************************************************/ \ +#define ISERIES_PCI_WRITE_OP(size, call, type) \ +/***********************************************************************************/ \ +int iSeries_pci_write_config_##size(struct pci_dev* PciDev, int PciOffset, type WriteValue) { \ + struct i_device_node* DeviceNode = PciDev->sysdata; \ + DeviceNode->ReturnCode = HvCallPci_config##call(DeviceNode->BusNumber, DeviceNode->SubBus, \ + DeviceNode->DevFn, PciOffset, WriteValue); \ + if(DeviceNode->ReturnCode != 0 ) { \ + PCIFR("RC##size: %02X,%02X,%02X,%04X Rtn: %04X", \ + DeviceNode->BusNumber, DeviceNode->SubBus, DeviceNode->DevFn, PciOffset,DeviceNode->ReturnCode); \ + } \ + return DeviceNode->ReturnCode; \ +} + + ISERIES_PCI_READ_OP( byte, Load8, u8*) + ISERIES_PCI_READ_OP( word, Load16, u16*) + ISERIES_PCI_READ_OP( dword,Load32, u32*) + ISERIES_PCI_WRITE_OP(byte, Store8, u8) + ISERIES_PCI_WRITE_OP(word, Store16,u16) + ISERIES_PCI_WRITE_OP(dword,Store32,u32) + +/************************************************************************/ +/* Branch Table */ +/************************************************************************/ +struct pci_ops iSeries_pci_ops = { + iSeries_pci_read_config_byte, + iSeries_pci_read_config_word, + iSeries_pci_read_config_dword, + iSeries_pci_write_config_byte, + iSeries_pci_write_config_word, + iSeries_pci_write_config_dword +}; + + +/**************************************************************************** + * + * unsigned int __init find_and_init_phbs(void) + * + * Description: + * This function checks for all possible system PCI host bridges that connect + * PCI buses. The system hypervisor is queried as to the guest partition + * ownership status. A pci_controller is build for any bus which is partially + * owned or fully owned by this guest partition. + ****************************************************************************/ +unsigned long __init find_and_init_phbs(void) { + struct pci_controller* hose; + HvBusNumber BusNumber; + int LxBusNumber = 0; /* Linux Bus number for grins */ + + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry\n"); + + /* Check to make sure the device probing will work on this iSeries Release. */ + if(hvReleaseData.xVrmIndex !=3) { + printk("PCI: iSeries Lpar and Linux native PCI I/O code is incompatible.\n"); + printk("PCI: A newer version of the Linux kernel is need for this iSeries release.\n"); + return -1; + } + /* Check all possible buses. */ + for (BusNumber = 0; BusNumber < 256; BusNumber++) { + int RtnCode = HvCallXm_testBus(BusNumber); + if (RtnCode == 0) { + PPCDBG(PPCDBG_BUSWALK, "Create iSeries PHB controller: %04X\n",BusNumber); + PCIFR("Create iSeries PHB controller: %04X",BusNumber); + + PPCDBG(PPCDBG_BUSWALK, "PCI: Allocate pci_controller for PBH HV\n"); + hose = (struct pci_controller *)kmalloc(sizeof(struct pci_controller),GFP_KERNEL); + if(hose == NULL) { + printk("PCI: Allocate pci_controller failed.\n"); + PCIFR( "PCI: Allocate pci_controller failed.\n"); + return -1; + } + memset(hose, 0, sizeof(struct pci_controller)); + *hose_tail = hose; + hose_tail = &hose->next; + strcpy(hose->what,"PHB HV"); + hose->type = phb_type_hypervisor; + hose->global_number = BusNumber; + *hose_tail = hose; + hose_tail = &hose->next; + + hose->local_number = BusNumber; /* Stuff HV bus number away. */ + hose->first_busno = LxBusNumber;/* This just for debug. pcibios will */ + hose->last_busno = 0xff; /* assign the bus numbers later. */ + hose->ops = &iSeries_pci_ops; /* Cnfg Reg Ops routines. */ + LxBusNumber += 1; /* Keep track for debug. */ + + /*********************/ + /* TCE Table for Bus */ + /*********************/ + //create_pci_bus_tce_table(BusNumber); + + /************************************************************************* + * Find and connect the devices. + *************************************************************************/ + iSeries_Scan_PHBs_Slots(hose); + + } + } + return 0; +} +/*********************************************************************** + * ppc64_pcibios_init + * + * Chance to initialize and structures or variable before PCI Bus walk. + * + ***********************************************************************/ +void iSeries_pcibios_init(void) { + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry.\n"); + /* find_and_init_phbs(); comment out until debugged. */ + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Exit.\n"); +} + +/*********************************************************************** + * iSeries_pcibios_fixup(void) + * + * + * + ***********************************************************************/ +void __init iSeries_pcibios_fixup(void) { + struct pci_dev *dev; + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry.\n"); + return; /* Just return until debugged. */ + + pci_assign_all_busses = 0; + + pci_for_each_dev(dev) { + PPCDBGCALL(PPCDBG_BUSWALK, dumpPci_Dev(dev) ); + } +} +/*********************************************************************** + * find_floppy(void) + * + * Finds the default floppy device, if the system has one, and returns + * the pci_dev for the isa bridge for the floppy device. + * + * Note: On iSeries there will only be a virtual diskette. + ***********************************************************************/ +struct pci_dev* +find_floppy() { + PPCDBG(PPCDBG_BUSWALK,"\tFind Floppy pci_dev.. None on iSeries.\n"); + return NULL; +} + + +/*********************************************************************** + * fixup_resources(struct pci_dev *dev) + * + * + * + * + ***********************************************************************/ +void +fixup_resources(struct pci_dev *dev) { + struct pci_controller* phb = (struct pci_controller *)dev->sysdata; + + PPCDBG(PPCDBG_BUSWALK, "fixup_resources:\n"); + PPCDBG(PPCDBG_BUSWALK, "\tphb = 0x%016LX\n", phb); + PPCDBG(PPCDBG_BUSWALK, "\tphb->local_number = 0x%004X \n", phb->local_number); +} + + +/*********************************************************************** + * build_device_node(u16 Bus, int SubBus, u8 DevFn) + * + * + * char* loc-code + * int vendor-id + * int device-id.. + * int BusNumber + * int SubBus + * int ReturnCode + * + * int regs + * int interrupts + * char* loc-code + ***********************************************************************/ +void +build_device_node(HvBusNumber BusNumber, HvSubBusNumber SubBus, u8 DevFn) {\ + struct i_device_node* iDevice = kmalloc(sizeof(struct i_device_node), GFP_KERNEL); + list_add_tail(&iDevice->iDevice_Chain,&iDevice_List); + + + +} + +void list_device_nodes(void) { + struct list_head* iDevice_Node_Ptr = iDevice_List.next; + int index = 1; + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry\n"); + while(iDevice_Node_Ptr != &iDevice_List) { + iDevice_Node_Ptr = iDevice_Node_Ptr->next; + ++index; + } +} + + +/******************************************************************************** +* Loop through each node function to find usable bridges. +*********************************************************************************/ +void iSeries_Scan_PHBs_Slots(struct pci_controller* Phb) { + struct HvCallPci_DeviceInfo* DevInfo; + HvBusNumber Bus = Phb->local_number; /* System Bus */ + HvSubBusNumber SubBus = 0; /* EADs is always 0. */ + int HvRc = 0; + int IdSel = 1; + int MaxAgents = 8; + + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry\n"); + + DevInfo = (struct HvCallPci_DeviceInfo*)kmalloc(sizeof(struct HvCallPci_DeviceInfo), GFP_KERNEL); + if(DevInfo == NULL) return; + + /******************************************************************************** + * Probe for EADs Bridges + ********************************************************************************/ + for (IdSel=1; IdSel < MaxAgents; ++IdSel) { + HvRc = HvCallPci_getDeviceInfo(Bus, SubBus, IdSel,REALADDR(DevInfo), sizeof(struct HvCallPci_DeviceInfo)); + if (HvRc == 0) { + PPCDBG(PPCDBG_BUSWALK,"\tFound Device 0x%02X\n",DevInfo->deviceType); + if(DevInfo->deviceType == HvCallPci_NodeDevice) { + PPCDBG(PPCDBG_BUSWALK,"\tFound EADs Bridge(NodeDevice)\n"); + iSeries_Scan_EADs_Bridge(Bus, SubBus, IdSel); + } + else { + printk("PCI: Invalid System Configuration. \n"); + } + } + } + kfree(DevInfo); + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Exit\n"); +} + + +/******************************************************************************** +* +*********************************************************************************/ +void iSeries_Scan_EADs_Bridge(HvBusNumber Bus, HvSubBusNumber SubBus, int IdSel) { + struct HvCallPci_BridgeInfo* BridgeInfo; + HvAgentId AgentId; + int Function; + int HvRc; + + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry\n"); + + BridgeInfo = (struct HvCallPci_BridgeInfo*)kmalloc(sizeof(struct HvCallPci_BridgeInfo), GFP_KERNEL); + if(BridgeInfo == NULL) return; + + /********************************************************************* + * Note: hvSubBus and irq is always be 0 at this level! + *********************************************************************/ + for (Function=0; Function < 8; ++Function) { + AgentId = ISERIES_PCI_AGENTID(IdSel, Function); + HvRc = HvCallXm_connectBusUnit(Bus, SubBus, AgentId, 0); + if (HvRc == 0) { + PPCDBG(PPCDBG_BUSWALK,"Connect EADs: 0x%02X.%02X.%02X = 0x%02X\n",Bus, SubBus, AgentId); + HvRc = HvCallPci_getBusUnitInfo(Bus, SubBus, AgentId, + REALADDR(BridgeInfo), sizeof(struct HvCallPci_BridgeInfo)); + if (HvRc == 0) { + PPCDBG(PPCDBG_BUSWALK,"\tBridgeInfo..: 0x%02X.%02X.%02X = 0x%02X\n",Bus, SubBus, AgentId, + BridgeInfo->busUnitInfo.deviceType); + + if (BridgeInfo->busUnitInfo.deviceType == HvCallPci_BridgeDevice) { + PPCDBG(PPCDBG_BUSWALK,"\tScan_Bridge_Slot...: 0x%02X.%02X.%02X\n",Bus, SubBus, AgentId); + iSeries_Scan_Bridge_Slot(Bus, BridgeInfo->subBusNumber,BridgeInfo->maxAgents); + } + } + } + } + kfree(BridgeInfo); + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Exit\n"); +} + +/******************************************************************************** +* +* This assumes that the node slot is always on the primary bus! +*********************************************************************************/ +int iSeries_Scan_Bridge_Slot(HvBusNumber Bus, HvSubBusNumber SubBus, int MaxAgents) { + u16 VendorId = 0; + int HvRc = 0; + int Irq = 0; + int IdSel = ISERIES_GET_DEVICE_FROM_SUBBUS(SubBus); + int Function = ISERIES_GET_FUNCTION_FROM_SUBBUS(SubBus); + HvAgentId AgentId = ISERIES_PCI_AGENTID(IdSel, Function); + int ValidSlots = 0; + + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry\n"); + + Irq = iSeries_allocate_IRQ(Bus, 0, AgentId); + PPCDBG(PPCDBG_BUSWALK,"\tiSeries_allocate_IRQ.: 0x%02X.%02X.%02X(0x%02X)\n", Bus, SubBus, AgentId, Irq); + + /**************************************************************************** + * Connect all functions of any device found. + ****************************************************************************/ + for (IdSel = 1; IdSel <= MaxAgents; ++IdSel) { + for (Function = 0; Function < 8; ++Function) { + AgentId = ISERIES_PCI_AGENTID(IdSel, Function); + HvRc = HvCallXm_connectBusUnit(Bus, SubBus, AgentId, Irq); + if (HvRc == 0) { + PPCDBG(PPCDBG_BUSWALK,"\tConnect....: 0x%02X.%02X.%02X Irq: 0x%02X\n",Bus, SubBus, AgentId, Irq); + HvRc = HvCallPci_configLoad16(Bus, SubBus, AgentId, PCI_VENDOR_ID, &VendorId); + if (HvRc == 0) { + PPCDBG(PPCDBG_BUSWALK,"\tVendorId...: 0x%02X.%02X.%02X = 0x%04X\n",Bus, SubBus, AgentId, VendorId); + ++ValidSlots; + } + } + if (HvRc != 0) { + PPCDBG(PPCDBG_BUSWALK,"\tConnect/Read Vendor failed: 0x%02X.%02X.%02X = 0x%02X\n",Bus, SubBus, AgentId, HvRc); + } + } + /**************************************************************************** + * If a device is found, assign irq to slot + ****************************************************************************/ + if (ValidSlots > 0) { + PPCDBG(PPCDBG_BUSWALK,"\tiSeries_assign_IRQ 0x%02X.%02X.%02X = 0x%02X\n",Bus, SubBus, AgentId, Irq ); + iSeries_assign_IRQ(Irq, Bus, SubBus, AgentId); + } + } + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Exit\n"); + return HvRc; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/iSeries_proc.c linux.ac/arch/ppc64/kernel/iSeries_proc.c --- linux.vanilla/arch/ppc64/kernel/iSeries_proc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/iSeries_proc.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,154 @@ +/* + * iSeries_proc.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 + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#include +#include +#ifndef _ISERIES_PROC_H +#include +#endif + + +static struct proc_dir_entry * iSeries_proc_root = NULL; +static int iSeries_proc_initializationDone = 0; +static spinlock_t iSeries_proc_lock; + +struct iSeries_proc_registration +{ + struct iSeries_proc_registration *next; + iSeriesProcFunction functionMember; +}; + + +struct iSeries_proc_registration preallocated[16]; +#define MYQUEUETYPE(T) struct MYQueue##T +#define MYQUEUE(T) \ +MYQUEUETYPE(T) \ +{ \ +struct T *head; \ +struct T *tail; \ +} +#define MYQUEUECTOR(q) do { (q)->head = NULL; (q)->tail = NULL; } while(0) +#define MYQUEUEENQ(q, p) \ +do { \ +(p)->next = NULL; \ +if ((q)->head != NULL) \ +{ \ +(q)->head->next = (p); \ +(q)->head = (p); \ +} \ +else \ +{ \ +(q)->tail = (q)->head = (p); \ +} \ +} while(0) + +#define MYQUEUEDEQ(q,p) \ +do { \ +(p) = (q)->tail; \ +if ((p) != NULL) \ +{ \ +(q)->tail = (p)->next; \ +(p)->next = NULL; \ +} \ +if ((q)->tail == NULL) \ +(q)->head = NULL; \ +} while(0) +MYQUEUE(iSeries_proc_registration); +typedef MYQUEUETYPE(iSeries_proc_registration) aQueue; + + +aQueue iSeries_free; +aQueue iSeries_queued; + +void iSeries_proc_early_init(void) +{ + int i = 0; + unsigned long flags; + iSeries_proc_initializationDone = 0; + spin_lock_init(&iSeries_proc_lock); + MYQUEUECTOR(&iSeries_free); + MYQUEUECTOR(&iSeries_queued); + + spin_lock_irqsave(&iSeries_proc_lock, flags); + for (i = 0; i < 16; ++i) + { + MYQUEUEENQ(&iSeries_free, preallocated+i); + } + spin_unlock_irqrestore(&iSeries_proc_lock, flags); +} + +void iSeries_proc_create(void) +{ + unsigned long flags; + struct iSeries_proc_registration *reg = NULL; + spin_lock_irqsave(&iSeries_proc_lock, flags); + printk("iSeries_proc: Creating /proc/iSeries\n"); + + iSeries_proc_root = proc_mkdir("iSeries", 0); + if (!iSeries_proc_root) return; + + MYQUEUEDEQ(&iSeries_queued, reg); + + while (reg != NULL) + { + (*(reg->functionMember))(iSeries_proc_root); + + MYQUEUEDEQ(&iSeries_queued, reg); + } + + iSeries_proc_initializationDone = 1; + spin_unlock_irqrestore(&iSeries_proc_lock, flags); +} + +void iSeries_proc_callback(iSeriesProcFunction initFunction) +{ + unsigned long flags; + spin_lock_irqsave(&iSeries_proc_lock, flags); + + if (iSeries_proc_initializationDone) + { + (*initFunction)(iSeries_proc_root); + } + else + { + struct iSeries_proc_registration *reg = NULL; + + MYQUEUEDEQ(&iSeries_free, reg); + + if (reg != NULL) + { +/* printk("Registering %p in reg %p\n", initFunction, reg); */ + reg->functionMember = initFunction; + + MYQUEUEENQ(&iSeries_queued, reg); + } + else + { + printk("Couldn't get a queue entry\n"); + } + } + + spin_unlock_irqrestore(&iSeries_proc_lock, flags); +} + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/iSeries_rtc.c linux.ac/arch/ppc64/kernel/iSeries_rtc.c --- linux.vanilla/arch/ppc64/kernel/iSeries_rtc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/iSeries_rtc.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,265 @@ +/* + * Real Time Clock interface for IBM iSeries + * + * Based on rtc.c by Paul Gortmaker + * + * This driver allows use of the real time clock + * from user space. It exports the /dev/rtc + * interface supporting various ioctl() and also the + * /proc/driver/rtc pseudo-file for status information. + * + * iSeries does not support RTC interrupts nor an alarm. + * + * 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. + * + * 1.0 Mike Corrigan: IBM iSeries rtc support + */ + +#define RTC_VERSION "1.0" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* + * We sponge a minor off of the misc major. No need slurping + * up another valuable major dev number for this. If you add + * an ioctl, make sure you don't conflict with SPARC's RTC + * ioctls. + */ + +static loff_t rtc_llseek(struct file *file, loff_t offset, int origin); + +static ssize_t rtc_read(struct file *file, char *buf, + size_t count, loff_t *ppos); + +static int rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +static void get_rtc_time (struct rtc_time *rtc_tm); + +static int rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); + +/* + * If this driver ever becomes modularised, it will be really nice + * to make the epoch retain its value across module reload... + */ + +static unsigned long epoch = 1900; /* year corresponding to 0x00 */ + +static const unsigned char days_in_mo[] = +{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +/* + * Now all the various file operations that we export. + */ + +static loff_t rtc_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static ssize_t rtc_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + return -EIO; +} + +static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct rtc_time wtime; + + switch (cmd) { + case RTC_RD_TIME: /* Read the time/date from RTC */ + { + get_rtc_time(&wtime); + break; + } + case RTC_SET_TIME: /* Set the RTC */ + { + struct rtc_time rtc_tm; + unsigned char mon, day, hrs, min, sec, leap_yr; + unsigned int yrs; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + + yrs = rtc_tm.tm_year; + mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ + day = rtc_tm.tm_mday; + hrs = rtc_tm.tm_hour; + min = rtc_tm.tm_min; + sec = rtc_tm.tm_sec; + + if (yrs < 70) + return -EINVAL; + + leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); + + if ((mon > 12) || (day == 0)) + return -EINVAL; + + if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) + return -EINVAL; + + if ((hrs >= 24) || (min >= 60) || (sec >= 60)) + return -EINVAL; + + if ( yrs > 169 ) + return -EINVAL; + + mf_setRtc( &rtc_tm ); + + return 0; + } + case RTC_EPOCH_READ: /* Read the epoch. */ + { + return put_user (epoch, (unsigned long *)arg); + } + case RTC_EPOCH_SET: /* Set the epoch. */ + { + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) + return -EINVAL; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + epoch = arg; + return 0; + } + default: + return -EINVAL; + } + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; +} + +static int rtc_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* + * The various file operations we support. + */ + +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + llseek: rtc_llseek, + read: rtc_read, + ioctl: rtc_ioctl, + open: rtc_open, + release: rtc_release, +}; + +static struct miscdevice rtc_dev= +{ + RTC_MINOR, + "rtc", + &rtc_fops +}; + +static int __init rtc_init(void) +{ + misc_register(&rtc_dev); + create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); + + printk(KERN_INFO "iSeries Real Time Clock Driver v" RTC_VERSION "\n"); + + return 0; +} + +static void __exit rtc_exit (void) +{ + remove_proc_entry ("driver/rtc", NULL); + misc_deregister(&rtc_dev); +} + +module_init(rtc_init); +module_exit(rtc_exit); +EXPORT_NO_SYMBOLS; + +/* + * Info exported via "/proc/driver/rtc". + */ + +static int rtc_proc_output (char *buf) +{ + + char *p; + struct rtc_time tm; + + p = buf; + + get_rtc_time(&tm); + + /* + * There is no way to tell if the luser has the RTC set for local + * time or for Universal Standard Time (GMT). Probably local though. + */ + p += sprintf(p, + "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04lu\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch); + + p += sprintf(p, + "DST_enable\t: no\n" + "BCD\t\t: yes\n" + "24hr\t\t: yes\n" ); + + return p - buf; +} + +static int rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = rtc_proc_output (page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static void get_rtc_time(struct rtc_time *rtc_tm) +{ + mf_getRtc( rtc_tm ); + + rtc_tm->tm_mon--; +} + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/iSeries_setup.c linux.ac/arch/ppc64/kernel/iSeries_setup.c --- linux.vanilla/arch/ppc64/kernel/iSeries_setup.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/iSeries_setup.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,760 @@ +/* + * + * + * Copyright (c) 2000 Mike Corrigan + * Copyright (c) 1999-2000 Grant Erickson + * + * Module name: iSeries_setup.c + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM iSeries LPAR. Adapted from original code by Grant Erickson and + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "iSeries_setup.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Function Prototypes */ + +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 saddr, unsigned long eaddr); +void build_valid_hpte( unsigned long vsid, unsigned long ea, unsigned long pa, + pte_t * ptep, unsigned hpteflags, unsigned bolted ); +extern void ppcdbg_initialize(void); + +/* Global Variables */ + +unsigned long procFreqHz = 0; +unsigned long procFreqMhz = 0; +unsigned long procFreqMhzHundreths = 0; + +unsigned long tbFreqHz = 0; +unsigned long tbFreqMhz = 0; +unsigned long tbFreqMhzHundreths = 0; + +int piranha_simulator = 0; + +extern char _end[]; + +extern struct Naca *naca; +extern int rd_size; /* Defined in drivers/block/rd.c */ +extern unsigned long klimit; + +/* + * void __init iSeries_init_early() + */ + +void __init +iSeries_init_early(void) +{ + + ppcdbg_initialize(); + +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured and there is + * a non-zero starting address for it, set it up + */ + + if ( 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 ); + + if ( ((rd_size*1024)/PAGE_SIZE) < naca->xRamDiskSize ) + rd_size = (naca->xRamDiskSize*PAGE_SIZE)/1024; + } else + +#endif /* CONFIG_BLK_DEV_INITRD */ + { + + ROOT_DEV = MKDEV( VIODASD_MAJOR, 1 ); + } + + /* Initialize the table which translate Linux physical addresses to + * AS/400 absolute addresses + */ + + build_iSeries_Memory_Map(); + + setup_iSeries_cache_sizes(); + + /* Initialize machine-dependency vectors */ + + ppc_md.setup_arch = iSeries_setup_arch; + ppc_md.setup_residual = iSeries_setup_residual; + ppc_md.get_cpuinfo = iSeries_get_cpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = iSeries_init_IRQ; + ppc_md.get_irq = iSeries_get_irq; + ppc_md.init = NULL; + + ppc_md.restart = iSeries_restart; + ppc_md.power_off = iSeries_power_off; + ppc_md.halt = iSeries_halt; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = iSeries_set_rtc_time; + ppc_md.get_rtc_time = iSeries_get_rtc_time; + ppc_md.calibrate_decr = iSeries_calibrate_decr; + ppc_md.progress = iSeries_progress; + + ppc_md.kbd_setkeycode = NULL; + ppc_md.kbd_getkeycode = NULL; + ppc_md.kbd_translate = NULL; + ppc_md.kbd_unexpected_up = NULL; + ppc_md.kbd_leds = NULL; + ppc_md.kbd_init_hw = NULL; + +#if defined(CONFIG_MAGIC_SYSRQ) + ppc_md.ppc_kbd_sysrq_xlate = NULL; +#endif + + if ( itLpNaca.xPirEnvironMode == 0 ) + piranha_simulator = 1; + + return; + +} + +/* + * void __init iSeries_init() + */ + +void __init +iSeries_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* Associate Lp Event Queue 0 with processor 0 */ + HvCallEvent_setLpEventQueueInterruptProc( 0, 0 ); + + { + /* copy the command line parameter from the primary VSP */ + char *p, *q; + HvCallEvent_dmaToSp( cmd_line, + 2*64*1024, + 256, + HvLpDma_Direction_RemoteToLocal ); + + p = q = cmd_line + 255; + while( p > cmd_line ) { + if ((*p == 0) || (*p == ' ') || (*p == '\n')) + --p; + else + break; + } + if ( p < q ) + *(p+1) = 0; + } + + iSeries_proc_early_init(); + mf_init(); + + iSeries_proc_callback( &pmc_proc_init ); + + viopath_init(); + + return; +} + + +/* + * The iSeries may have very large memories ( > 128 GB ) and a partition + * may get memory in "chunks" that may be anywhere in the 2**52 real + * address space. The chunks are 256K in size. To map this to the + * memory model Linux expects, the AS/400 specific code builds a + * translation table to translate what Linux thinks are "physical" + * addresses to the actual real addresses. This allows us to make + * it appear to Linux that we have contiguous memory starting at + * physical address zero while in fact this could be far from the truth. + * To avoid confusion, I'll let the words physical and/or real address + * apply to the Linux addresses while I'll use "absolute address" to + * refer to the actual hardware real address. + * + * build_iSeries_Memory_Map gets information from the Hypervisor and + * looks at the Main Store VPD to determine the absolute addresses + * of the memory that has been assigned to our partition and builds + * a table used to translate Linux's physical addresses to these + * absolute addresses. Absolute addresses are needed when + * communicating with the hypervisor (e.g. to build HPT entries) + */ + +static void __init build_iSeries_Memory_Map(void) +{ + u32 loadAreaFirstChunk, loadAreaLastChunk, loadAreaSize; + u32 nextPhysChunk; + u32 hptFirstChunk, hptLastChunk, hptSizeChunks, hptSizePages; + u32 num_ptegs; + u32 holeFirstChunk, holeSizeChunks; + u32 totalChunks,moreChunks; + u32 currChunk, thisChunk, absChunk; + u32 currDword; + u32 chunkBit; + u64 holeStart, holeEnd, holeSize; + u64 map; + struct IoHriMainStoreSegment4 * msVpd; + + /* Chunk size on iSeries is 256K bytes */ + totalChunks = (u32)HvLpConfig_getMsChunks(); + klimit = msChunks_alloc(klimit, totalChunks, 1UL<<18); + + /* Get absolute address of our load area + * and map it to physical address 0 + * This guarantees that the loadarea ends up at physical 0 + * otherwise, it might not be returned by PLIC as the first + * chunks + */ + + 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 + * aren't in the HPT yet and can still + * be assigned an arbitrary physical address + */ + if ( (loadAreaSize * 64) > HvPagesToMap ) + loadAreaSize = HvPagesToMap / 64; + + loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1; + + printk( "Mapping load area - physical addr = 0000000000000000\n" + " absolute addr = %016lx\n", + chunk_to_addr(loadAreaFirstChunk) ); + printk( "Load area size %dK\n", loadAreaSize*256 ); + + for ( nextPhysChunk = 0; + nextPhysChunk < loadAreaSize; + ++nextPhysChunk ) { + msChunks.abs[nextPhysChunk] = loadAreaFirstChunk+nextPhysChunk; + } + + /* Get absolute address of our HPT and remember it so + * we won't map it to any physical address + */ + + hptFirstChunk = (u32)addr_to_chunk(HvCallHpt_getHptAddress()); + hptSizePages = (u32)(HvCallHpt_getHptPages()); + hptSizeChunks = hptSizePages >> (msChunks.chunk_shift-PAGE_SHIFT); + hptLastChunk = hptFirstChunk + hptSizeChunks - 1; + + printk( "HPT absolute addr = %016lx, size = %dK\n", + chunk_to_addr(hptFirstChunk), hptSizeChunks*256 ); + + /* Fill in the htab_data structure */ + + /* Fill in size of hashed page table */ + num_ptegs = hptSizePages * (PAGE_SIZE/(sizeof(HPTE)*HPTES_PER_GROUP)); + htab_data.htab_num_ptegs = num_ptegs; + htab_data.htab_hash_mask = num_ptegs - 1; + + /* The actual hashed page table is in the hypervisor, we have no direct access */ + htab_data.htab = NULL; + + /* Determine if absolute memory has any + * holes so that we can interpret the + * access map we get back from the hypervisor + * correctly. + */ + + msVpd = (struct IoHriMainStoreSegment4 *)xMsVpd; + holeStart = msVpd->nonInterleavedBlocksStartAdr; + holeEnd = msVpd->nonInterleavedBlocksEndAdr; + holeSize = holeEnd - holeStart; + + if ( holeSize ) { + holeStart = holeStart & 0x000fffffffffffff; + holeStart = addr_to_chunk(holeStart); + holeFirstChunk = (u32)holeStart; + holeSize = addr_to_chunk(holeSize); + holeSizeChunks = (u32)holeSize; + printk( "Main store hole: start chunk = %0x, size = %0x chunks\n", + holeFirstChunk, holeSizeChunks ); + } + else { + holeFirstChunk = 0xffffffff; + holeSizeChunks = 0; + } + + /* Process the main store access map from the hypervisor + * to build up our physical -> absolute translation table + */ + + currChunk = 0; + currDword = 0; + moreChunks = totalChunks; + + while ( moreChunks ) { + map = HvCallSm_get64BitsOfAccessMap( itLpNaca.xLpIndex, + currDword ); + thisChunk = currChunk; + while ( map ) { + chunkBit = map >> 63; + map <<= 1; + if ( chunkBit ) { + --moreChunks; + absChunk = thisChunk; + if ( absChunk >= holeFirstChunk ) + absChunk += holeSizeChunks; + if ( ( ( absChunk < hptFirstChunk ) || + ( absChunk > hptLastChunk ) ) && + ( ( absChunk < loadAreaFirstChunk ) || + ( absChunk > loadAreaLastChunk ) ) ) { + msChunks.abs[nextPhysChunk] = absChunk; + ++nextPhysChunk; + } + } + ++thisChunk; + } + ++currDword; + currChunk += 64; + } + + /* main store size (in chunks) is + * totalChunks - hptSizeChunks + * which should be equal to + * nextPhysChunk + */ + naca->physicalMemorySize = chunk_to_addr(nextPhysChunk); + + /* Bolt kernel mappings for all of memory */ + iSeries_bolt_kernel( 0, naca->physicalMemorySize ); + + lmb_init(); + lmb_add( 0, naca->physicalMemorySize ); + lmb_reserve( 0, __pa(klimit)); + +} + +/* + * Set up the variables that describe the cache line sizes + * for this machine. + */ + +static void __init setup_iSeries_cache_sizes(void) +{ + unsigned i,n; + naca->iCacheL1LineSize = xIoHriProcessorVpd[0].xInstCacheOperandSize; + naca->dCacheL1LineSize = xIoHriProcessorVpd[0].xDataCacheOperandSize; + naca->iCacheL1LinesPerPage = PAGE_SIZE / naca->iCacheL1LineSize; + naca->dCacheL1LinesPerPage = PAGE_SIZE / naca->dCacheL1LineSize; + i = naca->iCacheL1LineSize; + n = 0; + while ((i=(i/2))) ++n; + naca->iCacheL1LogLineSize = n; + i = naca->dCacheL1LineSize; + n = 0; + while ((i=(i/2))) ++n; + naca->dCacheL1LogLineSize = n; + + printk( "D-cache line size = %d (log = %d)\n", + (unsigned)naca->dCacheL1LineSize, + (unsigned)naca->dCacheL1LogLineSize ); + printk( "I-cache line size = %d (log = %d)\n", + (unsigned)naca->iCacheL1LineSize, + (unsigned)naca->iCacheL1LogLineSize ); + +} + +/* + * Bolt the kernel addr space into the HPT + */ + +static void __init iSeries_bolt_kernel(unsigned long saddr, unsigned long eaddr) +{ + unsigned long pa; + unsigned long mode_rw = _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX; + HPTE hpte; + + for (pa=saddr; pa < eaddr ;pa+=PAGE_SIZE) { + unsigned long ea = (unsigned long)__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 ) { + /* 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); + } + } +} + +/* + * Document me. + */ +void __init +iSeries_setup_arch(void) +{ + void * eventStack; + + /* Setup the Lp Event Queue */ + + /* Allocate a page for the Event Stack + * The hypervisor wants the absolute real address, so + * we subtract out the KERNELBASE and add in the + * absolute real address of the kernel load area + */ + + eventStack = alloc_bootmem_pages( LpEventStackSize ); + + memset( eventStack, 0, LpEventStackSize ); + + /* Invoke the hypervisor to initialize the event stack */ + + HvCallEvent_setLpEventStack( 0, eventStack, LpEventStackSize ); + + /* Initialize fields in our Lp Event Queue */ + + xItLpQueue.xSlicEventStackPtr = (char *)eventStack; + xItLpQueue.xSlicCurEventPtr = (char *)eventStack; + xItLpQueue.xSlicLastValidEventPtr = (char *)eventStack + + (LpEventStackSize - LpEventMaxSize); + xItLpQueue.xIndex = 0; + + /* Compute processor frequency */ + procFreqHz = (((1UL<<34) * 1000000) / xIoHriProcessorVpd[0].xProcFreq ); + procFreqMhz = procFreqHz / 1000000; + procFreqMhzHundreths = (procFreqHz/10000) - (procFreqMhz*100); + + /* Compute time base frequency */ + tbFreqHz = (((1UL<<32) * 1000000) / xIoHriProcessorVpd[0].xTimeBaseFreq ); + tbFreqMhz = tbFreqHz / 1000000; + tbFreqMhzHundreths = (tbFreqHz/10000) - (tbFreqMhz*100); + + printk("Max logical processors = %d\n", + itVpdAreas.xSlicMaxLogicalProcs ); + printk("Max physical processors = %d\n", + itVpdAreas.xSlicMaxPhysicalProcs ); + printk("Processor frequency = %lu.%02lu\n", + procFreqMhz, + procFreqMhzHundreths ); + printk("Time base frequency = %lu.%02lu\n", + tbFreqMhz, + tbFreqMhzHundreths ); + printk("Processor version = %x\n", + xIoHriProcessorVpd[0].xPVR ); + +} + +/* + * int as400_setup_residual() + * + * Description: + * This routine pretty-prints CPU information gathered from the VPD + * for use in /proc/cpuinfo + * + * Input(s): + * *buffer - Buffer into which CPU data is to be printed. + * + * Output(s): + * *buffer - Buffer with CPU data. + * + * Returns: + * The number of bytes copied into 'buffer' if OK, otherwise zero or less + * on error. + */ +int +iSeries_setup_residual(char *buffer) +{ + int len = 0; + + len += sprintf(len+buffer,"clock\t\t: %lu.%02luMhz\n", + procFreqMhz, procFreqMhzHundreths ); + len += sprintf(len+buffer,"time base\t: %lu.%02luMHz\n", + tbFreqMhz, tbFreqMhzHundreths ); + len += sprintf(len+buffer,"i-cache\t\t: %d\n", + naca->iCacheL1LineSize); + len += sprintf(len+buffer,"d-cache\t\t: %d\n", + naca->dCacheL1LineSize); + + + 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; + + len += sprintf(len+buffer,"machine\t\t: 64-bit iSeries Logical Partition\n"); + + return len; +} + +static char * lpEventTypes[9]= { +/* 0123456789012345678901234567890 */ + "Hypervisor\t\t", + "Machine Facilities\t", + "Session Manager\t\t", + "SPD I/O\t\t\t", + "Virtual Bus\t\t", + "PCI I/O\t\t\t", + "RIO I/O\t\t\t", + "Virtual Lan\t\t", + "Virtual I/O\t\t" + }; + +int get_ioevent_data(char *buffer) +{ + int len = 0; + unsigned i; + + len += sprintf(len+buffer,"LpEventQueue 0\n"); + len += sprintf(len+buffer," events processed:\t%lu\n", + (unsigned long)xItLpQueue.xLpIntCount ); + /* display event counts by type */ + for (i=0; i + * Copyright (c) 1999-2000 Grant Erickson + * + * Module name: as400_setup.h + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM AS/400 LPAR. Adapted from original code by Grant Erickson and + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + * 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. + */ + +#ifndef __ISERIES_SETUP_H__ +#define __ISERIES_SETUP_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + +extern void iSeries_init_early(void); +extern void iSeries_init(unsigned long r3, + unsigned long ird_start, + unsigned long ird_end, + unsigned long cline_start, + unsigned long cline_end); +extern void iSeries_setup_arch(void); +extern int iSeries_setup_residual(char *buffer); +extern int iSeries_get_cpuinfo(char *buffer); +extern void iSeries_init_IRQ(void); +extern int iSeries_get_irq(struct pt_regs *regs); +extern void iSeries_restart(char *cmd); +extern void iSeries_power_off(void); +extern void iSeries_halt(void); +extern void iSeries_time_init(void); +extern int iSeries_set_rtc_time(unsigned long now); +extern unsigned long iSeries_get_rtc_time(void); +extern void iSeries_calibrate_decr(void); +extern void iSeries_progress( char *, unsigned short ); + + +#ifdef __cplusplus +} +#endif + +#endif /* __ISERIES_SETUP_H__ */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/idle.c linux.ac/arch/ppc64/kernel/idle.c --- linux.vanilla/arch/ppc64/kernel/idle.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/idle.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,143 @@ +/* + * + * + * Idle daemon for PowerPC. Idle daemon will handle any action + * that needs to be taken when the system becomes idle. + * + * Written by Cort Dougan (cort@cs.nmt.edu) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +unsigned long maxYieldTime = 0; +unsigned long minYieldTime = 0xffffffffffffffffUL; + +static void yield_shared_processor(void) +{ + 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) +{ + struct Paca *paca; + long oldval; + + /* endless loop with no priority at all */ + current->nice = 20; + current->counter = -100; + init_idle(); + + paca = (struct Paca *)mfspr(SPRG3); + + 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) { +#ifdef CONFIG_PPC_ISERIES + HMT_medium(); + if ( ItLpQueue_isLpIntPending( paca->lpQueuePtr ) ) + process_iSeries_events(); + HMT_low(); +#endif + } + } + } + if (current->need_resched) { + HMT_medium(); + schedule(); + check_pgt_cache(); + } + } + return 0; +} + +/* + * SMP entry into the idle task - calls the same thing as the + * non-smp versions. -- Cort + */ +int cpu_idle(void) +{ + idled(); + return 0; +} + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/ioctl32.c linux.ac/arch/ppc64/kernel/ioctl32.c --- linux.vanilla/arch/ppc64/kernel/ioctl32.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/ioctl32.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,3803 @@ +/* + * ioctl32.c: Conversion between 32bit and 64bit native ioctls. + * + * Based on sparc64 ioctl32.c by: + * + * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + * + * ppc64 changes: + * + * Copyright (C) 2000 Ken Aaker (kdaaker@rchland.vnet.ibm.com) + * Copyright (C) 2001 Anton Blanchard (antonb@au.ibm.com) + * + * These routines maintain argument size conversion between 32bit and 64bit + * ioctls. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +/* Ugh. This header really is not clean */ +#define min min +#define max max +#include +#endif /* LVM */ + +#include +/* Ugly hack. */ +#undef __KERNEL__ +#include +#define __KERNEL__ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Use this to get at 32-bit user passed pointers. + See sys_sparc32.c for description about these. */ +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) \ +({ unsigned long __ret; \ + __asm__ ("clrldi %0, %0, 32" \ + : "=r" (__ret) \ + : "0" (__x)); \ + __ret; \ +}) + +/* Aiee. Someone does not find a difference between int and long */ +#define EXT2_IOC32_GETFLAGS _IOR('f', 1, int) +#define EXT2_IOC32_SETFLAGS _IOW('f', 2, int) +#define EXT2_IOC32_GETVERSION _IOR('v', 1, int) +#define EXT2_IOC32_SETVERSION _IOW('v', 2, int) + +extern asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); + +static int w_long(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + int err; + unsigned long val; + + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&val); + set_fs (old_fs); + if (!err && put_user(val, (u32 *)arg)) + return -EFAULT; + return err; +} + +static int rw_long(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + int err; + unsigned long val; + + if(get_user(val, (u32 *)arg)) + return -EFAULT; + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&val); + set_fs (old_fs); + if (!err && put_user(val, (u32 *)arg)) + return -EFAULT; + return err; +} + +static int do_ext2_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + /* These are just misnamed, they actually get/put from/to user an int */ + switch (cmd) { + case EXT2_IOC32_GETFLAGS: cmd = EXT2_IOC_GETFLAGS; break; + case EXT2_IOC32_SETFLAGS: cmd = EXT2_IOC_SETFLAGS; break; + case EXT2_IOC32_GETVERSION: cmd = EXT2_IOC_GETVERSION; break; + case EXT2_IOC32_SETVERSION: cmd = EXT2_IOC_SETVERSION; break; + } + return sys_ioctl(fd, cmd, arg); +} + +struct video_tuner32 { + s32 tuner; + u8 name[32]; + u32 rangelow, rangehigh; + u32 flags; + u16 mode, signal; +}; + +static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 *up) +{ + int i; + + if(get_user(kp->tuner, &up->tuner)) + return -EFAULT; + for(i = 0; i < 32; i++) + __get_user(kp->name[i], &up->name[i]); + __get_user(kp->rangelow, &up->rangelow); + __get_user(kp->rangehigh, &up->rangehigh); + __get_user(kp->flags, &up->flags); + __get_user(kp->mode, &up->mode); + __get_user(kp->signal, &up->signal); + return 0; +} + +static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 *up) +{ + int i; + + if(put_user(kp->tuner, &up->tuner)) + return -EFAULT; + for(i = 0; i < 32; i++) + __put_user(kp->name[i], &up->name[i]); + __put_user(kp->rangelow, &up->rangelow); + __put_user(kp->rangehigh, &up->rangehigh); + __put_user(kp->flags, &up->flags); + __put_user(kp->mode, &up->mode); + __put_user(kp->signal, &up->signal); + return 0; +} + +struct video_buffer32 { + /* void * */ u32 base; + s32 height, width, depth, bytesperline; +}; + +static int get_video_buffer32(struct video_buffer *kp, struct video_buffer32 *up) +{ + u32 tmp; + + if(get_user(tmp, &up->base)) + return -EFAULT; + kp->base = (void *) ((unsigned long)tmp); + __get_user(kp->height, &up->height); + __get_user(kp->width, &up->width); + __get_user(kp->depth, &up->depth); + __get_user(kp->bytesperline, &up->bytesperline); + return 0; +} + +static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 *up) +{ + u32 tmp = (u32)((unsigned long)kp->base); + + if(put_user(tmp, &up->base)) + return -EFAULT; + __put_user(kp->height, &up->height); + __put_user(kp->width, &up->width); + __put_user(kp->depth, &up->depth); + __put_user(kp->bytesperline, &up->bytesperline); + return 0; +} + +struct video_clip32 { + s32 x, y, width, height; + /* struct video_clip32 * */ u32 next; +}; + +struct video_window32 { + u32 x, y, width, height, chromakey, flags; + /* struct video_clip32 * */ u32 clips; + s32 clipcount; +}; + +static void free_kvideo_clips(struct video_window *kp) +{ + struct video_clip *cp; + + cp = kp->clips; + if(cp != NULL) + kfree(cp); +} + +static int get_video_window32(struct video_window *kp, struct video_window32 *up) +{ + struct video_clip32 *ucp; + struct video_clip *kcp; + int nclips, err, i; + u32 tmp; + + if(get_user(kp->x, &up->x)) + return -EFAULT; + __get_user(kp->y, &up->y); + __get_user(kp->width, &up->width); + __get_user(kp->height, &up->height); + __get_user(kp->chromakey, &up->chromakey); + __get_user(kp->flags, &up->flags); + __get_user(kp->clipcount, &up->clipcount); + __get_user(tmp, &up->clips); + ucp = (struct video_clip32 *)A(tmp); + kp->clips = NULL; + + nclips = kp->clipcount; + if(nclips == 0) + return 0; + + if(ucp == 0) + return -EINVAL; + + /* Peculiar interface... */ + if(nclips < 0) + nclips = VIDEO_CLIPMAP_SIZE; + + kcp = kmalloc(nclips * sizeof(struct video_clip), GFP_KERNEL); + err = -ENOMEM; + if(kcp == NULL) + goto cleanup_and_err; + + kp->clips = kcp; + for(i = 0; i < nclips; i++) { + __get_user(kcp[i].x, &ucp[i].x); + __get_user(kcp[i].y, &ucp[i].y); + __get_user(kcp[i].width, &ucp[i].width); + __get_user(kcp[i].height, &ucp[i].height); + kcp[nclips].next = NULL; + } + + return 0; + +cleanup_and_err: + free_kvideo_clips(kp); + return err; +} + +/* You get back everything except the clips... */ +static int put_video_window32(struct video_window *kp, struct video_window32 *up) +{ + if(put_user(kp->x, &up->x)) + return -EFAULT; + __put_user(kp->y, &up->y); + __put_user(kp->width, &up->width); + __put_user(kp->height, &up->height); + __put_user(kp->chromakey, &up->chromakey); + __put_user(kp->flags, &up->flags); + __put_user(kp->clipcount, &up->clipcount); + return 0; +} + +#define VIDIOCGTUNER32 _IOWR('v',4, struct video_tuner32) +#define VIDIOCSTUNER32 _IOW('v',5, struct video_tuner32) +#define VIDIOCGWIN32 _IOR('v',9, struct video_window32) +#define VIDIOCSWIN32 _IOW('v',10, struct video_window32) +#define VIDIOCGFBUF32 _IOR('v',11, struct video_buffer32) +#define VIDIOCSFBUF32 _IOW('v',12, struct video_buffer32) +#define VIDIOCGFREQ32 _IOR('v',14, u32) +#define VIDIOCSFREQ32 _IOW('v',15, u32) + +static int do_video_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + union { + struct video_tuner vt; + struct video_buffer vb; + struct video_window vw; + unsigned long vx; + } karg; + mm_segment_t old_fs = get_fs(); + void *up = (void *)arg; + int err = 0; + + /* First, convert the command. */ + switch(cmd) { + case VIDIOCGTUNER32: cmd = VIDIOCGTUNER; break; + case VIDIOCSTUNER32: cmd = VIDIOCSTUNER; break; + case VIDIOCGWIN32: cmd = VIDIOCGWIN; break; + case VIDIOCSWIN32: cmd = VIDIOCSWIN; break; + case VIDIOCGFBUF32: cmd = VIDIOCGFBUF; break; + case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break; + case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break; + case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break; + }; + + switch(cmd) { + case VIDIOCSTUNER: + case VIDIOCGTUNER: + err = get_video_tuner32(&karg.vt, up); + break; + + case VIDIOCSWIN: + err = get_video_window32(&karg.vw, up); + break; + + case VIDIOCSFBUF: + err = get_video_buffer32(&karg.vb, up); + break; + + case VIDIOCSFREQ: + err = get_user(karg.vx, (u32 *)up); + break; + }; + if(err) + goto out; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&karg); + set_fs(old_fs); + + if(cmd == VIDIOCSWIN) + free_kvideo_clips(&karg.vw); + + if(err == 0) { + switch(cmd) { + case VIDIOCGTUNER: + err = put_video_tuner32(&karg.vt, up); + break; + + case VIDIOCGWIN: + err = put_video_window32(&karg.vw, up); + break; + + case VIDIOCGFBUF: + err = put_video_buffer32(&karg.vb, up); + break; + + case VIDIOCGFREQ: + err = put_user(((u32)karg.vx), (u32 *)up); + break; + }; + } +out: + return err; +} + +struct timeval32 { + int tv_sec; + int tv_usec; +}; + +static int do_siocgstamp(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct timeval32 *up = (struct timeval32 *)arg; + struct timeval ktv; + mm_segment_t old_fs = get_fs(); + int err; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&ktv); + set_fs(old_fs); + if(!err) { + err = put_user(ktv.tv_sec, &up->tv_sec); + err |= __put_user(ktv.tv_usec, &up->tv_usec); + } + return err; +} + +struct ifmap32 { + u32 mem_start; + u32 mem_end; + unsigned short base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; +}; + +struct ifreq32 { +#define IFHWADDRLEN 6 +#define IFNAMSIZ 16 + union { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + } ifr_ifrn; + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_ivalue; + int ifru_mtu; + struct ifmap32 ifru_map; + char ifru_slave[IFNAMSIZ]; /* Just fits the size */ + char ifru_newname[IFNAMSIZ]; + __kernel_caddr_t32 ifru_data; + } ifr_ifru; +}; + +struct ifconf32 { + int ifc_len; /* size of buffer */ + __kernel_caddr_t32 ifcbuf; +}; + +static int dev_ifname32(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct net_device *dev; + struct ifreq32 ifr32; + int err; + + if (copy_from_user(&ifr32, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + + dev = dev_get_by_index(ifr32.ifr_ifindex); + if (!dev) + return -ENODEV; + + strcpy(ifr32.ifr_name, dev->name); + + err = copy_to_user((struct ifreq32 *)arg, &ifr32, sizeof(struct ifreq32)); + return (err ? -EFAULT : 0); +} + +static int dev_ifconf(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifconf32 ifc32; + struct ifconf ifc; + struct ifreq32 *ifr32; + struct ifreq *ifr; + mm_segment_t old_fs; + unsigned int i, j; + int err; + + if (copy_from_user(&ifc32, (struct ifconf32 *)arg, sizeof(struct ifconf32))) + return -EFAULT; + + if(ifc32.ifcbuf == 0) { + ifc32.ifc_len = 0; + ifc.ifc_len = 0; + ifc.ifc_buf = NULL; + } else { + ifc.ifc_len = ((ifc32.ifc_len / sizeof (struct ifreq32)) + 1) * + sizeof (struct ifreq); + ifc.ifc_buf = kmalloc (ifc.ifc_len, GFP_KERNEL); + if (!ifc.ifc_buf) + return -ENOMEM; + } + ifr = ifc.ifc_req; + ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf); + for (i = 0; i < ifc32.ifc_len; i += sizeof (struct ifreq32)) { + if (copy_from_user(ifr++, ifr32++, sizeof (struct ifreq32))) { + kfree (ifc.ifc_buf); + return -EFAULT; + } + } + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, SIOCGIFCONF, (unsigned long)&ifc); + set_fs (old_fs); + if (!err) { + ifr = ifc.ifc_req; + ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf); + for (i = 0, j = 0; i < ifc32.ifc_len && j < ifc.ifc_len; + i += sizeof (struct ifreq32), j += sizeof (struct ifreq)) { + if (copy_to_user(ifr32++, ifr++, sizeof (struct ifreq32))) { + err = -EFAULT; + break; + } + } + if (!err) { + if (ifc32.ifcbuf == 0) { + /* Translate from 64-bit structure multiple to + * a 32-bit one. + */ + i = ifc.ifc_len; + i = ((i / sizeof(struct ifreq)) * sizeof(struct ifreq32)); + ifc32.ifc_len = i; + } else { + if (i <= ifc32.ifc_len) + ifc32.ifc_len = i; + else + ifc32.ifc_len = i - sizeof (struct ifreq32); + } + if (copy_to_user((struct ifconf32 *)arg, &ifc32, sizeof(struct ifconf32))) + err = -EFAULT; + } + } + if(ifc.ifc_buf != NULL) + kfree (ifc.ifc_buf); + return err; +} + +static int ethtool_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifreq ifr; + mm_segment_t old_fs; + int err, len; + u32 data, ethcmd; + + if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL); + if (!ifr.ifr_data) + return -EAGAIN; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + + if (get_user(ethcmd, (u32 *)A(data))) { + err = -EFAULT; + goto out; + } + switch (ethcmd) { + case ETHTOOL_GDRVINFO: len = sizeof(struct ethtool_drvinfo); break; + case ETHTOOL_GSET: + case ETHTOOL_SSET: + default: len = sizeof(struct ethtool_cmd); break; + } + + if (copy_from_user(ifr.ifr_data, (char *)A(data), len)) { + err = -EFAULT; + goto out; + } + + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&ifr); + set_fs (old_fs); + if (!err) { + u32 data; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + len = copy_to_user((char *)A(data), ifr.ifr_data, len); + if (len) + err = -EFAULT; + } + +out: + free_page((unsigned long)ifr.ifr_data); + return err; +} + +static int dev_ifsioc(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifreq ifr; + mm_segment_t old_fs; + int err; + + switch (cmd) { + case SIOCSIFMAP: + err = copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(ifr.ifr_name)); + err |= __get_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_start)); + err |= __get_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_end)); + err |= __get_user(ifr.ifr_map.base_addr, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.base_addr)); + err |= __get_user(ifr.ifr_map.irq, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.irq)); + err |= __get_user(ifr.ifr_map.dma, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.dma)); + err |= __get_user(ifr.ifr_map.port, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.port)); + if (err) + return -EFAULT; + break; + case SIOCGPPPSTATS: + case SIOCGPPPCSTATS: + case SIOCGPPPVER: + default: + if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + break; + } + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&ifr); + set_fs (old_fs); + if (!err) { + switch (cmd) { + case SIOCGIFFLAGS: + case SIOCGIFMETRIC: + case SIOCGIFMTU: + case SIOCGIFMEM: + case SIOCGIFHWADDR: + case SIOCGIFINDEX: + case SIOCGIFADDR: + case SIOCGIFBRDADDR: + case SIOCGIFDSTADDR: + case SIOCGIFNETMASK: + case SIOCGIFTXQLEN: + if (copy_to_user((struct ifreq32 *)arg, &ifr, sizeof(struct ifreq32))) + return -EFAULT; + break; + case SIOCGPPPSTATS: + case SIOCGPPPCSTATS: + case SIOCGPPPVER: + case SIOCGIFMAP: + err = copy_to_user((struct ifreq32 *)arg, &ifr, sizeof(ifr.ifr_name)); + err |= __put_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_start)); + err |= __put_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_end)); + err |= __put_user(ifr.ifr_map.base_addr, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.base_addr)); + err |= __put_user(ifr.ifr_map.irq, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.irq)); + err |= __put_user(ifr.ifr_map.dma, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.dma)); + err |= __put_user(ifr.ifr_map.port, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.port)); + if (err) + err = -EFAULT; + break; + } + } + return err; +} + +struct rtentry32 { + u32 rt_pad1; + struct sockaddr rt_dst; /* target address */ + struct sockaddr rt_gateway; /* gateway addr (RTF_GATEWAY) */ + struct sockaddr rt_genmask; /* target network mask (IP) */ + unsigned short rt_flags; + short rt_pad2; + u32 rt_pad3; + unsigned char rt_tos; + unsigned char rt_class; + short rt_pad4; + short rt_metric; /* +1 for binary compatibility! */ + /* char * */ u32 rt_dev; /* forcing the device at add */ + u32 rt_mtu; /* per route MTU/Window */ + u32 rt_window; /* Window clamping */ + unsigned short rt_irtt; /* Initial RTT */ + +}; + +struct in6_rtmsg32 { + struct in6_addr rtmsg_dst; + struct in6_addr rtmsg_src; + struct in6_addr rtmsg_gateway; + u32 rtmsg_type; + u16 rtmsg_dst_len; + u16 rtmsg_src_len; + u32 rtmsg_metric; + u32 rtmsg_info; + u32 rtmsg_flags; + s32 rtmsg_ifindex; +}; + +extern struct socket *sockfd_lookup(int fd, int *err); + +static int routing_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + int ret; + void *r = NULL; + struct in6_rtmsg r6; + struct rtentry r4; + char devname[16]; + u32 rtdev; + mm_segment_t old_fs = get_fs(); + + struct socket *mysock = sockfd_lookup(fd, &ret); + + if (mysock && mysock->sk && mysock->sk->family == AF_INET6) { /* ipv6 */ + ret = copy_from_user (&r6.rtmsg_dst, &(((struct in6_rtmsg32 *)arg)->rtmsg_dst), + 3 * sizeof(struct in6_addr)); + ret |= __get_user (r6.rtmsg_type, &(((struct in6_rtmsg32 *)arg)->rtmsg_type)); + ret |= __get_user (r6.rtmsg_dst_len, &(((struct in6_rtmsg32 *)arg)->rtmsg_dst_len)); + ret |= __get_user (r6.rtmsg_src_len, &(((struct in6_rtmsg32 *)arg)->rtmsg_src_len)); + ret |= __get_user (r6.rtmsg_metric, &(((struct in6_rtmsg32 *)arg)->rtmsg_metric)); + ret |= __get_user (r6.rtmsg_info, &(((struct in6_rtmsg32 *)arg)->rtmsg_info)); + ret |= __get_user (r6.rtmsg_flags, &(((struct in6_rtmsg32 *)arg)->rtmsg_flags)); + ret |= __get_user (r6.rtmsg_ifindex, &(((struct in6_rtmsg32 *)arg)->rtmsg_ifindex)); + + r = (void *) &r6; + } else { /* ipv4 */ + ret = copy_from_user (&r4.rt_dst, &(((struct rtentry32 *)arg)->rt_dst), 3 * sizeof(struct sockaddr)); + ret |= __get_user (r4.rt_flags, &(((struct rtentry32 *)arg)->rt_flags)); + ret |= __get_user (r4.rt_metric, &(((struct rtentry32 *)arg)->rt_metric)); + ret |= __get_user (r4.rt_mtu, &(((struct rtentry32 *)arg)->rt_mtu)); + ret |= __get_user (r4.rt_window, &(((struct rtentry32 *)arg)->rt_window)); + ret |= __get_user (r4.rt_irtt, &(((struct rtentry32 *)arg)->rt_irtt)); + ret |= __get_user (rtdev, &(((struct rtentry32 *)arg)->rt_dev)); + if (rtdev) { + ret |= copy_from_user (devname, (char *)A(rtdev), 15); + r4.rt_dev = devname; devname[15] = 0; + } else + r4.rt_dev = 0; + + r = (void *) &r4; + } + + if (ret) + return -EFAULT; + + set_fs (KERNEL_DS); + ret = sys_ioctl (fd, cmd, (long) r); + set_fs (old_fs); + + return ret; +} + +struct hd_geometry32 { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + u32 start; +}; + +static int hdio_getgeo(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct hd_geometry geo; + int err; + + set_fs (KERNEL_DS); + err = sys_ioctl(fd, HDIO_GETGEO, (unsigned long)&geo); + set_fs (old_fs); + if (!err) { + err = copy_to_user ((struct hd_geometry32 *)arg, &geo, 4); + err |= __put_user (geo.start, &(((struct hd_geometry32 *)arg)->start)); + } + return err ? -EFAULT : 0; +} + + +static int hdio_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + unsigned long kval; + unsigned int *uvp; + int error; + + set_fs(KERNEL_DS); + error = sys_ioctl(fd, cmd, (long)&kval); + set_fs(old_fs); + + if(error == 0) { + uvp = (unsigned int *)arg; + if(put_user(kval, uvp)) + error = -EFAULT; + } + return error; +} + +struct floppy_struct32 { + unsigned int size; + unsigned int sect; + unsigned int head; + unsigned int track; + unsigned int stretch; + unsigned char gap; + unsigned char rate; + unsigned char spec1; + unsigned char fmt_gap; + const __kernel_caddr_t32 name; +}; + +struct floppy_drive_params32 { + char cmos; + u32 max_dtr; + u32 hlt; + u32 hut; + u32 srt; + u32 spinup; + u32 spindown; + unsigned char spindown_offset; + unsigned char select_delay; + unsigned char rps; + unsigned char tracks; + u32 timeout; + unsigned char interleave_sect; + struct floppy_max_errors max_errors; + char flags; + char read_track; + short autodetect[8]; + int checkfreq; + int native_format; +}; + +struct floppy_drive_struct32 { + signed char flags; + u32 spinup_date; + u32 select_date; + u32 first_read_date; + short probed_format; + short track; + short maxblock; + short maxtrack; + int generation; + int keep_data; + int fd_ref; + int fd_device; + int last_checked; + __kernel_caddr_t32 dmabuf; + int bufblocks; +}; + +struct floppy_fdc_state32 { + int spec1; + int spec2; + int dtr; + unsigned char version; + unsigned char dor; + u32 address; + unsigned int rawcmd:2; + unsigned int reset:1; + unsigned int need_configure:1; + unsigned int perp_mode:2; + unsigned int has_fifo:1; + unsigned int driver_version; + unsigned char track[4]; +}; + +struct floppy_write_errors32 { + unsigned int write_errors; + u32 first_error_sector; + int first_error_generation; + u32 last_error_sector; + int last_error_generation; + unsigned int badness; +}; + +#define FDSETPRM32 _IOW(2, 0x42, struct floppy_struct32) +#define FDDEFPRM32 _IOW(2, 0x43, struct floppy_struct32) +#define FDGETPRM32 _IOR(2, 0x04, struct floppy_struct32) +#define FDSETDRVPRM32 _IOW(2, 0x90, struct floppy_drive_params32) +#define FDGETDRVPRM32 _IOR(2, 0x11, struct floppy_drive_params32) +#define FDGETDRVSTAT32 _IOR(2, 0x12, struct floppy_drive_struct32) +#define FDPOLLDRVSTAT32 _IOR(2, 0x13, struct floppy_drive_struct32) +#define FDGETFDCSTAT32 _IOR(2, 0x15, struct floppy_fdc_state32) +#define FDWERRORGET32 _IOR(2, 0x17, struct floppy_write_errors32) + +static struct { + unsigned int cmd32; + unsigned int cmd; +} fd_ioctl_trans_table[] = { + { FDSETPRM32, FDSETPRM }, + { FDDEFPRM32, FDDEFPRM }, + { FDGETPRM32, FDGETPRM }, + { FDSETDRVPRM32, FDSETDRVPRM }, + { FDGETDRVPRM32, FDGETDRVPRM }, + { FDGETDRVSTAT32, FDGETDRVSTAT }, + { FDPOLLDRVSTAT32, FDPOLLDRVSTAT }, + { FDGETFDCSTAT32, FDGETFDCSTAT }, + { FDWERRORGET32, FDWERRORGET } +}; + +#define NR_FD_IOCTL_TRANS (sizeof(fd_ioctl_trans_table)/sizeof(fd_ioctl_trans_table[0])) + +static int fd_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + void *karg = NULL; + unsigned int kcmd = 0; + int i, err; + + for (i = 0; i < NR_FD_IOCTL_TRANS; i++) + if (cmd == fd_ioctl_trans_table[i].cmd32) { + kcmd = fd_ioctl_trans_table[i].cmd; + break; + } + if (!kcmd) + return -EINVAL; + + switch (cmd) { + case FDSETPRM32: + case FDDEFPRM32: + case FDGETPRM32: + { + struct floppy_struct *f; + + f = karg = kmalloc(sizeof(struct floppy_struct), GFP_KERNEL); + if (!karg) + return -ENOMEM; + if (cmd == FDGETPRM32) + break; + err = __get_user(f->size, &((struct floppy_struct32 *)arg)->size); + err |= __get_user(f->sect, &((struct floppy_struct32 *)arg)->sect); + err |= __get_user(f->head, &((struct floppy_struct32 *)arg)->head); + err |= __get_user(f->track, &((struct floppy_struct32 *)arg)->track); + err |= __get_user(f->stretch, &((struct floppy_struct32 *)arg)->stretch); + err |= __get_user(f->gap, &((struct floppy_struct32 *)arg)->gap); + err |= __get_user(f->rate, &((struct floppy_struct32 *)arg)->rate); + err |= __get_user(f->spec1, &((struct floppy_struct32 *)arg)->spec1); + err |= __get_user(f->fmt_gap, &((struct floppy_struct32 *)arg)->fmt_gap); + err |= __get_user((u64)f->name, &((struct floppy_struct32 *)arg)->name); + if (err) { + err = -EFAULT; + goto out; + } + break; + } + case FDSETDRVPRM32: + case FDGETDRVPRM32: + { + struct floppy_drive_params *f; + + f = karg = kmalloc(sizeof(struct floppy_drive_params), GFP_KERNEL); + if (!karg) + return -ENOMEM; + if (cmd == FDGETDRVPRM32) + break; + err = __get_user(f->cmos, &((struct floppy_drive_params32 *)arg)->cmos); + err |= __get_user(f->max_dtr, &((struct floppy_drive_params32 *)arg)->max_dtr); + err |= __get_user(f->hlt, &((struct floppy_drive_params32 *)arg)->hlt); + err |= __get_user(f->hut, &((struct floppy_drive_params32 *)arg)->hut); + err |= __get_user(f->srt, &((struct floppy_drive_params32 *)arg)->srt); + err |= __get_user(f->spinup, &((struct floppy_drive_params32 *)arg)->spinup); + err |= __get_user(f->spindown, &((struct floppy_drive_params32 *)arg)->spindown); + err |= __get_user(f->spindown_offset, &((struct floppy_drive_params32 *)arg)->spindown_offset); + err |= __get_user(f->select_delay, &((struct floppy_drive_params32 *)arg)->select_delay); + err |= __get_user(f->rps, &((struct floppy_drive_params32 *)arg)->rps); + err |= __get_user(f->tracks, &((struct floppy_drive_params32 *)arg)->tracks); + err |= __get_user(f->timeout, &((struct floppy_drive_params32 *)arg)->timeout); + err |= __get_user(f->interleave_sect, &((struct floppy_drive_params32 *)arg)->interleave_sect); + err |= __copy_from_user(&f->max_errors, &((struct floppy_drive_params32 *)arg)->max_errors, sizeof(f->max_errors)); + err |= __get_user(f->flags, &((struct floppy_drive_params32 *)arg)->flags); + err |= __get_user(f->read_track, &((struct floppy_drive_params32 *)arg)->read_track); + err |= __copy_from_user(f->autodetect, ((struct floppy_drive_params32 *)arg)->autodetect, sizeof(f->autodetect)); + err |= __get_user(f->checkfreq, &((struct floppy_drive_params32 *)arg)->checkfreq); + err |= __get_user(f->native_format, &((struct floppy_drive_params32 *)arg)->native_format); + if (err) { + err = -EFAULT; + goto out; + } + break; + } + case FDGETDRVSTAT32: + case FDPOLLDRVSTAT32: + karg = kmalloc(sizeof(struct floppy_drive_struct), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + case FDGETFDCSTAT32: + karg = kmalloc(sizeof(struct floppy_fdc_state), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + case FDWERRORGET32: + karg = kmalloc(sizeof(struct floppy_write_errors), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + default: + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + goto out; + switch (cmd) { + case FDGETPRM32: + { + struct floppy_struct *f = karg; + + err = __put_user(f->size, &((struct floppy_struct32 *)arg)->size); + err |= __put_user(f->sect, &((struct floppy_struct32 *)arg)->sect); + err |= __put_user(f->head, &((struct floppy_struct32 *)arg)->head); + err |= __put_user(f->track, &((struct floppy_struct32 *)arg)->track); + err |= __put_user(f->stretch, &((struct floppy_struct32 *)arg)->stretch); + err |= __put_user(f->gap, &((struct floppy_struct32 *)arg)->gap); + err |= __put_user(f->rate, &((struct floppy_struct32 *)arg)->rate); + err |= __put_user(f->spec1, &((struct floppy_struct32 *)arg)->spec1); + err |= __put_user(f->fmt_gap, &((struct floppy_struct32 *)arg)->fmt_gap); + err |= __put_user((u64)f->name, &((struct floppy_struct32 *)arg)->name); + break; + } + case FDGETDRVPRM32: + { + struct floppy_drive_params *f = karg; + + err = __put_user(f->cmos, &((struct floppy_drive_params32 *)arg)->cmos); + err |= __put_user(f->max_dtr, &((struct floppy_drive_params32 *)arg)->max_dtr); + err |= __put_user(f->hlt, &((struct floppy_drive_params32 *)arg)->hlt); + err |= __put_user(f->hut, &((struct floppy_drive_params32 *)arg)->hut); + err |= __put_user(f->srt, &((struct floppy_drive_params32 *)arg)->srt); + err |= __put_user(f->spinup, &((struct floppy_drive_params32 *)arg)->spinup); + err |= __put_user(f->spindown, &((struct floppy_drive_params32 *)arg)->spindown); + err |= __put_user(f->spindown_offset, &((struct floppy_drive_params32 *)arg)->spindown_offset); + err |= __put_user(f->select_delay, &((struct floppy_drive_params32 *)arg)->select_delay); + err |= __put_user(f->rps, &((struct floppy_drive_params32 *)arg)->rps); + err |= __put_user(f->tracks, &((struct floppy_drive_params32 *)arg)->tracks); + err |= __put_user(f->timeout, &((struct floppy_drive_params32 *)arg)->timeout); + err |= __put_user(f->interleave_sect, &((struct floppy_drive_params32 *)arg)->interleave_sect); + err |= __copy_to_user(&((struct floppy_drive_params32 *)arg)->max_errors, &f->max_errors, sizeof(f->max_errors)); + err |= __put_user(f->flags, &((struct floppy_drive_params32 *)arg)->flags); + err |= __put_user(f->read_track, &((struct floppy_drive_params32 *)arg)->read_track); + err |= __copy_to_user(((struct floppy_drive_params32 *)arg)->autodetect, f->autodetect, sizeof(f->autodetect)); + err |= __put_user(f->checkfreq, &((struct floppy_drive_params32 *)arg)->checkfreq); + err |= __put_user(f->native_format, &((struct floppy_drive_params32 *)arg)->native_format); + break; + } + case FDGETDRVSTAT32: + case FDPOLLDRVSTAT32: + { + struct floppy_drive_struct *f = karg; + + err = __put_user(f->flags, &((struct floppy_drive_struct32 *)arg)->flags); + err |= __put_user(f->spinup_date, &((struct floppy_drive_struct32 *)arg)->spinup_date); + err |= __put_user(f->select_date, &((struct floppy_drive_struct32 *)arg)->select_date); + err |= __put_user(f->first_read_date, &((struct floppy_drive_struct32 *)arg)->first_read_date); + err |= __put_user(f->probed_format, &((struct floppy_drive_struct32 *)arg)->probed_format); + err |= __put_user(f->track, &((struct floppy_drive_struct32 *)arg)->track); + err |= __put_user(f->maxblock, &((struct floppy_drive_struct32 *)arg)->maxblock); + err |= __put_user(f->maxtrack, &((struct floppy_drive_struct32 *)arg)->maxtrack); + err |= __put_user(f->generation, &((struct floppy_drive_struct32 *)arg)->generation); + err |= __put_user(f->keep_data, &((struct floppy_drive_struct32 *)arg)->keep_data); + err |= __put_user(f->fd_ref, &((struct floppy_drive_struct32 *)arg)->fd_ref); + err |= __put_user(f->fd_device, &((struct floppy_drive_struct32 *)arg)->fd_device); + err |= __put_user(f->last_checked, &((struct floppy_drive_struct32 *)arg)->last_checked); + err |= __put_user((u64)f->dmabuf, &((struct floppy_drive_struct32 *)arg)->dmabuf); + err |= __put_user((u64)f->bufblocks, &((struct floppy_drive_struct32 *)arg)->bufblocks); + break; + } + case FDGETFDCSTAT32: + { + struct floppy_fdc_state *f = karg; + + err = __put_user(f->spec1, &((struct floppy_fdc_state32 *)arg)->spec1); + err |= __put_user(f->spec2, &((struct floppy_fdc_state32 *)arg)->spec2); + err |= __put_user(f->dtr, &((struct floppy_fdc_state32 *)arg)->dtr); + err |= __put_user(f->version, &((struct floppy_fdc_state32 *)arg)->version); + err |= __put_user(f->dor, &((struct floppy_fdc_state32 *)arg)->dor); + err |= __put_user(f->address, &((struct floppy_fdc_state32 *)arg)->address); + err |= __copy_to_user((char *)&((struct floppy_fdc_state32 *)arg)->address + + sizeof(((struct floppy_fdc_state32 *)arg)->address), + (char *)&f->address + sizeof(f->address), sizeof(int)); + err |= __put_user(f->driver_version, &((struct floppy_fdc_state32 *)arg)->driver_version); + err |= __copy_to_user(((struct floppy_fdc_state32 *)arg)->track, f->track, sizeof(f->track)); + break; + } + case FDWERRORGET32: + { + struct floppy_write_errors *f = karg; + + err = __put_user(f->write_errors, &((struct floppy_write_errors32 *)arg)->write_errors); + err |= __put_user(f->first_error_sector, &((struct floppy_write_errors32 *)arg)->first_error_sector); + err |= __put_user(f->first_error_generation, &((struct floppy_write_errors32 *)arg)->first_error_generation); + err |= __put_user(f->last_error_sector, &((struct floppy_write_errors32 *)arg)->last_error_sector); + err |= __put_user(f->last_error_generation, &((struct floppy_write_errors32 *)arg)->last_error_generation); + err |= __put_user(f->badness, &((struct floppy_write_errors32 *)arg)->badness); + break; + } + default: + break; + } + if (err) + err = -EFAULT; + +out: if (karg) kfree(karg); + return err; +} + +struct ppp_option_data32 { + __kernel_caddr_t32 ptr; + __u32 length; + int transmit; +}; +#define PPPIOCSCOMPRESS32 _IOW('t', 77, struct ppp_option_data32) + +struct ppp_idle32 { + __kernel_time_t32 xmit_idle; + __kernel_time_t32 recv_idle; +}; +#define PPPIOCGIDLE32 _IOR('t', 63, struct ppp_idle32) + +static int ppp_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct ppp_option_data32 data32; + struct ppp_option_data data; + struct ppp_idle32 idle32; + struct ppp_idle idle; + unsigned int kcmd; + void *karg; + int err = 0; + + switch (cmd) { + case PPPIOCGIDLE32: + kcmd = PPPIOCGIDLE; + karg = &idle; + break; + case PPPIOCSCOMPRESS32: + if (copy_from_user(&data32, (struct ppp_option_data32 *)arg, sizeof(struct ppp_option_data32))) + return -EFAULT; + data.ptr = kmalloc (data32.length, GFP_KERNEL); + if (!data.ptr) + return -ENOMEM; + if (copy_from_user(data.ptr, (__u8 *)A(data32.ptr), data32.length)) { + kfree(data.ptr); + return -EFAULT; + } + data.length = data32.length; + data.transmit = data32.transmit; + kcmd = PPPIOCSCOMPRESS; + karg = &data; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("ppp_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while(0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + switch (cmd) { + case PPPIOCGIDLE32: + if (err) + return err; + idle32.xmit_idle = idle.xmit_idle; + idle32.recv_idle = idle.recv_idle; + if (copy_to_user((struct ppp_idle32 *)arg, &idle32, sizeof(struct ppp_idle32))) + return -EFAULT; + break; + case PPPIOCSCOMPRESS32: + kfree(data.ptr); + break; + default: + break; + } + return err; +} + + +struct mtget32 { + __u32 mt_type; + __u32 mt_resid; + __u32 mt_dsreg; + __u32 mt_gstat; + __u32 mt_erreg; + __kernel_daddr_t32 mt_fileno; + __kernel_daddr_t32 mt_blkno; +}; +#define MTIOCGET32 _IOR('m', 2, struct mtget32) + +struct mtpos32 { + __u32 mt_blkno; +}; +#define MTIOCPOS32 _IOR('m', 3, struct mtpos32) + +struct mtconfiginfo32 { + __u32 mt_type; + __u32 ifc_type; + __u16 irqnr; + __u16 dmanr; + __u16 port; + __u32 debug; + __u32 have_dens:1; + __u32 have_bsf:1; + __u32 have_fsr:1; + __u32 have_bsr:1; + __u32 have_eod:1; + __u32 have_seek:1; + __u32 have_tell:1; + __u32 have_ras1:1; + __u32 have_ras2:1; + __u32 have_ras3:1; + __u32 have_qfa:1; + __u32 pad1:5; + char reserved[10]; +}; +#define MTIOCGETCONFIG32 _IOR('m', 4, struct mtconfiginfo32) +#define MTIOCSETCONFIG32 _IOW('m', 5, struct mtconfiginfo32) + +static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct mtconfiginfo info; + struct mtget get; + struct mtpos pos; + unsigned long kcmd; + void *karg; + int err = 0; + + switch(cmd) { + case MTIOCPOS32: + kcmd = MTIOCPOS; + karg = &pos; + break; + case MTIOCGET32: + kcmd = MTIOCGET; + karg = &get; + break; + case MTIOCGETCONFIG32: + kcmd = MTIOCGETCONFIG; + karg = &info; + break; + case MTIOCSETCONFIG32: + kcmd = MTIOCSETCONFIG; + karg = &info; + err = __get_user(info.mt_type, &((struct mtconfiginfo32 *)arg)->mt_type); + err |= __get_user(info.ifc_type, &((struct mtconfiginfo32 *)arg)->ifc_type); + err |= __get_user(info.irqnr, &((struct mtconfiginfo32 *)arg)->irqnr); + err |= __get_user(info.dmanr, &((struct mtconfiginfo32 *)arg)->dmanr); + err |= __get_user(info.port, &((struct mtconfiginfo32 *)arg)->port); + err |= __get_user(info.debug, &((struct mtconfiginfo32 *)arg)->debug); + err |= __copy_from_user((char *)&info.debug + sizeof(info.debug), + (char *)&((struct mtconfiginfo32 *)arg)->debug + + sizeof(((struct mtconfiginfo32 *)arg)->debug), sizeof(__u32)); + if (err) + return -EFAULT; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("mt_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while(0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + return err; + switch (cmd) { + case MTIOCPOS32: + err = __put_user(pos.mt_blkno, &((struct mtpos32 *)arg)->mt_blkno); + break; + case MTIOCGET32: + err = __put_user(get.mt_type, &((struct mtget32 *)arg)->mt_type); + err |= __put_user(get.mt_resid, &((struct mtget32 *)arg)->mt_resid); + err |= __put_user(get.mt_dsreg, &((struct mtget32 *)arg)->mt_dsreg); + err |= __put_user(get.mt_gstat, &((struct mtget32 *)arg)->mt_gstat); + err |= __put_user(get.mt_erreg, &((struct mtget32 *)arg)->mt_erreg); + err |= __put_user(get.mt_fileno, &((struct mtget32 *)arg)->mt_fileno); + err |= __put_user(get.mt_blkno, &((struct mtget32 *)arg)->mt_blkno); + break; + case MTIOCGETCONFIG32: + err = __put_user(info.mt_type, &((struct mtconfiginfo32 *)arg)->mt_type); + err |= __put_user(info.ifc_type, &((struct mtconfiginfo32 *)arg)->ifc_type); + err |= __put_user(info.irqnr, &((struct mtconfiginfo32 *)arg)->irqnr); + err |= __put_user(info.dmanr, &((struct mtconfiginfo32 *)arg)->dmanr); + err |= __put_user(info.port, &((struct mtconfiginfo32 *)arg)->port); + err |= __put_user(info.debug, &((struct mtconfiginfo32 *)arg)->debug); + err |= __copy_to_user((char *)&((struct mtconfiginfo32 *)arg)->debug + + sizeof(((struct mtconfiginfo32 *)arg)->debug), + (char *)&info.debug + sizeof(info.debug), sizeof(__u32)); + break; + case MTIOCSETCONFIG32: + break; + } + return err ? -EFAULT: 0; +} + +struct cdrom_read32 { + int cdread_lba; + __kernel_caddr_t32 cdread_bufaddr; + int cdread_buflen; +}; + +struct cdrom_read_audio32 { + union cdrom_addr addr; + u_char addr_format; + int nframes; + __kernel_caddr_t32 buf; +}; + +struct cdrom_generic_command32 { + unsigned char cmd[CDROM_PACKET_SIZE]; + __kernel_caddr_t32 buffer; + unsigned int buflen; + int stat; + __kernel_caddr_t32 sense; + __kernel_caddr_t32 reserved[3]; +}; + +static int cdrom_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct cdrom_read cdread; + struct cdrom_read_audio cdreadaudio; + struct cdrom_generic_command cgc; + __kernel_caddr_t32 addr; + char *data = 0; + void *karg; + int err = 0; + + switch(cmd) { + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + karg = &cdread; + err = __get_user(cdread.cdread_lba, &((struct cdrom_read32 *)arg)->cdread_lba); + err |= __get_user(addr, &((struct cdrom_read32 *)arg)->cdread_bufaddr); + err |= __get_user(cdread.cdread_buflen, &((struct cdrom_read32 *)arg)->cdread_buflen); + if (err) + return -EFAULT; + data = kmalloc(cdread.cdread_buflen, GFP_KERNEL); + if (!data) + return -ENOMEM; + cdread.cdread_bufaddr = data; + break; + case CDROMREADAUDIO: + karg = &cdreadaudio; + err = copy_from_user(&cdreadaudio.addr, &((struct cdrom_read_audio32 *)arg)->addr, sizeof(cdreadaudio.addr)); + err |= __get_user(cdreadaudio.addr_format, &((struct cdrom_read_audio32 *)arg)->addr_format); + err |= __get_user(cdreadaudio.nframes, &((struct cdrom_read_audio32 *)arg)->nframes); + err |= __get_user(addr, &((struct cdrom_read_audio32 *)arg)->buf); + if (err) + return -EFAULT; + data = kmalloc(cdreadaudio.nframes * 2352, GFP_KERNEL); + if (!data) + return -ENOMEM; + cdreadaudio.buf = data; + break; + case CDROM_SEND_PACKET: + karg = &cgc; + err = copy_from_user(cgc.cmd, &((struct cdrom_generic_command32 *)arg)->cmd, sizeof(cgc.cmd)); + err |= __get_user(addr, &((struct cdrom_generic_command32 *)arg)->buffer); + err |= __get_user(cgc.buflen, &((struct cdrom_generic_command32 *)arg)->buflen); + if (err) + return -EFAULT; + if ((data = kmalloc(cgc.buflen, GFP_KERNEL)) == NULL) + return -ENOMEM; + cgc.buffer = data; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("cdrom_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while(0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + goto out; + switch (cmd) { + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + err = copy_to_user((char *)A(addr), data, cdread.cdread_buflen); + break; + case CDROMREADAUDIO: + err = copy_to_user((char *)A(addr), data, cdreadaudio.nframes * 2352); + break; + case CDROM_SEND_PACKET: + err = copy_to_user((char *)A(addr), data, cgc.buflen); + break; + default: + break; + } +out: if (data) + kfree(data); + return err ? -EFAULT : 0; +} + +struct loop_info32 { + int lo_number; /* ioctl r/o */ + __kernel_dev_t32 lo_device; /* ioctl r/o */ + unsigned int lo_inode; /* ioctl r/o */ + __kernel_dev_t32 lo_rdevice; /* ioctl r/o */ + int lo_offset; + int lo_encrypt_type; + int lo_encrypt_key_size; /* ioctl w/o */ + int lo_flags; /* ioctl r/o */ + char lo_name[LO_NAME_SIZE]; + unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ + unsigned int lo_init[2]; + char reserved[4]; +}; + +static int loop_status(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct loop_info l; + int err = -EINVAL; + + switch(cmd) { + case LOOP_SET_STATUS: + err = get_user(l.lo_number, &((struct loop_info32 *)arg)->lo_number); + err |= __get_user(l.lo_device, &((struct loop_info32 *)arg)->lo_device); + err |= __get_user(l.lo_inode, &((struct loop_info32 *)arg)->lo_inode); + err |= __get_user(l.lo_rdevice, &((struct loop_info32 *)arg)->lo_rdevice); + err |= __copy_from_user((char *)&l.lo_offset, (char *)&((struct loop_info32 *)arg)->lo_offset, + 8 + (unsigned long)l.lo_init - (unsigned long)&l.lo_offset); + if (err) { + err = -EFAULT; + } else { + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&l); + set_fs (old_fs); + } + break; + case LOOP_GET_STATUS: + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&l); + set_fs (old_fs); + if (!err) { + err = put_user(l.lo_number, &((struct loop_info32 *)arg)->lo_number); + err |= __put_user(l.lo_device, &((struct loop_info32 *)arg)->lo_device); + err |= __put_user(l.lo_inode, &((struct loop_info32 *)arg)->lo_inode); + err |= __put_user(l.lo_rdevice, &((struct loop_info32 *)arg)->lo_rdevice); + err |= __copy_to_user((char *)&((struct loop_info32 *)arg)->lo_offset, + (char *)&l.lo_offset, (unsigned long)l.lo_init - (unsigned long)&l.lo_offset); + if (err) + err = -EFAULT; + } + break; + default: { + static int count = 0; + if (++count <= 20) + printk("%s: Unknown loop ioctl cmd, fd(%d) " + "cmd(%08x) arg(%08lx)\n", + __FUNCTION__, fd, cmd, arg); + } + } + return err; +} + +extern int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg); + +#ifdef CONFIG_VT +static int vt_check(struct file *file) +{ + struct tty_struct *tty; + struct inode *inode = file->f_dentry->d_inode; + + if (file->f_op->ioctl != tty_ioctl) + return -EINVAL; + + tty = (struct tty_struct *)file->private_data; + if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl")) + return -EINVAL; + + if (tty->driver.ioctl != vt_ioctl) + return -EINVAL; + + /* + * To have permissions to do most of the vt ioctls, we either have + * to be the owner of the tty, or super-user. + */ + if (current->tty == tty || suser()) + return 1; + return 0; +} + +struct consolefontdesc32 { + unsigned short charcount; /* characters in font (256 or 512) */ + unsigned short charheight; /* scan lines per character (1-32) */ + u32 chardata; /* font data in expanded form */ +}; + +static int do_fontx_ioctl(unsigned int fd, int cmd, struct consolefontdesc32 *user_cfd, struct file *file) +{ + struct consolefontdesc cfdarg; + struct console_font_op op; + int i, perm; + + perm = vt_check(file); + if (perm < 0) return perm; + + if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc32))) + return -EFAULT; + + cfdarg.chardata = (unsigned char *)A(((struct consolefontdesc32 *)&cfdarg)->chardata); + + switch (cmd) { + case PIO_FONTX: + if (!perm) + return -EPERM; + op.op = KD_FONT_OP_SET; + op.flags = 0; + op.width = 8; + op.height = cfdarg.charheight; + op.charcount = cfdarg.charcount; + op.data = cfdarg.chardata; + return con_font_op(fg_console, &op); + case GIO_FONTX: + if (!cfdarg.chardata) + return 0; + op.op = KD_FONT_OP_GET; + op.flags = 0; + op.width = 8; + op.height = cfdarg.charheight; + op.charcount = cfdarg.charcount; + op.data = cfdarg.chardata; + i = con_font_op(fg_console, &op); + if (i) + return i; + cfdarg.charheight = op.height; + cfdarg.charcount = op.charcount; + ((struct consolefontdesc32 *)&cfdarg)->chardata = (unsigned long)cfdarg.chardata; + if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc32))) + return -EFAULT; + return 0; + } + return -EINVAL; +} + +struct console_font_op32 { + unsigned int op; /* operation code KD_FONT_OP_* */ + unsigned int flags; /* KD_FONT_FLAG_* */ + unsigned int width, height; /* font size */ + unsigned int charcount; + u32 data; /* font data with height fixed to 32 */ +}; + +static int do_kdfontop_ioctl(unsigned int fd, unsigned int cmd, struct console_font_op32 *fontop, struct file *file) +{ + struct console_font_op op; + int perm = vt_check(file), i; + struct vt_struct *vt; + + if (perm < 0) return perm; + + if (copy_from_user(&op, (void *) fontop, sizeof(struct console_font_op32))) + return -EFAULT; + if (!perm && op.op != KD_FONT_OP_GET) + return -EPERM; + op.data = (unsigned char *)A(((struct console_font_op32 *)&op)->data); + op.flags |= KD_FONT_FLAG_OLD; + vt = (struct vt_struct *)((struct tty_struct *)file->private_data)->driver_data; + i = con_font_op(vt->vc_num, &op); + if (i) return i; + ((struct console_font_op32 *)&op)->data = (unsigned long)op.data; + if (copy_to_user((void *) fontop, &op, sizeof(struct console_font_op32))) + return -EFAULT; + return 0; +} + +struct fb_fix_screeninfo32 { + char id[16]; /* identification string eg "TT Builtin" */ + unsigned int smem_start; /* Start of frame buffer mem */ + /* (physical address) */ + __u32 smem_len; /* Length of frame buffer mem */ + __u32 type; /* see FB_TYPE_* */ + __u32 type_aux; /* Interleave for interleaved Planes */ + __u32 visual; /* see FB_VISUAL_* */ + __u16 xpanstep; /* zero if no hardware panning */ + __u16 ypanstep; /* zero if no hardware panning */ + __u16 ywrapstep; /* zero if no hardware ywrap */ + __u32 line_length; /* length of a line in bytes */ + unsigned int mmio_start; /* Start of Memory Mapped I/O */ + /* (physical address) */ + __u32 mmio_len; /* Length of Memory Mapped I/O */ + __u32 accel; /* Type of acceleration available */ + __u16 reserved[3]; /* Reserved for future compatibility */ +}; + +static int do_fbioget_fscreeninfo_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct fb_fix_screeninfo fix; + int err; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (long)&fix); + set_fs(old_fs); + + if(err == 0) { + unsigned int smem_start = fix.smem_start; /* lose top 32 bits */ + unsigned int mmio_start = fix.mmio_start; /* lose top 32 bits */ + int i; + + err = put_user(fix.id[0], &((struct fb_fix_screeninfo32 *)arg)->id[0]); + for (i=1; i<16; i++) { + err |= __put_user(fix.id[i], &((struct fb_fix_screeninfo32 *)arg)->id[i]); + } + err |= __put_user(smem_start, &((struct fb_fix_screeninfo32 *)arg)->smem_start); + err |= __put_user(fix.smem_len, &((struct fb_fix_screeninfo32 *)arg)->smem_len); + err |= __put_user(fix.type, &((struct fb_fix_screeninfo32 *)arg)->type); + err |= __put_user(fix.type_aux, &((struct fb_fix_screeninfo32 *)arg)->type_aux); + err |= __put_user(fix.visual, &((struct fb_fix_screeninfo32 *)arg)->visual); + err |= __put_user(fix.xpanstep, &((struct fb_fix_screeninfo32 *)arg)->xpanstep); + err |= __put_user(fix.ypanstep, &((struct fb_fix_screeninfo32 *)arg)->ypanstep); + err |= __put_user(fix.ywrapstep, &((struct fb_fix_screeninfo32 *)arg)->ywrapstep); + err |= __put_user(fix.line_length, &((struct fb_fix_screeninfo32 *)arg)->line_length); + err |= __put_user(mmio_start, &((struct fb_fix_screeninfo32 *)arg)->mmio_start); + err |= __put_user(fix.mmio_len, &((struct fb_fix_screeninfo32 *)arg)->mmio_len); + err |= __put_user(fix.accel, &((struct fb_fix_screeninfo32 *)arg)->accel); + err |= __put_user(fix.reserved[0], &((struct fb_fix_screeninfo32 *)arg)->reserved[0]); + err |= __put_user(fix.reserved[1], &((struct fb_fix_screeninfo32 *)arg)->reserved[1]); + err |= __put_user(fix.reserved[2], &((struct fb_fix_screeninfo32 *)arg)->reserved[2]); + if (err) + err = -EFAULT; + } + return err; +} + +struct fb_cmap32 { + __u32 start; /* First entry */ + __u32 len; /* Number of entries */ + __u32 redptr; /* Red values */ + __u32 greenptr; + __u32 blueptr; + __u32 transpptr; /* transparency, can be NULL */ +}; + +static int do_fbiogetcmap_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct fb_cmap cmap; + int err; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (long)&cmap); + set_fs(old_fs); + + if(err == 0) { + __u32 redptr = (__u32)cmap.red; + __u32 greenptr = (__u32)cmap.green; + __u32 blueptr = (__u32)cmap.blue; + __u32 transpptr = (__u32)cmap.transp; + + err = put_user(cmap.start, &((struct fb_cmap32 *)arg)->start); + err |= __put_user(cmap.len, &((struct fb_cmap32 *)arg)->len); + err |= __put_user(redptr, &((struct fb_cmap32 *)arg)->redptr); + err |= __put_user(greenptr, &((struct fb_cmap32 *)arg)->greenptr); + err |= __put_user(blueptr, &((struct fb_cmap32 *)arg)->blueptr); + err |= __put_user(transpptr, &((struct fb_cmap32 *)arg)->transpptr); + if (err) + err = -EFAULT; + } + return err; +} + +static int do_fbioputcmap_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct fb_cmap cmap; + __u32 redptr, greenptr, blueptr, transpptr; + int err; + + err = get_user(cmap.start, &((struct fb_cmap32 *)arg)->start); + err |= __get_user(cmap.len, &((struct fb_cmap32 *)arg)->len); + err |= __get_user(redptr, &((struct fb_cmap32 *)arg)->redptr); + err |= __get_user(greenptr, &((struct fb_cmap32 *)arg)->greenptr); + err |= __get_user(blueptr, &((struct fb_cmap32 *)arg)->blueptr); + err |= __get_user(transpptr, &((struct fb_cmap32 *)arg)->transpptr); + + if (err) { + err = -EFAULT; + } else { + cmap.red = (__u16 *)redptr; + cmap.green = (__u16 *)greenptr; + cmap.blue = (__u16 *)blueptr; + cmap.transp = (__u16 *)transpptr; + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&cmap); + set_fs (old_fs); + } + return err; +} + +struct unimapdesc32 { + unsigned short entry_ct; + u32 entries; +}; + +static int do_unimap_ioctl(unsigned int fd, unsigned int cmd, struct unimapdesc32 *user_ud, struct file *file) +{ + struct unimapdesc32 tmp; + int perm = vt_check(file); + + if (perm < 0) return perm; + if (copy_from_user(&tmp, user_ud, sizeof tmp)) + return -EFAULT; + switch (cmd) { + case PIO_UNIMAP: + if (!perm) return -EPERM; + return con_set_unimap(fg_console, tmp.entry_ct, (struct unipair *)A(tmp.entries)); + case GIO_UNIMAP: + return con_get_unimap(fg_console, tmp.entry_ct, &(user_ud->entry_ct), (struct unipair *)A(tmp.entries)); + } + return 0; +} +#endif /* CONFIG_VT */ +static int do_smb_getmountuid(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + __kernel_uid_t kuid; + int err; + + cmd = SMB_IOC_GETMOUNTUID; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&kuid); + set_fs(old_fs); + + if (err >= 0) + err = put_user(kuid, (__kernel_uid_t32 *)arg); + + return err; +} + +struct atmif_sioc32 { + int number; + int length; + __kernel_caddr_t32 arg; +}; + +struct atm_iobuf32 { + int length; + __kernel_caddr_t32 buffer; +}; + +#define ATM_GETLINKRATE32 _IOW('a', ATMIOC_ITF+1, struct atmif_sioc32) +#define ATM_GETNAMES32 _IOW('a', ATMIOC_ITF+3, struct atm_iobuf32) +#define ATM_GETTYPE32 _IOW('a', ATMIOC_ITF+4, struct atmif_sioc32) +#define ATM_GETESI32 _IOW('a', ATMIOC_ITF+5, struct atmif_sioc32) +#define ATM_GETADDR32 _IOW('a', ATMIOC_ITF+6, struct atmif_sioc32) +#define ATM_RSTADDR32 _IOW('a', ATMIOC_ITF+7, struct atmif_sioc32) +#define ATM_ADDADDR32 _IOW('a', ATMIOC_ITF+8, struct atmif_sioc32) +#define ATM_DELADDR32 _IOW('a', ATMIOC_ITF+9, struct atmif_sioc32) +#define ATM_GETCIRANGE32 _IOW('a', ATMIOC_ITF+10, struct atmif_sioc32) +#define ATM_SETCIRANGE32 _IOW('a', ATMIOC_ITF+11, struct atmif_sioc32) +#define ATM_SETESI32 _IOW('a', ATMIOC_ITF+12, struct atmif_sioc32) +#define ATM_SETESIF32 _IOW('a', ATMIOC_ITF+13, struct atmif_sioc32) +#define ATM_GETSTAT32 _IOW('a', ATMIOC_SARCOM+0, struct atmif_sioc32) +#define ATM_GETSTATZ32 _IOW('a', ATMIOC_SARCOM+1, struct atmif_sioc32) +#define ATM_GETLOOP32 _IOW('a', ATMIOC_SARCOM+2, struct atmif_sioc32) +#define ATM_SETLOOP32 _IOW('a', ATMIOC_SARCOM+3, struct atmif_sioc32) +#define ATM_QUERYLOOP32 _IOW('a', ATMIOC_SARCOM+4, struct atmif_sioc32) + +static struct { + unsigned int cmd32; + unsigned int cmd; +} atm_ioctl_map[] = { + { ATM_GETLINKRATE32, ATM_GETLINKRATE }, + { ATM_GETNAMES32, ATM_GETNAMES }, + { ATM_GETTYPE32, ATM_GETTYPE }, + { ATM_GETESI32, ATM_GETESI }, + { ATM_GETADDR32, ATM_GETADDR }, + { ATM_RSTADDR32, ATM_RSTADDR }, + { ATM_ADDADDR32, ATM_ADDADDR }, + { ATM_DELADDR32, ATM_DELADDR }, + { ATM_GETCIRANGE32, ATM_GETCIRANGE }, + { ATM_SETCIRANGE32, ATM_SETCIRANGE }, + { ATM_SETESI32, ATM_SETESI }, + { ATM_SETESIF32, ATM_SETESIF }, + { ATM_GETSTAT32, ATM_GETSTAT }, + { ATM_GETSTATZ32, ATM_GETSTATZ }, + { ATM_GETLOOP32, ATM_GETLOOP }, + { ATM_SETLOOP32, ATM_SETLOOP }, + { ATM_QUERYLOOP32, ATM_QUERYLOOP } +}; + +#define NR_ATM_IOCTL (sizeof(atm_ioctl_map)/sizeof(atm_ioctl_map[0])) + + +static int do_atm_iobuf(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct atm_iobuf32 iobuf32; + struct atm_iobuf iobuf = { 0, NULL }; + mm_segment_t old_fs; + int err; + + err = copy_from_user(&iobuf32, (struct atm_iobuf32*)arg, + sizeof(struct atm_iobuf32)); + if (err) + return -EFAULT; + + iobuf.length = iobuf32.length; + + if (iobuf32.buffer == (__kernel_caddr_t32) NULL || iobuf32.length == 0) { + iobuf.buffer = (void*)(unsigned long)iobuf32.buffer; + } else { + iobuf.buffer = kmalloc(iobuf.length, GFP_KERNEL); + if (iobuf.buffer == NULL) { + err = -ENOMEM; + goto out; + } + + err = copy_from_user(iobuf.buffer, A(iobuf32.buffer), iobuf.length); + if (err) { + err = -EFAULT; + goto out; + } + } + + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&iobuf); + set_fs (old_fs); + if(err) + goto out; + + if(iobuf.buffer && iobuf.length > 0) { + err = copy_to_user(A(iobuf32.buffer), iobuf.buffer, iobuf.length); + if (err) { + err = -EFAULT; + goto out; + } + } + err = __put_user(iobuf.length, &(((struct atm_iobuf32*)arg)->length)); + + out: + if(iobuf32.buffer && iobuf32.length > 0) + kfree(iobuf.buffer); + + return err; +} + + +static int do_atmif_sioc(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct atmif_sioc32 sioc32; + struct atmif_sioc sioc = { 0, 0, NULL }; + mm_segment_t old_fs; + int err; + + err = copy_from_user(&sioc32, (struct atmif_sioc32*)arg, + sizeof(struct atmif_sioc32)); + if (err) + return -EFAULT; + + sioc.number = sioc32.number; + sioc.length = sioc32.length; + + if (sioc32.arg == (__kernel_caddr_t32) NULL || sioc32.length == 0) { + sioc.arg = (void*)(unsigned long)sioc32.arg; + } else { + sioc.arg = kmalloc(sioc.length, GFP_KERNEL); + if (sioc.arg == NULL) { + err = -ENOMEM; + goto out; + } + + err = copy_from_user(sioc.arg, A(sioc32.arg), sioc32.length); + if (err) { + err = -EFAULT; + goto out; + } + } + + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&sioc); + set_fs (old_fs); + if(err) { + goto out; + } + + if(sioc.arg && sioc.length > 0) { + err = copy_to_user(A(sioc32.arg), sioc.arg, sioc.length); + if (err) { + err = -EFAULT; + goto out; + } + } + err = __put_user(sioc.length, &(((struct atmif_sioc32*)arg)->length)); + + out: + if(sioc32.arg && sioc32.length > 0) + kfree(sioc.arg); + + return err; +} + + +static int do_atm_ioctl(unsigned int fd, unsigned int cmd32, unsigned long arg) +{ + int i; + unsigned int cmd = 0; + + switch (cmd32) { + case SONET_GETSTAT: + case SONET_GETSTATZ: + case SONET_GETDIAG: + case SONET_SETDIAG: + case SONET_CLRDIAG: + case SONET_SETFRAMING: + case SONET_GETFRAMING: + case SONET_GETFRSENSE: + return do_atmif_sioc(fd, cmd32, arg); + } + + for (i = 0; i < NR_ATM_IOCTL; i++) { + if (cmd32 == atm_ioctl_map[i].cmd32) { + cmd = atm_ioctl_map[i].cmd; + break; + } + } + if (i == NR_ATM_IOCTL) { + return -EINVAL; + } + + switch (cmd) { + case ATM_GETNAMES: + return do_atm_iobuf(fd, cmd, arg); + + case ATM_GETLINKRATE: + case ATM_GETTYPE: + case ATM_GETESI: + case ATM_GETADDR: + case ATM_RSTADDR: + case ATM_ADDADDR: + case ATM_DELADDR: + case ATM_GETCIRANGE: + case ATM_SETCIRANGE: + case ATM_SETESI: + case ATM_SETESIF: + case ATM_GETSTAT: + case ATM_GETSTATZ: + case ATM_GETLOOP: + case ATM_SETLOOP: + case ATM_QUERYLOOP: + return do_atmif_sioc(fd, cmd, arg); + } + + return -EINVAL; +} + +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +/* Ugh, LVM. Pitty it was not cleaned up before accepted :((. */ +typedef struct { + uint8_t vg_name[NAME_LEN]; + uint32_t vg_number; + uint32_t vg_access; + uint32_t vg_status; + uint32_t lv_max; + uint32_t lv_cur; + uint32_t lv_open; + uint32_t pv_max; + uint32_t pv_cur; + uint32_t pv_act; + uint32_t dummy; + uint32_t vgda; + uint32_t pe_size; + uint32_t pe_total; + uint32_t pe_allocated; + uint32_t pvg_total; + u32 proc; + u32 pv[ABS_MAX_PV + 1]; + u32 lv[ABS_MAX_LV + 1]; + uint8_t vg_uuid[UUID_LEN+1]; /* volume group UUID */ +} vg32_t; + +typedef struct { + uint8_t id[2]; + uint16_t version; + lvm_disk_data_t pv_on_disk; + lvm_disk_data_t vg_on_disk; + lvm_disk_data_t pv_namelist_on_disk; + lvm_disk_data_t lv_on_disk; + lvm_disk_data_t pe_on_disk; + uint8_t pv_name[NAME_LEN]; + uint8_t vg_name[NAME_LEN]; + uint8_t system_id[NAME_LEN]; + kdev_t pv_dev; + uint32_t pv_number; + uint32_t pv_status; + uint32_t pv_allocatable; + uint32_t pv_size; + uint32_t lv_cur; + uint32_t pe_size; + uint32_t pe_total; + uint32_t pe_allocated; + uint32_t pe_stale; + u32 pe; + u32 inode; + uint8_t pv_uuid[UUID_LEN+1]; +} pv32_t; + +typedef struct { + char lv_name[NAME_LEN]; + u32 lv; +} lv_req32_t; + +typedef struct { + u32 lv_index; + u32 lv; + /* Transfer size because user space and kernel space differ */ + uint16_t size; +} lv_status_byindex_req32_t; + +typedef struct { + dev_t dev; + u32 lv; +} lv_status_bydev_req32_t; + +typedef struct { + uint8_t lv_name[NAME_LEN]; + kdev_t old_dev; + kdev_t new_dev; + u32 old_pe; + u32 new_pe; +} le_remap_req32_t; + +typedef struct { + char pv_name[NAME_LEN]; + u32 pv; +} pv_status_req32_t; + +typedef struct { + uint8_t lv_name[NAME_LEN]; + uint8_t vg_name[NAME_LEN]; + uint32_t lv_access; + uint32_t lv_status; + uint32_t lv_open; + kdev_t lv_dev; + uint32_t lv_number; + uint32_t lv_mirror_copies; + uint32_t lv_recovery; + uint32_t lv_schedule; + uint32_t lv_size; + u32 lv_current_pe; + uint32_t lv_current_le; + uint32_t lv_allocated_le; + uint32_t lv_stripes; + uint32_t lv_stripesize; + uint32_t lv_badblock; + uint32_t lv_allocation; + uint32_t lv_io_timeout; + uint32_t lv_read_ahead; + /* delta to version 1 starts here */ + u32 lv_snapshot_org; + u32 lv_snapshot_prev; + u32 lv_snapshot_next; + u32 lv_block_exception; + uint32_t lv_remap_ptr; + uint32_t lv_remap_end; + uint32_t lv_chunk_size; + uint32_t lv_snapshot_minor; + char dummy[200]; +} lv32_t; + +typedef struct { + u32 hash[2]; + u32 rsector_org; + kdev_t rdev_org; + u32 rsector_new; + kdev_t rdev_new; +} lv_block_exception32_t; + +static void put_lv_t(lv_t *l) +{ + if (l->lv_current_pe) vfree(l->lv_current_pe); + if (l->lv_block_exception) vfree(l->lv_block_exception); + kfree(l); +} + +static lv_t *get_lv_t(u32 p, int *errp) +{ + int err, i; + u32 ptr1, ptr2; + size_t size; + lv_block_exception32_t *lbe32; + lv_block_exception_t *lbe; + lv32_t *ul = (lv32_t *)A(p); + lv_t *l = (lv_t *)kmalloc(sizeof(lv_t), GFP_KERNEL); + if (!l) { + *errp = -ENOMEM; + return NULL; + } + memset(l, 0, sizeof(lv_t)); + err = copy_from_user(l, ul, (long)&((lv32_t *)0)->lv_current_pe); + err |= __copy_from_user(&l->lv_current_le, &ul->lv_current_le, + ((long)&ul->lv_snapshot_org) - ((long)&ul->lv_current_le)); + err |= __copy_from_user(&l->lv_remap_ptr, &ul->lv_remap_ptr, + ((long)&ul->dummy[0]) - ((long)&ul->lv_remap_ptr)); + err |= __get_user(ptr1, &ul->lv_current_pe); + err |= __get_user(ptr2, &ul->lv_block_exception); + if (err) { + kfree(l); + *errp = -EFAULT; + return NULL; + } + if (ptr1) { + size = l->lv_allocated_le * sizeof(pe_t); + l->lv_current_pe = vmalloc(size); + if (l->lv_current_pe) + err = copy_from_user(l->lv_current_pe, (void *)A(ptr1), size); + } + if (!err && ptr2) { + size = l->lv_remap_end * sizeof(lv_block_exception_t); + l->lv_block_exception = lbe = vmalloc(size); + if (l->lv_block_exception) { + lbe32 = (lv_block_exception32_t *)A(ptr2); + memset(lbe, 0, size); + for (i = 0; i < l->lv_remap_end; i++, lbe++, lbe32++) { + err |= get_user(lbe->rsector_org, &lbe32->rsector_org); + err |= __get_user(lbe->rdev_org, &lbe32->rdev_org); + err |= __get_user(lbe->rsector_new, &lbe32->rsector_new); + err |= __get_user(lbe->rdev_new, &lbe32->rdev_new); + + } + } + } + if (err || (ptr1 && !l->lv_current_pe) || (ptr2 && !l->lv_block_exception)) { + if (!err) + *errp = -ENOMEM; + else + *errp = -EFAULT; + put_lv_t(l); + return NULL; + } + return l; +} + +static int copy_lv_t(u32 ptr, lv_t *l) +{ + int err; + lv32_t *ul = (lv32_t *)A(ptr); + u32 ptr1; + size_t size; + + err = get_user(ptr1, &ul->lv_current_pe); + if (err) + return -EFAULT; + err = copy_to_user(ul, l, (long)&((lv32_t *)0)->lv_current_pe); + err |= __copy_to_user(&ul->lv_current_le, &l->lv_current_le, + ((long)&ul->lv_snapshot_org) - ((long)&ul->lv_current_le)); + err |= __copy_to_user(&ul->lv_remap_ptr, &l->lv_remap_ptr, + ((long)&ul->dummy[0]) - ((long)&ul->lv_remap_ptr)); + size = l->lv_allocated_le * sizeof(pe_t); + if (ptr1) + err |= __copy_to_user((void *)A(ptr1), l->lv_current_pe, size); + return err ? -EFAULT : 0; +} + +static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + vg_t *v; + union { + lv_req_t lv_req; + le_remap_req_t le_remap; + lv_status_byindex_req_t lv_byindex; + lv_status_bydev_req_t lv_bydev; + pv_status_req_t pv_status; + } u; + pv_t p; + int err; + u32 ptr = 0; + int i; + mm_segment_t old_fs; + void *karg = &u; + + switch (cmd) { + case VG_STATUS: + v = kmalloc(sizeof(vg_t), GFP_KERNEL); + if (!v) return -ENOMEM; + karg = v; + break; + case VG_CREATE: + v = kmalloc(sizeof(vg_t), GFP_KERNEL); + if (!v) return -ENOMEM; + if (copy_from_user(v, (void *)arg, (long)&((vg32_t *)0)->proc) || + __get_user(v->proc, &((vg32_t *)arg)->proc)) { + kfree(v); + return -EFAULT; + } + if (copy_from_user(v->vg_uuid, ((vg32_t *)arg)->vg_uuid, UUID_LEN+1)) { + kfree(v); + return -EFAULT; + } + + karg = v; + memset(v->pv, 0, sizeof(v->pv) + sizeof(v->lv)); + if (v->pv_max > ABS_MAX_PV || v->lv_max > ABS_MAX_LV) + return -EPERM; + for (i = 0; i < v->pv_max; i++) { + err = __get_user(ptr, &((vg32_t *)arg)->pv[i]); + if (err) break; + if (ptr) { + v->pv[i] = kmalloc(sizeof(pv_t), GFP_KERNEL); + if (!v->pv[i]) { + err = -ENOMEM; + break; + } + err = copy_from_user(v->pv[i], (void *)A(ptr), sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) { + err = -EFAULT; + break; + } + err = copy_from_user(v->pv[i]->pv_uuid, ((pv32_t *)A(ptr))->pv_uuid, UUID_LEN+1); + if (err) { + err = -EFAULT; + break; + } + + + v->pv[i]->pe = NULL; v->pv[i]->inode = NULL; + } + } + if (!err) { + for (i = 0; i < v->lv_max; i++) { + err = __get_user(ptr, &((vg32_t *)arg)->lv[i]); + if (err) break; + if (ptr) { + v->lv[i] = get_lv_t(ptr, &err); + if (err) break; + } + } + } + break; + case LV_CREATE: + case LV_EXTEND: + case LV_REDUCE: + case LV_REMOVE: + case LV_RENAME: + case LV_STATUS_BYNAME: + err = copy_from_user(&u.pv_status, arg, sizeof(u.pv_status.pv_name)); + if (err) return -EFAULT; + if (cmd != LV_REMOVE) { + err = __get_user(ptr, &((lv_req32_t *)arg)->lv); + if (err) return err; + u.lv_req.lv = get_lv_t(ptr, &err); + } else + u.lv_req.lv = NULL; + break; + + + case LV_STATUS_BYINDEX: + err = get_user(u.lv_byindex.lv_index, &((lv_status_byindex_req32_t *)arg)->lv_index); + err |= __get_user(ptr, &((lv_status_byindex_req32_t *)arg)->lv); + if (err) return err; + u.lv_byindex.lv = get_lv_t(ptr, &err); + break; + case LV_STATUS_BYDEV: + err = get_user(u.lv_bydev.dev, &((lv_status_bydev_req32_t *)arg)->dev); + u.lv_bydev.lv = get_lv_t(ptr, &err); + if (err) return err; + u.lv_bydev.lv = &p; + p.pe = NULL; p.inode = NULL; + break; + case VG_EXTEND: + err = copy_from_user(&p, (void *)arg, sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) return -EFAULT; + err = copy_from_user(p.pv_uuid, ((pv32_t *)arg)->pv_uuid, UUID_LEN+1); + if (err) return -EFAULT; + p.pe = NULL; p.inode = NULL; + karg = &p; + break; + case PV_CHANGE: + case PV_STATUS: + err = copy_from_user(&u.pv_status, arg, sizeof(u.lv_req.lv_name)); + if (err) return -EFAULT; + err = __get_user(ptr, &((pv_status_req32_t *)arg)->pv); + if (err) return err; + u.pv_status.pv = &p; + if (cmd == PV_CHANGE) { + err = copy_from_user(&p, (void *)A(ptr), sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) return -EFAULT; + p.pe = NULL; p.inode = NULL; + } + break; + } + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)karg); + set_fs (old_fs); + switch (cmd) { + case VG_STATUS: + if (!err) { + if (copy_to_user((void *)arg, v, (long)&((vg32_t *)0)->proc) || + clear_user(&((vg32_t *)arg)->proc, sizeof(vg32_t) - (long)&((vg32_t *)0)->proc)) + err = -EFAULT; + } + if (copy_to_user(((vg32_t *)arg)->vg_uuid, v->vg_uuid, UUID_LEN+1)) { + err = -EFAULT; + } + kfree(v); + break; + case VG_CREATE: + for (i = 0; i < v->pv_max; i++) + if (v->pv[i]) kfree(v->pv[i]); + for (i = 0; i < v->lv_max; i++) + if (v->lv[i]) put_lv_t(v->lv[i]); + kfree(v); + break; + case LV_STATUS_BYNAME: + if (!err && u.lv_req.lv) err = copy_lv_t(ptr, u.lv_req.lv); + /* Fall through */ + case LV_CREATE: + case LV_EXTEND: + case LV_REDUCE: + if (u.lv_req.lv) put_lv_t(u.lv_req.lv); + break; + case LV_STATUS_BYINDEX: + if (u.lv_byindex.lv) { + if (!err) err = copy_lv_t(ptr, u.lv_byindex.lv); + put_lv_t(u.lv_byindex.lv); + } + break; + case PV_STATUS: + if (!err) { + err = copy_to_user((void *)A(ptr), &p, sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) return -EFAULT; + err = copy_to_user(((pv_t *)A(ptr))->pv_uuid, p.pv_uuid, UUID_LEN + 1); + if (err) return -EFAULT; + } + break; + case LV_STATUS_BYDEV: + if (!err) { + if (!err) err = copy_lv_t(ptr, u.lv_bydev.lv); + put_lv_t(u.lv_byindex.lv); + } + break; + } + return err; +} +#endif + +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +/* This really belongs in include/linux/drm.h -DaveM */ +#include "../../../drivers/char/drm/drm.h" + +typedef struct drm32_version { + int version_major; /* Major version */ + int version_minor; /* Minor version */ + int version_patchlevel;/* Patch level */ + int name_len; /* Length of name buffer */ + u32 name; /* Name of driver */ + int date_len; /* Length of date buffer */ + u32 date; /* User-space buffer to hold date */ + int desc_len; /* Length of desc buffer */ + u32 desc; /* User-space buffer to hold desc */ +} drm32_version_t; +#define DRM32_IOCTL_VERSION DRM_IOWR(0x00, drm32_version_t) + +static int drm32_version(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_version_t *uversion = (drm32_version_t *)arg; + char *name_ptr, *date_ptr, *desc_ptr; + u32 tmp1, tmp2, tmp3; + drm_version_t kversion; + mm_segment_t old_fs; + int ret; + + memset(&kversion, 0, sizeof(kversion)); + if (get_user(kversion.name_len, &uversion->name_len) || + get_user(kversion.date_len, &uversion->date_len) || + get_user(kversion.desc_len, &uversion->desc_len) || + get_user(tmp1, &uversion->name) || + get_user(tmp2, &uversion->date) || + get_user(tmp3, &uversion->desc)) + return -EFAULT; + + name_ptr = (char *) A(tmp1); + date_ptr = (char *) A(tmp2); + desc_ptr = (char *) A(tmp3); + + ret = -ENOMEM; + if (kversion.name_len && name_ptr) { + kversion.name = kmalloc(kversion.name_len, GFP_KERNEL); + if (!kversion.name) + goto out; + } + if (kversion.date_len && date_ptr) { + kversion.date = kmalloc(kversion.date_len, GFP_KERNEL); + if (!kversion.date) + goto out; + } + if (kversion.desc_len && desc_ptr) { + kversion.desc = kmalloc(kversion.desc_len, GFP_KERNEL); + if (!kversion.desc) + goto out; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl (fd, DRM_IOCTL_VERSION, (unsigned long)&kversion); + set_fs(old_fs); + + if (!ret) { + if ((kversion.name && + copy_to_user(name_ptr, kversion.name, kversion.name_len)) || + (kversion.date && + copy_to_user(date_ptr, kversion.date, kversion.date_len)) || + (kversion.desc && + copy_to_user(desc_ptr, kversion.desc, kversion.desc_len))) + ret = -EFAULT; + if (put_user(kversion.version_major, &uversion->version_major) || + put_user(kversion.version_minor, &uversion->version_minor) || + put_user(kversion.version_patchlevel, &uversion->version_patchlevel) || + put_user(kversion.name_len, &uversion->name_len) || + put_user(kversion.date_len, &uversion->date_len) || + put_user(kversion.desc_len, &uversion->desc_len)) + ret = -EFAULT; + } + +out: + if (kversion.name) + kfree(kversion.name); + if (kversion.date) + kfree(kversion.date); + if (kversion.desc) + kfree(kversion.desc); + return ret; +} + +typedef struct drm32_unique { + int unique_len; /* Length of unique */ + u32 unique; /* Unique name for driver instantiation */ +} drm32_unique_t; +#define DRM32_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm32_unique_t) +#define DRM32_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm32_unique_t) + +static int drm32_getsetunique(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_unique_t *uarg = (drm32_unique_t *)arg; + drm_unique_t karg; + mm_segment_t old_fs; + char *uptr; + u32 tmp; + int ret; + + if (get_user(karg.unique_len, &uarg->unique_len)) + return -EFAULT; + karg.unique = NULL; + + if (get_user(tmp, &uarg->unique)) + return -EFAULT; + + uptr = (char *) A(tmp); + + if (uptr) { + karg.unique = kmalloc(karg.unique_len, GFP_KERNEL); + if (!karg.unique) + return -ENOMEM; + if (cmd == DRM32_IOCTL_SET_UNIQUE && + copy_from_user(karg.unique, uptr, karg.unique_len)) { + kfree(karg.unique); + return -EFAULT; + } + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + if (cmd == DRM32_IOCTL_GET_UNIQUE) + ret = sys_ioctl (fd, DRM_IOCTL_GET_UNIQUE, (unsigned long)&karg); + else + ret = sys_ioctl (fd, DRM_IOCTL_SET_UNIQUE, (unsigned long)&karg); + set_fs(old_fs); + + if (!ret) { + if (cmd == DRM32_IOCTL_GET_UNIQUE && + uptr != NULL && + copy_to_user(uptr, karg.unique, karg.unique_len)) + ret = -EFAULT; + if (put_user(karg.unique_len, &uarg->unique_len)) + ret = -EFAULT; + } + + if (karg.unique != NULL) + kfree(karg.unique); + + return ret; +} + +typedef struct drm32_map { + u32 offset; /* Requested physical address (0 for SAREA)*/ + u32 size; /* Requested physical size (bytes) */ + drm_map_type_t type; /* Type of memory to map */ + drm_map_flags_t flags; /* Flags */ + u32 handle; /* User-space: "Handle" to pass to mmap */ + /* Kernel-space: kernel-virtual address */ + int mtrr; /* MTRR slot used */ + /* Private data */ +} drm32_map_t; +#define DRM32_IOCTL_ADD_MAP DRM_IOWR(0x15, drm32_map_t) + +static int drm32_addmap(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_map_t *uarg = (drm32_map_t *) arg; + drm_map_t karg; + mm_segment_t old_fs; + u32 tmp; + int ret; + + ret = get_user(karg.offset, &uarg->offset); + ret |= get_user(karg.size, &uarg->size); + ret |= get_user(karg.type, &uarg->type); + ret |= get_user(karg.flags, &uarg->flags); + ret |= get_user(tmp, &uarg->handle); + ret |= get_user(karg.mtrr, &uarg->mtrr); + if (ret) + return -EFAULT; + + karg.handle = (void *) A(tmp); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_ADD_MAP, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + ret = put_user(karg.offset, &uarg->offset); + ret |= put_user(karg.size, &uarg->size); + ret |= put_user(karg.type, &uarg->type); + ret |= put_user(karg.flags, &uarg->flags); + tmp = (u32) (long)karg.handle; + ret |= put_user(tmp, &uarg->handle); + ret |= put_user(karg.mtrr, &uarg->mtrr); + if (ret) + ret = -EFAULT; + } + + return ret; +} + +typedef struct drm32_buf_info { + int count; /* Entries in list */ + u32 list; /* (drm_buf_desc_t *) */ +} drm32_buf_info_t; +#define DRM32_IOCTL_INFO_BUFS DRM_IOWR(0x18, drm32_buf_info_t) + +static int drm32_info_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_info_t *uarg = (drm32_buf_info_t *)arg; + drm_buf_desc_t *ulist; + drm_buf_info_t karg; + mm_segment_t old_fs; + int orig_count, ret; + u32 tmp; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->list)) + return -EFAULT; + + ulist = (drm_buf_desc_t *) A(tmp); + + orig_count = karg.count; + + karg.list = kmalloc(karg.count * sizeof(drm_buf_desc_t), GFP_KERNEL); + if (!karg.list) + return -EFAULT; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_INFO_BUFS, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (karg.count <= orig_count && + (copy_to_user(ulist, karg.list, + karg.count * sizeof(drm_buf_desc_t)))) + ret = -EFAULT; + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + + kfree(karg.list); + + return ret; +} + +typedef struct drm32_buf_free { + int count; + u32 list; /* (int *) */ +} drm32_buf_free_t; +#define DRM32_IOCTL_FREE_BUFS DRM_IOW( 0x1a, drm32_buf_free_t) + +static int drm32_free_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_free_t *uarg = (drm32_buf_free_t *)arg; + drm_buf_free_t karg; + mm_segment_t old_fs; + int *ulist; + int ret; + u32 tmp; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->list)) + return -EFAULT; + + ulist = (int *) A(tmp); + + karg.list = kmalloc(karg.count * sizeof(int), GFP_KERNEL); + if (!karg.list) + return -ENOMEM; + + ret = -EFAULT; + if (copy_from_user(karg.list, ulist, (karg.count * sizeof(int)))) + goto out; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_FREE_BUFS, (unsigned long) &karg); + set_fs(old_fs); + +out: + kfree(karg.list); + + return ret; +} + +typedef struct drm32_buf_pub { + int idx; /* Index into master buflist */ + int total; /* Buffer size */ + int used; /* Amount of buffer in use (for DMA) */ + u32 address; /* Address of buffer (void *) */ +} drm32_buf_pub_t; + +typedef struct drm32_buf_map { + int count; /* Length of buflist */ + u32 virtual; /* Mmaped area in user-virtual (void *) */ + u32 list; /* Buffer information (drm_buf_pub_t *) */ +} drm32_buf_map_t; +#define DRM32_IOCTL_MAP_BUFS DRM_IOWR(0x19, drm32_buf_map_t) + +static int drm32_map_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_map_t *uarg = (drm32_buf_map_t *)arg; + drm32_buf_pub_t *ulist; + drm_buf_map_t karg; + mm_segment_t old_fs; + int orig_count, ret, i; + u32 tmp1, tmp2; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp1, &uarg->virtual) || + get_user(tmp2, &uarg->list)) + return -EFAULT; + + karg.virtual = (void *) A(tmp1); + ulist = (drm32_buf_pub_t *) A(tmp2); + + orig_count = karg.count; + + karg.list = kmalloc(karg.count * sizeof(drm_buf_pub_t), GFP_KERNEL); + if (!karg.list) + return -ENOMEM; + + ret = -EFAULT; + for (i = 0; i < karg.count; i++) { + if (get_user(karg.list[i].idx, &ulist[i].idx) || + get_user(karg.list[i].total, &ulist[i].total) || + get_user(karg.list[i].used, &ulist[i].used) || + get_user(tmp1, &ulist[i].address)) + goto out; + + karg.list[i].address = (void *) A(tmp1); + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_MAP_BUFS, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + for (i = 0; i < orig_count; i++) { + tmp1 = (u32) (long) karg.list[i].address; + if (put_user(karg.list[i].idx, &ulist[i].idx) || + put_user(karg.list[i].total, &ulist[i].total) || + put_user(karg.list[i].used, &ulist[i].used) || + put_user(tmp1, &ulist[i].address)) { + ret = -EFAULT; + goto out; + } + } + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + +out: + kfree(karg.list); + return ret; +} + +typedef struct drm32_dma { + /* Indices here refer to the offset into + buflist in drm_buf_get_t. */ + int context; /* Context handle */ + int send_count; /* Number of buffers to send */ + u32 send_indices; /* List of handles to buffers (int *) */ + u32 send_sizes; /* Lengths of data to send (int *) */ + drm_dma_flags_t flags; /* Flags */ + int request_count; /* Number of buffers requested */ + int request_size; /* Desired size for buffers */ + u32 request_indices; /* Buffer information (int *) */ + u32 request_sizes; /* (int *) */ + int granted_count; /* Number of buffers granted */ +} drm32_dma_t; +#define DRM32_IOCTL_DMA DRM_IOWR(0x29, drm32_dma_t) + +/* RED PEN The DRM layer blindly dereferences the send/request + * indice/size arrays even though they are userland + * pointers. -DaveM + */ +static int drm32_dma(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_dma_t *uarg = (drm32_dma_t *) arg; + int *u_si, *u_ss, *u_ri, *u_rs; + drm_dma_t karg; + mm_segment_t old_fs; + int ret; + u32 tmp1, tmp2, tmp3, tmp4; + + karg.send_indices = karg.send_sizes = NULL; + karg.request_indices = karg.request_sizes = NULL; + + if (get_user(karg.context, &uarg->context) || + get_user(karg.send_count, &uarg->send_count) || + get_user(tmp1, &uarg->send_indices) || + get_user(tmp2, &uarg->send_sizes) || + get_user(karg.flags, &uarg->flags) || + get_user(karg.request_count, &uarg->request_count) || + get_user(karg.request_size, &uarg->request_size) || + get_user(tmp3, &uarg->request_indices) || + get_user(tmp4, &uarg->request_sizes) || + get_user(karg.granted_count, &uarg->granted_count)) + return -EFAULT; + + u_si = (int *) A(tmp1); + u_ss = (int *) A(tmp2); + u_ri = (int *) A(tmp3); + u_rs = (int *) A(tmp4); + + if (karg.send_count) { + karg.send_indices = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL); + karg.send_sizes = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL); + + ret = -ENOMEM; + if (!karg.send_indices || !karg.send_sizes) + goto out; + + ret = -EFAULT; + if (copy_from_user(karg.send_indices, u_si, + (karg.send_count * sizeof(int))) || + copy_from_user(karg.send_sizes, u_ss, + (karg.send_count * sizeof(int)))) + goto out; + } + + if (karg.request_count) { + karg.request_indices = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL); + karg.request_sizes = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL); + + ret = -ENOMEM; + if (!karg.request_indices || !karg.request_sizes) + goto out; + + ret = -EFAULT; + if (copy_from_user(karg.request_indices, u_ri, + (karg.request_count * sizeof(int))) || + copy_from_user(karg.request_sizes, u_rs, + (karg.request_count * sizeof(int)))) + goto out; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_DMA, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (put_user(karg.context, &uarg->context) || + put_user(karg.send_count, &uarg->send_count) || + put_user(karg.flags, &uarg->flags) || + put_user(karg.request_count, &uarg->request_count) || + put_user(karg.request_size, &uarg->request_size) || + put_user(karg.granted_count, &uarg->granted_count)) + ret = -EFAULT; + + if (karg.send_count) { + if (copy_to_user(u_si, karg.send_indices, + (karg.send_count * sizeof(int))) || + copy_to_user(u_ss, karg.send_sizes, + (karg.send_count * sizeof(int)))) + ret = -EFAULT; + } + if (karg.request_count) { + if (copy_to_user(u_ri, karg.request_indices, + (karg.request_count * sizeof(int))) || + copy_to_user(u_rs, karg.request_sizes, + (karg.request_count * sizeof(int)))) + ret = -EFAULT; + } + } + +out: + if (karg.send_indices) + kfree(karg.send_indices); + if (karg.send_sizes) + kfree(karg.send_sizes); + if (karg.request_indices) + kfree(karg.request_indices); + if (karg.request_sizes) + kfree(karg.request_sizes); + + return ret; +} + +typedef struct drm32_ctx_res { + int count; + u32 contexts; /* (drm_ctx_t *) */ +} drm32_ctx_res_t; +#define DRM32_IOCTL_RES_CTX DRM_IOWR(0x26, drm32_ctx_res_t) + +static int drm32_res_ctx(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_ctx_res_t *uarg = (drm32_ctx_res_t *) arg; + drm_ctx_t *ulist; + drm_ctx_res_t karg; + mm_segment_t old_fs; + int orig_count, ret; + u32 tmp; + + karg.contexts = NULL; + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->contexts)) + return -EFAULT; + + ulist = (drm_ctx_t *) A(tmp); + + orig_count = karg.count; + if (karg.count && ulist) { + karg.contexts = kmalloc((karg.count * sizeof(drm_ctx_t)), GFP_KERNEL); + if (!karg.contexts) + return -ENOMEM; + if (copy_from_user(karg.contexts, ulist, + (karg.count * sizeof(drm_ctx_t)))) { + kfree(karg.contexts); + return -EFAULT; + } + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_RES_CTX, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (orig_count) { + if (copy_to_user(ulist, karg.contexts, + (orig_count * sizeof(drm_ctx_t)))) + ret = -EFAULT; + } + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + + if (karg.contexts) + kfree(karg.contexts); + + return ret; +} + +#endif + +static int ret_einval(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + return -EINVAL; +} + +static int broken_blkgetsize(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + /* The mkswap binary hard codes it to Intel value :-((( */ + return w_long(fd, BLKGETSIZE, arg); +} + +struct blkpg_ioctl_arg32 { + int op; + int flags; + int datalen; + u32 data; +}; + +static int blkpg_ioctl_trans(unsigned int fd, unsigned int cmd, struct blkpg_ioctl_arg32 *arg) +{ + struct blkpg_ioctl_arg a; + struct blkpg_partition p; + int err; + mm_segment_t old_fs = get_fs(); + + err = get_user(a.op, &arg->op); + err |= __get_user(a.flags, &arg->flags); + err |= __get_user(a.datalen, &arg->datalen); + err |= __get_user((long)a.data, &arg->data); + if (err) return err; + switch (a.op) { + case BLKPG_ADD_PARTITION: + case BLKPG_DEL_PARTITION: + if (a.datalen < sizeof(struct blkpg_partition)) + return -EINVAL; + if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) + return -EFAULT; + a.data = &p; + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&a); + set_fs (old_fs); + default: + return -EINVAL; + } + return err; +} + +static int ioc_settimeout(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + return rw_long(fd, AUTOFS_IOC_SETTIMEOUT, arg); +} + +struct ioctl_trans { + unsigned long cmd; + unsigned long handler; + unsigned long next; +}; + +#define COMPATIBLE_IOCTL(cmd) { cmd, (unsigned long)sys_ioctl, 0 } + +#define HANDLE_IOCTL(cmd,handler) { cmd, (unsigned long)handler, 0 } + +#define AUTOFS_IOC_SETTIMEOUT32 _IOWR(0x93,0x64,unsigned int) +#define SMB_IOC_GETMOUNTUID_32 _IOR('u', 1, __kernel_uid_t32) + +static struct ioctl_trans ioctl_translations[] = { + /* List here explicitly which ioctl's need translation, + * all others default to calling sys_ioctl(). + */ +/* Big T */ +COMPATIBLE_IOCTL(TCGETA), +COMPATIBLE_IOCTL(TCSETA), +COMPATIBLE_IOCTL(TCSETAW), +COMPATIBLE_IOCTL(TCSETAF), +COMPATIBLE_IOCTL(TCSBRK), +COMPATIBLE_IOCTL(TCXONC), +COMPATIBLE_IOCTL(TCFLSH), +COMPATIBLE_IOCTL(TCGETS), +COMPATIBLE_IOCTL(TCSETS), +COMPATIBLE_IOCTL(TCSETSW), +COMPATIBLE_IOCTL(TCSETSF), +COMPATIBLE_IOCTL(TIOCLINUX), +COMPATIBLE_IOCTL(TIOCSTART), +/* Little t */ +COMPATIBLE_IOCTL(TIOCGETD), +COMPATIBLE_IOCTL(TIOCSETD), +COMPATIBLE_IOCTL(TIOCEXCL), +COMPATIBLE_IOCTL(TIOCNXCL), +COMPATIBLE_IOCTL(TIOCCONS), +COMPATIBLE_IOCTL(TIOCGSOFTCAR), +COMPATIBLE_IOCTL(TIOCSSOFTCAR), +COMPATIBLE_IOCTL(TIOCSWINSZ), +COMPATIBLE_IOCTL(TIOCGWINSZ), +COMPATIBLE_IOCTL(TIOCMGET), +COMPATIBLE_IOCTL(TIOCMBIC), +COMPATIBLE_IOCTL(TIOCMBIS), +COMPATIBLE_IOCTL(TIOCMSET), +COMPATIBLE_IOCTL(TIOCPKT), +COMPATIBLE_IOCTL(TIOCNOTTY), +COMPATIBLE_IOCTL(TIOCSTI), +COMPATIBLE_IOCTL(TIOCOUTQ), +COMPATIBLE_IOCTL(TIOCSPGRP), +COMPATIBLE_IOCTL(TIOCGPGRP), +COMPATIBLE_IOCTL(TIOCSCTTY), +COMPATIBLE_IOCTL(TIOCGPTN), +COMPATIBLE_IOCTL(TIOCSPTLCK), +COMPATIBLE_IOCTL(TIOCGSERIAL), +COMPATIBLE_IOCTL(TIOCSSERIAL), +COMPATIBLE_IOCTL(TIOCSERGETLSR), +/* Big F */ +COMPATIBLE_IOCTL(FBIOGET_VSCREENINFO), +COMPATIBLE_IOCTL(FBIOPUT_VSCREENINFO), +COMPATIBLE_IOCTL(FBIOPAN_DISPLAY), +COMPATIBLE_IOCTL(FBIOGET_FCURSORINFO), +COMPATIBLE_IOCTL(FBIOGET_VCURSORINFO), +COMPATIBLE_IOCTL(FBIOPUT_VCURSORINFO), +COMPATIBLE_IOCTL(FBIOGET_CURSORSTATE), +COMPATIBLE_IOCTL(FBIOPUT_CURSORSTATE), +COMPATIBLE_IOCTL(FBIOGET_CON2FBMAP), +COMPATIBLE_IOCTL(FBIOPUT_CON2FBMAP), +#if 0 +COMPATIBLE_IOCTL(FBIOBLANK), +#endif +/* Little f */ +COMPATIBLE_IOCTL(FIOCLEX), +COMPATIBLE_IOCTL(FIONCLEX), +COMPATIBLE_IOCTL(FIOASYNC), +COMPATIBLE_IOCTL(FIONBIO), +COMPATIBLE_IOCTL(FIONREAD), /* This is also TIOCINQ */ +/* 0x00 */ +COMPATIBLE_IOCTL(FIBMAP), +COMPATIBLE_IOCTL(FIGETBSZ), +/* 0x03 -- HD/IDE ioctl's used by hdparm and friends. + * Some need translations, these do not. + */ +COMPATIBLE_IOCTL(HDIO_GET_IDENTITY), +COMPATIBLE_IOCTL(HDIO_SET_DMA), +COMPATIBLE_IOCTL(HDIO_SET_KEEPSETTINGS), +COMPATIBLE_IOCTL(HDIO_SET_UNMASKINTR), +COMPATIBLE_IOCTL(HDIO_SET_NOWERR), +COMPATIBLE_IOCTL(HDIO_SET_32BIT), +COMPATIBLE_IOCTL(HDIO_SET_MULTCOUNT), +COMPATIBLE_IOCTL(HDIO_DRIVE_CMD), +COMPATIBLE_IOCTL(HDIO_SET_PIO_MODE), +COMPATIBLE_IOCTL(HDIO_SCAN_HWIF), +COMPATIBLE_IOCTL(HDIO_SET_NICE), +/* 0x02 -- Floppy ioctls */ +COMPATIBLE_IOCTL(FDMSGON), +COMPATIBLE_IOCTL(FDMSGOFF), +COMPATIBLE_IOCTL(FDSETEMSGTRESH), +COMPATIBLE_IOCTL(FDFLUSH), +COMPATIBLE_IOCTL(FDWERRORCLR), +COMPATIBLE_IOCTL(FDSETMAXERRS), +COMPATIBLE_IOCTL(FDGETMAXERRS), +COMPATIBLE_IOCTL(FDGETDRVTYP), +COMPATIBLE_IOCTL(FDEJECT), +COMPATIBLE_IOCTL(FDCLRPRM), +COMPATIBLE_IOCTL(FDFMTBEG), +COMPATIBLE_IOCTL(FDFMTEND), +COMPATIBLE_IOCTL(FDRESET), +COMPATIBLE_IOCTL(FDTWADDLE), +COMPATIBLE_IOCTL(FDFMTTRK), +COMPATIBLE_IOCTL(FDRAWCMD), +/* 0x12 */ +COMPATIBLE_IOCTL(BLKROSET), +COMPATIBLE_IOCTL(BLKROGET), +COMPATIBLE_IOCTL(BLKRRPART), +COMPATIBLE_IOCTL(BLKFLSBUF), +COMPATIBLE_IOCTL(BLKRASET), +COMPATIBLE_IOCTL(BLKFRASET), +COMPATIBLE_IOCTL(BLKSECTSET), +COMPATIBLE_IOCTL(BLKSSZGET), + +/* RAID */ +COMPATIBLE_IOCTL(RAID_VERSION), +COMPATIBLE_IOCTL(GET_ARRAY_INFO), +COMPATIBLE_IOCTL(GET_DISK_INFO), +COMPATIBLE_IOCTL(PRINT_RAID_DEBUG), +COMPATIBLE_IOCTL(CLEAR_ARRAY), +COMPATIBLE_IOCTL(ADD_NEW_DISK), +COMPATIBLE_IOCTL(HOT_REMOVE_DISK), +COMPATIBLE_IOCTL(SET_ARRAY_INFO), +COMPATIBLE_IOCTL(SET_DISK_INFO), +COMPATIBLE_IOCTL(WRITE_RAID_INFO), +COMPATIBLE_IOCTL(UNPROTECT_ARRAY), +COMPATIBLE_IOCTL(PROTECT_ARRAY), +COMPATIBLE_IOCTL(HOT_ADD_DISK), +COMPATIBLE_IOCTL(SET_DISK_FAULTY), +COMPATIBLE_IOCTL(RUN_ARRAY), +COMPATIBLE_IOCTL(START_ARRAY), +COMPATIBLE_IOCTL(STOP_ARRAY), +COMPATIBLE_IOCTL(STOP_ARRAY_RO), +COMPATIBLE_IOCTL(RESTART_ARRAY_RW), +/* Big K */ +COMPATIBLE_IOCTL(PIO_FONT), +COMPATIBLE_IOCTL(GIO_FONT), +COMPATIBLE_IOCTL(KDSIGACCEPT), +COMPATIBLE_IOCTL(KDGETKEYCODE), +COMPATIBLE_IOCTL(KDSETKEYCODE), +COMPATIBLE_IOCTL(KIOCSOUND), +COMPATIBLE_IOCTL(KDMKTONE), +COMPATIBLE_IOCTL(KDGKBTYPE), +COMPATIBLE_IOCTL(KDSETMODE), +COMPATIBLE_IOCTL(KDGETMODE), +COMPATIBLE_IOCTL(KDSKBMODE), +COMPATIBLE_IOCTL(KDGKBMODE), +COMPATIBLE_IOCTL(KDSKBMETA), +COMPATIBLE_IOCTL(KDGKBMETA), +COMPATIBLE_IOCTL(KDGKBENT), +COMPATIBLE_IOCTL(KDSKBENT), +COMPATIBLE_IOCTL(KDGKBSENT), +COMPATIBLE_IOCTL(KDSKBSENT), +COMPATIBLE_IOCTL(KDGKBDIACR), +COMPATIBLE_IOCTL(KDSKBDIACR), +COMPATIBLE_IOCTL(KDGKBLED), +COMPATIBLE_IOCTL(KDSKBLED), +COMPATIBLE_IOCTL(KDGETLED), +COMPATIBLE_IOCTL(KDSETLED), +COMPATIBLE_IOCTL(GIO_SCRNMAP), +COMPATIBLE_IOCTL(PIO_SCRNMAP), +COMPATIBLE_IOCTL(GIO_UNISCRNMAP), +COMPATIBLE_IOCTL(PIO_UNISCRNMAP), +COMPATIBLE_IOCTL(PIO_FONTRESET), +COMPATIBLE_IOCTL(PIO_UNIMAPCLR), +/* Big S */ +COMPATIBLE_IOCTL(SCSI_IOCTL_GET_IDLUN), +COMPATIBLE_IOCTL(SCSI_IOCTL_DOORLOCK), +COMPATIBLE_IOCTL(SCSI_IOCTL_DOORUNLOCK), +COMPATIBLE_IOCTL(SCSI_IOCTL_TEST_UNIT_READY), +COMPATIBLE_IOCTL(SCSI_IOCTL_TAGGED_ENABLE), +COMPATIBLE_IOCTL(SCSI_IOCTL_TAGGED_DISABLE), +COMPATIBLE_IOCTL(SCSI_IOCTL_GET_BUS_NUMBER), +COMPATIBLE_IOCTL(SCSI_IOCTL_SEND_COMMAND), +/* Big V */ +COMPATIBLE_IOCTL(VT_SETMODE), +COMPATIBLE_IOCTL(VT_GETMODE), +COMPATIBLE_IOCTL(VT_GETSTATE), +COMPATIBLE_IOCTL(VT_OPENQRY), +COMPATIBLE_IOCTL(VT_ACTIVATE), +COMPATIBLE_IOCTL(VT_WAITACTIVE), +COMPATIBLE_IOCTL(VT_RELDISP), +COMPATIBLE_IOCTL(VT_DISALLOCATE), +COMPATIBLE_IOCTL(VT_RESIZE), +COMPATIBLE_IOCTL(VT_RESIZEX), +COMPATIBLE_IOCTL(VT_LOCKSWITCH), +COMPATIBLE_IOCTL(VT_UNLOCKSWITCH), +/* Little v, the video4linux ioctls */ +COMPATIBLE_IOCTL(VIDIOCGCAP), +COMPATIBLE_IOCTL(VIDIOCGCHAN), +COMPATIBLE_IOCTL(VIDIOCSCHAN), +COMPATIBLE_IOCTL(VIDIOCGPICT), +COMPATIBLE_IOCTL(VIDIOCSPICT), +COMPATIBLE_IOCTL(VIDIOCCAPTURE), +COMPATIBLE_IOCTL(VIDIOCKEY), +COMPATIBLE_IOCTL(VIDIOCGAUDIO), +COMPATIBLE_IOCTL(VIDIOCSAUDIO), +COMPATIBLE_IOCTL(VIDIOCSYNC), +COMPATIBLE_IOCTL(VIDIOCMCAPTURE), +COMPATIBLE_IOCTL(VIDIOCGMBUF), +COMPATIBLE_IOCTL(VIDIOCGUNIT), +COMPATIBLE_IOCTL(VIDIOCGCAPTURE), +COMPATIBLE_IOCTL(VIDIOCSCAPTURE), +/* BTTV specific... */ +COMPATIBLE_IOCTL(_IOW('v', BASE_VIDIOCPRIVATE+0, char [256])), +COMPATIBLE_IOCTL(_IOR('v', BASE_VIDIOCPRIVATE+1, char [256])), +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int)), +COMPATIBLE_IOCTL(_IOW('v' , BASE_VIDIOCPRIVATE+3, char [16])), /* struct bttv_pll_info */ +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+4, int)), +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+5, int)), +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+6, int)), +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+7, int)), +/* Little p (/dev/rtc, /dev/envctrl, etc.) */ +COMPATIBLE_IOCTL(_IOR('p', 20, int[7])), /* RTCGET */ +COMPATIBLE_IOCTL(_IOW('p', 21, int[7])), /* RTCSET */ +COMPATIBLE_IOCTL(RTC_AIE_ON), +COMPATIBLE_IOCTL(RTC_AIE_OFF), +COMPATIBLE_IOCTL(RTC_UIE_ON), +COMPATIBLE_IOCTL(RTC_UIE_OFF), +COMPATIBLE_IOCTL(RTC_PIE_ON), +COMPATIBLE_IOCTL(RTC_PIE_OFF), +COMPATIBLE_IOCTL(RTC_WIE_ON), +COMPATIBLE_IOCTL(RTC_WIE_OFF), +COMPATIBLE_IOCTL(RTC_ALM_SET), +COMPATIBLE_IOCTL(RTC_ALM_READ), +COMPATIBLE_IOCTL(RTC_RD_TIME), +COMPATIBLE_IOCTL(RTC_SET_TIME), +COMPATIBLE_IOCTL(RTC_WKALM_SET), +COMPATIBLE_IOCTL(RTC_WKALM_RD), +/* Little m */ +COMPATIBLE_IOCTL(MTIOCTOP), +/* Socket level stuff */ +COMPATIBLE_IOCTL(FIOSETOWN), +COMPATIBLE_IOCTL(SIOCSPGRP), +COMPATIBLE_IOCTL(FIOGETOWN), +COMPATIBLE_IOCTL(SIOCGPGRP), +COMPATIBLE_IOCTL(SIOCATMARK), +COMPATIBLE_IOCTL(SIOCSIFLINK), +COMPATIBLE_IOCTL(SIOCSIFENCAP), +COMPATIBLE_IOCTL(SIOCGIFENCAP), +COMPATIBLE_IOCTL(SIOCSIFBR), +COMPATIBLE_IOCTL(SIOCGIFBR), +COMPATIBLE_IOCTL(SIOCSARP), +COMPATIBLE_IOCTL(SIOCGARP), +COMPATIBLE_IOCTL(SIOCDARP), +COMPATIBLE_IOCTL(SIOCSRARP), +COMPATIBLE_IOCTL(SIOCGRARP), +COMPATIBLE_IOCTL(SIOCDRARP), +COMPATIBLE_IOCTL(SIOCADDDLCI), +COMPATIBLE_IOCTL(SIOCDELDLCI), +/* SG stuff */ +COMPATIBLE_IOCTL(SG_SET_TIMEOUT), +COMPATIBLE_IOCTL(SG_GET_TIMEOUT), +COMPATIBLE_IOCTL(SG_EMULATED_HOST), +COMPATIBLE_IOCTL(SG_SET_TRANSFORM), +COMPATIBLE_IOCTL(SG_GET_TRANSFORM), +COMPATIBLE_IOCTL(SG_SET_RESERVED_SIZE), +COMPATIBLE_IOCTL(SG_GET_RESERVED_SIZE), +COMPATIBLE_IOCTL(SG_GET_SCSI_ID), +COMPATIBLE_IOCTL(SG_SET_FORCE_LOW_DMA), +COMPATIBLE_IOCTL(SG_GET_LOW_DMA), +COMPATIBLE_IOCTL(SG_SET_FORCE_PACK_ID), +COMPATIBLE_IOCTL(SG_GET_PACK_ID), +COMPATIBLE_IOCTL(SG_GET_NUM_WAITING), +COMPATIBLE_IOCTL(SG_SET_DEBUG), +COMPATIBLE_IOCTL(SG_GET_SG_TABLESIZE), +COMPATIBLE_IOCTL(SG_GET_COMMAND_Q), +COMPATIBLE_IOCTL(SG_SET_COMMAND_Q), +COMPATIBLE_IOCTL(SG_GET_VERSION_NUM), +COMPATIBLE_IOCTL(SG_NEXT_CMD_LEN), +COMPATIBLE_IOCTL(SG_SCSI_RESET), +COMPATIBLE_IOCTL(SG_IO), +COMPATIBLE_IOCTL(SG_GET_REQUEST_TABLE), +COMPATIBLE_IOCTL(SG_SET_KEEP_ORPHAN), +COMPATIBLE_IOCTL(SG_GET_KEEP_ORPHAN), +/* PPP stuff */ +COMPATIBLE_IOCTL(PPPIOCGFLAGS), +COMPATIBLE_IOCTL(PPPIOCSFLAGS), +COMPATIBLE_IOCTL(PPPIOCGASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCSASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCGUNIT), +COMPATIBLE_IOCTL(PPPIOCGRASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCSRASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCGMRU), +COMPATIBLE_IOCTL(PPPIOCSMRU), +COMPATIBLE_IOCTL(PPPIOCSMAXCID), +COMPATIBLE_IOCTL(PPPIOCGXASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCSXASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCXFERUNIT), +COMPATIBLE_IOCTL(PPPIOCGNPMODE), +COMPATIBLE_IOCTL(PPPIOCSNPMODE), +COMPATIBLE_IOCTL(PPPIOCGDEBUG), +COMPATIBLE_IOCTL(PPPIOCSDEBUG), +COMPATIBLE_IOCTL(PPPIOCNEWUNIT), +COMPATIBLE_IOCTL(PPPIOCATTACH), +COMPATIBLE_IOCTL(PPPIOCDETACH), +COMPATIBLE_IOCTL(PPPIOCSMRRU), +COMPATIBLE_IOCTL(PPPIOCCONNECT), +COMPATIBLE_IOCTL(PPPIOCDISCONN), +COMPATIBLE_IOCTL(PPPIOCATTCHAN), +COMPATIBLE_IOCTL(PPPIOCGCHAN), +/* PPPOX */ +COMPATIBLE_IOCTL(PPPOEIOCSFWD), +COMPATIBLE_IOCTL(PPPOEIOCDFWD), +/* CDROM stuff */ +COMPATIBLE_IOCTL(CDROMPAUSE), +COMPATIBLE_IOCTL(CDROMRESUME), +COMPATIBLE_IOCTL(CDROMPLAYMSF), +COMPATIBLE_IOCTL(CDROMPLAYTRKIND), +COMPATIBLE_IOCTL(CDROMREADTOCHDR), +COMPATIBLE_IOCTL(CDROMREADTOCENTRY), +COMPATIBLE_IOCTL(CDROMSTOP), +COMPATIBLE_IOCTL(CDROMSTART), +COMPATIBLE_IOCTL(CDROMEJECT), +COMPATIBLE_IOCTL(CDROMVOLCTRL), +COMPATIBLE_IOCTL(CDROMSUBCHNL), +COMPATIBLE_IOCTL(CDROMEJECT_SW), +COMPATIBLE_IOCTL(CDROMMULTISESSION), +COMPATIBLE_IOCTL(CDROM_GET_MCN), +COMPATIBLE_IOCTL(CDROMRESET), +COMPATIBLE_IOCTL(CDROMVOLREAD), +COMPATIBLE_IOCTL(CDROMSEEK), +COMPATIBLE_IOCTL(CDROMPLAYBLK), +COMPATIBLE_IOCTL(CDROMCLOSETRAY), +COMPATIBLE_IOCTL(CDROM_SET_OPTIONS), +COMPATIBLE_IOCTL(CDROM_CLEAR_OPTIONS), +COMPATIBLE_IOCTL(CDROM_SELECT_SPEED), +COMPATIBLE_IOCTL(CDROM_SELECT_DISC), +COMPATIBLE_IOCTL(CDROM_MEDIA_CHANGED), +COMPATIBLE_IOCTL(CDROM_DRIVE_STATUS), +COMPATIBLE_IOCTL(CDROM_DISC_STATUS), +COMPATIBLE_IOCTL(CDROM_CHANGER_NSLOTS), +COMPATIBLE_IOCTL(CDROM_LOCKDOOR), +COMPATIBLE_IOCTL(CDROM_DEBUG), +COMPATIBLE_IOCTL(CDROM_GET_CAPABILITY), +/* Big L */ +COMPATIBLE_IOCTL(LOOP_SET_FD), +COMPATIBLE_IOCTL(LOOP_CLR_FD), +/* Big Q for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_SEQ_RESET), +COMPATIBLE_IOCTL(SNDCTL_SEQ_SYNC), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_INFO), +COMPATIBLE_IOCTL(SNDCTL_SEQ_CTRLRATE), +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETOUTCOUNT), +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETINCOUNT), +COMPATIBLE_IOCTL(SNDCTL_SEQ_PERCMODE), +COMPATIBLE_IOCTL(SNDCTL_FM_LOAD_INSTR), +COMPATIBLE_IOCTL(SNDCTL_SEQ_TESTMIDI), +COMPATIBLE_IOCTL(SNDCTL_SEQ_RESETSAMPLES), +COMPATIBLE_IOCTL(SNDCTL_SEQ_NRSYNTHS), +COMPATIBLE_IOCTL(SNDCTL_SEQ_NRMIDIS), +COMPATIBLE_IOCTL(SNDCTL_MIDI_INFO), +COMPATIBLE_IOCTL(SNDCTL_SEQ_THRESHOLD), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_MEMAVL), +COMPATIBLE_IOCTL(SNDCTL_FM_4OP_ENABLE), +COMPATIBLE_IOCTL(SNDCTL_SEQ_PANIC), +COMPATIBLE_IOCTL(SNDCTL_SEQ_OUTOFBAND), +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETTIME), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_ID), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_CONTROL), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_REMOVESAMPLE), +/* Big T for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_TMR_TIMEBASE), +COMPATIBLE_IOCTL(SNDCTL_TMR_START), +COMPATIBLE_IOCTL(SNDCTL_TMR_STOP), +COMPATIBLE_IOCTL(SNDCTL_TMR_CONTINUE), +COMPATIBLE_IOCTL(SNDCTL_TMR_TEMPO), +COMPATIBLE_IOCTL(SNDCTL_TMR_SOURCE), +COMPATIBLE_IOCTL(SNDCTL_TMR_METRONOME), +COMPATIBLE_IOCTL(SNDCTL_TMR_SELECT), +/* Little m for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_MIDI_PRETIME), +COMPATIBLE_IOCTL(SNDCTL_MIDI_MPUMODE), +COMPATIBLE_IOCTL(SNDCTL_MIDI_MPUCMD), +/* Big P for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_DSP_RESET), +COMPATIBLE_IOCTL(SNDCTL_DSP_SYNC), +COMPATIBLE_IOCTL(SNDCTL_DSP_SPEED), +COMPATIBLE_IOCTL(SNDCTL_DSP_STEREO), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETBLKSIZE), +COMPATIBLE_IOCTL(SNDCTL_DSP_CHANNELS), +COMPATIBLE_IOCTL(SOUND_PCM_WRITE_FILTER), +COMPATIBLE_IOCTL(SNDCTL_DSP_POST), +COMPATIBLE_IOCTL(SNDCTL_DSP_SUBDIVIDE), +COMPATIBLE_IOCTL(SNDCTL_DSP_SETFRAGMENT), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETFMTS), +COMPATIBLE_IOCTL(SNDCTL_DSP_SETFMT), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETOSPACE), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETISPACE), +COMPATIBLE_IOCTL(SNDCTL_DSP_NONBLOCK), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETCAPS), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETTRIGGER), +COMPATIBLE_IOCTL(SNDCTL_DSP_SETTRIGGER), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETIPTR), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETOPTR), +/* SNDCTL_DSP_MAPINBUF, XXX needs translation */ +/* SNDCTL_DSP_MAPOUTBUF, XXX needs translation */ +COMPATIBLE_IOCTL(SNDCTL_DSP_SETSYNCRO), +COMPATIBLE_IOCTL(SNDCTL_DSP_SETDUPLEX), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETODELAY), +COMPATIBLE_IOCTL(SNDCTL_DSP_PROFILE), +COMPATIBLE_IOCTL(SOUND_PCM_READ_RATE), +COMPATIBLE_IOCTL(SOUND_PCM_READ_CHANNELS), +COMPATIBLE_IOCTL(SOUND_PCM_READ_BITS), +COMPATIBLE_IOCTL(SOUND_PCM_READ_FILTER), +/* Big C for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_COPR_RESET), +COMPATIBLE_IOCTL(SNDCTL_COPR_LOAD), +COMPATIBLE_IOCTL(SNDCTL_COPR_RDATA), +COMPATIBLE_IOCTL(SNDCTL_COPR_RCODE), +COMPATIBLE_IOCTL(SNDCTL_COPR_WDATA), +COMPATIBLE_IOCTL(SNDCTL_COPR_WCODE), +COMPATIBLE_IOCTL(SNDCTL_COPR_RUN), +COMPATIBLE_IOCTL(SNDCTL_COPR_HALT), +COMPATIBLE_IOCTL(SNDCTL_COPR_SENDMSG), +COMPATIBLE_IOCTL(SNDCTL_COPR_RCVMSG), +/* Big M for sound/OSS */ +COMPATIBLE_IOCTL(SOUND_MIXER_READ_VOLUME), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_BASS), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_TREBLE), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_SYNTH), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_PCM), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_SPEAKER), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_MIC), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_CD), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_IMIX), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_ALTPCM), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECLEV), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_IGAIN), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_OGAIN), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE1), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE2), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE3), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL1)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL2)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL3)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_PHONEIN)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_PHONEOUT)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_VIDEO)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_RADIO)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_MONITOR)), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_MUTE), +/* SOUND_MIXER_READ_ENHANCE, same value as READ_MUTE */ +/* SOUND_MIXER_READ_LOUD, same value as READ_MUTE */ +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECSRC), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_DEVMASK), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECMASK), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_STEREODEVS), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_CAPS), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_VOLUME), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_BASS), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_TREBLE), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_SYNTH), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_PCM), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_SPEAKER), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_MIC), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_CD), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_IMIX), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_ALTPCM), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_RECLEV), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_IGAIN), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_OGAIN), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE1), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE2), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE3), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL1)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL2)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL3)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_PHONEIN)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_PHONEOUT)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_VIDEO)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_RADIO)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_MONITOR)), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_MUTE), +/* SOUND_MIXER_WRITE_ENHANCE, same value as WRITE_MUTE */ +/* SOUND_MIXER_WRITE_LOUD, same value as WRITE_MUTE */ +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_RECSRC), +COMPATIBLE_IOCTL(SOUND_MIXER_INFO), +COMPATIBLE_IOCTL(SOUND_OLD_MIXER_INFO), +COMPATIBLE_IOCTL(SOUND_MIXER_ACCESS), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE1), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE2), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE3), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE4), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE5), +COMPATIBLE_IOCTL(SOUND_MIXER_GETLEVELS), +COMPATIBLE_IOCTL(SOUND_MIXER_SETLEVELS), +COMPATIBLE_IOCTL(OSS_GETVERSION), +/* AUTOFS */ +COMPATIBLE_IOCTL(AUTOFS_IOC_READY), +COMPATIBLE_IOCTL(AUTOFS_IOC_FAIL), +COMPATIBLE_IOCTL(AUTOFS_IOC_CATATONIC), +COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER), +COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE), +/* DEVFS */ +COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV), +COMPATIBLE_IOCTL(DEVFSDIOC_SET_EVENT_MASK), +COMPATIBLE_IOCTL(DEVFSDIOC_RELEASE_EVENT_QUEUE), +COMPATIBLE_IOCTL(DEVFSDIOC_SET_DEBUG_MASK), +/* Raw devices */ +COMPATIBLE_IOCTL(RAW_SETBIND), +COMPATIBLE_IOCTL(RAW_GETBIND), +/* SMB ioctls which do not need any translations */ +COMPATIBLE_IOCTL(SMB_IOC_NEWCONN), +/* Little a */ +COMPATIBLE_IOCTL(ATMSIGD_CTRL), +COMPATIBLE_IOCTL(ATMARPD_CTRL), +COMPATIBLE_IOCTL(ATMLEC_CTRL), +COMPATIBLE_IOCTL(ATMLEC_MCAST), +COMPATIBLE_IOCTL(ATMLEC_DATA), +COMPATIBLE_IOCTL(ATM_SETSC), +COMPATIBLE_IOCTL(SIOCSIFATMTCP), +COMPATIBLE_IOCTL(SIOCMKCLIP), +COMPATIBLE_IOCTL(ATMARP_MKIP), +COMPATIBLE_IOCTL(ATMARP_SETENTRY), +COMPATIBLE_IOCTL(ATMARP_ENCAP), +COMPATIBLE_IOCTL(ATMTCP_CREATE), +COMPATIBLE_IOCTL(ATMTCP_REMOVE), +COMPATIBLE_IOCTL(ATMMPC_CTRL), +COMPATIBLE_IOCTL(ATMMPC_DATA), +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +/* 0xfe - lvm */ +COMPATIBLE_IOCTL(VG_SET_EXTENDABLE), +COMPATIBLE_IOCTL(VG_STATUS_GET_COUNT), +COMPATIBLE_IOCTL(VG_STATUS_GET_NAMELIST), +COMPATIBLE_IOCTL(VG_REMOVE), +COMPATIBLE_IOCTL(VG_RENAME), +COMPATIBLE_IOCTL(VG_REDUCE), +COMPATIBLE_IOCTL(PE_LOCK_UNLOCK), +COMPATIBLE_IOCTL(PV_FLUSH), +COMPATIBLE_IOCTL(LVM_LOCK_LVM), +COMPATIBLE_IOCTL(LVM_GET_IOP_VERSION), +#ifdef LVM_TOTAL_RESET +COMPATIBLE_IOCTL(LVM_RESET), +#endif +COMPATIBLE_IOCTL(LV_SET_ACCESS), +COMPATIBLE_IOCTL(LV_SET_STATUS), +COMPATIBLE_IOCTL(LV_SET_ALLOCATION), +COMPATIBLE_IOCTL(LE_REMAP), +COMPATIBLE_IOCTL(LV_BMAP), +COMPATIBLE_IOCTL(LV_SNAPSHOT_USE_RATE), +#endif /* LVM */ +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +COMPATIBLE_IOCTL(DRM_IOCTL_GET_MAGIC), +COMPATIBLE_IOCTL(DRM_IOCTL_IRQ_BUSID), +COMPATIBLE_IOCTL(DRM_IOCTL_AUTH_MAGIC), +COMPATIBLE_IOCTL(DRM_IOCTL_BLOCK), +COMPATIBLE_IOCTL(DRM_IOCTL_UNBLOCK), +COMPATIBLE_IOCTL(DRM_IOCTL_CONTROL), +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_BUFS), +COMPATIBLE_IOCTL(DRM_IOCTL_MARK_BUFS), +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_RM_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_MOD_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_GET_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_SWITCH_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_NEW_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_DRAW), +COMPATIBLE_IOCTL(DRM_IOCTL_RM_DRAW), +COMPATIBLE_IOCTL(DRM_IOCTL_LOCK), +OMPATIBLE_IOCTL(DRM_IOCTL_UNLOCK), +COMPATIBLE_IOCTL(DRM_IOCTL_FINISH), +#endif /* DRM */ +/* elevator */ +COMPATIBLE_IOCTL(BLKELVGET), +COMPATIBLE_IOCTL(BLKELVSET), +/* Big W */ +/* WIOC_GETSUPPORT not yet implemented -E */ +COMPATIBLE_IOCTL(WDIOC_GETSTATUS), +COMPATIBLE_IOCTL(WDIOC_GETBOOTSTATUS), +COMPATIBLE_IOCTL(WDIOC_GETTEMP), +COMPATIBLE_IOCTL(WDIOC_SETOPTIONS), +COMPATIBLE_IOCTL(WDIOC_KEEPALIVE), +COMPATIBLE_IOCTL(PCIIOC_CONTROLLER), +COMPATIBLE_IOCTL(PCIIOC_MMAP_IS_IO), +COMPATIBLE_IOCTL(PCIIOC_MMAP_IS_MEM), +COMPATIBLE_IOCTL(PCIIOC_WRITE_COMBINE), + + HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32), + HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf), + HANDLE_IOCTL(SIOCGIFFLAGS, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFFLAGS, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFMETRIC, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFMETRIC, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFMTU, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFMTU, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFMEM, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFMEM, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFHWADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFHWADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCADDMULTI, dev_ifsioc), + HANDLE_IOCTL(SIOCDELMULTI, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFINDEX, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFMAP, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFMAP, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFBRDADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFBRDADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFDSTADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFDSTADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFNETMASK, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFNETMASK, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFPFLAGS, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFPFLAGS, dev_ifsioc), + HANDLE_IOCTL(SIOCGPPPSTATS, dev_ifsioc), + HANDLE_IOCTL(SIOCGPPPCSTATS, dev_ifsioc), + HANDLE_IOCTL(SIOCGPPPVER, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFTXQLEN, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFTXQLEN, dev_ifsioc), + HANDLE_IOCTL(SIOCADDRT, routing_ioctl), + HANDLE_IOCTL(SIOCDELRT, routing_ioctl), + /* Note SIOCRTMSG is no longer, so this is safe and + * the user would have seen just an -EINVAL anyways. */ + HANDLE_IOCTL(SIOCRTMSG, ret_einval), + HANDLE_IOCTL(SIOCGSTAMP, do_siocgstamp), + HANDLE_IOCTL(HDIO_GETGEO, hdio_getgeo), + HANDLE_IOCTL(BLKRAGET, w_long), + HANDLE_IOCTL(BLKGETSIZE, w_long), + HANDLE_IOCTL(0x1260, broken_blkgetsize), + HANDLE_IOCTL(BLKFRAGET, w_long), + HANDLE_IOCTL(BLKSECTGET, w_long), + HANDLE_IOCTL(BLKPG, blkpg_ioctl_trans), + HANDLE_IOCTL(HDIO_GET_KEEPSETTINGS, hdio_ioctl_trans), + HANDLE_IOCTL(HDIO_GET_UNMASKINTR, hdio_ioctl_trans), + HANDLE_IOCTL(HDIO_GET_DMA, hdio_ioctl_trans), + HANDLE_IOCTL(HDIO_GET_32BIT, hdio_ioctl_trans), + HANDLE_IOCTL(HDIO_GET_MULTCOUNT, hdio_ioctl_trans), + HANDLE_IOCTL(HDIO_GET_NOWERR, hdio_ioctl_trans), + HANDLE_IOCTL(HDIO_GET_NICE, hdio_ioctl_trans), + HANDLE_IOCTL(FDSETPRM32, fd_ioctl_trans), + HANDLE_IOCTL(FDDEFPRM32, fd_ioctl_trans), + HANDLE_IOCTL(FDGETPRM32, fd_ioctl_trans), + HANDLE_IOCTL(FDSETDRVPRM32, fd_ioctl_trans), + HANDLE_IOCTL(FDGETDRVPRM32, fd_ioctl_trans), + HANDLE_IOCTL(FDGETDRVSTAT32, fd_ioctl_trans), + HANDLE_IOCTL(FDPOLLDRVSTAT32, fd_ioctl_trans), + HANDLE_IOCTL(FDGETFDCSTAT32, fd_ioctl_trans), + HANDLE_IOCTL(FDWERRORGET32, fd_ioctl_trans), + HANDLE_IOCTL(PPPIOCGIDLE32, ppp_ioctl_trans), + HANDLE_IOCTL(PPPIOCSCOMPRESS32, ppp_ioctl_trans), + HANDLE_IOCTL(MTIOCGET32, mt_ioctl_trans), + HANDLE_IOCTL(MTIOCPOS32, mt_ioctl_trans), + HANDLE_IOCTL(MTIOCGETCONFIG32, mt_ioctl_trans), + HANDLE_IOCTL(MTIOCSETCONFIG32, mt_ioctl_trans), + HANDLE_IOCTL(CDROMREADMODE2, cdrom_ioctl_trans), + HANDLE_IOCTL(CDROMREADMODE1, cdrom_ioctl_trans), + HANDLE_IOCTL(CDROMREADRAW, cdrom_ioctl_trans), + HANDLE_IOCTL(CDROMREADCOOKED, cdrom_ioctl_trans), + HANDLE_IOCTL(CDROMREADAUDIO, cdrom_ioctl_trans), + HANDLE_IOCTL(CDROMREADALL, cdrom_ioctl_trans), + HANDLE_IOCTL(CDROM_SEND_PACKET, cdrom_ioctl_trans), + HANDLE_IOCTL(LOOP_SET_STATUS, loop_status), + HANDLE_IOCTL(LOOP_GET_STATUS, loop_status), + HANDLE_IOCTL(AUTOFS_IOC_SETTIMEOUT32, ioc_settimeout), +#ifdef CONFIG_VT + HANDLE_IOCTL(PIO_FONTX, do_fontx_ioctl), + HANDLE_IOCTL(GIO_FONTX, do_fontx_ioctl), + HANDLE_IOCTL(PIO_UNIMAP, do_unimap_ioctl), + HANDLE_IOCTL(GIO_UNIMAP, do_unimap_ioctl), + HANDLE_IOCTL(KDFONTOP, do_kdfontop_ioctl), + HANDLE_IOCTL(FBIOGET_FSCREENINFO, do_fbioget_fscreeninfo_ioctl), + HANDLE_IOCTL(FBIOGETCMAP, do_fbiogetcmap_ioctl), + HANDLE_IOCTL(FBIOPUTCMAP, do_fbioputcmap_ioctl), +#endif + HANDLE_IOCTL(EXT2_IOC32_GETFLAGS, do_ext2_ioctl), + HANDLE_IOCTL(EXT2_IOC32_SETFLAGS, do_ext2_ioctl), + HANDLE_IOCTL(EXT2_IOC32_GETVERSION, do_ext2_ioctl), + HANDLE_IOCTL(EXT2_IOC32_SETVERSION, do_ext2_ioctl), + HANDLE_IOCTL(VIDIOCGTUNER32, do_video_ioctl), + HANDLE_IOCTL(VIDIOCSTUNER32, do_video_ioctl), + HANDLE_IOCTL(VIDIOCGWIN32, do_video_ioctl), + HANDLE_IOCTL(VIDIOCSWIN32, do_video_ioctl), + HANDLE_IOCTL(VIDIOCGFBUF32, do_video_ioctl), + HANDLE_IOCTL(VIDIOCSFBUF32, do_video_ioctl), + HANDLE_IOCTL(VIDIOCGFREQ32, do_video_ioctl), + HANDLE_IOCTL(VIDIOCSFREQ32, do_video_ioctl), + /* One SMB ioctl needs translations. */ + HANDLE_IOCTL(SMB_IOC_GETMOUNTUID_32, do_smb_getmountuid), + HANDLE_IOCTL(ATM_GETLINKRATE32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETNAMES32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETTYPE32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETESI32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETADDR32, do_atm_ioctl), + HANDLE_IOCTL(ATM_RSTADDR32, do_atm_ioctl), + HANDLE_IOCTL(ATM_ADDADDR32, do_atm_ioctl), + HANDLE_IOCTL(ATM_DELADDR32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETCIRANGE32, do_atm_ioctl), + HANDLE_IOCTL(ATM_SETCIRANGE32, do_atm_ioctl), + HANDLE_IOCTL(ATM_SETESI32, do_atm_ioctl), + HANDLE_IOCTL(ATM_SETESIF32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETSTAT32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETSTATZ32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETLOOP32, do_atm_ioctl), + HANDLE_IOCTL(ATM_SETLOOP32, do_atm_ioctl), + HANDLE_IOCTL(ATM_QUERYLOOP32, do_atm_ioctl), + HANDLE_IOCTL(SONET_GETSTAT, do_atm_ioctl), + HANDLE_IOCTL(SONET_GETSTATZ, do_atm_ioctl), + HANDLE_IOCTL(SONET_GETDIAG, do_atm_ioctl), + HANDLE_IOCTL(SONET_SETDIAG, do_atm_ioctl), + HANDLE_IOCTL(SONET_CLRDIAG, do_atm_ioctl), + HANDLE_IOCTL(SONET_SETFRAMING, do_atm_ioctl), + HANDLE_IOCTL(SONET_GETFRAMING, do_atm_ioctl), + HANDLE_IOCTL(SONET_GETFRSENSE, do_atm_ioctl) +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) + ,HANDLE_IOCTL(VG_STATUS, do_lvm_ioctl), + HANDLE_IOCTL(VG_CREATE, do_lvm_ioctl), + HANDLE_IOCTL(VG_EXTEND, do_lvm_ioctl), + HANDLE_IOCTL(LV_CREATE, do_lvm_ioctl), + HANDLE_IOCTL(LV_REMOVE, do_lvm_ioctl), + HANDLE_IOCTL(LV_EXTEND, do_lvm_ioctl), + HANDLE_IOCTL(LV_REDUCE, do_lvm_ioctl), + HANDLE_IOCTL(LV_RENAME, do_lvm_ioctl), + HANDLE_IOCTL(LV_STATUS_BYNAME, do_lvm_ioctl), + HANDLE_IOCTL(LV_STATUS_BYINDEX, do_lvm_ioctl), + HANDLE_IOCTL(PV_CHANGE, do_lvm_ioctl), + HANDLE_IOCTL(PV_STATUS, do_lvm_ioctl) +#endif /* LVM */ +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) + ,HANDLE_IOCTL(DRM32_IOCTL_VERSION, drm32_version), + HANDLE_IOCTL(DRM32_IOCTL_GET_UNIQUE, drm32_getsetunique), + HANDLE_IOCTL(DRM32_IOCTL_SET_UNIQUE, drm32_getsetunique), + HANDLE_IOCTL(DRM32_IOCTL_ADD_MAP, drm32_addmap), + HANDLE_IOCTL(DRM32_IOCTL_INFO_BUFS, drm32_info_bufs), + HANDLE_IOCTL(DRM32_IOCTL_FREE_BUFS, drm32_free_bufs), + HANDLE_IOCTL(DRM32_IOCTL_MAP_BUFS, drm32_map_bufs), + HANDLE_IOCTL(DRM32_IOCTL_DMA, drm32_dma), + HANDLE_IOCTL(DRM32_IOCTL_RES_CTX, drm32_res_ctx) +#endif /* DRM */ +}; + +unsigned long ioctl32_hash_table[1024]; + +static inline unsigned long ioctl32_hash(unsigned long cmd) +{ + return ((cmd >> 6) ^ (cmd >> 4) ^ cmd) & 0x3ff; +} + +static void ioctl32_insert_translation(struct ioctl_trans *trans) +{ + unsigned long hash; + struct ioctl_trans *t; + + hash = ioctl32_hash (trans->cmd); + if (!ioctl32_hash_table[hash]) + ioctl32_hash_table[hash] = (long)trans; + else { + t = (struct ioctl_trans *)ioctl32_hash_table[hash]; + while (t->next) + t = (struct ioctl_trans *)(long)t->next; + trans->next = 0; + t->next = (long)trans; + } +} + +static int __init init_sys32_ioctl(void) +{ + int i, size = sizeof(ioctl_translations) / sizeof(struct ioctl_trans); + for (i=0; i < size ;i++) + ioctl32_insert_translation(&ioctl_translations[i]); + return 0; +} + +__initcall(init_sys32_ioctl); + +static struct ioctl_trans *additional_ioctls; + +/* Always call these with kernel lock held! */ + +int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)) +{ + int i; + if (!additional_ioctls) { + additional_ioctls = module_map(PAGE_SIZE); + if (!additional_ioctls) + return -ENOMEM; + memset(additional_ioctls, 0, PAGE_SIZE); + } + for (i = 0; i < PAGE_SIZE/sizeof(struct ioctl_trans); i++) + if (!additional_ioctls[i].cmd) + break; + if (i == PAGE_SIZE/sizeof(struct ioctl_trans)) + return -ENOMEM; + additional_ioctls[i].cmd = cmd; + if (!handler) + additional_ioctls[i].handler = (long)sys_ioctl; + else + additional_ioctls[i].handler = (long)handler; + ioctl32_insert_translation(&additional_ioctls[i]); + return 0; +} + +int unregister_ioctl32_conversion(unsigned int cmd) +{ + unsigned long hash = ioctl32_hash(cmd); + struct ioctl_trans *t, *t1; + + t = (struct ioctl_trans *)ioctl32_hash_table[hash]; + if (!t) return -EINVAL; + if (t->cmd == cmd && t >= additional_ioctls && + (unsigned long)t < ((unsigned long)additional_ioctls) + PAGE_SIZE) { + ioctl32_hash_table[hash] = t->next; + t->cmd = 0; + return 0; + } else while (t->next) { + t1 = (struct ioctl_trans *)t->next; + if (t1->cmd == cmd && t1 >= additional_ioctls && + (unsigned long)t1 < ((unsigned long)additional_ioctls) + PAGE_SIZE) { + t1->cmd = 0; + t->next = t1->next; + return 0; + } + t = t1; + } + return -EINVAL; +} + +asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct file * filp; + int error = -EBADF; + int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp); + struct ioctl_trans *t; + + filp = fget(fd); + if(!filp) + goto out2; + + if (!filp->f_op || !filp->f_op->ioctl) { + error = sys_ioctl (fd, cmd, arg); + goto out; + } + + t = (struct ioctl_trans *)ioctl32_hash_table [ioctl32_hash (cmd)]; + + while (t && t->cmd != cmd) + t = (struct ioctl_trans *)t->next; + if (t) { + handler = (void *)t->handler; + error = handler(fd, cmd, arg, filp); + } else { + static int count = 0; + if (++count <= 20) + printk("sys32_ioctl(%s:%d): Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + current->comm, current->pid, + (int)fd, (unsigned int)cmd, (unsigned int)arg); + error = -EINVAL; + } +out: + fput(filp); +out2: + return error; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/irq.c linux.ac/arch/ppc64/kernel/irq.c --- linux.vanilla/arch/ppc64/kernel/irq.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/irq.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,930 @@ +/* + * + * + * arch/ppc/kernel/irq.c + * + * Derived from arch/i386/kernel/irq.c + * Copyright (C) 1992 Linus Torvalds + * Adapted from arch/i386 by Gary Thomas + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Updated and modified by Cort Dougan (cort@cs.nmt.edu) + * Copyright (C) 1996 Cort Dougan + * Adapted for Power Macintosh by Paul Mackerras + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * 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 file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + * + * The MPC8xx has an interrupt mask in the SIU. If a bit is set, the + * interrupt is _enabled_. As expected, IRQ0 is bit 0 in the 32-bit + * mask register (of which only 16 are defined), hence the weird shifting + * and compliment of the cached_irq_mask. I want to be able to stuff + * this right into the SIU SMASK register. + * Many of the prep/chrp functions are conditional compiled on CONFIG_8xx + * to reduce code space and undefined function references. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "local_irq.h" + +atomic_t ipi_recv; +atomic_t ipi_sent; +void enable_irq(unsigned int irq_nr); +void disable_irq(unsigned int irq_nr); + +#ifdef CONFIG_SMP +extern void iSeries_smp_message_recv( struct pt_regs * ); +#endif + +volatile unsigned char *chrp_int_ack_special; +static void register_irq_proc (unsigned int irq); + +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = + { [0 ... NR_IRQS-1] = { 0, NULL, NULL, 0, SPIN_LOCK_UNLOCKED}}; + +int ppc_spurious_interrupts = 0; +struct irqaction *ppc_irq_action[NR_IRQS]; +unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; +unsigned long ppc_lost_interrupts[NR_MASK_WORDS]; +atomic_t ppc_n_lost_interrupts; +unsigned long lpEvent_count = 0; +#ifdef CONFIG_XMON +extern void xmon(struct pt_regs *regs); +extern int xmon_bpt(struct pt_regs *regs); +extern int xmon_sstep(struct pt_regs *regs); +extern int xmon_iabr_match(struct pt_regs *regs); +extern int xmon_dabr_match(struct pt_regs *regs); +extern void (*xmon_fault_handler)(struct pt_regs *regs); +#endif +#ifdef CONFIG_XMON +extern void (*debugger)(struct pt_regs *regs); +extern int (*debugger_bpt)(struct pt_regs *regs); +extern int (*debugger_sstep)(struct pt_regs *regs); +extern int (*debugger_iabr_match)(struct pt_regs *regs); +extern int (*debugger_dabr_match)(struct pt_regs *regs); +extern void (*debugger_fault_handler)(struct pt_regs *regs); +#endif + +/* nasty hack for shared irq's since we need to do kmalloc calls but + * can't very early in the boot when we need to do a request irq. + * this needs to be removed. + * -- Cort + */ +#define IRQ_KMALLOC_ENTRIES 8 +static int cache_bitmask = 0; +static struct irqaction malloc_cache[IRQ_KMALLOC_ENTRIES]; +extern int mem_init_done; + +void *irq_kmalloc(size_t size, int pri) +{ + unsigned int i; + if ( mem_init_done ) + return kmalloc(size,pri); + for ( i = 0; i < IRQ_KMALLOC_ENTRIES ; i++ ) + if ( ! ( cache_bitmask & (1<flags & SA_SAMPLE_RANDOM) { + /* + * This function might sleep, we want to call it first, + * outside of the atomic block. + * Yes, this might clear the entropy pool if the wrong + * driver is attempted to be loaded, without actually + * installing a new handler, but is this really a problem, + * only the sysadmin is able to do this. + */ + rand_initialize_irq(irq); + } + + /* + * The following block of code has to be executed atomically + */ + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) { + spin_unlock_irqrestore(&desc->lock,flags); + return -EBUSY; + } + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + *p = new; + + if (!shared) { + desc->depth = 0; + desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING); + unmask_irq(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + + register_irq_proc(irq); + return 0; +} + +/* This could be promoted to a real free_irq() ... */ +static int +do_free_irq(int irq, void* dev_id) +{ + irq_desc_t *desc; + struct irqaction **p; + unsigned long flags; + + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + for (;;) { + struct irqaction * action = *p; + if (action) { + struct irqaction **pp = p; + p = &action->next; + if (action->dev_id != dev_id) + continue; + + /* Found it - now remove it from the list of entries */ + *pp = action->next; + if (!desc->action) { + desc->status |= IRQ_DISABLED; + mask_irq(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + +#ifdef CONFIG_SMP + /* Wait to make sure it's not being used on another CPU */ + while (desc->status & IRQ_INPROGRESS) + barrier(); +#endif + irq_kfree(action); + return 0; + } + printk("Trying to free free IRQ%d\n",irq); + spin_unlock_irqrestore(&desc->lock,flags); + break; + } + return -ENOENT; +} + +int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, void *dev_id) +{ + struct irqaction *action; + int retval; + + if (irq >= NR_IRQS) + return -EINVAL; + if (!handler) + /* We could implement really free_irq() instead of that... */ + return do_free_irq(irq, dev_id); + + action = (struct irqaction *) + irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) { + printk(KERN_ERR "irq_kmalloc() failed for irq %d !\n", irq); + return -ENOMEM; + } + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->dev_id = dev_id; + action->next = NULL; + + retval = setup_irq(irq, action); + if (retval) + kfree(action); + + return 0; +} + +void free_irq(unsigned int irq, void *dev_id) +{ + request_irq(irq, NULL, 0, NULL, dev_id); +} + +/* + * Generic enable/disable code: this just calls + * down into the PIC-specific version for the actual + * hardware disable after having gotten the irq + * controller lock. + */ + +/** + * disable_irq_nosync - disable an irq without waiting + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables of an interrupt + * stack. Unlike disable_irq(), this function does not ensure existing + * instances of the IRQ handler have completed before returning. + * + * This function may be called from IRQ context. + */ + + void disable_irq_nosync(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->depth++) { + if (!(desc->status & IRQ_PER_CPU)) + desc->status |= IRQ_DISABLED; + mask_irq(irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/** + * disable_irq - disable an irq and wait for completion + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables of an interrupt + * stack. That is for two disables you need two enables. This + * function waits for any pending IRQ handlers for this interrupt + * to complete before returning. If you use this function while + * holding a resource the IRQ handler may need you will deadlock. + * + * This function may be called - with care - from IRQ context. + */ + +void disable_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + + if (!local_irq_count(smp_processor_id())) { + do { + barrier(); + } while (irq_desc[irq].status & IRQ_INPROGRESS); + } +} + +/** + * enable_irq - enable interrupt handling on an irq + * @irq: Interrupt to enable + * + * Re-enables the processing of interrupts on this IRQ line + * providing no disable_irq calls are now in effect. + * + * This function may be called from IRQ context. + */ + +void enable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + switch (desc->depth) { + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + desc->status = status; + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status = status | IRQ_REPLAY; + hw_resend_irq(desc->handler,irq); + } + unmask_irq(irq); + /* fall-through */ + } + default: + desc->depth--; + break; + case 0: + printk("enable_irq(%u) unbalanced\n", irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +int get_irq_list(char *buf) +{ + int i, len = 0, j; + struct irqaction * action; + + len += sprintf(buf+len, " "); + for (j=0; jhandler ) + continue; + len += sprintf(buf+len, "%3d: ", i); +#ifdef CONFIG_SMP + for (j = 0; j < smp_num_cpus; j++) + len += sprintf(buf+len, "%10u ", + kstat.irqs[cpu_logical_map(j)][i]); +#else + len += sprintf(buf+len, "%10u ", kstat_irqs(i)); +#endif /* CONFIG_SMP */ + if ( irq_desc[i].handler ) + len += sprintf(buf+len, " %s ", irq_desc[i].handler->typename ); + else + len += sprintf(buf+len, " None "); + len += sprintf(buf+len, "%s", (irq_desc[i].status & IRQ_LEVEL) ? "Level " : "Edge "); + len += sprintf(buf+len, " %s",action->name); + for (action=action->next; action; action = action->next) { + len += sprintf(buf+len, ", %s", action->name); + } + len += sprintf(buf+len, "\n"); + } +#ifdef CONFIG_SMP + /* should this be per processor send/receive? */ + len += sprintf(buf+len, "IPI (recv/sent): %10u/%u\n", + atomic_read(&ipi_recv), atomic_read(&ipi_sent)); +#endif + len += sprintf(buf+len, "BAD: %10u\n", ppc_spurious_interrupts); + return len; +} + +static inline void +handle_irq_event(int irq, struct pt_regs *regs, struct irqaction *action) +{ + int status = 0; + + if (!(action->flags & SA_INTERRUPT)) + __sti(); + + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); +} + +/* + * Eventually, this should take an array of interrupts and an array size + * so it can dispatch multiple interrupts. + */ +void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq) +{ + int status; + struct irqaction *action; + int cpu = smp_processor_id(); + irq_desc_t *desc = irq_desc + irq; + + kstat.irqs[cpu][irq]++; + spin_lock(&desc->lock); + ack_irq(irq); + /* + REPLAY is when Linux resends an IRQ that was dropped earlier + WAITING is used by probe to mark irqs that are being tested + */ + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + if (!(status & IRQ_PER_CPU)) + status |= IRQ_PENDING; /* we _want_ to handle it */ + + /* + * If the IRQ is disabled for whatever reason, we cannot + * use the action we have. + */ + action = NULL; + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + action = desc->action; + if (!action || !action->handler) { + ppc_spurious_interrupts++; + printk(KERN_DEBUG "Unhandled interrupt %x, disabled\n", irq); + /* We can't call disable_irq here, it would deadlock */ + if (!desc->depth) + desc->depth = 1; + desc->status |= IRQ_DISABLED; + /* This is not a real spurrious interrupt, we + * have to eoi it, so we jump to out + */ + mask_irq(irq); + goto out; + } + status &= ~IRQ_PENDING; /* we commit to handling */ + if (!(status & IRQ_PER_CPU)) + status |= IRQ_INPROGRESS; /* we are handling it */ + } + desc->status = status; + + /* + * If there is no IRQ handler or it was disabled, exit early. + Since we set PENDING, if another processor is handling + a different instance of this same irq, the other processor + will take care of it. + */ + if (!action) + goto out; + + + /* + * Edge triggered interrupts need to remember + * pending events. + * This applies to any hw interrupts that allow a second + * instance of the same irq to arrive while we are in do_IRQ + * or in the handler. But the code here only handles the _second_ + * instance of the irq, not the third or fourth. So it is mostly + * useful for irq hardware that does not mask cleanly in an + * SMP environment. + */ + for (;;) { + spin_unlock(&desc->lock); + handle_irq_event(irq, regs, action); + spin_lock(&desc->lock); + + if (!(desc->status & IRQ_PENDING)) + break; + desc->status &= ~IRQ_PENDING; + } + desc->status &= ~IRQ_INPROGRESS; +out: + /* + * The ->end() handler has to deal with interrupts which got + * disabled while the handler was running. + */ + if (irq_desc[irq].handler) { + if (irq_desc[irq].handler->end) + irq_desc[irq].handler->end(irq); + else if (irq_desc[irq].handler->enable) + irq_desc[irq].handler->enable(irq); + } + spin_unlock(&desc->lock); +} + +int do_IRQ(struct pt_regs *regs, int isfake) +{ + int cpu = smp_processor_id(); + int irq; + struct Paca * paca; + struct ItLpQueue * lpq; + + /* if(cpu) udbg_printf("Entering do_IRQ\n"); */ + + irq_enter(cpu); + + if ( _machine != _MACH_iSeries ) { + + /* every arch is required to have a get_irq -- Cort */ + irq = ppc_md.get_irq( regs ); + + if ( irq < 0 ) + { + /* -2 means ignore, already handled */ + if (irq != -2) + { + printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", + irq, regs->nip); + ppc_spurious_interrupts++; + } + goto out; + } + ppc_irq_dispatch_handler( regs, irq ); + if (ppc_md.post_irq) + ppc_md.post_irq( regs, irq ); + + out: + } + /* if on iSeries partition */ + else { + paca = (struct Paca *)mfspr(SPRG3); +#ifdef CONFIG_SMP + if ( paca->xLpPaca.xIntDword.xFields.xIpiCnt ) { + paca->xLpPaca.xIntDword.xFields.xIpiCnt = 0; + iSeries_smp_message_recv( regs ); + } +#endif /* CONFIG_SMP */ + lpq = paca->lpQueuePtr; + if ( lpq ) + lpEvent_count += ItLpQueue_process( lpq, regs ); + } + + irq_exit(cpu); + + if ( _machine == _MACH_iSeries ) { + if ( paca->xLpPaca.xIntDword.xFields.xDecrInt ) { + paca->xLpPaca.xIntDword.xFields.xDecrInt = 0; + /* Signal a fake decrementer interrupt */ + timer_interrupt( regs ); + } + } + + if (softirq_pending(cpu)) + do_softirq(); + + return 1; /* lets ret_from_int know we can do checks */ +} + +unsigned long probe_irq_on (void) +{ + return 0; +} + +int probe_irq_off (unsigned long irqs) +{ + return 0; +} + +unsigned int probe_irq_mask(unsigned long irqs) +{ + return 0; +} + +void __init init_IRQ(void) +{ + static int once = 0; + + if ( once ) + return; + else + once++; + + ppc_md.init_IRQ(); +} + +#ifdef CONFIG_SMP +unsigned char global_irq_holder = NO_PROC_ID; + +static void show(char * str) +{ + int cpu = smp_processor_id(); + int i; + + printk("\n%s, CPU %d:\n", str, cpu); + printk("irq: %d [ ", irqs_running()); + for (i = 0; i < smp_num_cpus; i++) + printk("%u ", __brlock_array[i][BR_GLOBALIRQ_LOCK]); + printk("]\nbh: %d [ ", + (spin_is_locked(&global_bh_lock) ? 1 : 0)); + for (i = 0; i < smp_num_cpus; i++) + printk("%u ", local_bh_count(i)); + printk("]\n"); +} + +#define MAXCOUNT 10000000 + +void synchronize_irq(void) +{ + if (irqs_running()) { + cli(); + sti(); + } +} + +static inline void get_irqlock(int cpu) +{ + int count; + + if ((unsigned char)cpu == global_irq_holder) + return; + + count = MAXCOUNT; +again: + br_write_lock(BR_GLOBALIRQ_LOCK); + for (;;) { + spinlock_t *lock; + + if (!irqs_running() && + (local_bh_count(smp_processor_id()) || !spin_is_locked(&global_bh_lock))) + break; + + br_write_unlock(BR_GLOBALIRQ_LOCK); + lock = &__br_write_locks[BR_GLOBALIRQ_LOCK].lock; + while (irqs_running() || + spin_is_locked(lock) || + (!local_bh_count(smp_processor_id()) && spin_is_locked(&global_bh_lock))) { + if (!--count) { + show("get_irqlock"); + count = (~0 >> 1); + } + __sti(); + barrier(); + __cli(); + } + goto again; + } + + global_irq_holder = cpu; +} + +/* + * A global "cli()" while in an interrupt context + * turns into just a local cli(). Interrupts + * should use spinlocks for the (very unlikely) + * case that they ever want to protect against + * each other. + * + * If we already have local interrupts disabled, + * this will not turn a local disable into a + * global one (problems with spinlocks: this makes + * save_flags+cli+sti usable inside a spinlock). + */ +void __global_cli(void) +{ + unsigned long flags; + + __save_flags(flags); + if (flags & (1UL << 15)) { + int cpu = smp_processor_id(); + __cli(); + if (!local_irq_count(cpu)) + get_irqlock(cpu); + } +} + +void __global_sti(void) +{ + int cpu = smp_processor_id(); + + if (!local_irq_count(cpu)) + release_irqlock(cpu); + __sti(); +} + +/* + * SMP flags value to restore to: + * 0 - global cli + * 1 - global sti + * 2 - local cli + * 3 - local sti + */ +unsigned long __global_save_flags(void) +{ + int retval; + int local_enabled; + unsigned long flags; + + __save_flags(flags); + local_enabled = (flags >> 15) & 1; + /* default to local */ + retval = 2 + local_enabled; + + /* check for global flags if we're not in an interrupt */ + if (!local_irq_count(smp_processor_id())) { + if (local_enabled) + retval = 1; + if (global_irq_holder == (unsigned char) smp_processor_id()) + retval = 0; + } + return retval; +} + +void __global_restore_flags(unsigned long flags) +{ + switch (flags) { + case 0: + __global_cli(); + break; + case 1: + __global_sti(); + break; + case 2: + __cli(); + break; + case 3: + __sti(); + break; + default: + printk("global_restore_flags: %016lx caller %p\n", + flags, __builtin_return_address(0)); + } +} + +#endif /* CONFIG_SMP */ + +static struct proc_dir_entry * root_irq_dir; +static struct proc_dir_entry * irq_dir [NR_IRQS]; +static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; + +#ifdef CONFIG_IRQ_ALL_CPUS +unsigned int irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = 0xffffffff}; +#else /* CONFIG_IRQ_ALL_CPUS */ +unsigned int irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = 0x00000000}; +#endif /* CONFIG_IRQ_ALL_CPUS */ + +#define HEX_DIGITS 8 + +static int irq_affinity_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08x\n", irq_affinity[(int)data]); +} + +static unsigned int parse_hex_value (const char *buffer, + unsigned long count, unsigned long *ret) +{ + unsigned char hexnum [HEX_DIGITS]; + unsigned long value; + int i; + + if (!count) + return -EINVAL; + if (count > HEX_DIGITS) + count = HEX_DIGITS; + if (copy_from_user(hexnum, buffer, count)) + return -EFAULT; + + /* + * Parse the first 8 characters as a hex string, any non-hex char + * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same. + */ + value = 0; + + for (i = 0; i < count; i++) { + unsigned int c = hexnum[i]; + + switch (c) { + case '0' ... '9': c -= '0'; break; + case 'a' ... 'f': c -= 'a'-10; break; + case 'A' ... 'F': c -= 'A'-10; break; + default: + goto out; + } + value = (value << 4) | c; + } +out: + *ret = value; + return 0; +} + +static int irq_affinity_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int irq = (int) data, full_count = count, err; + unsigned long new_value; + + if (!irq_desc[irq].handler->set_affinity) + return -EIO; + + err = parse_hex_value(buffer, count, &new_value); + +/* Why is this disabled ? --BenH */ +#if 0/*CONFIG_SMP*/ + /* + * Do not allow disabling IRQs completely - it's a too easy + * way to make the system unusable accidentally :-) At least + * one online CPU still has to be targeted. + */ + if (!(new_value & cpu_online_map)) + return -EINVAL; +#endif + + irq_affinity[irq] = new_value; + irq_desc[irq].handler->set_affinity(irq, new_value); + + return full_count; +} + +static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long *mask = (unsigned long *) data; + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", *mask); +} + +static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned long *mask = (unsigned long *) data, full_count = count, err; + unsigned long new_value; + + err = parse_hex_value(buffer, count, &new_value); + if (err) + return err; + + *mask = new_value; + return full_count; +} + +#define MAX_NAMELEN 10 + +static void register_irq_proc (unsigned int irq) +{ + struct proc_dir_entry *entry; + char name [MAX_NAMELEN]; + + if (!root_irq_dir || (irq_desc[irq].handler == NULL)) + return; + + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + irq_dir[irq] = proc_mkdir(name, root_irq_dir); + + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + + entry->nlink = 1; + entry->data = (void *)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + + smp_affinity_entry[irq] = entry; +} + +unsigned long prof_cpu_mask = -1; + +void init_irq_proc (void) +{ + struct proc_dir_entry *entry; + int i; + + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", 0); + + /* create /proc/irq/prof_cpu_mask */ + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); + + entry->nlink = 1; + entry->data = (void *)&prof_cpu_mask; + entry->read_proc = prof_cpu_mask_read_proc; + entry->write_proc = prof_cpu_mask_write_proc; + + /* + * Create entries for all existing IRQs. + */ + for (i = 0; i < NR_IRQS; i++) { + if (irq_desc[i].handler == NULL) + continue; + register_irq_proc(i); + } +} + +void no_action(int irq, void *dev, struct pt_regs *regs) +{ +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/lmb.c linux.ac/arch/ppc64/kernel/lmb.c --- linux.vanilla/arch/ppc64/kernel/lmb.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/lmb.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,342 @@ +/* + * + * Procedures for interfacing to Open Firmware. + * + * Peter Bergner, IBM Corp. June 2001. + * Copyright (C) 2001 Peter Bergner. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +extern unsigned long klimit; +extern unsigned long reloc_offset(void); + + +static long lmb_add_region(struct lmb_region *, unsigned long, unsigned long, unsigned long); + +struct lmb lmb = { + 0, + {0,0,0,{{0,0,0}}}, + {0,0,0,{{0,0,0}}} +}; + + +/* Assumption: base addr of region 1 < base addr of region 2 */ +static void +lmb_coalesce_regions(struct lmb_region *rgn, unsigned long r1, unsigned long r2) +{ + unsigned long i; + + 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--; +} + + +/* This routine called with relocation disabled. */ +void +lmb_init(void) +{ + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + + /* Create a dummy zero size LMB which will get coalesced away later. + * This simplifies the lmb_add() code below... + */ + _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; +} + +/* This routine called with relocation disabled. */ +void +lmb_analyze(void) +{ + 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; + } + _lmb->memory.size = total_size; + _lmb->memory.lcd_size = (1UL << cnt_trailing_zeros(size_mask)); +} + +/* This routine called with relocation disabled. */ +long +lmb_add(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_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 +lmb_reserve(unsigned long base, unsigned long size) +{ + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb_region *_rgn = &(_lmb->reserved); + + 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, + 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; + 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; + rgn->region[i].size += size; + coalesced++; + break; + } + else if ( adjacent < 0 ) { + rgn->region[i].size += size; + coalesced++; + break; + } + } + + if ((i < rgn->cnt-1) && lmb_regions_adjacent(rgn, i, i+1) ) { + lmb_coalesce_regions(rgn, i, i+1); + coalesced++; + } + + if ( coalesced ) { + return coalesced; + } else if ( rgn->cnt >= MAX_LMB_REGIONS ) { + return -1; + } + + /* Couldn't coalesce the LMB, so add it to the sorted table. */ + for(i=rgn->cnt-1; i >= 0 ;i--) { + if (base < rgn->region[i].base) { + 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; + } + } + rgn->cnt++; + + return 0; +} + +long +lmb_overlaps_region(struct lmb_region *rgn, unsigned long base, unsigned long size) +{ + unsigned long i; + + for(i=0; i < rgn->cnt ;i++) { + unsigned long rgnbase = rgn->region[i].base; + unsigned long rgnsize = rgn->region[i].size; + if ( lmb_addrs_overlap(base,size,rgnbase,rgnsize) ) { + break; + } + } + + return (i < rgn->cnt) ? i : -1; +} + + +unsigned long +lmb_alloc(unsigned long size, unsigned long align) +{ + long i, j; + unsigned long base; + 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--) { + 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) && + ((j = lmb_overlaps_region(_rsv,base,size)) >= 0) ) { + base = _ALIGN_DOWN(_rsv->region[j].base-size, align); + } + + if ( (base != 0) && (lmbbase <= base) ) + break; + } + + if ( i < 0 ) + return 0; + + 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(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb_region *_mem = &(_lmb->memory); + unsigned long idx = _mem->cnt-1; +#ifdef CONFIG_MSCHUNKS + unsigned long lastbase = _mem->region[idx].physbase; +#else + unsigned long lastbase = _mem->region[idx].base; +#endif /* CONFIG_MSCHUNKS */ + unsigned long lastsize = _mem->region[idx].size; + + return (lastbase + lastsize); +} + + +unsigned long +lmb_abs_to_phys(unsigned long aa) +{ + unsigned long i, pa = 0; + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb_region *_mem = &(_lmb->memory); + + for(i=0; i < _mem->cnt ;i++) { + unsigned long lmbbase = _mem->region[i].base; + unsigned long lmbsize = _mem->region[i].size; + if ( lmb_addrs_overlap(aa,1,lmbbase,lmbsize) ) { + pa = _mem->region[i].physbase + (aa - lmbbase); + break; + } + } + + 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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/local_irq.h linux.ac/arch/ppc64/kernel/local_irq.h --- linux.vanilla/arch/ppc64/kernel/local_irq.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/local_irq.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,29 @@ +/* + * c 2001 PowerPC 64 Team, 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. + */ +#ifndef _PPC_KERNEL_LOCAL_IRQ_H +#define _PPC_KERNEL_LOCAL_IRQ_H + +#include +#include +#include +#include +#include + +void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq); + +#define NR_MASK_WORDS ((NR_IRQS + 63) / 64) + +extern int ppc_spurious_interrupts; +extern int ppc_second_irq; +extern struct irqaction *ppc_irq_action[NR_IRQS]; +extern unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; +extern unsigned long ppc_lost_interrupts[NR_MASK_WORDS]; +extern atomic_t ppc_n_lost_interrupts; + +#endif /* _PPC_KERNEL_LOCAL_IRQ_H */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/mf.c linux.ac/arch/ppc64/kernel/mf.c --- linux.vanilla/arch/ppc64/kernel/mf.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/mf.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,1213 @@ +/* + * mf.c + * Copyright (C) 2001 Troy D. Armstrong IBM Corporation + * + * This modules exists as an interface between a Linux secondary partition + * running on an iSeries and the primary partition's Virtual Service + * Processor (VSP) object. The VSP has final authority over powering on/off + * all partitions in the iSeries. It also provides miscellaneous low-level + * machine facility type operations. + * + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * This is the structure layout for the Machine Facilites LPAR event + * flows. + */ +struct VspCmdData; +struct CeMsgData; +union SafeCast +{ + u64 ptrAsU64; + void *ptr; +}; + + +typedef void (*CeMsgCompleteHandler)( void *token, struct CeMsgData *vspCmdRsp ); + +struct CeMsgCompleteData +{ + CeMsgCompleteHandler xHdlr; + void *xToken; +}; + +struct VspRspData +{ + struct semaphore *xSemaphore; + struct VspCmdData *xResponse; +}; + +struct IoMFLpEvent +{ + struct HvLpEvent xHvLpEvent; + + u16 xSubtypeRc; + u16 xRsvd1; + u32 xRsvd2; + + union + { + + struct AllocData + { + u16 xSize; + u16 xType; + u32 xCount; + u16 xRsvd3; + u8 xRsvd4; + HvLpIndex xTargetLp; + } xAllocData; + + struct CeMsgData + { + u8 xCEMsg[12]; + char xReserved[4]; + struct CeMsgCompleteData *xToken; + } xCEMsgData; + + struct VspCmdData + { + union SafeCast xTokenUnion; + u16 xCmd; + HvLpIndex xLpIndex; + u8 xRc; + u32 xReserved1; + + union VspCmdSubData + { + struct + { + u64 xState; + } xGetStateOut; + + struct + { + u64 xIplType; + } xGetIplTypeOut, xFunction02SelectIplTypeIn; + + struct + { + u64 xIplMode; + } xGetIplModeOut, xFunction02SelectIplModeIn; + + struct + { + u64 xPage[4]; + } xGetSrcHistoryIn; + + struct + { + u64 xFlag; + } xGetAutoIplWhenPrimaryIplsOut, xSetAutoIplWhenPrimaryIplsIn, xWhiteButtonPowerOffIn, xFunction08FastPowerOffIn, xIsSpcnRackPowerIncompleteOut; + + struct + { + u64 xToken; + u64 xAddressType; + u64 xSide; + u32 xTransferLength; + u32 xOffset; + } xSetKernelImageIn, xGetKernelImageIn, xSetKernelCmdLineIn, xGetKernelCmdLineIn; + + struct + { + u32 xTransferLength; + } xGetKernelImageOut,xGetKernelCmdLineOut; + + + u8 xReserved2[80]; + + } xSubData; + } xVspCmd; + } xUnion; +}; + + +/* + * All outgoing event traffic is kept on a FIFO queue. The first + * pointer points to the one that is outstanding, and all new + * requests get stuck on the end. Also, we keep a certain number of + * preallocated stack elements so that we can operate very early in + * the boot up sequence (before kmalloc is ready). + */ +struct StackElement +{ + struct StackElement * next; + struct IoMFLpEvent event; + MFCompleteHandler hdlr; + char dmaData[72]; + unsigned dmaDataLength; + unsigned remoteAddress; +}; +static spinlock_t spinlock; +static struct StackElement * head = NULL; +static struct StackElement * tail = NULL; +static struct StackElement * avail = NULL; +static struct StackElement prealloc[16]; + +/* + * Put a stack element onto the available queue, so it can get reused. + * Attention! You must have the spinlock before calling! + */ +void free( struct StackElement * element ) +{ + if( element != NULL ) + { + element->next = avail; + avail = element; + } +} + +/* + * Enqueue the outbound event onto the stack. If the queue was + * empty to begin with, we must also issue it via the Hypervisor + * interface. There is a section of code below that will touch + * the first stack pointer without the protection of the spinlock. + * This is OK, because we know that nobody else will be modifying + * the first pointer when we do this. + */ +static int signalEvent( struct StackElement * newElement ) +{ + int rc = 0; + unsigned long flags; + int go = 1; + struct StackElement * element; + HvLpEvent_Rc hvRc; + + /* enqueue the event */ + if( newElement != NULL ) + { + spin_lock_irqsave( &spinlock, flags ); + if( head == NULL ) + head = newElement; + else + { + go = 0; + tail->next = newElement; + } + newElement->next = NULL; + tail = newElement; + spin_unlock_irqrestore( &spinlock, flags ); + } + + /* send the event */ + while( go ) + { + go = 0; + + /* any DMA data to send beforehand? */ + if( head->dmaDataLength > 0 ) + HvCallEvent_dmaToSp( head->dmaData, head->remoteAddress, head->dmaDataLength, HvLpDma_Direction_LocalToRemote ); + + hvRc = HvCallEvent_signalLpEvent(&head->event.xHvLpEvent); + if( hvRc != HvLpEvent_Rc_Good ) + { + printk( KERN_ERR "mf.c: HvCallEvent_signalLpEvent() failed with %d\n", (int)hvRc ); + + spin_lock_irqsave( &spinlock, flags ); + element = head; + head = head->next; + if( head != NULL ) + go = 1; + spin_unlock_irqrestore( &spinlock, flags ); + + if( element == newElement ) + rc = -EIO; + else + { + if( element->hdlr != NULL ) + { + union SafeCast mySafeCast; + mySafeCast.ptrAsU64 = element->event.xHvLpEvent.xCorrelationToken; + (*element->hdlr)( mySafeCast.ptr, -EIO ); + } + } + + spin_lock_irqsave( &spinlock, flags ); + free( element ); + spin_unlock_irqrestore( &spinlock, flags ); + } + } + + return rc; +} + +/* + * Allocate a new StackElement structure, and initialize it. + */ +static struct StackElement * newStackElement( void ) +{ + struct StackElement * newElement = NULL; + HvLpIndex primaryLp = HvLpConfig_getPrimaryLpIndex(); + unsigned long flags; + + if( newElement == NULL ) + { + spin_lock_irqsave( &spinlock, flags ); + if( avail != NULL ) + { + newElement = avail; + avail = avail->next; + } + spin_unlock_irqrestore( &spinlock, flags ); + } + + if( newElement == NULL ) + newElement = kmalloc(sizeof(struct StackElement),GFP_ATOMIC); + + if( newElement == NULL ) + { + printk( KERN_ERR "mf.c: unable to kmalloc %d bytes\n", sizeof(struct StackElement) ); + return NULL; + } + + memset( newElement, 0, sizeof(struct StackElement) ); + newElement->event.xHvLpEvent.xFlags.xValid = 1; + newElement->event.xHvLpEvent.xFlags.xAckType = HvLpEvent_AckType_ImmediateAck; + newElement->event.xHvLpEvent.xFlags.xAckInd = HvLpEvent_AckInd_DoAck; + newElement->event.xHvLpEvent.xFlags.xFunction = HvLpEvent_Function_Int; + newElement->event.xHvLpEvent.xType = HvLpEvent_Type_MachineFac; + newElement->event.xHvLpEvent.xSourceLp = HvLpConfig_getLpIndex(); + newElement->event.xHvLpEvent.xTargetLp = primaryLp; + newElement->event.xHvLpEvent.xSizeMinus1 = sizeof(newElement->event)-1; + newElement->event.xHvLpEvent.xRc = HvLpEvent_Rc_Good; + newElement->event.xHvLpEvent.xSourceInstanceId = HvCallEvent_getSourceLpInstanceId(primaryLp,HvLpEvent_Type_MachineFac); + newElement->event.xHvLpEvent.xTargetInstanceId = HvCallEvent_getTargetLpInstanceId(primaryLp,HvLpEvent_Type_MachineFac); + + return newElement; +} + +static int signalVspInstruction( struct VspCmdData *vspCmd ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + struct VspRspData response; + DECLARE_MUTEX_LOCKED(Semaphore); + response.xSemaphore = &Semaphore; + response.xResponse = vspCmd; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + newElement->event.xHvLpEvent.xSubtype = 6; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('V'<<8)+('I'<<0); + newElement->event.xUnion.xVspCmd.xTokenUnion.ptr = &response; + newElement->event.xUnion.xVspCmd.xCmd = vspCmd->xCmd; + newElement->event.xUnion.xVspCmd.xLpIndex = HvLpConfig_getLpIndex(); + newElement->event.xUnion.xVspCmd.xRc = 0xFF; + newElement->event.xUnion.xVspCmd.xReserved1 = 0; + memcpy(&(newElement->event.xUnion.xVspCmd.xSubData),&(vspCmd->xSubData), sizeof(vspCmd->xSubData)); + mb(); + + rc = signalEvent(newElement); + } + + if (rc == 0) + { + down(&Semaphore); + } + + return rc; +} + + +/* + * Send a 12-byte CE message to the primary partition VSP object + */ +static int signalCEMsg( char * ceMsg, void * token ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + newElement->event.xHvLpEvent.xSubtype = 0; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('C'<<8)+('E'<<0); + memcpy( newElement->event.xUnion.xCEMsgData.xCEMsg, ceMsg, 12 ); + newElement->event.xUnion.xCEMsgData.xToken = token; + rc = signalEvent(newElement); + } + + return rc; +} + +/* + * Send a 12-byte CE message and DMA data to the primary partition VSP object + */ +static int dmaAndSignalCEMsg( char * ceMsg, void * token, void * dmaData, unsigned dmaDataLength, unsigned remoteAddress ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + newElement->event.xHvLpEvent.xSubtype = 0; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('C'<<8)+('E'<<0); + memcpy( newElement->event.xUnion.xCEMsgData.xCEMsg, ceMsg, 12 ); + newElement->event.xUnion.xCEMsgData.xToken = token; + memcpy( newElement->dmaData, dmaData, dmaDataLength ); + newElement->dmaDataLength = dmaDataLength; + newElement->remoteAddress = remoteAddress; + rc = signalEvent(newElement); + } + + return rc; +} + +/* + * Initiate a nice (hopefully) shutdown of Linux. We simply are + * going to try and send the init process a SIGINT signal. If + * this fails (why?), we'll simply force it off in a not-so-nice + * manner. + */ +static int shutdown( void ) +{ + int rc = kill_proc(1,SIGINT,1); + + if( rc ) + { + printk( KERN_ALERT "mf.c: SIGINT to init failed (%d), hard shutdown commencing\n", rc ); + mf_powerOff(); + } + else + printk( KERN_ALERT "mf.c: init has been successfully notified to proceed with shutdown\n" ); + + return rc; +} + +/* + * The primary partition VSP object is sending us a new + * event flow. Handle it... + */ +static void intReceived( struct IoMFLpEvent * event ) +{ + int freeIt = 0; + struct StackElement * two = NULL; + /* ack the interrupt */ + event->xHvLpEvent.xRc = HvLpEvent_Rc_Good; + HvCallEvent_ackLpEvent( &event->xHvLpEvent ); + + /* process interrupt */ + switch( event->xHvLpEvent.xSubtype ) + { + case 0: /* CE message */ + switch( event->xUnion.xCEMsgData.xCEMsg[3] ) + { + case 0x5B: /* power control notification */ + if( (event->xUnion.xCEMsgData.xCEMsg[5]&0x20) != 0 ) + { + printk( KERN_ALERT "mf.c: Commencing partition shutdown\n" ); + if( shutdown() == 0 ) + signalCEMsg( "\x00\x00\x00\xDB\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + } + break; + case 0xC0: /* get time */ + { + if ( (head != NULL) && ( head->event.xUnion.xCEMsgData.xCEMsg[3] == 0x40 ) ) + { + freeIt = 1; + if ( head->event.xUnion.xCEMsgData.xToken != 0 ) + { + CeMsgCompleteHandler xHdlr = head->event.xUnion.xCEMsgData.xToken->xHdlr; + void * token = head->event.xUnion.xCEMsgData.xToken->xToken; + + if (xHdlr != NULL) + (*xHdlr)( token, &(event->xUnion.xCEMsgData) ); + } + } + } + break; + } + + /* remove from queue */ + if ( freeIt == 1 ) + { + unsigned long flags; + spin_lock_irqsave( &spinlock, flags ); + if( head != NULL ) + { + struct StackElement *oldHead = head; + head = head->next; + two = head; + free( oldHead ); + } + spin_unlock_irqrestore( &spinlock, flags ); + } + + /* send next waiting event */ + if( two != NULL ) + signalEvent( NULL ); + break; + case 1: /* IT sys shutdown */ + printk( KERN_ALERT "mf.c: Commencing system shutdown\n" ); + shutdown(); + break; + } +} + +/* + * The primary partition VSP object is acknowledging the receipt + * of a flow we sent to them. If there are other flows queued + * up, we must send another one now... + */ +static void ackReceived( struct IoMFLpEvent * event ) +{ + unsigned long flags; + struct StackElement * two = NULL; + unsigned long freeIt = 0; + + /* handle current event */ + if( head != NULL ) + { + switch( event->xHvLpEvent.xSubtype ) + { + case 0: /* CE msg */ + if( event->xUnion.xCEMsgData.xCEMsg[3] == 0x40 ) + { + if ( event->xUnion.xCEMsgData.xCEMsg[2] != 0 ) + { + freeIt = 1; + if ( head->event.xUnion.xCEMsgData.xToken != 0 ) + { + CeMsgCompleteHandler xHdlr = head->event.xUnion.xCEMsgData.xToken->xHdlr; + void * token = head->event.xUnion.xCEMsgData.xToken->xToken; + + if (xHdlr != NULL) + (*xHdlr)( token, &(event->xUnion.xCEMsgData) ); + } + } + } + else + { + freeIt = 1; + } + break; + case 4: /* allocate */ + case 5: /* deallocate */ + if( head->hdlr != NULL ) + { + union SafeCast mySafeCast; + mySafeCast.ptrAsU64 = event->xHvLpEvent.xCorrelationToken; + (*head->hdlr)( mySafeCast.ptr, event->xUnion.xAllocData.xCount ); + } + freeIt = 1; + break; + case 6: + { + struct VspRspData *rsp = (struct VspRspData *)event->xUnion.xVspCmd.xTokenUnion.ptr; + + if (rsp != NULL) + { + if (rsp->xResponse != NULL) + memcpy(rsp->xResponse, &(event->xUnion.xVspCmd), sizeof(event->xUnion.xVspCmd)); + if (rsp->xSemaphore != NULL) + up(rsp->xSemaphore); + } + else + { + printk( KERN_ERR "mf.c: no rsp\n"); + } + freeIt = 1; + } + break; + } + } + else + printk( KERN_ERR "mf.c: stack empty for receiving ack\n" ); + + /* remove from queue */ + spin_lock_irqsave( &spinlock, flags ); + if(( head != NULL ) && ( freeIt == 1 )) + { + struct StackElement *oldHead = head; + head = head->next; + two = head; + free( oldHead ); + } + spin_unlock_irqrestore( &spinlock, flags ); + + /* send next waiting event */ + if( two != NULL ) + signalEvent( NULL ); +} + +/* + * This is the generic event handler we are registering with + * the Hypervisor. Ensure the flows are for us, and then + * parse it enough to know if it is an interrupt or an + * acknowledge. + */ +static void hvHandler( struct HvLpEvent * event, struct pt_regs * regs ) +{ + if( (event != NULL) && (event->xType == HvLpEvent_Type_MachineFac) ) + { + switch( event->xFlags.xFunction ) + { + case HvLpEvent_Function_Ack: + ackReceived( (struct IoMFLpEvent *)event ); + break; + case HvLpEvent_Function_Int: + intReceived( (struct IoMFLpEvent *)event ); + break; + default: + printk( KERN_ERR "mf.c: non ack/int event received\n" ); + break; + } + } + else + printk( KERN_ERR "mf.c: alien event received\n" ); +} + +/* + * Global kernel interface to allocate and seed events into the + * Hypervisor. + */ +void mf_allocateLpEvents( HvLpIndex targetLp, + HvLpEvent_Type type, + unsigned size, + unsigned count, + MFCompleteHandler hdlr, + void * userToken ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + union SafeCast mine; + mine.ptr = userToken; + newElement->event.xHvLpEvent.xSubtype = 4; + newElement->event.xHvLpEvent.xCorrelationToken = mine.ptrAsU64; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('M'<<8)+('A'<<0); + newElement->event.xUnion.xAllocData.xTargetLp = targetLp; + newElement->event.xUnion.xAllocData.xType = type; + newElement->event.xUnion.xAllocData.xSize = size; + newElement->event.xUnion.xAllocData.xCount = count; + newElement->hdlr = hdlr; + rc = signalEvent(newElement); + } + + if( (rc != 0) && (hdlr != NULL) ) + (*hdlr)( userToken, rc ); +} + +/* + * Global kernel interface to unseed and deallocate events already in + * Hypervisor. + */ +void mf_deallocateLpEvents( HvLpIndex targetLp, + HvLpEvent_Type type, + unsigned count, + MFCompleteHandler hdlr, + void * userToken ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + union SafeCast mine; + mine.ptr = userToken; + newElement->event.xHvLpEvent.xSubtype = 5; + newElement->event.xHvLpEvent.xCorrelationToken = mine.ptrAsU64; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('M'<<8)+('D'<<0); + newElement->event.xUnion.xAllocData.xTargetLp = targetLp; + newElement->event.xUnion.xAllocData.xType = type; + newElement->event.xUnion.xAllocData.xCount = count; + newElement->hdlr = hdlr; + rc = signalEvent(newElement); + } + + if( (rc != 0) && (hdlr != NULL) ) + (*hdlr)( userToken, rc ); +} + +/* + * Global kernel interface to tell the VSP object in the primary + * partition to power this partition off. + */ +void mf_powerOff( void ) +{ + printk( KERN_ALERT "mf.c: Down it goes...\n" ); + signalCEMsg( "\x00\x00\x00\x4D\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + for(;;); +} + +/* + * Global kernel interface to tell the VSP object in the primary + * partition to reboot this partition. + */ +void mf_reboot( void ) +{ + printk( KERN_ALERT "mf.c: Preparing to bounce...\n" ); + signalCEMsg( "\x00\x00\x00\x4E\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + for(;;); +} + +/* + * Display a single word SRC onto the VSP control panel. + */ +void mf_displaySrc( u32 word ) +{ + u8 ce[12]; + + memcpy( ce, "\x00\x00\x00\x4A\x00\x00\x00\x01\x00\x00\x00\x00", 12 ); + ce[8] = word>>24; + ce[9] = word>>16; + ce[10] = word>>8; + ce[11] = word; + signalCEMsg( ce, NULL ); +} + +/* + * Display a single word SRC of the form "PROGXXXX" on the VSP control panel. + */ +void mf_displayProgress( u16 value ) +{ + u8 ce[12]; + u8 src[72]; + + memcpy( ce, "\x00\x00\x04\x4A\x00\x00\x00\x48\x00\x00\x00\x00", 12 ); + memcpy( src, + "\x01\x00\x00\x01" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "PROGxxxx" + " ", + 72 ); + src[6] = value>>8; + src[7] = value&255; + src[44] = "0123456789ABCDEF"[(value>>12)&15]; + src[45] = "0123456789ABCDEF"[(value>>8)&15]; + src[46] = "0123456789ABCDEF"[(value>>4)&15]; + src[47] = "0123456789ABCDEF"[value&15]; + dmaAndSignalCEMsg( ce, NULL, src, sizeof(src), 9*64*1024 ); +} + +/* + * Clear the VSP control panel. Used to "erase" an SRC that was + * previously displayed. + */ +void mf_clearSrc( void ) +{ + signalCEMsg( "\x00\x00\x00\x4B\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); +} + +/* + * Initialization code here. + */ +void mf_init( void ) +{ + int i; + + /* initialize */ + spin_lock_init( &spinlock ); + for( i = 0; i < sizeof(prealloc)/sizeof(*prealloc); ++i ) + free( &prealloc[i] ); + HvLpEvent_registerHandler( HvLpEvent_Type_MachineFac, &hvHandler ); + + /* virtual continue ack */ + signalCEMsg( "\x00\x00\x00\x57\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + + /* initialization complete */ + printk( KERN_NOTICE "mf.c: iSeries Linux LPAR Machine Facilities initialized\n" ); + + iSeries_proc_callback(&mf_proc_init); +} + +void mf_setSide(char side) +{ + int rc = 0; + u64 newSide = 0; + struct VspCmdData myVspCmd; + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + if (side == 'A') + newSide = 0; + else if (side == 'B') + newSide = 1; + else if (side == 'C') + newSide = 2; + else + newSide = 3; + + myVspCmd.xSubData.xFunction02SelectIplTypeIn.xIplType = newSide; + myVspCmd.xCmd = 10; + + rc = signalVspInstruction(&myVspCmd); +} + +char mf_getSide(void) +{ + char returnValue = ' '; + int rc = 0; + struct VspCmdData myVspCmd; + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 2; + myVspCmd.xSubData.xFunction02SelectIplTypeIn.xIplType = 0; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if(rc != 0) + { + return returnValue; + } + else + { + if (myVspCmd.xRc == 0) + { + if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 0) + returnValue = 'A'; + else if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 1) + returnValue = 'B'; + else if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 2) + returnValue = 'C'; + else + returnValue = 'D'; + } + } + + return returnValue; +} + +void mf_getSrcHistory(char *buffer, int size) +{ + /* struct IplTypeReturnStuff returnStuff; + struct StackElement * newElement = newStackElement(); + int rc = 0; + char *pages[4]; + + pages[0] = kmalloc(4096, GFP_ATOMIC); + pages[1] = kmalloc(4096, GFP_ATOMIC); + pages[2] = kmalloc(4096, GFP_ATOMIC); + pages[3] = kmalloc(4096, GFP_ATOMIC); + if(( newElement == NULL ) || (pages[0] == NULL) || (pages[1] == NULL) || (pages[2] == NULL) || (pages[3] == NULL)) + rc = -ENOMEM; + else + { + returnStuff.xType = 0; + returnStuff.xRc = 0; + returnStuff.xDone = 0; + newElement->event.xHvLpEvent.xSubtype = 6; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('V'<<8)+('I'<<0); + newElement->event.xUnion.xVspCmd.xEvent = &returnStuff; + newElement->event.xUnion.xVspCmd.xCmd = 4; + newElement->event.xUnion.xVspCmd.xLpIndex = HvLpConfig_getLpIndex(); + newElement->event.xUnion.xVspCmd.xRc = 0xFF; + newElement->event.xUnion.xVspCmd.xReserved1 = 0; + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[0] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[0])); + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[1] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[1])); + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[2] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[2])); + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[3] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[3])); + mb(); + rc = signalEvent(newElement); + } + + if(rc != 0) + { + return; + } + else + { + while (returnStuff.xDone != 1) + { + udelay(10); + } + + if (returnStuff.xRc == 0) + { + memcpy(buffer, pages[0], size); + } + } + + kfree(pages[0]); + kfree(pages[1]); + kfree(pages[2]); + kfree(pages[3]);*/ +} + +void mf_setCmdLine(const char *cmdline, int size, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + dma_addr_t dma_addr = 0; + char *page = pci_alloc_consistent(NULL, size, &dma_addr); + + if (page == NULL) { + printk(KERN_ERR "mf.c: couldn't allocate memory to set command line\n"); + return; + } + + copy_from_user(page, cmdline, size); + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 31; + myVspCmd.xSubData.xSetKernelCmdLineIn.xToken = dma_addr; + myVspCmd.xSubData.xSetKernelCmdLineIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xSetKernelCmdLineIn.xSide = side; + myVspCmd.xSubData.xSetKernelCmdLineIn.xTransferLength = size; + mb(); + rc = signalVspInstruction(&myVspCmd); + + pci_free_consistent(NULL, size, page, dma_addr); +} + +int mf_getCmdLine(char *cmdline, int *size, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + int len = *size; + dma_addr_t dma_addr = pci_map_single(NULL, cmdline, *size, PCI_DMA_FROMDEVICE); + + memset(cmdline, 0, *size); + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 33; + myVspCmd.xSubData.xGetKernelCmdLineIn.xToken = dma_addr; + myVspCmd.xSubData.xGetKernelCmdLineIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xGetKernelCmdLineIn.xSide = side; + myVspCmd.xSubData.xGetKernelCmdLineIn.xTransferLength = *size; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if ( ! rc ) { + + if (myVspCmd.xRc == 0) + { + len = myVspCmd.xSubData.xGetKernelCmdLineOut.xTransferLength; + } + /* else + { + memcpy(cmdline, "Bad cmdline", 11); + } + */ + } + + pci_unmap_single(NULL, dma_addr, *size, PCI_DMA_FROMDEVICE); + + return len; +} + + +int mf_setVmlinuxChunk(const char *buffer, int size, int offset, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + + dma_addr_t dma_addr = 0; + + char *page = pci_alloc_consistent(NULL, size, &dma_addr); + + if (page == NULL) { + printk(KERN_ERR "mf.c: couldn't allocate memory to set vmlinux chunk\n"); + return -ENOMEM; + } + + copy_from_user(page, buffer, size); + memset(&myVspCmd, 0, sizeof(myVspCmd)); + + myVspCmd.xCmd = 30; + myVspCmd.xSubData.xGetKernelImageIn.xToken = dma_addr; + myVspCmd.xSubData.xGetKernelImageIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xGetKernelImageIn.xSide = side; + myVspCmd.xSubData.xGetKernelImageIn.xOffset = offset; + myVspCmd.xSubData.xGetKernelImageIn.xTransferLength = size; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if(rc == 0) + { + if (myVspCmd.xRc == 0) + { + rc = 0; + } + else + { + rc = -ENOMEM; + } + } + + pci_free_consistent(NULL, size, page, dma_addr); + + return rc; +} + +int mf_getVmlinuxChunk(char *buffer, int *size, int offset, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + int len = *size; + + dma_addr_t dma_addr = pci_map_single(NULL, buffer, *size, PCI_DMA_FROMDEVICE); + + memset(buffer, 0, len); + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 32; + myVspCmd.xSubData.xGetKernelImageIn.xToken = dma_addr; + myVspCmd.xSubData.xGetKernelImageIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xGetKernelImageIn.xSide = side; + myVspCmd.xSubData.xGetKernelImageIn.xOffset = offset; + myVspCmd.xSubData.xGetKernelImageIn.xTransferLength = len; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if(rc == 0) + { + if (myVspCmd.xRc == 0) + { + *size = myVspCmd.xSubData.xGetKernelImageOut.xTransferLength; + } + else + { + rc = -ENOMEM; + } + } + + pci_unmap_single(NULL, dma_addr, *size, PCI_DMA_FROMDEVICE); + + return rc; +} + +int mf_setRtcTime(unsigned long time) +{ + struct rtc_time tm; + + to_tm(time, &tm); + + return mf_setRtc( &tm ); +} + +struct RtcTimeData +{ + struct semaphore *xSemaphore; + struct CeMsgData xCeMsg; + int xRc; +}; + +void getRtcTimeComplete(void * token, struct CeMsgData *ceMsg) +{ + struct RtcTimeData *rtc = (struct RtcTimeData *)token; + + memcpy(&(rtc->xCeMsg), ceMsg, sizeof(rtc->xCeMsg)); + + rtc->xRc = 0; + up(rtc->xSemaphore); +} + +static unsigned long lastsec = 1; + +int mf_getRtcTime(unsigned long *time) +{ +/* unsigned long usec, tsec; */ + + u32 dataWord1 = *((u32 *)(&xSpCommArea.xBcdTimeAtIplStart)); + u32 dataWord2 = *(((u32 *)&(xSpCommArea.xBcdTimeAtIplStart)) + 1); + int year = 1970; + int year1 = ( dataWord1 >> 24 ) & 0x000000FF; + int year2 = ( dataWord1 >> 16 ) & 0x000000FF; + int sec = ( dataWord1 >> 8 ) & 0x000000FF; + int min = dataWord1 & 0x000000FF; + int hour = ( dataWord2 >> 24 ) & 0x000000FF; + int day = ( dataWord2 >> 8 ) & 0x000000FF; + int mon = dataWord2 & 0x000000FF; + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year1); + BCD_TO_BIN(year2); + year = year1 * 100 + year2; + + *time = mktime(year, mon, day, hour, min, sec); + + *time += ( jiffies / HZ ); + + /* Now THIS is a nasty hack! + * It ensures that the first two calls to mf_getRtcTime get different + * answers. That way the loop in init_time (time.c) will not think + * the clock is stuck. + */ + if ( lastsec ) { + *time -= lastsec; + --lastsec; + } + + return 0; + +} + +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; + + rc = signalCEMsg( "\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00", &ceComplete ); + + if ( rc == 0 ) + { + 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; + u8 sec = ( dataWord1 >> 8 ) & 0x000000FF; + u8 min = dataWord1 & 0x000000FF; + u8 hour = ( dataWord2 >> 24 ) & 0x000000FF; + u8 day = ( dataWord2 >> 8 ) & 0x000000FF; + u8 mon = dataWord2 & 0x000000FF; + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + + if ( year <= 69 ) + year += 100; + + tm->tm_sec = sec; + tm->tm_min = min; + tm->tm_hour = hour; + tm->tm_mday = day; + tm->tm_mon = mon; + tm->tm_year = year; + } + } + else + { + rc = rtcData.xRc; + tm->tm_sec = 0; + tm->tm_min = 0; + tm->tm_hour = 0; + tm->tm_mday = 15; + tm->tm_mon = 5; + tm->tm_year = 52; + + } + tm->tm_wday = 0; + tm->tm_yday = 0; + tm->tm_isdst = 0; + + } + + return rc; + +} + +int mf_setRtc(struct rtc_time * tm) +{ + char ceTime[12] = "\x00\x00\x00\x41\x00\x00\x00\x00\x00\x00\x00\x00"; + int rc = 0; + u8 day, mon, hour, min, sec, y1, y2; + unsigned year; + + year = 1900 + tm->tm_year; + y1 = year / 100; + y2 = year % 100; + + sec = tm->tm_sec; + min = tm->tm_min; + hour = tm->tm_hour; + day = tm->tm_mday; + mon = tm->tm_mon + 1; + + BIN_TO_BCD(sec); + BIN_TO_BCD(min); + BIN_TO_BCD(hour); + BIN_TO_BCD(mon); + BIN_TO_BCD(day); + BIN_TO_BCD(y1); + BIN_TO_BCD(y2); + + ceTime[4] = y1; + ceTime[5] = y2; + ceTime[6] = sec; + ceTime[7] = min; + ceTime[8] = hour; + ceTime[10] = day; + ceTime[11] = mon; + + rc = signalCEMsg( ceTime, NULL ); + + return rc; +} + + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/mf_proc.c linux.ac/arch/ppc64/kernel/mf_proc.c --- linux.vanilla/arch/ppc64/kernel/mf_proc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/mf_proc.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,309 @@ +/* + * mf_proc.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 + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _MF_PROC_H +#include +#endif +#ifndef MF_H_INCLUDED +#include +#endif +#include + +static struct proc_dir_entry *mf_proc_root = NULL; + +int proc_mf_dump_cmdline +(char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_mf_dump_vmlinux +(char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_mf_dump_side +(char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_mf_change_side +(struct file *file, const char *buffer, unsigned long count, void *data); + +int proc_mf_dump_src +(char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_mf_change_src (struct file *file, const char *buffer, unsigned long count, void *data); +int proc_mf_change_cmdline(struct file *file, const char *buffer, unsigned long count, void *data); +int proc_mf_change_vmlinux(struct file *file, const char *buffer, unsigned long count, void *data); + + +void mf_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent = NULL; + struct proc_dir_entry *mf_a = NULL; + struct proc_dir_entry *mf_b = NULL; + struct proc_dir_entry *mf_c = NULL; + struct proc_dir_entry *mf_d = NULL; + + mf_proc_root = proc_mkdir("mf", iSeries_proc); + if (!mf_proc_root) return; + + mf_a = proc_mkdir("A", mf_proc_root); + if (!mf_a) return; + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_a); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_a); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = proc_mf_change_vmlinux; + + mf_b = proc_mkdir("B", mf_proc_root); + if (!mf_b) return; + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_b); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)1; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_b); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)1; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = proc_mf_change_vmlinux; + + mf_c = proc_mkdir("C", mf_proc_root); + if (!mf_c) return; + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_c); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)2; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_c); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)2; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = proc_mf_change_vmlinux; + + mf_d = proc_mkdir("D", mf_proc_root); + if (!mf_d) return; + + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_d); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)3; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR, mf_d); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)3; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = NULL; + + ent = create_proc_entry("side", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_side; + ent->write_proc = proc_mf_change_side; + + ent = create_proc_entry("src", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_src; + ent->write_proc = proc_mf_change_src; + + +} + +int proc_mf_dump_cmdline +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = count; + char *p; + + len = mf_getCmdLine(page, &len, (u64)data); + + p = page + len - 1; + while ( p > page ) { + if ( (*p == 0) || (*p == ' ') ) + --p; + else + break; + } + if ( *p != '\n' ) { + ++p; + *p = '\n'; + } + ++p; + *p = 0; + len = p - page; + + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + +int proc_mf_dump_vmlinux +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int sizeToGet = count; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (mf_getVmlinuxChunk(page, &sizeToGet, off, (u64)data) == 0) + { + if (sizeToGet != 0) + { + *start = page + off; + printk("mf_proc.c: got count %d off %d\n", sizeToGet, (int)off); + return sizeToGet; + } + else + { + printk("mf_proc.c: eof\n"); + *eof = 1; + return 0; + } + } + else + { + printk("mf_proc.c: eof\n"); + *eof = 1; + return 0; + } +} + + +int proc_mf_dump_side +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + + char mf_current_side = mf_getSide(); + len = sprintf(page, "%c\n", mf_current_side); + + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +int proc_mf_change_side(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if ((*buffer != 'A') && + (*buffer != 'B') && + (*buffer != 'C') && + (*buffer != 'D')) + { + printk(KERN_ERR "mf_proc.c: proc_mf_change_side: invalid side\n"); + return -EINVAL; + } + + mf_setSide(*buffer); + + return count; +} + +int proc_mf_dump_src +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + mf_getSrcHistory(page, count); + len = count; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + +int proc_mf_change_src(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if ((count < 4) && (count != 1)) + { + printk(KERN_ERR "mf_proc: invalid src\n"); + return -EINVAL; + } + + if ((count == 1) && ((*buffer) == '\0')) + { + mf_clearSrc(); + } + else + { + mf_displaySrc(*(u32 *)buffer); + } + + return count; +} + +int proc_mf_change_cmdline(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + mf_setCmdLine(buffer, count, (u64)data); + + return count; +} + +int proc_mf_change_vmlinux(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + mf_setVmlinuxChunk(buffer, count, file->f_pos, (u64)data); + file->f_pos += count; + + return count; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/misc.S linux.ac/arch/ppc64/kernel/misc.S --- linux.vanilla/arch/ppc64/kernel/misc.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/misc.S Wed Oct 10 01:47:34 2001 @@ -0,0 +1,1176 @@ +/* + * arch/ppc/kernel/misc.S + * + * + * + * This file contains miscellaneous low-level functions. + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras. + * Adapted for iSeries by Mike Corrigan (mikejc@us.ibm.com) + * PPC64 updates by Dave Engebretsen (engebret@us.ibm.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ppc_asm.h" + + .text + +/* + * Returns (address we're running at) - (address we were linked at) + * for use before the text and data are mapped to KERNELBASE. + */ + +_GLOBAL(reloc_offset) + mflr r0 + bl 1f +1: mflr r3 + LOADADDR(r4,1b) + sub r3,r4,r3 + mtlr r0 + blr + +_GLOBAL(get_msr) + mfmsr r3 + blr + +_GLOBAL(get_dar) + mfdar r3 + blr + +_GLOBAL(get_srr0) + mfsrr0 r3 + blr + +_GLOBAL(get_srr1) + mfsrr1 r3 + blr + +_GLOBAL(get_sp) + mr r3,r1 + blr + +_GLOBAL(get_paca) + mfspr r3,SPRG3; + blr + +/* void __no_use_save_flags(unsigned long *flags) */ +_GLOBAL(__no_use_save_flags) + mfmsr r4 + std r4,0(r3) + blr + +/* void __no_use_restore_flags(unsigned long flags) */ +_GLOBAL(__no_use_restore_flags) +/* + * Just set/clear the MSR_EE bit through restore/flags but do not + * change anything else. This is needed by the RT system and makes + * sense anyway. + * -- Cort + */ + mfmsr r5 + mr r4,r3 + mr r3,r5 + rlwimi r3,r4,0,16,16 + /* Copy all except the MSR_EE bit from r4 (current MSR value) + to r3. This is the sort of thing the rlwimi instruction is + designed for. -- paulus. */ + /* Check if things are setup the way we want _already_. */ + cmpw 0,r3,r5 + beqlr + /* are we enabling interrupts? */ + rlwinm. r0,r3,0,16,16 + beq 1f + /* Check pending interrupts + * A decrementer, IPI or PMC interrupt may have occurred + * while we were in the hypervisor (which enables) + */ + CHECKANYINT(r4,r5) + beq+ 1f + + /* + * Handle pending interrupts in interrupt context + */ + mtmsrd r3 + li r0,0x5555 + sc + blr +1: + sync + mtmsrd r3 + isync + blr +/* void __no_lpq_restore_flags(unsigned long flags) */ +_GLOBAL(__no_lpq_restore_flags) + mfmsr r5 + mr r4,r3 + mr r3,r5 + rlwimi r3,r4,0,16,16 + /* Copy all except the MSR_EE bit from r4 (current MSR value) + to r3. This is the sort of thing the rlwimi instruction is + designed for. -- paulus. */ + /* Check if things are setup the way we want _already_. */ + cmpw 0,r3,r5 + beqlr + sync + mtmsrd r3 + isync + blr + +_GLOBAL(__no_use_cli) + mfmsr r0 /* Get current interrupt state */ + rlwinm r3,r0,16+1,32-1,31 /* Extract old value of 'EE' */ + rldicl r0,r0,48,1 + rldicl r0,r0,16,0 /* clear MSR_EE in r0 */ + mtmsrd r0 /* Update machine state */ + blr /* Done */ + +_GLOBAL(__no_use_sti) + mfmsr r3 /* Get current state */ + ori r3,r3,MSR_EE /* Turn on 'EE' bit */ + + /* Check for pending interrupts + * A decrementer, IPI or PMC interrupt may have occurred + * while we were in the hypervisor (which enables) + */ + CHECKANYINT(r4,r5) + beq+ 1f + + /* + * Handle pending interrupts in interrupt context + */ + mtmsrd r3 + li r0,0x5555 + sc + blr +1: + sync /* Some chip revs have problems here... */ + mtmsrd r3 /* Update machine state */ + blr + +/* + * We were about to enable interrupts but we have to simulate + * some interrupts that were lost by enable_irq first. + */ +#if 0 +_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 + /* Check for pending interrupt + * A decrementer, IPI or PMC interrupt may have occurred + * while we were in the hypervisor (which enables) + */ + CHECKANYINT(r4,r5) + bne- 1b + addi r1,r1,STACK_FRAME_OVERHEAD+16 + ld r3,-8(r1) + ld r0,16(r1) + mtlr r0 + mtmsrd r3 + blr +#endif +/* + * complement mask on the msr then "or" some values on. + * _nmask_and_or_msr(nmask, value_to_or) + */ +_GLOBAL(_nmask_and_or_msr) + mfmsr r0 /* Get current msr */ + andc r0,r0,r3 /* And off the bits set in r3 (first parm) */ + or r0,r0,r4 /* Or on the bits in r4 (second parm) */ + mtmsrd r0 /* Update machine state */ + SYNC + blr /* Done */ + +/* + * Flush instruction cache. + * This is a no-op on the 601. + */ +_GLOBAL(flush_instruction_cache) + +/* + * This is called by kgdb code + * and should probably go away + * to be replaced by invalidating + * the cache lines that are actually + * modified + */ + mfspr r3,PVR + rlwinm r3,r3,16,16,31 + cmpi 0,r3,1 + beqlr /* for 601, do nothing */ + /* 603/604 processor - use invalidate-all bit in HID0 */ + mfspr r3,HID0 + ori r3,r3,HID0_ICFI + mtspr HID0,r3 + SYNC + blr + +/* + * Write any modified data cache blocks out to memory + * and invalidate the corresponding instruction cache blocks. + * + * flush_icache_range(unsigned long start, unsigned long stop) + * + * flush all bytes from start through stop-1 inclusive + */ + +_GLOBAL(flush_icache_range) + +/* + * Flush the data cache to memory + * + * Different systems have different cache line sizes + * and in some cases i-cache and d-cache line sizes differ from + * each other. + */ + LOADADDR(r10,naca) /* Get Naca address */ + ld r10,0(r10) + lhz r7,DCACHEL1LINESIZE(r10) /* Get cache line size */ + addi r5,r7,-1 + andc r6,r3,r5 /* round low to line bdy */ + subf r8,r6,r4 /* compute length */ + add r8,r8,r5 /* ensure we get enough */ + lhz r9,DCACHEL1LOGLINESIZE(r10) /* Get log-2 of cache line size */ + srw. r8,r8,r9 /* compute line count */ + beqlr /* nothing to do? */ + mtctr r8 +1: dcbst 0,r6 + add r6,r6,r7 + bdnz 1b + sync + +/* Now invalidate the instruction cache */ + + lhz r7,ICACHEL1LINESIZE(r10) /* Get Icache line size */ + addi r5,r7,-1 + andc r6,r3,r5 /* round low to line bdy */ + subf r8,r6,r4 /* compute length */ + add r8,r8,r5 + lhz r9,ICACHEL1LOGLINESIZE(r10) /* Get log-2 of Icache line size */ + srw. r8,r8,r9 /* compute line count */ + beqlr /* nothing to do? */ + mtctr r8 +2: icbi 0,r6 + add r6,r6,r7 + bdnz 2b + sync + isync + blr + +/* + * Like above, but only do the D-cache. + * + * flush_dcache_range(unsigned long start, unsigned long stop) + * + * flush all bytes from start to stop-1 inclusive + */ +_GLOBAL(flush_dcache_range) + +/* + * Flush the data cache to memory + * + * Different systems have different cache line sizes + */ + LOADADDR(r10,naca) /* Get Naca address */ + ld r10,0(r10) + lhz r7,DCACHEL1LINESIZE(r10) /* Get dcache line size */ + addi r5,r7,-1 + andc r6,r3,r5 /* round low to line bdy */ + subf r8,r6,r4 /* compute length */ + add r8,r8,r5 /* ensure we get enough */ + lhz r9,DCACHEL1LOGLINESIZE(r10) /* Get log-2 of dcache line size */ + srw. r8,r8,r9 /* compute line count */ + beqlr /* nothing to do? */ + mtctr r8 +0: dcbst 0,r6 + add r6,r6,r7 + bdnz 0b + sync + blr + +/* + * Flush a particular page from the data cache to RAM. + * Note: this is necessary because the instruction cache does *not* + * snoop from the data cache. + * This is a no-op on the 601 which has a unified cache. + * + * void __flush_page_to_ram(void *page) + */ +_GLOBAL(__flush_page_to_ram) +/* + * Flush the data cache to memory + * + * Different systems have different cache line sizes + */ + +/* Flush the dcache */ + LOADADDR(r7,naca) + ld r7,0(r7) + clrrdi r3,r3,12 /* Page align */ + lhz r4,DCACHEL1LINESPERPAGE(r7) /* Get # dcache lines per page */ + lhz r5,DCACHEL1LINESIZE(r7) /* Get dcache line size */ + mr r6,r3 + mtctr r4 +0: dcbst 0,r6 + add r6,r6,r5 + bdnz 0b + sync + +/* Now invalidate the icache */ + + lhz r4,ICACHEL1LINESPERPAGE(r7) /* Get # icache lines per page */ + lhz r5,ICACHEL1LINESIZE(r7) /* Get icache line size */ + mtctr r4 +1: icbi 0,r3 + add r3,r3,r5 + bdnz 1b + sync + isync + blr + + +/* + * Flush a particular page from the instruction cache. + * Note: this is necessary because the instruction cache does *not* + * snoop from the data cache. + * + * void __flush_icache_page(void *page) + */ +_GLOBAL(__flush_icache_page) +/* + * Different systems have different cache line sizes + */ + +/* Invalidate the icache */ + + LOADADDR(r6,naca) + ld r6,0(r6) + clrrdi r3,r3,12 /* Page align */ + lhz r4,ICACHEL1LINESPERPAGE(r6) /* Get # icache lines per page */ + lhz r5,ICACHEL1LINESIZE(r6) /* Get icache line size */ + mtctr r4 +1: icbi 0,r3 + add r3,r3,r5 + bdnz 1b + sync + isync + blr + +/* + * Clear a page using the dcbz instruction, which doesn't cause any + * memory traffic (except to write out any cache lines which get + * displaced). This only works on cacheable memory. + */ +_GLOBAL(clear_page) + LOADADDR(r6,naca) + ld r6,0(r6) + clrrdi r3,r3,12 /* Page align */ + lhz r4,DCACHEL1LINESPERPAGE(r6) /* Get # dcache lines per page */ + lhz r5,DCACHEL1LINESIZE(r6) /* Get dcache line size */ + mtctr r4 +0: dcbz 0,r3 + add r3,r3,r5 + bdnz 0b + blr + +/* + * Copy a whole page. We use the dcbz instruction on the destination + * to reduce memory traffic (it eliminates the unnecessary reads of + * the destination into cache). This requires that the destination + * is cacheable. + */ +#define COPY_32_BYTES \ + ld r6,8(r4); \ + ld r7,16(r4); \ + ld r8,24(r4); \ + ldu r9,32(r4); \ + std r6,8(r3); \ + std r7,16(r3); \ + std r8,24(r3); \ + stdu r9,32(r3) + +_GLOBAL(copy_page) + + LOADADDR(r9,naca) + ld r9,0(r9) + clrrdi r3,r3,12 /* Page align */ + clrrdi r4,r4,12 /* Page align */ + lhz r5,DCACHEL1LINESPERPAGE(r9) /* Get # dcache lines per page */ + lhz r0,DCACHEL1LINESIZE(r9) /* Get dcache line size */ + mtctr r5 + addi r3,r3,-8 + addi r4,r4,-8 + li r10,8 + + cmpi 0,r0,32 + beq do_32_byte_line + cmpi 0,r0,64 + beq do_64_byte_line + cmpi 0,r0,128 + beq do_128_byte_line + + /* We don't have code specifically for this cache line size */ + /* Assume that the cache line size is at least 32 (and of */ + /* course a multiple of 32) */ + /* This code will work for all power-of-2 cache line sizes */ + /* from 32 to 4096 */ + +1: mr r5,r0 + dcbz r10,r3 +0: COPY_32_BYTES + addi r5,r5,-32 + or. r5,r5,r5 + bne 0b + bdnz 1b + blr + + .globl do_32_byte_line +do_32_byte_line: + dcbz r10,r3 + COPY_32_BYTES + bdnz do_32_byte_line + blr + + .globl do_64_byte_line +do_64_byte_line: + dcbz r10,r3 + COPY_32_BYTES + COPY_32_BYTES + bdnz do_64_byte_line + blr + + .globl do_128_byte_line +do_128_byte_line: + dcbz r10,r3 + COPY_32_BYTES + COPY_32_BYTES + COPY_32_BYTES + COPY_32_BYTES + bdnz do_128_byte_line + blr + +/* + * I/O string operations + * + * insb(port, buf, len) + * outsb(port, buf, len) + * insw(port, buf, len) + * outsw(port, buf, len) + * insl(port, buf, len) + * outsl(port, buf, len) + * insw_ns(port, buf, len) + * outsw_ns(port, buf, len) + * insl_ns(port, buf, len) + * outsl_ns(port, buf, len) + * + * The *_ns versions don't do byte-swapping. + */ +_GLOBAL(_insb) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,1 + blelr- +00: lbz r5,0(r3) + eieio + stbu r5,1(r4) + bdnz 00b + blr + +_GLOBAL(_outsb) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,1 + blelr- +00: lbzu r5,1(r4) + stb r5,0(r3) + eieio + bdnz 00b + blr + +_GLOBAL(_insw) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhbrx r5,0,r3 + eieio + sthu r5,2(r4) + bdnz 00b + blr + +_GLOBAL(_outsw) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhzu r5,2(r4) + eieio + sthbrx r5,0,r3 + bdnz 00b + blr + +_GLOBAL(_insl) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwbrx r5,0,r3 + eieio + stwu r5,4(r4) + bdnz 00b + blr + +_GLOBAL(_outsl) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwzu r5,4(r4) + stwbrx r5,0,r3 + eieio + bdnz 00b + blr + +_GLOBAL(ide_insw) +_GLOBAL(_insw_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhz r5,0(r3) + eieio + sthu r5,2(r4) + bdnz 00b + blr + +_GLOBAL(ide_outsw) +_GLOBAL(_outsw_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhzu r5,2(r4) + sth r5,0(r3) + eieio + bdnz 00b + blr + +_GLOBAL(_insl_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwz r5,0(r3) + eieio + stwu r5,4(r4) + bdnz 00b + blr + +_GLOBAL(_outsl_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwzu r5,4(r4) + stw r5,0(r3) + eieio + bdnz 00b + blr + +/* + * Extended precision shifts + * + * R3/R4 has 64 bit value + * R5 has shift count + * result in R3/R4 + * + * ashrdi3: XXXYYY/ZZZAAA -> SSSXXX/YYYZZZ + * ashldi3: XXXYYY/ZZZAAA -> YYYZZZ/AAA000 + * lshrdi3: XXXYYY/ZZZAAA -> 000XXX/YYYZZZ + */ +/* MIKEC: These may no longer be needed...what does gcc expect ? */ + +_GLOBAL(__ashrdi3) + li r6,32 + sub r6,r6,r5 + slw r7,r3,r6 /* isolate YYY */ + srw r4,r4,r5 /* isolate ZZZ */ + or r4,r4,r7 /* YYYZZZ */ + sraw r3,r3,r5 /* SSSXXX */ + blr + +_GLOBAL(__ashldi3) + li r6,32 + sub r6,r6,r5 + srw r7,r4,r6 /* isolate ZZZ */ + slw r4,r4,r5 /* AAA000 */ + slw r3,r3,r5 /* YYY--- */ + or r3,r3,r7 /* YYYZZZ */ + blr + +_GLOBAL(__lshrdi3) + li r6,32 + sub r6,r6,r5 + slw r7,r3,r6 /* isolate YYY */ + srw r4,r4,r5 /* isolate ZZZ */ + or r4,r4,r7 /* YYYZZZ */ + srw r3,r3,r5 /* 000XXX */ + blr + +_GLOBAL(abs) + cmpi 0,r3,0 + bge 10f + neg r3,r3 +10: blr + +_GLOBAL(_get_SP) + mr r3,r1 /* Close enough */ + blr + +_GLOBAL(_get_PVR) + mfspr r3,PVR + blr + +_GLOBAL(_get_HID0) + mfspr r3,HID0 + blr + +_GLOBAL(_get_ICTC) + mfspr r3,ICTC + blr + +_GLOBAL(_set_ICTC) + mtspr ICTC,r3 + blr + + + +_GLOBAL(cvt_fd) + lfd 0,-4(r5) /* load up fpscr value */ + mtfsf 0xff,0 + lfs 0,0(r3) + stfd 0,0(r4) + mffs 0 /* save new fpscr value */ + stfd 0,-4(r5) + blr + +_GLOBAL(cvt_df) + lfd 0,-4(r5) /* load up fpscr value */ + mtfsf 0xff,0 + lfd 0,0(r3) + stfs 0,0(r4) + mffs 0 /* save new fpscr value */ + stfd 0,-4(r5) + blr + +_GLOBAL(__clear_msr_me) + mfmsr r0 /* Get current interrupt state */ + lis r3,0 + ori r3,r3,MSR_ME + andc r0,r0,r3 /* Clears bit in (r4) */ + sync /* Some chip revs have problems here */ + mtmsrd r0 /* Update machine state */ + blr + +/* + * Create a kernel thread + * kernel_thread(fn, arg, flags) + */ +_GLOBAL(kernel_thread) + mr r6,r3 /* function */ + ori r3,r5,CLONE_VM /* flags */ + li r0,__NR_clone + sc + cmpi 0,r3,0 /* parent or child? */ + bnelr /* return if parent */ + + mfspr r5,SPRG3 /* Get Paca */ + ld r5,PACACURRENT(r5) /* Get 'current' */ + li r0,0 /* clear out p->thread.regs */ + std r0,THREAD+PT_REGS(r5) /* since we don't have user ctx */ + + ld r6,0(r6) + mtlr r6 /* fn addr in lr */ + mr r3,r4 /* load arg and call fn */ + blrl + li r0,__NR_exit /* exit after child exits */ + li r3,0 + sc + +/* + * This routine is just here to keep GCC happy - sigh... + */ +_GLOBAL(__main) + blr + +#define SYSCALL(name) \ +_GLOBAL(name) \ + li r0,__NR_##name; \ + sc; \ + bnslr; \ + /* MIKEC: Is errno a 32-bit int or a 64-bit long ? */\ + LOADBASE(r4,errno); \ + stw r3,errno@l(r4); \ + li r3,-1; \ + blr + +#define __NR__exit __NR_exit + +SYSCALL(sync) +SYSCALL(setsid) +SYSCALL(write) +SYSCALL(dup) +SYSCALL(execve) +SYSCALL(open) +SYSCALL(close) +SYSCALL(waitpid) +SYSCALL(fork) +SYSCALL(delete_module) +SYSCALL(_exit) +SYSCALL(lseek) +SYSCALL(read) +#ifdef CONFIG_BINFMT_ELF32 +/* Why isn't this a) automatic, b) written in 'C'? */ + .data + .align 8 +_GLOBAL(sys_call_table32) + .llong .sys_ni_syscall /* 0 - old "setup()" system call */ + .llong .sys32_exit + .llong .sys32_fork + .llong .sys_read + .llong .sys_write + .llong .sys32_open /* 5 */ + .llong .sys_close + .llong .sys32_waitpid + .llong .sys32_creat + .llong .sys_link + .llong .sys_unlink /* 10 */ + .llong .sys32_execve + .llong .sys_chdir + .llong .sys_time + .llong .sys32_mknod + .llong .sys32_chmod /* 15 */ + .llong .sys_lchown + .llong .sys_ni_syscall /* old break syscall holder */ + .llong .sys32_stat + .llong .sys32_lseek + .llong .sys_getpid /* 20 */ + .llong .sys32_mount + .llong .sys_oldumount + .llong .sys_setuid + .llong .sys_getuid + .llong .sys_stime /* 25 */ + .llong .sys32_ptrace + .llong .sys_alarm + .llong .sys32_fstat + .llong .sys32_pause + .llong .sys32_utime /* 30 */ + .llong .sys_ni_syscall /* old stty syscall holder */ + .llong .sys_ni_syscall /* old gtty syscall holder */ + .llong .sys32_access + .llong .sys32_nice + .llong .sys_ni_syscall /* 35 */ /* old ftime syscall holder */ + .llong .sys_sync + .llong .sys32_kill + .llong .sys_rename + .llong .sys32_mkdir + .llong .sys_rmdir /* 40 */ + .llong .sys_dup + .llong .sys_pipe + .llong .sys32_times + .llong .sys_ni_syscall /* old prof syscall holder */ + .llong .sys_brk /* 45 */ + .llong .sys_setgid + .llong .sys_getgid + .llong .sys_signal + .llong .sys_geteuid + .llong .sys_getegid /* 50 */ + .llong .sys_acct + .llong .sys32_umount /* recycled never used phys() */ + .llong .sys_ni_syscall /* old lock syscall holder */ + .llong .sys32_ioctl + .llong .sys32_fcntl /* 55 */ + .llong .sys_ni_syscall /* old mpx syscall holder */ + .llong .sys32_setpgid + .llong .sys_ni_syscall /* old ulimit syscall holder */ + .llong .sys_olduname + .llong .sys32_umask /* 60 */ + .llong .sys_chroot + .llong .sys_ustat + .llong .sys_dup2 + .llong .sys_getppid + .llong .sys_getpgrp /* 65 */ + .llong .sys_setsid + .llong .sys32_sigaction + .llong .sys_sgetmask + .llong .sys32_ssetmask + .llong .sys_setreuid /* 70 */ + .llong .sys_setregid + .llong .sys_sigsuspend + .llong .sys32_sigpending + .llong .sys32_sethostname + .llong .sys32_setrlimit /* 75 */ + .llong .sys32_old_getrlimit + .llong .sys32_getrusage + .llong .sys32_gettimeofday + .llong .sys32_settimeofday + .llong .sys32_getgroups /* 80 */ + .llong .sys32_setgroups + .llong .ppc32_select + .llong .sys_symlink + .llong .sys32_lstat + .llong .sys32_readlink /* 85 */ + .llong .sys_uselib + .llong .sys32_swapon + .llong .sys32_reboot + .llong .old32_readdir + .llong .sys32_mmap /* 90 */ + .llong .sys_munmap + .llong .sys_truncate + .llong .sys_ftruncate + .llong .sys_fchmod + .llong .sys_fchown /* 95 */ + .llong .sys32_getpriority + .llong .sys32_setpriority + .llong .sys_ni_syscall /* old profil syscall holder */ + .llong .sys32_statfs + .llong .sys32_fstatfs /* 100 */ + .llong .sys32_ioperm + .llong .sys32_socketcall + .llong .sys32_syslog + .llong .sys32_setitimer + .llong .sys32_getitimer /* 105 */ + .llong .sys32_newstat + .llong .sys32_newlstat + .llong .sys32_newfstat + .llong .sys_uname + .llong .sys32_iopl /* 110 */ + .llong .sys_vhangup + .llong .sys_ni_syscall /* old 'idle' syscall */ + .llong .sys32_vm86 + .llong .sys32_wait4 + .llong .sys_swapoff /* 115 */ + .llong .sys32_sysinfo + .llong .sys32_ipc + .llong .sys_fsync + .llong .sys32_sigreturn + .llong .sys32_clone /* 120 */ + .llong .sys32_setdomainname + .llong .ppc64_newuname + .llong .sys32_modify_ldt + .llong .sys32_adjtimex + .llong .sys_mprotect /* 125 */ + .llong .sys32_sigprocmask + .llong .sys32_create_module + .llong .sys32_init_module + .llong .sys32_delete_module + .llong .sys32_get_kernel_syms /* 130 */ + .llong .sys32_quotactl + .llong .sys32_getpgid + .llong .sys_fchdir + .llong .sys32_bdflush + .llong .sys32_sysfs /* 135 */ + .llong .sys32_personality + .llong .sys_ni_syscall /* for afs_syscall */ + .llong .sys_setfsuid + .llong .sys_setfsgid + .llong .sys_llseek /* 140 */ + .llong .sys32_getdents + .llong .ppc32_select + .llong .sys_flock + .llong .sys32_msync + .llong .sys32_readv /* 145 */ + .llong .sys32_writev + .llong .sys32_getsid + .llong .sys_fdatasync + .llong .sys_sysctl + .llong .sys_mlock /* 150 */ + .llong .sys_munlock + .llong .sys32_mlockall + .llong .sys_munlockall + .llong .sys32_sched_setparam + .llong .sys32_sched_getparam /* 155 */ + .llong .sys32_sched_setscheduler + .llong .sys32_sched_getscheduler + .llong .sys_sched_yield + .llong .sys32_sched_get_priority_max + .llong .sys32_sched_get_priority_min /* 160 */ + .llong .sys32_sched_rr_get_interval + .llong .sys32_nanosleep + .llong .sys32_mremap + .llong .sys_setresuid + .llong .sys_getresuid /* 165 */ + .llong .sys32_query_module + .llong .sys_poll + .llong .sys32_nfsservctl + .llong .sys_setresgid + .llong .sys_getresgid /* 170 */ + .llong .sys32_prctl + .llong .sys32_rt_sigreturn + .llong .sys32_rt_sigaction + .llong .sys32_rt_sigprocmask + .llong .sys32_rt_sigpending /* 175 */ + .llong .sys32_rt_sigtimedwait + .llong .sys32_rt_sigqueueinfo + .llong .sys32_rt_sigsuspend + .llong .sys32_pread + .llong .sys32_pwrite /* 180 */ + .llong .sys_chown + .llong .sys_getcwd + .llong .sys_capget + .llong .sys_capset + .llong .sys32_sigaltstack /* 185 */ + .llong .sys32_sendfile + .llong .sys_ni_syscall /* streams1 */ + .llong .sys_ni_syscall /* streams2 */ + .llong .sys32_vfork + .llong .sys32_getrlimit /* 190 */ + .llong .sys_ni_syscall /* 191 */ /* Unused */ + .llong .sys_ni_syscall /* 192 - reserved - mmap2 */ + .llong .sys32_truncate64 /* 193 - truncate64 */ + .llong .sys32_ftruncate64 /* 194 - ftruncate64 */ + .llong .sys_stat64 /* 195 - stat64 */ + .llong .sys_lstat64 /* 196 - lstat64 */ + .llong .sys_fstat64 /* 197 - fstat64 */ + .llong .sys32_pciconfig_read /* 198 */ + .llong .sys32_pciconfig_write /* 199 */ + .llong .sys_pciconfig_iobase /* 200 */ + .llong .sys_ni_syscall /* 201 - reserved - MacOnLinux - new */ + .llong .sys_getdents64 /* 202 */ + .llong .sys_pivot_root /* 203 */ + .llong .sys32_fcntl64 /* 204 */ + .llong .sys_madvise /* 205 */ + .llong .sys_mincore /* 206 */ + .rept NR_syscalls-206 + .llong .sys_ni_syscall + .endr +#endif + .data + .align 8 +_GLOBAL(sys_call_table) + .llong .sys_ni_syscall /* 0 - old "setup()" system call */ + .llong .sys_exit + .llong .sys_fork + .llong .sys_read + .llong .sys_write + .llong .sys_open /* 5 */ + .llong .sys_close + .llong .sys_waitpid + .llong .sys_creat + .llong .sys_link + .llong .sys_unlink /* 10 */ + .llong .sys_execve + .llong .sys_chdir + .llong .sys_time + .llong .sys_mknod + .llong .sys_chmod /* 15 */ + .llong .sys_lchown + .llong .sys_ni_syscall /* old break syscall holder */ + .llong .sys_stat + .llong .sys_lseek + .llong .sys_getpid /* 20 */ + .llong .sys_mount + .llong .sys_oldumount + .llong .sys_setuid + .llong .sys_getuid + .llong .sys_stime /* 25 */ + .llong .sys_ptrace + .llong .sys_alarm + .llong .sys_fstat + .llong .sys_pause + .llong .sys_utime /* 30 */ + .llong .sys_ni_syscall /* old stty syscall holder */ + .llong .sys_ni_syscall /* old gtty syscall holder */ + .llong .sys_access + .llong .sys_nice + .llong .sys_ni_syscall /* 35 */ /* old ftime syscall holder */ + .llong .sys_sync + .llong .sys_kill + .llong .sys_rename + .llong .sys_mkdir + .llong .sys_rmdir /* 40 */ + .llong .sys_dup + .llong .sys_pipe + .llong .sys_times + .llong .sys_ni_syscall /* old prof syscall holder */ + .llong .sys_brk /* 45 */ + .llong .sys_setgid + .llong .sys_getgid + .llong .sys_signal + .llong .sys_geteuid + .llong .sys_getegid /* 50 */ + .llong .sys_acct + .llong .sys_umount /* recycled never used phys() */ + .llong .sys_ni_syscall /* old lock syscall holder */ + .llong .sys_ioctl + .llong .sys_fcntl /* 55 */ + .llong .sys_ni_syscall /* old mpx syscall holder */ + .llong .sys_setpgid + .llong .sys_ni_syscall /* old ulimit syscall holder */ + .llong .sys_olduname + .llong .sys_umask /* 60 */ + .llong .sys_chroot + .llong .sys_ustat + .llong .sys_dup2 + .llong .sys_getppid + .llong .sys_getpgrp /* 65 */ + .llong .sys_setsid + .llong .sys_sigaction + .llong .sys_sgetmask + .llong .sys_ssetmask + .llong .sys_setreuid /* 70 */ + .llong .sys_setregid + .llong .sys_sigsuspend + .llong .sys_sigpending + .llong .sys_sethostname + .llong .sys_setrlimit /* 75 */ + .llong .sys_old_getrlimit + .llong .sys_getrusage + .llong .sys_gettimeofday + .llong .sys_settimeofday + .llong .sys_getgroups /* 80 */ + .llong .sys_setgroups + .llong .sys_select + .llong .sys_symlink + .llong .sys_lstat + .llong .sys_readlink /* 85 */ + .llong .sys_uselib + .llong .sys_swapon + .llong .sys_reboot + .llong .old_readdir + .llong .sys_mmap /* 90 */ + .llong .sys_munmap + .llong .sys_truncate + .llong .sys_ftruncate + .llong .sys_fchmod + .llong .sys_fchown /* 95 */ + .llong .sys_getpriority + .llong .sys_setpriority + .llong .sys_ni_syscall /* old profil syscall holder */ + .llong .sys_statfs + .llong .sys_fstatfs /* 100 */ + .llong .sys_ioperm + .llong .sys_socketcall + .llong .sys_syslog + .llong .sys_setitimer + .llong .sys_getitimer /* 105 */ + .llong .sys_newstat + .llong .sys_newlstat + .llong .sys_newfstat + .llong .sys_uname + .llong .sys_iopl /* 110 */ + .llong .sys_vhangup + .llong .sys_ni_syscall /* old 'idle' syscall */ + .llong .sys_vm86 + .llong .sys_wait4 + .llong .sys_swapoff /* 115 */ + .llong .sys_sysinfo + .llong .sys_ipc + .llong .sys_fsync + .llong .sys_sigreturn + .llong .sys_clone /* 120 */ + .llong .sys_setdomainname + .llong .ppc64_newuname + .llong .sys_modify_ldt + .llong .sys_adjtimex + .llong .sys_mprotect /* 125 */ + .llong .sys_sigprocmask + .llong .sys_create_module + .llong .sys_init_module + .llong .sys_delete_module + .llong .sys_get_kernel_syms /* 130 */ + .llong .sys_quotactl + .llong .sys_getpgid + .llong .sys_fchdir + .llong .sys_bdflush + .llong .sys_sysfs /* 135 */ + .llong .sys_personality + .llong .sys_ni_syscall /* for afs_syscall */ + .llong .sys_setfsuid + .llong .sys_setfsgid + .llong .sys_llseek /* 140 */ + .llong .sys_getdents + .llong .sys_select + .llong .sys_flock + .llong .sys_msync + .llong .sys_readv /* 145 */ + .llong .sys_writev + .llong .sys_getsid + .llong .sys_fdatasync + .llong .sys_sysctl + .llong .sys_mlock /* 150 */ + .llong .sys_munlock + .llong .sys_mlockall + .llong .sys_munlockall + .llong .sys_sched_setparam + .llong .sys_sched_getparam /* 155 */ + .llong .sys_sched_setscheduler + .llong .sys_sched_getscheduler + .llong .sys_sched_yield + .llong .sys_sched_get_priority_max + .llong .sys_sched_get_priority_min /* 160 */ + .llong .sys_sched_rr_get_interval + .llong .sys_nanosleep + .llong .sys_mremap + .llong .sys_setresuid + .llong .sys_getresuid /* 165 */ + .llong .sys_query_module + .llong .sys_poll + .llong .sys_nfsservctl + .llong .sys_setresgid + .llong .sys_getresgid /* 170 */ + .llong .sys_prctl + .llong .sys_rt_sigreturn + .llong .sys_rt_sigaction + .llong .sys_rt_sigprocmask + .llong .sys_rt_sigpending /* 175 */ + .llong .sys_rt_sigtimedwait + .llong .sys_rt_sigqueueinfo + .llong .sys_rt_sigsuspend + .llong .sys_pread + .llong .sys_pwrite /* 180 */ + .llong .sys_chown + .llong .sys_getcwd + .llong .sys_capget + .llong .sys_capset + .llong .sys_sigaltstack /* 185 */ + .llong .sys_sendfile + .llong .sys_ni_syscall /* streams1 */ + .llong .sys_ni_syscall /* streams2 */ + .llong .sys_vfork + .llong .sys_getrlimit /* 190 */ + .llong .sys_ni_syscall /* 191 */ /* Unused */ + .llong .sys_ni_syscall /* 192 - reserved - mmap2 */ + .llong .sys_ni_syscall /* 193 - reserved - truncate64 */ + .llong .sys_ni_syscall /* 194 - reserved - ftruncate64 */ + .llong .sys_ni_syscall /* 195 - reserved - stat64 */ + .llong .sys_ni_syscall /* 196 - reserved - lstat64 */ + .llong .sys_ni_syscall /* 197 - reserved - fstat64 */ + .llong .sys_pciconfig_read /* 198 */ + .llong .sys_pciconfig_write /* 199 */ + .llong .sys_pciconfig_iobase /* 200 */ + .llong .sys_ni_syscall /* 201 - reserved - MacOnLinux - new */ + .llong .sys_getdents64 /* 202 */ + .llong .sys_pivot_root /* 203 */ + .llong .sys_ni_syscall /* 204 */ + .llong .sys_madvise /* 205 */ + .llong .sys_mincore /* 206 */ + .rept NR_syscalls-206 + .llong .sys_ni_syscall + .endr diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/mk_defs.c linux.ac/arch/ppc64/kernel/mk_defs.c --- linux.vanilla/arch/ppc64/kernel/mk_defs.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/mk_defs.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,138 @@ +/* + * This program is used to generate definitions needed by + * assembly language modules. + * + * We use the technique used in the OSF Mach kernel code: + * generate asm statements containing #defines, + * compile this file to assembler, and then extract the + * #defines from the assembly-language output. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DEFINE(sym, val) \ + asm volatile("\n#define\t" #sym "\t%0" : : "i" (val)) + +int +main(void) +{ + DEFINE(SIGPENDING, offsetof(struct task_struct, sigpending)); + DEFINE(THREAD, offsetof(struct task_struct, thread)); + DEFINE(MM, offsetof(struct task_struct, mm)); + DEFINE(TASK_STRUCT_SIZE, sizeof(struct task_struct)); + DEFINE(KSP, offsetof(struct thread_struct, ksp)); + + DEFINE(PACA, offsetof(struct Naca, paca)); + DEFINE(PACA_SIZE, sizeof(struct Paca)); + + DEFINE(DCACHEL1LINESIZE, offsetof(struct Naca, dCacheL1LineSize)); + DEFINE(DCACHEL1LOGLINESIZE, offsetof(struct Naca, dCacheL1LogLineSize)); + DEFINE(DCACHEL1LINESPERPAGE, offsetof(struct Naca, dCacheL1LinesPerPage)); + + DEFINE(ICACHEL1LINESIZE, offsetof(struct Naca, iCacheL1LineSize)); + DEFINE(ICACHEL1LOGLINESIZE, offsetof(struct Naca, iCacheL1LogLineSize)); + DEFINE(ICACHEL1LINESPERPAGE, offsetof(struct Naca, iCacheL1LinesPerPage)); + + DEFINE(PACAPACAINDEX, offsetof(struct Paca, xPacaIndex)); + DEFINE(PACAPROCSTART, offsetof(struct Paca, xProcStart)); + DEFINE(PACAKSAVE, offsetof(struct Paca, xKsave)); + DEFINE(PACACURRENT, offsetof(struct Paca, xCurrent)); + DEFINE(PACASAVEDMSR, offsetof(struct Paca, xSavedMsr)); + DEFINE(PACASTABREAL, offsetof(struct Paca, xStab_data.real)); + DEFINE(PACASTABVIRT, offsetof(struct Paca, xStab_data.virt)); + 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)); + DEFINE(LPPACA, offsetof(struct Paca, xLpPaca)); + DEFINE(LPPACASRR0, offsetof(struct ItLpPaca, xSavedSrr0)); + DEFINE(LPPACASRR1, offsetof(struct ItLpPaca, xSavedSrr1)); + DEFINE(LPPACAANYINT, offsetof(struct ItLpPaca, xIntDword.xAnyInt)); + DEFINE(LPPACADECRINT, offsetof(struct ItLpPaca, xIntDword.xFields.xDecrInt)); + DEFINE(LPQCUREVENTPTR, offsetof(struct ItLpQueue, xSlicCurEventPtr)); + DEFINE(LPQOVERFLOW, offsetof(struct ItLpQueue, xPlicOverflowIntPending)); + DEFINE(LPEVENTFLAGS, offsetof(struct HvLpEvent, xFlags)); + DEFINE(PROMENTRY, offsetof(struct prom_t, entry)); + + DEFINE(RTASBASE, offsetof(struct rtas_t, base)); + DEFINE(RTASENTRY, offsetof(struct rtas_t, entry)); + DEFINE(RTASSIZE, offsetof(struct rtas_t, size)); + + DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall)); + DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); + DEFINE(PT_TRACESYS, PT_TRACESYS); + DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace)); + DEFINE(NEED_RESCHED, offsetof(struct task_struct, need_resched)); + DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0])); + DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr)); + DEFINE(THREAD_FLAGS, offsetof(struct thread_struct, flags)); + DEFINE(PPC_FLAG_32BIT, PPC_FLAG_32BIT); + /* Interrupt register frame */ + DEFINE(TASK_UNION_SIZE, sizeof(union task_union)); + DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD); + /* 288 = # of volatile regs, int & fp, for leaf routines */ + /* which do not stack a frame. See the PPC64 ABI. */ + DEFINE(INT_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 288); + DEFINE(GPR0, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[0])); + DEFINE(GPR1, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[1])); + DEFINE(GPR2, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[2])); + DEFINE(GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[3])); + DEFINE(GPR4, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[4])); + DEFINE(GPR5, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[5])); + DEFINE(GPR6, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[6])); + DEFINE(GPR7, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[7])); + DEFINE(GPR8, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[8])); + DEFINE(GPR9, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[9])); + DEFINE(GPR20, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[20])); + DEFINE(GPR21, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[21])); + DEFINE(GPR22, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[22])); + DEFINE(GPR23, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[23])); + /* Note: these symbols include _ because they overlap with special + * register names + */ + DEFINE(_NIP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, nip)); + DEFINE(_MSR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, msr)); + DEFINE(_CTR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ctr)); + DEFINE(_LINK, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, link)); + DEFINE(_CCR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ccr)); + DEFINE(_XER, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, xer)); + DEFINE(_DAR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dar)); + DEFINE(_DSISR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr)); + DEFINE(ORIG_GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, orig_gpr3)); + DEFINE(RESULT, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, result)); + DEFINE(TRAP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, trap)); + DEFINE(CLONE_VM, CLONE_VM); + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/open_pic.c linux.ac/arch/ppc64/kernel/open_pic.c --- linux.vanilla/arch/ppc64/kernel/open_pic.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/open_pic.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,837 @@ +/* + * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "local_irq.h" +#include "open_pic.h" +#include "open_pic_defs.h" +#include "i8259.h" +#include + +void* OpenPIC_Addr; +static volatile struct OpenPIC *OpenPIC = NULL; +u_int OpenPIC_NumInitSenses __initdata = 0; +u_char *OpenPIC_InitSenses __initdata = NULL; +extern int use_of_interrupt_tree; + +void find_ISUs(void); + +static u_int NumProcessors; +static u_int NumSources; +static int NumISUs; +static int open_pic_irq_offset; +static volatile unsigned char* chrp_int_ack_special; +static int broken_ipi_registers; + +OpenPIC_SourcePtr ISU[OPENPIC_MAX_ISU]; + +static void openpic_end_irq(unsigned int irq_nr); +static void openpic_ack_irq(unsigned int irq_nr); +static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask); + +struct hw_interrupt_type open_pic = { + " OpenPIC ", + NULL, + NULL, + openpic_enable_irq, + openpic_disable_irq, + openpic_ack_irq, + openpic_end_irq, + openpic_set_affinity +}; + +#ifdef CONFIG_SMP +static void openpic_end_ipi(unsigned int irq_nr); +static void openpic_ack_ipi(unsigned int irq_nr); +static void openpic_enable_ipi(unsigned int irq_nr); +static void openpic_disable_ipi(unsigned int irq_nr); + +struct hw_interrupt_type open_pic_ipi = { + " OpenPIC ", + NULL, + NULL, + openpic_enable_ipi, + openpic_disable_ipi, + openpic_ack_ipi, + openpic_end_ipi, + 0 +}; +#endif /* CONFIG_SMP */ + +unsigned int openpic_vec_ipi; +unsigned int openpic_vec_timer; +unsigned int openpic_vec_spurious; + +/* + * Accesses to the current processor's openpic registers + */ +#ifdef CONFIG_SMP +#define THIS_CPU Processor[cpu] +#define DECL_THIS_CPU int cpu = cpu_hw_index[smp_processor_id()] +#define CHECK_THIS_CPU check_arg_cpu(cpu) +#else +#define THIS_CPU Processor[cpu_hw_index[0]] +#define DECL_THIS_CPU +#define CHECK_THIS_CPU +#endif /* CONFIG_SMP */ + +#if 1 +#define check_arg_ipi(ipi) \ + if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ + printk(KERN_ERR "open_pic.c:%d: illegal ipi %d\n", __LINE__, ipi); +#define check_arg_timer(timer) \ + if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ + printk(KERN_ERR "open_pic.c:%d: illegal timer %d\n", __LINE__, timer); +#define check_arg_vec(vec) \ + if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ + printk(KERN_ERR "open_pic.c:%d: illegal vector %d\n", __LINE__, vec); +#define check_arg_pri(pri) \ + if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ + printk(KERN_ERR "open_pic.c:%d: illegal priority %d\n", __LINE__, pri); +/* + * Print out a backtrace if it's out of range, since if it's larger than NR_IRQ's + * data has probably been corrupted and we're going to panic or deadlock later + * anyway --Troy + */ +extern unsigned long* _get_SP(void); +#define check_arg_irq(irq) \ + if (irq < open_pic_irq_offset || irq >= (NumSources+open_pic_irq_offset)){ \ + printk(KERN_ERR "open_pic.c:%d: illegal irq %d\n", __LINE__, irq); \ + print_backtrace(_get_SP()); } +#define check_arg_cpu(cpu) \ + if (cpu < 0 || cpu >= OPENPIC_MAX_PROCESSORS){ \ + printk(KERN_ERR "open_pic.c:%d: illegal cpu %d\n", __LINE__, cpu); \ + print_backtrace(_get_SP()); } +#else +#define check_arg_ipi(ipi) do {} while (0) +#define check_arg_timer(timer) do {} while (0) +#define check_arg_vec(vec) do {} while (0) +#define check_arg_pri(pri) do {} while (0) +#define check_arg_irq(irq) do {} while (0) +#define check_arg_cpu(cpu) do {} while (0) +#endif + +#define GET_ISU(source) ISU[(source) >> 4][(source) & 0xf] + +void __init openpic_init_IRQ(void) +{ + struct device_node *np; + int i; + unsigned int *addrp; + unsigned char* chrp_int_ack_special = 0; + unsigned char init_senses[NR_IRQS - NUM_8259_INTERRUPTS]; + int nmi_irq = -1; +#if defined(CONFIG_VT) && defined(CONFIG_ADB_KEYBOARD) && defined(XMON) + struct device_node *kbd; +#endif + + if (!(np = find_devices("pci")) + || !(addrp = (unsigned int *) + get_property(np, "8259-interrupt-acknowledge", NULL))) + printk(KERN_ERR "Cannot find pci to get ack address\n"); + else + chrp_int_ack_special = (unsigned char *) + ioremap(addrp[prom_n_addr_cells(np)-1], 1); + /* hydra still sets OpenPIC_InitSenses to a static set of values */ + if (OpenPIC_InitSenses == NULL) { + prom_get_irq_senses(init_senses, NUM_8259_INTERRUPTS, NR_IRQS); + OpenPIC_InitSenses = init_senses; + OpenPIC_NumInitSenses = NR_IRQS - NUM_8259_INTERRUPTS; + } + openpic_init(1, NUM_8259_INTERRUPTS, chrp_int_ack_special, nmi_irq); + for ( i = 0 ; i < NUM_8259_INTERRUPTS ; i++ ) + irq_desc[i].handler = &i8259_pic; + i8259_init(); +} + +u_int openpic_read(volatile u_int *addr) +{ + u_int val; + + val = in_le32(addr); + return val; +} + +static inline void openpic_write(volatile u_int *addr, u_int val) +{ + out_le32(addr, val); +} + +static inline u_int openpic_readfield(volatile u_int *addr, u_int mask) +{ + u_int val = openpic_read(addr); + return val & mask; +} + +inline void openpic_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + u_int val = openpic_read(addr); + openpic_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic_clearfield(volatile u_int *addr, u_int mask) +{ + openpic_writefield(addr, mask, 0); +} + +static inline void openpic_setfield(volatile u_int *addr, u_int mask) +{ + openpic_writefield(addr, mask, mask); +} + +static void openpic_safe_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + unsigned int loops = 100000; + + openpic_setfield(addr, OPENPIC_MASK); + while (openpic_read(addr) & OPENPIC_ACTIVITY) { + if (!loops--) { + printk(KERN_ERR "openpic_safe_writefield timeout\n"); + break; + } + } + openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); +} + +#ifdef CONFIG_SMP +u_int openpic_read_IPI(volatile u_int* addr) +{ + u_int val = 0; + + if (broken_ipi_registers) + /* yes this is right ... bug, feature, you decide! -- tgall */ + val = in_be32(addr); + else + val = in_le32(addr); + + return val; +} + +static void openpic_test_broken_IPI(void) +{ + u_int t; + + openpic_write(&OpenPIC->Global.IPI_Vector_Priority(0), OPENPIC_MASK); + t = openpic_read(&OpenPIC->Global.IPI_Vector_Priority(0)); + if (t == le32_to_cpu(OPENPIC_MASK)) { + printk(KERN_INFO "OpenPIC reversed IPI registers detected\n"); + broken_ipi_registers = 1; + } +} + +/* because of the power3 be / le above, this is needed */ +inline void openpic_writefield_IPI(volatile u_int* addr, u_int mask, u_int field) +{ + u_int val = openpic_read_IPI(addr); + openpic_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic_clearfield_IPI(volatile u_int *addr, u_int mask) +{ + openpic_writefield_IPI(addr, mask, 0); +} + +static inline void openpic_setfield_IPI(volatile u_int *addr, u_int mask) +{ + openpic_writefield_IPI(addr, mask, mask); +} + +static void openpic_safe_writefield_IPI(volatile u_int *addr, u_int mask, u_int field) +{ + unsigned int loops = 100000; + + openpic_setfield_IPI(addr, OPENPIC_MASK); + + /* wait until it's not in use */ + /* BenH: Is this code really enough ? I would rather check the result + * and eventually retry ... + */ + while(openpic_read_IPI(addr) & OPENPIC_ACTIVITY) { + if (!loops--) { + printk(KERN_ERR "openpic_safe_writefield timeout\n"); + break; + } + } + + openpic_writefield_IPI(addr, mask, field | OPENPIC_MASK); +} +#endif /* CONFIG_SMP */ + +void __init openpic_init(int main_pic, int offset, unsigned char* chrp_ack, + int programmer_switch_irq) +{ + u_int t, i; + u_int timerfreq; + const char *version; + + if (!OpenPIC_Addr) { + printk(KERN_INFO "No OpenPIC found !\n"); + return; + } + OpenPIC = (volatile struct OpenPIC *)OpenPIC_Addr; + + ppc_md.progress("openpic enter",0x122); + + t = openpic_read(&OpenPIC->Global.Feature_Reporting0); + switch (t & OPENPIC_FEATURE_VERSION_MASK) { + case 1: + version = "1.0"; + break; + case 2: + version = "1.2"; + break; + case 3: + version = "1.3"; + break; + default: + version = "?"; + break; + } + NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; + NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> + OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; + printk(KERN_INFO "OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", + version, NumProcessors, NumSources, OpenPIC); + timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); + if (timerfreq) + printk(KERN_INFO "OpenPIC timer frequency is %d.%06d MHz\n", + timerfreq / 1000000, timerfreq % 1000000); + + if (!main_pic) + return; + + open_pic_irq_offset = offset; + chrp_int_ack_special = (volatile unsigned char*)chrp_ack; + + find_ISUs(); + + /* Initialize timer interrupts */ + ppc_md.progress("openpic timer",0x3ba); + for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { + /* Disabled, Priority 0 */ + openpic_inittimer(i, 0, openpic_vec_timer+i); + /* No processor */ + openpic_maptimer(i, 0); + } + +#ifdef CONFIG_SMP + /* Initialize IPI interrupts */ + ppc_md.progress("openpic ipi",0x3bb); + openpic_test_broken_IPI(); + for (i = 0; i < OPENPIC_NUM_IPI; i++) { + /* Disabled, Priority 10..13 */ + openpic_initipi(i, 10+i, openpic_vec_ipi+i); + /* IPIs are per-CPU */ + irq_desc[openpic_vec_ipi+i].status |= IRQ_PER_CPU; + irq_desc[openpic_vec_ipi+i].handler = &open_pic_ipi; + } +#endif + + /* Initialize external interrupts */ + ppc_md.progress("openpic ext",0x3bc); + + openpic_set_priority(0xf); + + /* SIOint (8259 cascade) is special */ + if (offset) { + openpic_initirq(0, 8, offset, 1, 1); + openpic_mapirq(0, 1<= OPENPIC_MAX_ISU) + return; + ISU[isu_num] = (OpenPIC_SourcePtr) ioremap(addr, 0x400); + if (isu_num >= NumISUs) + NumISUs = isu_num + 1; +} + +void find_ISUs(void) +{ + /* Use /interrupt-controller/reg and + * /interrupt-controller/interrupt-ranges from OF device tree + * the ISU array is setup in chrp_pci.c in ibm_add_bridges + * as a result + * -- tgall + */ + + /* basically each ISU is a bus, and this assumes that + * open_pic_isu_count interrupts per bus are possible + * ISU == Interrupt Source + */ + NumSources = NumISUs * 0x10; + openpic_vec_ipi = NumSources + open_pic_irq_offset; + openpic_vec_timer = openpic_vec_ipi + OPENPIC_NUM_IPI; + openpic_vec_spurious = openpic_vec_timer + OPENPIC_NUM_TIMERS; +} + +static inline void openpic_reset(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET); +} + +static inline void openpic_enable_8259_pass_through(void) +{ + openpic_clearfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +static void openpic_disable_8259_pass_through(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +/* + * Find out the current interrupt + */ +static u_int openpic_irq(void) +{ + u_int vec; + DECL_THIS_CPU; + + CHECK_THIS_CPU; + vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge, + OPENPIC_VECTOR_MASK); + return vec; +} + +static void openpic_eoi(void) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + openpic_write(&OpenPIC->THIS_CPU.EOI, 0); + /* Handle PCI write posting */ + (void)openpic_read(&OpenPIC->THIS_CPU.EOI); +} + + +static inline u_int openpic_get_priority(void) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK); +} + +static void openpic_set_priority(u_int pri) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + check_arg_pri(pri); + openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); +} + +/* + * Get/set the spurious vector + */ +static inline u_int openpic_get_spurious(void) +{ + return openpic_readfield(&OpenPIC->Global.Spurious_Vector, + OPENPIC_VECTOR_MASK); +} + +static void openpic_set_spurious(u_int vec) +{ + check_arg_vec(vec); + openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, + vec); +} + +/* + * Convert a cpu mask from logical to physical cpu numbers. + */ +static inline u32 physmask(u32 cpumask) +{ + int i; + u32 mask = 0; + + for (i = 0; i < smp_num_cpus; ++i, cpumask >>= 1) + mask |= (cpumask & 1) << cpu_hw_index[i]; + return mask; +} + +void openpic_init_processor(u_int cpumask) +{ + openpic_write(&OpenPIC->Global.Processor_Initialization, + physmask(cpumask)); +} + +#ifdef CONFIG_SMP +/* + * Initialize an interprocessor interrupt (and disable it) + * + * ipi: OpenPIC interprocessor interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + */ +static void __init openpic_initipi(u_int ipi, u_int pri, u_int vec) +{ + check_arg_ipi(ipi); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield_IPI(&OpenPIC->Global.IPI_Vector_Priority(ipi), + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + +/* + * Send an IPI to one or more CPUs + * + * Externally called, however, it takes an IPI number (0...OPENPIC_NUM_IPI) + * and not a system-wide interrupt number + */ +void openpic_cause_IPI(u_int ipi, u_int cpumask) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + check_arg_ipi(ipi); + openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), + physmask(cpumask)); +} + +void openpic_request_IPIs(void) +{ + int i; + + /* + * Make sure this matches what is defined in smp.c for + * smp_message_{pass|recv}() or what shows up in + * /proc/interrupts will be wrong!!! --Troy */ + + if (OpenPIC == NULL) + return; + + request_irq(openpic_vec_ipi, + openpic_ipi_action, 0, "IPI0 (call function)", 0); + request_irq(openpic_vec_ipi+1, + openpic_ipi_action, 0, "IPI1 (reschedule)", 0); + request_irq(openpic_vec_ipi+2, + openpic_ipi_action, 0, "IPI2 (invalidate tlb)", 0); + request_irq(openpic_vec_ipi+3, + openpic_ipi_action, 0, "IPI3 (xmon break)", 0); + + for ( i = 0; i < OPENPIC_NUM_IPI ; i++ ) + openpic_enable_ipi(openpic_vec_ipi+i); +} + +/* + * Do per-cpu setup for SMP systems. + * + * Get IPI's working and start taking interrupts. + * -- Cort + */ +static spinlock_t openpic_setup_lock __initdata = SPIN_LOCK_UNLOCKED; + +void __init do_openpic_setup_cpu(void) +{ +#ifdef CONFIG_IRQ_ALL_CPUS + int i; + u32 msk = 1 << cpu_hw_index[smp_processor_id()]; +#endif + + spin_lock(&openpic_setup_lock); + +#ifdef CONFIG_IRQ_ALL_CPUS + /* let the openpic know we want intrs. default affinity + * is 0xffffffff until changed via /proc + * That's how it's done on x86. If we want it differently, then + * we should make sure we also change the default values of irq_affinity + * in irq.c. + */ + for (i = 0; i < NumSources ; i++) + openpic_mapirq(i, openpic_read(&GET_ISU(i).Destination) | msk); +#endif /* CONFIG_IRQ_ALL_CPUS */ + openpic_set_priority(0); + + spin_unlock(&openpic_setup_lock); +} +#endif /* CONFIG_SMP */ + +/* + * Initialize a timer interrupt (and disable it) + * + * timer: OpenPIC timer number + * pri: interrupt source priority + * vec: the vector it will produce + */ +static void __init openpic_inittimer(u_int timer, u_int pri, u_int vec) +{ + check_arg_timer(timer); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + +/* + * Map a timer interrupt to one or more CPUs + */ +static void __init openpic_maptimer(u_int timer, u_int cpumask) +{ + check_arg_timer(timer); + openpic_write(&OpenPIC->Global.Timer[timer].Destination, + physmask(cpumask)); +} + + +/* + * + * All functions below take an offset'ed irq argument + * + */ + + +/* + * Enable/disable an external interrupt source + * + * Externally called, irq is an offseted system-wide interrupt number + */ +static void openpic_enable_irq(u_int irq) +{ + unsigned int loops = 100000; + check_arg_irq(irq); + + openpic_clearfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + if (!loops--) { + printk(KERN_ERR "openpic_enable_irq timeout\n"); + break; + } + + mb(); /* sync is probably useless here */ + } while(openpic_readfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, + OPENPIC_MASK)); +} + +static void openpic_disable_irq(u_int irq) +{ + u32 vp; + unsigned int loops = 100000; + + check_arg_irq(irq); + + openpic_setfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + if (!loops--) { + printk(KERN_ERR "openpic_disable_irq timeout\n"); + break; + } + + mb(); /* sync is probably useless here */ + vp = openpic_readfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, + OPENPIC_MASK | OPENPIC_ACTIVITY); + } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK)); +} + +#ifdef CONFIG_SMP +/* + * Enable/disable an IPI interrupt source + * + * Externally called, irq is an offseted system-wide interrupt number + */ +void openpic_enable_ipi(u_int irq) +{ + irq -= openpic_vec_ipi; + check_arg_ipi(irq); + openpic_clearfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK); + +} +void openpic_disable_ipi(u_int irq) +{ + /* NEVER disable an IPI... that's just plain wrong! */ +} + +#endif + +/* + * Initialize an interrupt source (and disable it!) + * + * irq: OpenPIC interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + * pol: polarity (1 for positive, 0 for negative) + * sense: 1 for level, 0 for edge + */ +static void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) +{ + openpic_safe_writefield(&GET_ISU(irq).Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec | + (pol ? OPENPIC_POLARITY_POSITIVE : + OPENPIC_POLARITY_NEGATIVE) | + (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE)); +} + +/* + * Map an interrupt source to one or more CPUs + */ +static void openpic_mapirq(u_int irq, u_int physmask) +{ + openpic_write(&GET_ISU(irq).Destination, physmask); +} + +/* + * Set the sense for an interrupt source (and disable it!) + * + * sense: 1 for level, 0 for edge + */ +static inline void openpic_set_sense(u_int irq, int sense) +{ + openpic_safe_writefield(&GET_ISU(irq).Vector_Priority, + OPENPIC_SENSE_LEVEL, + (sense ? OPENPIC_SENSE_LEVEL : 0)); +} + +/* No spinlocks, should not be necessary with the OpenPIC + * (1 register = 1 interrupt and we have the desc lock). + */ +static void openpic_ack_irq(unsigned int irq_nr) +{ +#if 1 /* masking should be unnecessary, but I still get spurrious */ + openpic_disable_irq(irq_nr); +#endif + if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0) + openpic_eoi(); +} + +static void openpic_end_irq(unsigned int irq_nr) +{ + if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0) + openpic_eoi(); + +#if 1 /* masking should be unnecessary, but I still get spurrious */ + if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + openpic_enable_irq(irq_nr); +#endif +} + +static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask) +{ + openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask)); +} + +#ifdef CONFIG_SMP +static void openpic_ack_ipi(unsigned int irq_nr) +{ +} + +static void openpic_end_ipi(unsigned int irq_nr) +{ + /* IPIs are marked IRQ_PER_CPU. This has the side effect of + * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from + * applying to them. We EOI them late to avoid re-entering. + * however, I'm wondering if we could simply let them have the + * SA_INTERRUPT flag and let them execute with all interrupts OFF. + * This would have the side effect of either running cross-CPU + * functions with interrupts off, or we can re-enable them explicitely + * with a __sti() in smp_call_function_interrupt(), since + * smp_call_function() is protected by a spinlock. + * Or maybe we shouldn't set the IRQ_PER_CPU flag on cross-CPU + * function calls IPI at all but that would make a special case. + */ + openpic_eoi(); +} + +static void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + smp_message_recv(cpl-openpic_vec_ipi, regs); +} + +#endif /* CONFIG_SMP */ + +int openpic_get_irq(struct pt_regs *regs) +{ + extern int i8259_irq(int cpu); + + int irq = openpic_irq(); + + /* Management of the cascade should be moved out of here */ + if (open_pic_irq_offset && irq == open_pic_irq_offset) + { + /* + * This magic address generates a PCI IACK cycle. + */ + if ( chrp_int_ack_special ) + irq = *chrp_int_ack_special; + else + irq = i8259_irq( smp_processor_id() ); + openpic_eoi(); + } + if (irq == openpic_vec_spurious) + irq = -1; + return irq; +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/open_pic.h linux.ac/arch/ppc64/kernel/open_pic.h --- linux.vanilla/arch/ppc64/kernel/open_pic.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/open_pic.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,43 @@ +/* + * arch/ppc/kernel/open_pic.h -- OpenPIC Interrupt Handling + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + */ + +#ifndef _PPC64_KERNEL_OPEN_PIC_H +#define _PPC64_KERNEL_OPEN_PIC_H + +#define OPENPIC_SIZE 0x40000 + +/* OpenPIC IRQ controller structure */ +extern struct hw_interrupt_type open_pic; + +/* OpenPIC IPI controller structure */ +#ifdef CONFIG_SMP +extern struct hw_interrupt_type open_pic_ipi; +#endif /* CONFIG_SMP */ + +extern u_int OpenPIC_NumInitSenses; +extern u_char *OpenPIC_InitSenses; +extern void* OpenPIC_Addr; + +/* Exported functions */ +extern void openpic_init(int, int, unsigned char *, int); +extern void openpic_request_IPIs(void); +extern void do_openpic_setup_cpu(void); +extern int openpic_get_irq(struct pt_regs *regs); +extern void openpic_init_processor(u_int cpumask); +extern void openpic_setup_ISU(int isu_num, unsigned long addr); +extern void openpic_cause_IPI(u_int ipi, u_int cpumask); + +extern inline int openpic_to_irq(int irq) +{ + return irq += NUM_8259_INTERRUPTS; +} +/*extern int open_pic_irq_offset;*/ +#endif /* _PPC64_KERNEL_OPEN_PIC_H */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/open_pic_defs.h linux.ac/arch/ppc64/kernel/open_pic_defs.h --- linux.vanilla/arch/ppc64/kernel/open_pic_defs.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/open_pic_defs.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,318 @@ +/* + * linux/openpic.h -- OpenPIC definitions + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This file is based on the following documentation: + * + * The Open Programmable Interrupt Controller (PIC) + * Register Interface Specification Revision 1.2 + * + * Issue Date: October 1995 + * + * Issued jointly by Advanced Micro Devices and Cyrix Corporation + * + * AMD is a registered trademark of Advanced Micro Devices, Inc. + * Copyright (C) 1995, Advanced Micro Devices, Inc. and Cyrix, Inc. + * All Rights Reserved. + * + * To receive a copy of this documentation, send an email to openpic@amd.com. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#ifndef _LINUX_OPENPIC_H +#define _LINUX_OPENPIC_H + +#ifdef __KERNEL__ + +#include + + /* + * OpenPIC supports up to 2048 interrupt sources and up to 32 processors + */ + +#define OPENPIC_MAX_SOURCES 2048 +#define OPENPIC_MAX_PROCESSORS 32 +#define OPENPIC_MAX_ISU 32 + +#define OPENPIC_NUM_TIMERS 4 +#define OPENPIC_NUM_IPI 4 +#define OPENPIC_NUM_PRI 16 +#define OPENPIC_NUM_VECTORS OPENPIC_MAX_SOURCES + + /* + * OpenPIC Registers are 32 bits and aligned on 128 bit boundaries + */ + +typedef struct _OpenPIC_Reg { + u_int Reg; /* Little endian! */ + char Pad[0xc]; +} OpenPIC_Reg; + + + /* + * Per Processor Registers + */ + +typedef struct _OpenPIC_Processor { + /* + * Private Shadow Registers (for SLiC backwards compatibility) + */ + u_int IPI0_Dispatch_Shadow; /* Write Only */ + char Pad1[0x4]; + u_int IPI0_Vector_Priority_Shadow; /* Read/Write */ + char Pad2[0x34]; + /* + * Interprocessor Interrupt Command Ports + */ + OpenPIC_Reg _IPI_Dispatch[OPENPIC_NUM_IPI]; /* Write Only */ + /* + * Current Task Priority Register + */ + OpenPIC_Reg _Current_Task_Priority; /* Read/Write */ + char Pad3[0x10]; + /* + * Interrupt Acknowledge Register + */ + OpenPIC_Reg _Interrupt_Acknowledge; /* Read Only */ + /* + * End of Interrupt (EOI) Register + */ + OpenPIC_Reg _EOI; /* Read/Write */ + char Pad5[0xf40]; +} OpenPIC_Processor; + + + /* + * Timer Registers + */ + +typedef struct _OpenPIC_Timer { + OpenPIC_Reg _Current_Count; /* Read Only */ + OpenPIC_Reg _Base_Count; /* Read/Write */ + OpenPIC_Reg _Vector_Priority; /* Read/Write */ + OpenPIC_Reg _Destination; /* Read/Write */ +} OpenPIC_Timer; + + + /* + * Global Registers + */ + +typedef struct _OpenPIC_Global { + /* + * Feature Reporting Registers + */ + OpenPIC_Reg _Feature_Reporting0; /* Read Only */ + OpenPIC_Reg _Feature_Reporting1; /* Future Expansion */ + /* + * Global Configuration Registers + */ + OpenPIC_Reg _Global_Configuration0; /* Read/Write */ + OpenPIC_Reg _Global_Configuration1; /* Future Expansion */ + /* + * Vendor Specific Registers + */ + OpenPIC_Reg _Vendor_Specific[4]; + /* + * Vendor Identification Register + */ + OpenPIC_Reg _Vendor_Identification; /* Read Only */ + /* + * Processor Initialization Register + */ + OpenPIC_Reg _Processor_Initialization; /* Read/Write */ + /* + * IPI Vector/Priority Registers + */ + OpenPIC_Reg _IPI_Vector_Priority[OPENPIC_NUM_IPI]; /* Read/Write */ + /* + * Spurious Vector Register + */ + OpenPIC_Reg _Spurious_Vector; /* Read/Write */ + /* + * Global Timer Registers + */ + OpenPIC_Reg _Timer_Frequency; /* Read/Write */ + OpenPIC_Timer Timer[OPENPIC_NUM_TIMERS]; + char Pad1[0xee00]; +} OpenPIC_Global; + + + /* + * Interrupt Source Registers + */ + +typedef struct _OpenPIC_Source { + OpenPIC_Reg _Vector_Priority; /* Read/Write */ + OpenPIC_Reg _Destination; /* Read/Write */ +} OpenPIC_Source, *OpenPIC_SourcePtr; + + + /* + * OpenPIC Register Map + */ + +struct OpenPIC { + char Pad1[0x1000]; + /* + * Global Registers + */ + OpenPIC_Global Global; + /* + * Interrupt Source Configuration Registers + */ + OpenPIC_Source Source[OPENPIC_MAX_SOURCES]; + /* + * Per Processor Registers + */ + OpenPIC_Processor Processor[OPENPIC_MAX_PROCESSORS]; +}; + +extern volatile struct OpenPIC *OpenPIC; + + + /* + * Current Task Priority Register + */ + +#define OPENPIC_CURRENT_TASK_PRIORITY_MASK 0x0000000f + + /* + * Who Am I Register + */ + +#define OPENPIC_WHO_AM_I_ID_MASK 0x0000001f + + /* + * Feature Reporting Register 0 + */ + +#define OPENPIC_FEATURE_LAST_SOURCE_MASK 0x07ff0000 +#define OPENPIC_FEATURE_LAST_SOURCE_SHIFT 16 +#define OPENPIC_FEATURE_LAST_PROCESSOR_MASK 0x00001f00 +#define OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT 8 +#define OPENPIC_FEATURE_VERSION_MASK 0x000000ff + + /* + * Global Configuration Register 0 + */ + +#define OPENPIC_CONFIG_RESET 0x80000000 +#define OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE 0x20000000 +#define OPENPIC_CONFIG_BASE_MASK 0x000fffff + + /* + * Vendor Identification Register + */ + +#define OPENPIC_VENDOR_ID_STEPPING_MASK 0x00ff0000 +#define OPENPIC_VENDOR_ID_STEPPING_SHIFT 16 +#define OPENPIC_VENDOR_ID_DEVICE_ID_MASK 0x0000ff00 +#define OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT 8 +#define OPENPIC_VENDOR_ID_VENDOR_ID_MASK 0x000000ff + + /* + * Vector/Priority Registers + */ + +#define OPENPIC_MASK 0x80000000 +#define OPENPIC_ACTIVITY 0x40000000 /* Read Only */ +#define OPENPIC_PRIORITY_MASK 0x000f0000 +#define OPENPIC_PRIORITY_SHIFT 16 +#define OPENPIC_VECTOR_MASK 0x000007ff + + + /* + * Interrupt Source Registers + */ + +#define OPENPIC_POLARITY_POSITIVE 0x00800000 +#define OPENPIC_POLARITY_NEGATIVE 0x00000000 +#define OPENPIC_POLARITY_MASK 0x00800000 +#define OPENPIC_SENSE_LEVEL 0x00400000 +#define OPENPIC_SENSE_EDGE 0x00000000 +#define OPENPIC_SENSE_MASK 0x00400000 + + + /* + * Timer Registers + */ + +#define OPENPIC_COUNT_MASK 0x7fffffff +#define OPENPIC_TIMER_TOGGLE 0x80000000 +#define OPENPIC_TIMER_COUNT_INHIBIT 0x80000000 + + + /* + * Aliases to make life simpler + */ + +/* Per Processor Registers */ +#define IPI_Dispatch(i) _IPI_Dispatch[i].Reg +#define Current_Task_Priority _Current_Task_Priority.Reg +#define Interrupt_Acknowledge _Interrupt_Acknowledge.Reg +#define EOI _EOI.Reg + +/* Global Registers */ +#define Feature_Reporting0 _Feature_Reporting0.Reg +#define Feature_Reporting1 _Feature_Reporting1.Reg +#define Global_Configuration0 _Global_Configuration0.Reg +#define Global_Configuration1 _Global_Configuration1.Reg +#define Vendor_Specific(i) _Vendor_Specific[i].Reg +#define Vendor_Identification _Vendor_Identification.Reg +#define Processor_Initialization _Processor_Initialization.Reg +#define IPI_Vector_Priority(i) _IPI_Vector_Priority[i].Reg +#define Spurious_Vector _Spurious_Vector.Reg +#define Timer_Frequency _Timer_Frequency.Reg + +/* Timer Registers */ +#define Current_Count _Current_Count.Reg +#define Base_Count _Base_Count.Reg +#define Vector_Priority _Vector_Priority.Reg +#define Destination _Destination.Reg + +/* Interrupt Source Registers */ +#define Vector_Priority _Vector_Priority.Reg +#define Destination _Destination.Reg + + /* + * Local (static) OpenPIC Operations + */ + + +/* Global Operations */ +static void openpic_reset(void); +static void openpic_enable_8259_pass_through(void); +static void openpic_disable_8259_pass_through(void); +static u_int openpic_irq(void); +static void openpic_eoi(void); +static u_int openpic_get_priority(void); +static void openpic_set_priority(u_int pri); +static u_int openpic_get_spurious(void); +static void openpic_set_spurious(u_int vector); + +#ifdef CONFIG_SMP +/* Interprocessor Interrupts */ +static void openpic_initipi(u_int ipi, u_int pri, u_int vector); +static void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs); +#endif + +/* Timer Interrupts */ +static void openpic_inittimer(u_int timer, u_int pri, u_int vector); +static void openpic_maptimer(u_int timer, u_int cpumask); + +/* Interrupt Sources */ +static void openpic_enable_irq(u_int irq); +static void openpic_disable_irq(u_int irq); +static void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity, + int is_level); +static void openpic_mapirq(u_int irq, u_int cpumask); +static void openpic_set_sense(u_int irq, int sense); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_OPENPIC_H */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/pSeries_pci.c linux.ac/arch/ppc64/kernel/pSeries_pci.c --- linux.vanilla/arch/ppc64/kernel/pSeries_pci.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/pSeries_pci.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,929 @@ +/* + * pSeries_pci.c + * + * pSeries_pcibios_init(void)opyright (C) 2001 Dave Engebretsen, IBM Corporation + * + * pSeries specific routines for PCI. + * + * Based on code from pci.c and chrp_pci.c + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xics.h" +#include "open_pic.h" +#include "pci.h" + +/* Supporting macros *********************************************/ +#define TESTBIT(value,bits) ((value&bits) == bits) +#define TRUE 1 +#define FALSE 0 + +extern struct pci_controller* hose_head; +extern struct pci_controller** hose_tail; +extern struct Naca *naca; +extern struct pci_ops rtas_pci_ops; +extern struct pci_ops ibm_phb_pci_ops; +extern struct device_node *allnodes; +extern unsigned long phb_tce_table_init(struct pci_controller *phb); + +/******************************************************************* + * Forward declares of prototypes. + *******************************************************************/ +unsigned long find_and_init_phbs(void); +struct pci_controller* alloc_phb(struct device_node *dev, char *model, unsigned int addr_size_words) ; +void pSeries_pcibios_fixup(void); +void pci_build_bar_resources(struct pci_dev* Pci_Dev, int BarCount); +void pci_build_rom_resources(struct pci_dev* Pci_Dev); +void pci_set_BARS(struct pci_dev* Pci_Dev); + +/********************************************************************************** + * + * pSeries I/O Operations to access the PCI configuration space. + * + **********************************************************************************/ +#define RTAS_PCI_READ_OP(size, type, nbytes) \ +int __chrp \ +rtas_read_config_##size(struct pci_dev *dev, int offset, type val) { \ + unsigned long ReturnValue, ConfigAddr; \ + int ReturnCode; \ + ConfigAddr = ((dev->bus->number&0x0000FF) << 16) | (dev->devfn << 8) | (offset & 0xff); \ + ReturnCode = call_rtas("read-pci-config", 2, 2, &ReturnValue, ConfigAddr, nbytes);\ + *val = ReturnValue; \ + return ReturnCode; \ +} +#define RTAS_PCI_WRITE_OP(size, type, nbytes) \ +int __chrp \ +rtas_write_config_##size(struct pci_dev *dev, int offset, type val) { \ + unsigned long ConfigAddr; \ + int ReturnCode; \ + ConfigAddr = ((dev->bus->number&0x0000FF) << 16) | (dev->devfn << 8) | (offset & 0xff); \ + ReturnCode = call_rtas("write-pci-config", 3, 1, NULL, ConfigAddr, nbytes, (ulong)val); \ + return ReturnCode; \ +} + +RTAS_PCI_READ_OP(byte, u8 *, 1) +RTAS_PCI_READ_OP(word, u16 *, 2) +RTAS_PCI_READ_OP(dword, u32 *, 4) +RTAS_PCI_WRITE_OP(byte, u8, 1) +RTAS_PCI_WRITE_OP(word, u16, 2) +RTAS_PCI_WRITE_OP(dword, u32, 4) + +struct pci_ops rtas_pci_ops = { + rtas_read_config_byte, + rtas_read_config_word, + rtas_read_config_dword, + rtas_write_config_byte, + rtas_write_config_word, + rtas_write_config_dword, + pci_read_bar_registers, + pci_read_irq_line +}; + +/********************************************************************************** + * + * pSeries I/O Operations to access the PCI configuration space. + * This is the support for Large Systems(>256 buses). + * + **********************************************************************************/ +#define RTAS64_PCI_READ_OP(size, type, nbytes) \ +int __chrp \ +rtas64_read_config_##size(struct pci_dev *dev, int offset, type val) \ +{ \ + struct pci_controller *hose = dev->sysdata; \ + unsigned long addr = (offset & 0xff) | ((dev->devfn & 0xff) << 8) \ + | (((dev->bus->number) & 0xff) << 16); \ + unsigned long ret = ~0UL; \ + int rval; \ + int buidhi = hose->buid >> 32; \ + int buidlo = hose->buid & 0xffffffff; \ + \ + rval = call_rtas("ibm,read-pci-config", 4, 2, &ret, \ + addr, buidhi, buidlo, nbytes); \ + *val = ret; \ + /* udbg_printf("%08x %08x SYM Read Config %d\n",addr, ret,rval); */ \ + return rval? PCIBIOS_DEVICE_NOT_FOUND: PCIBIOS_SUCCESSFUL; \ +} + +#define RTAS64_PCI_WRITE_OP(size, type, nbytes) \ +int __chrp \ +rtas64_write_config_##size(struct pci_dev *dev, int offset, type val) \ +{ \ + struct pci_controller *hose = dev->sysdata; \ + unsigned long addr = (offset & 0xff) | ((dev->devfn & 0xff) << 8) \ + | (((dev->bus->number) & 0xff) << 16); \ + int rval; \ + int buidhi = hose->buid >> 32; \ + int buidlo = hose->buid & 0xffffffff; \ + \ + rval = call_rtas("ibm,write-pci-config", 5, 1, NULL, \ + addr, buidhi, buidlo, nbytes, (ulong)val); \ + /* udbg_printf("%08x %08x SYM Write Config %d\n",addr, val,rval); */ \ + return rval? PCIBIOS_DEVICE_NOT_FOUND: PCIBIOS_SUCCESSFUL; \ +} + +RTAS64_PCI_READ_OP(byte, u8 *, 1) +RTAS64_PCI_READ_OP(word, u16 *, 2) +RTAS64_PCI_READ_OP(dword, u32 *, 4) +RTAS64_PCI_WRITE_OP(byte, u8, 1) +RTAS64_PCI_WRITE_OP(word, u16, 2) +RTAS64_PCI_WRITE_OP(dword, u32, 4) + +struct pci_ops rtas64_pci_ops = { + rtas64_read_config_byte, + rtas64_read_config_word, + rtas64_read_config_dword, + rtas64_write_config_byte, + rtas64_write_config_word, + rtas64_write_config_dword +}; + + +/****************************************************************** + * pci_read_irq_line + * + * Reads the Interrupt Pin to determine if interrupt is use by card. + * If the interrupt is used, then gets the interrupt line from the + * openfirmware and sets it in the pci_dev and pci_config line. + * + ******************************************************************/ +int +pci_read_irq_line(struct pci_dev* Pci_Dev) { + u8 InterruptPin; + struct device_node* Node; + + pci_read_config_byte(Pci_Dev, PCI_INTERRUPT_PIN, &InterruptPin); + if(InterruptPin == 0) { + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s No Interrupt used by device.\n",Pci_Dev->slot_name); + return 0; + } + Node = pci_device_to_OF_node(Pci_Dev); + if( Node == NULL) { + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s Device Node not found.\n",Pci_Dev->slot_name); + return -1; + } + if(Node->n_intrs == 0) { + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s No Device OF interrupts defined.\n",Pci_Dev->slot_name); + return -1; + } + Pci_Dev->irq = Node->intrs[0].line; + pci_write_config_byte(Pci_Dev, PCI_INTERRUPT_LINE, Pci_Dev->irq); + + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s pci_dev->irq = 0x%02X\n",Pci_Dev->slot_name,Pci_Dev->irq); + return 0; +} +/****************************************************************** + * pci_set_BARS + * + * Sets the IOA BAR registers from the values from the register + * values found in the OpenFirmware node for the device. Or in + * the future, assigns space in phb and sets the values. + ******************************************************************/ +void +pci_set_BARS(struct pci_dev* Pci_Dev) { + int AddrIndex; + struct device_node *Device_Node = pci_device_to_OF_node(Pci_Dev); + + if(Device_Node == 0 || Device_Node->n_addrs == 0 || Device_Node->addrs == NULL) { + PPCDBG(PPCDBG_BUSWALK, "\tDevice Node was not found or no defined bars\n"); + return; + } + PPCDBG(PPCDBG_BUSWALK, "\tSet Bar Regs(%d)\n",Device_Node->n_addrs); + for(AddrIndex = 0; AddrIndex < Device_Node->n_addrs; ++AddrIndex) { + int PciReg = (Device_Node->addrs[AddrIndex].space & 0x000000FF); + u32 PciBar = Device_Node->addrs[AddrIndex].address; + int BarSze = Device_Node->addrs[AddrIndex].size; + + PPCDBG(PPCDBG_BUSWALK, "\tPciBar 0x%02X, 0x%08X, 0x%08X\n",PciReg,PciBar,BarSze); + pci_write_config_dword(Pci_Dev, PciReg,PciBar); + } +} +/****************************************************************** + * + * pci_build_bar_registers + * + * This function will build the resources based on the information + * that is extracted from the BAR register. The original BAR value + * is saved and restored. + * + * Note: This is pSeries brand specific code. + * In the future, this code will be setting the initial BAR + * value and handing out the memory space in the PHB. + ******************************************************************/ +void +pci_build_bar_resources(struct pci_dev* Pci_Dev, int Count) { + int BarRegister = PCI_BASE_ADDRESS_0; + int EndingBar = BarRegister + (Count*4); + int ResourceIndex = 0; + PPCDBG(PPCDBG_BUSWALK, "\npci_read_bar_registers %s\n",Pci_Dev->slot_name); + + /************************************************************** + * Read Bars until the ending bar has been read. + **************************************************************/ + for(BarRegister = PCI_BASE_ADDRESS_0;BarRegister <= EndingBar; BarRegister += 4) { + struct resource* Resource = &Pci_Dev->resource[ResourceIndex]; + u32 BarSaveArea, BarSizeBits, BarSize; + u64 BarStart; + /********************************************************** + * Save the original bar value and check for success. + **********************************************************/ + if((pci_read_config_dword( Pci_Dev, BarRegister, &BarSaveArea) != 0) || + (BarSaveArea == 0xFFFFFFFF )) { + BarRegister += 4; + continue; + } + /********************************************************** + * Write all ones to register to get the size of area. + **********************************************************/ + pci_write_config_dword(Pci_Dev, BarRegister, 0xFFFFFFFF); + pci_read_config_dword( Pci_Dev, BarRegister, &BarSizeBits); + pci_write_config_dword(Pci_Dev, BarRegister, BarSaveArea); + + /********************************************************** + * Error reading bar(all ones back) or unimplemented BAR(zero) + **********************************************************/ + if(( BarSizeBits == 0xFFFFFFFF ) || BarSizeBits == 0 ) { + BarRegister += 4; + continue; + } + Resource->name = Pci_Dev->name; + /********************************************************** + * Test and read Io Space, It is always 32 bits. + **********************************************************/ + if(TESTBIT(BarSizeBits,PCI_BASE_ADDRESS_SPACE_IO) == TRUE) { + BarSize = (~(BarSizeBits&PCI_BASE_ADDRESS_IO_MASK)) +1; + Resource->flags = IORESOURCE_IO | PCI_BASE_ADDRESS_SPACE_IO; + Resource->start = BarSaveArea & PCI_BASE_ADDRESS_IO_MASK; + Resource->end = Resource->start + BarSize - 1; + ++ResourceIndex; + } + /********************************************************** + * Memory Space, could be 32 bits or 64 bits + **********************************************************/ + else { + Resource->flags = IORESOURCE_MEM; + if( TESTBIT(BarSizeBits,PCI_BASE_ADDRESS_MEM_PREFETCH) == TRUE) { + Resource->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH; + } + BarStart = BarSaveArea &PCI_BASE_ADDRESS_MEM_MASK; + BarSize = ~((BarSizeBits)&PCI_BASE_ADDRESS_MEM_MASK)+1; + /********************************************************** + * 64 bit register, read next register to get the high 32 + * bits. PCI Spec says to write both and THEN read. + **********************************************************/ + if( TESTBIT(BarSizeBits,PCI_BASE_ADDRESS_MEM_TYPE_64) == TRUE) { + u64 High64Bits; + u32 BarHigh64Save; + Resource->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; + BarRegister += 4; /* Index to next Bar */ + pci_read_config_dword( Pci_Dev, BarRegister, &BarHigh64Save); + High64Bits = BarHigh64Save; + BarStart += (High64Bits<< 32); + pci_write_config_dword(Pci_Dev, BarRegister-4,0xFFFFFFFF); + pci_write_config_dword(Pci_Dev, BarRegister, 0xFFFFFFFF); + pci_read_config_dword( Pci_Dev, BarRegister-4,&BarSizeBits); + BarSize = ~((BarSizeBits)&PCI_BASE_ADDRESS_MEM_MASK)+1; + pci_read_config_dword( Pci_Dev, BarRegister, &BarSizeBits); + High64Bits = ~(BarSizeBits); + BarSize += (High64Bits << 32); + pci_write_config_dword(Pci_Dev, BarRegister-4,BarSaveArea); + pci_write_config_dword(Pci_Dev, BarRegister, BarHigh64Save); + ++ResourceIndex; /* Skip next resource */ + } + /********************************************************** + * Set resource fields with values. + **********************************************************/ + Resource->start = BarStart; + Resource->end = BarStart + BarSize - 1; + ++ResourceIndex; + } + } + PPCDBGCALL(PPCDBG_BUSWALK, dumpPci_Dev(Pci_Dev) ); +} +/****************************************************************** + * + * Read the rom register + * + ******************************************************************/ +void +pci_build_rom_resources(struct pci_dev* Pci_Dev) { + u32 RomSaveArea, RomSizeBits; + u64 RomSize = 0; + struct resource* Resource = &Pci_Dev->resource[PCI_ROM_RESOURCE]; + PPCDBG(PPCDBG_BUSWALK, "\tpci_read_bar_Rom \n"); + + pci_read_config_dword( Pci_Dev, PCI_ROM_ADDRESS, &RomSaveArea); + pci_write_config_dword(Pci_Dev, PCI_ROM_ADDRESS, 0XFFFFFFFF); + pci_read_config_dword( Pci_Dev, PCI_ROM_ADDRESS, &RomSizeBits); + pci_write_config_dword(Pci_Dev, PCI_ROM_ADDRESS, RomSaveArea); + + if( (RomSizeBits&PCI_ROM_ADDRESS_ENABLE) == PCI_ROM_ADDRESS_ENABLE) { + RomSize = ~(RomSizeBits) & PCI_ROM_ADDRESS_MASK; + if(RomSize > 0) { + Resource->name = Pci_Dev->name; + Resource->flags = IORESOURCE_MEM | IORESOURCE_READONLY | IORESOURCE_PREFETCH; + Resource->start = RomSaveArea; + Resource->end = Resource->start + RomSize - 1; + } + } +} +/****************************************************************** + * + * pci_read_bar_registers + * + * This function is the hook from the independant code to the arch + * dependant code to set and read the BAR registers. + * + * Note: This is pSeries brand specific code. + * In the future, this code will be setting the initial BAR + * value and handing out the PBH memory space. + ******************************************************************/ +int +pci_read_bar_registers(struct pci_dev* Pci_Dev, int Count, int RomFlag) { + u16 Pci_Command_Register_Save = 0; + u16 Pci_Mask_Reg; + /************************************************************** + * If Io or Memory is enabled, disable while the BARs are being + * changed to avoid any side affects. + **************************************************************/ + if(( pci_read_config_word( Pci_Dev, PCI_COMMAND, &Pci_Command_Register_Save) != 0 ) || + (Pci_Command_Register_Save == (u16)0xFFFF) ) { + printk("PCI: Device %s Read Command Failed.\n",Pci_Dev->slot_name); + PPCDBG(PPCDBG_BUSWALK,"\tDevice %s Read Command Failed.\n",Pci_Dev->slot_name); + return -1; + } + Pci_Mask_Reg = Pci_Command_Register_Save &(PCI_COMMAND_IO|PCI_COMMAND_MEMORY); + if( Pci_Mask_Reg != 0) { + u16 ResetCmdReg = Pci_Command_Register_Save & (~Pci_Mask_Reg); + pci_write_config_word( Pci_Dev, PCI_COMMAND, ResetCmdReg); + } + else { + Pci_Command_Register_Save = 0; + } + /************************************************************** + * Do the base BARS. + **************************************************************/ + pci_build_bar_resources(Pci_Dev, Count); + + /************************************************************** + * Rom Flag on, read ROM BARS. + **************************************************************/ + if(RomFlag != 0) { + pci_build_rom_resources(Pci_Dev); + } + + /************************************************************** + * If the Command Register was modifed, restore it. + **************************************************************/ + if(Pci_Command_Register_Save != 0) { + pci_write_config_word( Pci_Dev, PCI_COMMAND, Pci_Command_Register_Save); + PPCDBG(PPCDBG_BUSWALK,"\tPCI_COMMAND Register Restored 0x%04X\n", + Pci_Command_Register_Save); + } + return 0; +} + +/****************************************************************** + * Find all PHBs in the system and initialize a set of data + * structures to represent them. + ******************************************************************/ +unsigned long __init +find_and_init_phbs(void) +{ + struct device_node *Pci_Node; + struct pci_controller *phb; + unsigned int root_addr_size_words = 0, this_addr_size_words = 0; + unsigned int this_addr_count = 0, range_stride; + unsigned int *ui_ptr = NULL, *ranges; + char *model; + struct pci_range64 range; + struct resource *res; + unsigned int memno, rlen, i, index; + unsigned int *opprop; + + PPCDBG(PPCDBG_PHBINIT, "find_and_init_phbs\n"); + + if(naca->interrupt_controller == IC_OPEN_PIC) { + opprop = (unsigned int *) + get_property(find_path_device("/"),"platform-open-pic", NULL); + } + + /* Get the root address word size. */ + ui_ptr = (unsigned int *) get_property(find_path_device("/"), + "#size-cells", NULL); + if(ui_ptr) root_addr_size_words = *ui_ptr; + else { + PPCDBG(PPCDBG_PHBINIT, "\tget #size-cells failed.\n"); + return(-1); + } + + index = 0; + + /****************************************************************** + * Find all PHB devices and create an object for them. + ******************************************************************/ + for( Pci_Node = find_devices("pci");Pci_Node != NULL;Pci_Node = Pci_Node->next) { + model = (char *) get_property(Pci_Node, "model", NULL); + if(model != NULL) { + phb = alloc_phb(Pci_Node, model, root_addr_size_words); + if(phb == NULL) return(-1); + } + else { + continue; + } + + /* Get this node's address word size. */ + ui_ptr = (unsigned int *) get_property(Pci_Node, "#size-cells", NULL); + if(ui_ptr) this_addr_size_words = *ui_ptr; + else this_addr_size_words = 1; + /* Get this node's address word count. */ + ui_ptr = (unsigned int *) get_property(Pci_Node, "#address-cells", NULL); + if(ui_ptr) this_addr_count = *ui_ptr; + else this_addr_count = 3; + + range_stride = this_addr_count + root_addr_size_words + this_addr_size_words; + + memno = 0; + phb->io_base_phys = 0; + + ranges = (unsigned int *) get_property(Pci_Node, "ranges", &rlen); + PPCDBG(PPCDBG_PHBINIT, "\trange_stride = 0x%lx, rlen = 0x%x\n", range_stride, rlen); + + for(i = 0; i < (rlen/sizeof(*ranges)); i+=range_stride) { + /* Put the PCI addr part of the current element into a + * '64' struct. + */ + range = *((struct pci_range64 *)(ranges + i)); + + /* If this is a '32' element, map into a 64 struct. */ + if((range_stride * sizeof(int)) == + sizeof(struct pci_range32)) { + range.parent_addr = + (unsigned long)(*(ranges + i + 3)); + range.size = + (((unsigned long)(*(ranges + i + 4)))<<32) | + (*(ranges + i + 5)); + } else { + range.parent_addr = + (((unsigned long)(*(ranges + i + 3)))<<32) | + (*(ranges + i + 4)); + range.size = + (((unsigned long)(*(ranges + i + 5)))<<32) | + (*(ranges + i + 6)); + } + + PPCDBG(PPCDBG_PHBINIT, "\trange.parent_addr = 0x%lx\n", + range.parent_addr); + PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.hi = 0x%lx\n", + range.child_addr.a_hi); + PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.mid = 0x%lx\n", + range.child_addr.a_mid); + PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.lo = 0x%lx\n", + range.child_addr.a_lo); + PPCDBG(PPCDBG_PHBINIT, "\trange.size = 0x%lx\n", + range.size); + + res = NULL; + switch ((range.child_addr.a_hi >> 24) & 0x3) { + case 1: /* I/O space */ + phb->io_base_phys = range.parent_addr; + res = &phb->io_resource; + res->flags = IORESOURCE_IO; + if (isa_io_base == 0) { + isa_io_base = (unsigned long) + ioremap(phb->io_base_phys, range.size); + PPCDBG(PPCDBG_PHBINIT, "\tisa_io_base = 0x%lx\n", isa_io_base); + } + phb->pci_io_offset = range.parent_addr - + ((((unsigned long) + range.child_addr.a_mid) << 32) | + (range.child_addr.a_lo)); + PPCDBG(PPCDBG_PHBINIT, "\tpci_io_offset = 0x%lx\n", + phb->pci_io_offset); + + break; + case 2: /* mem space */ + PPCDBG(PPCDBG_PHBINIT, "\tMem Space\n"); + phb->pci_mem_offset = range.parent_addr - + ((((unsigned long) + range.child_addr.a_mid) << 32) | + (range.child_addr.a_lo)); + PPCDBG(PPCDBG_PHBINIT, "\tpci_mem_offset = 0x%lx\n", + phb->pci_mem_offset); + res = &(phb->mem_resources[memno]); + res->flags = IORESOURCE_MEM; + ++memno; + break; + } + if (res) { + res->name = Pci_Node->full_name; + res->start = range.parent_addr; + res->end = range.parent_addr + range.size - 1; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; + } + } + PPCDBG(PPCDBG_PHBINIT, "\tphb->io_base_phys = 0x%lx\n", + phb->io_base_phys); + PPCDBG(PPCDBG_PHBINIT, "\tphb->pci_mem_offset = 0x%lx\n", + phb->pci_mem_offset); + + if(naca->interrupt_controller == IC_OPEN_PIC) { + if(root_addr_size_words == 1) { + openpic_setup_ISU(index, opprop[index+1]); + } else { + openpic_setup_ISU(index, + ((unsigned long)opprop[(index+1)*2]) << 32 | + opprop[(index+1)*2+1]); + } + } + + index++; + } + return 0; /*Success */ +} + +/****************************************************************** + * + * Allocate and partially initialize a structure to represent a PHB. + * + ******************************************************************/ +struct pci_controller * +alloc_phb(struct device_node *dev, char *model, unsigned int addr_size_words) +{ + struct pci_controller *phb; + unsigned int *ui_ptr = NULL, len; + struct reg_property64 reg_struct; + int *bus_range; + + PPCDBG(PPCDBG_PHBINIT, "alloc_phb: %s\n", dev->full_name); + PPCDBG(PPCDBG_PHBINIT, "\tdev = 0x%lx\n", dev); + PPCDBG(PPCDBG_PHBINIT, "\tmodel = 0x%lx\n", model); + PPCDBG(PPCDBG_PHBINIT, "\taddr_size_words = 0x%lx\n", addr_size_words); + + /* Found a PHB, now figure out where his registers are mapped. */ + ui_ptr = (unsigned int *) get_property(dev, "reg", &len); + if(ui_ptr == NULL) { + PPCDBG(PPCDBG_PHBINIT, "\tget reg failed.\n"); + return(NULL); + } + + if(addr_size_words == 1) { + reg_struct.address = ((struct reg_property32 *)ui_ptr)->address; + reg_struct.size = ((struct reg_property32 *)ui_ptr)->size; + } else { + reg_struct = *((struct reg_property64 *)ui_ptr); + } + + PPCDBG(PPCDBG_PHBINIT, "\treg_struct.address = 0x%lx\n", reg_struct.address); + PPCDBG(PPCDBG_PHBINIT, "\treg_struct.size = 0x%lx\n", reg_struct.size); + + /*************************************************************** + * Set chip specific data in the phb, including types & + * register pointers. + ***************************************************************/ + + /**************************************************************** + * Python + ***************************************************************/ + if(strstr(model, "Python")) { + PPCDBG(PPCDBG_PHBINIT, "\tCreate python\n"); + phb = pci_alloc_pci_controller("PHB PY",phb_type_python); + if(phb == NULL) return NULL; + + phb->cfg_addr = (volatile unsigned long *) + ioremap(reg_struct.address + 0xf8000, PAGE_SIZE); + PPCDBG(PPCDBG_PHBINIT, "\tcfg_addr_r = 0x%lx\n", + reg_struct.address + 0xf8000); + PPCDBG(PPCDBG_PHBINIT, "\tcfg_addr_v = 0x%lx\n", + phb->cfg_addr); + phb->cfg_data = (char*)(phb->cfg_addr + 0x02); + phb->phb_regs = (volatile unsigned long *) + ioremap(reg_struct.address + 0xf7000, PAGE_SIZE); + /* Python's register file is 1 MB in size. */ + phb->chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), + 0x100000); + /*************************************************************** + * Speedwagon + ***************************************************************/ + } else if(strstr(model, "Speedwagon")) { + PPCDBG(PPCDBG_PHBINIT, "\tCreate speedwagon\n"); + phb = pci_alloc_pci_controller("PHB SW",phb_type_speedwagon); + if(phb == NULL) return NULL; + + phb->cfg_addr = (volatile unsigned long *) + ioremap(reg_struct.address + 0x140, PAGE_SIZE); + phb->cfg_data = (char*)(phb->cfg_addr - 0x02); /* minus is correct */ + phb->phb_regs = (volatile unsigned long *) + ioremap(reg_struct.address, PAGE_SIZE); + + phb->local_number = ((reg_struct.address >> 12) & 0xf) - 0x8; + + /* Speedwagon's register file is 1 MB in size. */ + phb->chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), + 0x100000); + PPCDBG(PPCDBG_PHBINIT, "\tmapping chip_regs from 0x%lx -> 0x%lx\n", + reg_struct.address & 0xfffff, phb->chip_regs); + /*************************************************************** + * Trying to build a known just gets the code in trouble. + ***************************************************************/ + } else { + PPCDBG(PPCDBG_PHBINIT, "\tUnknown PHB Type!\n"); + printk("PCI: Unknown Phb Type!\n"); + return NULL; + } + + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + PPCDBG(PPCDBG_PHBINIT, "Can't get bus-range for %s\n", dev->full_name); + kfree(phb); + return(NULL); + } + + /*************************************************************** + * Finished with the initialization + ***************************************************************/ + phb->first_busno = bus_range[0]; + phb->last_busno = bus_range[1]; + //phb->first_busno = (phb->global_number <<8) + bus_range[0]; + //phb->last_busno = (phb->global_number <<8) + bus_range[1]; + + phb->arch_data = dev; + if( Pci_Large_Bus_System == 0 ) phb->ops = &rtas_pci_ops; + else phb->ops = &rtas64_pci_ops; + + /*************************************************************** + * Build tce table for phb + ***************************************************************/ + phb_tce_table_init(phb); + + /* Dump PHB information for Debug */ + PPCDBGCALL(PPCDBG_PHBINIT,dumpPci_Controller(phb) ); + + return phb; +} + +void +fixup_resources(struct pci_dev *dev) { + int i; + unsigned long size; + struct pci_controller* phb = (struct pci_controller *)dev->sysdata; + + PPCDBG(PPCDBG_PHBINIT, "fixup_resources:\n"); + PPCDBG(PPCDBG_PHBINIT, "\tphb = 0x%016LX\n", phb); + PPCDBG(PPCDBG_PHBINIT, "\tphb->pci_io_offset = 0x%016LX\n", phb->pci_io_offset); + PPCDBG(PPCDBG_PHBINIT, "\tphb->pci_mem_offset = 0x%016LX\n", phb->pci_mem_offset); + + PPCDBG(PPCDBG_PHBINIT, "\tdev->name = %s\n", dev->name); + PPCDBG(PPCDBG_PHBINIT, "\tdev->vendor:device = 0x%04X : 0x%04X\n", dev->vendor, dev->device); + + if(phb == NULL) return; + + for (i = 0; i < 6; ++i) { + PPCDBG(PPCDBG_PHBINIT, "\tdevice %x.%x[%d] (flags %x) [%lx..%lx]\n", + dev->bus->number, dev->devfn, i, + dev->resource[i].flags, + dev->resource[i].start, + dev->resource[i].end); + + if ((dev->resource[i].start == 0) && (dev->resource[i].end == 0)) { + continue; + } + + if (dev->resource[i].flags & IORESOURCE_IO) { + dev->resource[i].start += phb->pci_io_offset; + dev->resource[i].end += phb->pci_io_offset; + size = dev->resource[i].end - dev->resource[i].start; + dev->resource[i].start = + ((unsigned long) ioremap(dev->resource[i].start, + size)) - isa_io_base; + dev->resource[i].end = dev->resource[i].start + size; + + PPCDBG(PPCDBG_PHBINIT, "\t\t-> now [%lx (%lx) .. %lx (%lx)]\n", + dev->resource[i].start, ___pa(dev->resource[i].start + isa_io_base), + dev->resource[i].end, ___pa(dev->resource[i].end + isa_io_base)); + } else if (dev->resource[i].flags & IORESOURCE_MEM) { + dev->resource[i].start += phb->pci_mem_offset; + dev->resource[i].end += phb->pci_mem_offset; + PPCDBG(PPCDBG_PHBINIT, "\t\t-> now [%lx..%lx]\n", + dev->resource[i].start, dev->resource[i].end); + + } else { + continue; + } + + /* zap the 2nd function of the winbond chip */ + if (dev->resource[i].flags & IORESOURCE_IO + && dev->bus->number == 0 && dev->devfn == 0x81) + dev->resource[i].flags &= ~IORESOURCE_IO; + } +} + +void __init +pSeries_pcibios_fixup(void) { + struct pci_dev *dev; + + PPCDBG(PPCDBG_PHBINIT, "pSeries_pcibios_fixup: start\n"); + pci_assign_all_busses = 0; + + pci_for_each_dev(dev) { + PPCDBGCALL(PPCDBG_PHBINIT, dumpPci_Dev(dev) ); + } + + if(naca->interrupt_controller == IC_PPC_XIC) { + xics_isa_init(); + } +} + +/*********************************************************************** + * pci_find_hose_for_OF_device + * + * This function finds the PHB that matching device_node in the + * OpenFirmware by scanning all the pci_controllers. + * + ***********************************************************************/ +struct pci_controller* +pci_find_hose_for_OF_device(struct device_node* node) { + while(node) { + struct pci_controller* hose; + for (hose=hose_head;hose;hose=hose->next) + if (hose->arch_data == node) + return hose; + node=node->parent; + } + return NULL; +} + +/*********************************************************************** + * + * scan_OF_childs_for_device + * + * The function is a helper function for the pci_device_to_OF_node. It + * walks down the passed node, looking for a node entry that matches the + * requested bus and device function. NOTE: If a bridge is found in the + * scan, it will recurse this the function to to scan that bridge looking + * for a match. If none found, the return will continue down the orginal + * tree. + * + * Return: + * Node matching the bus and devfn passed or NULL. + ***********************************************************************/ +static struct device_node* +scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn) +{ + struct device_node* CurrentNode; /* Current node being scanned. */ + struct device_node* DeviceNode; /* Node of Device */ + u32* Register; /* Pointer to Register Array */ + u32* Class_Code; /* Pointer to ClassCode property */ + CurrentNode = node; + DeviceNode = NULL; + while(CurrentNode != NULL && DeviceNode == NULL) { + u32 IoaAddress; + u8 IoaBus, IoaDevFn; + + Register = (unsigned int *) get_property(CurrentNode,"reg", 0); + if(Register != NULL) { + /* 1st register entry is the Ioa Address = 00BBSS00 */ + IoaAddress = Register[0]; + IoaBus = (IoaAddress & 0x00FF0000) >> 16; + IoaDevFn = (IoaAddress & 0x0000FF00) >> 8; + if( (IoaBus == bus) && (IoaDevFn == dev_fn ) ) { + DeviceNode = CurrentNode; + return DeviceNode; + } + } + /*************************************************************** + * check for a bridge, if so scan the branch of the tree for a match. + ***************************************************************/ + Class_Code = (unsigned int*) get_property(CurrentNode,"class-code", 0); + if(Class_Code != NULL) { + u32 PciClassCode = ((*Class_Code)&0x00FFFF00)>>8; + + if(( PciClassCode == PCI_CLASS_BRIDGE_PCI) || + ( PciClassCode == PCI_CLASS_BRIDGE_CARDBUS) ) { + + PPCDBG(PPCDBG_BUSWALK,"\tScan OF behind bridge\n"); + DeviceNode = scan_OF_childs_for_device(CurrentNode->child, bus, dev_fn); + if(DeviceNode != NULL) { + return DeviceNode; + } + } + } + /*************************************************************** + * Try the next node. + ***************************************************************/ + CurrentNode = CurrentNode->sibling; + } + return NULL; +} +/*********************************************************************** + * pci_device_to_OF_node + * + * This function Finds the Open Firmware node for the passed in pci_dev. + * It starts at the Phb's node for the device and calls the + * scan_OF_childs_for_device to looking for the matching entry. + * + * Return: + * Node matching the device or NULL if it was not found. + ***********************************************************************/ +struct device_node* +pci_device_to_OF_node(struct pci_dev* Pci_Dev) +{ + struct pci_controller* Phb; + struct device_node* PhbNode; + struct device_node* DeviceNode; + Phb = PCI_GET_PHB_PTR(Pci_Dev); + PhbNode = (struct device_node *) Phb->arch_data; + DeviceNode = scan_OF_childs_for_device(PhbNode->child, PCI_GET_BUS_NUMBER(Pci_Dev), Pci_Dev->devfn); + return DeviceNode; +} +/*********************************************************************** + * find_floppy(void) + * + * Finds the default floppy device, if the system has one, and returns + * the pci_dev for the isa bridge for the floppy device. + * + * Note: This functions finds the first "fdc" device and then looks to + * the parent device which should be the isa bridge device. If there + * is more than one floppy on the system, it will find the first one + * and maybe that is okay. + ***********************************************************************/ +struct pci_dev* +find_floppy() { + struct device_node *FloppyParent, *FloppyNode; + struct pci_dev *floppy_dev = NULL; + int *Register; + + FloppyNode = find_type_devices("fdc"); + if(FloppyNode != NULL && FloppyNode->parent != NULL) { + FloppyParent = FloppyNode->parent; + Register = (unsigned int *)get_property(FloppyParent,"reg", 0); + if(Register != NULL) { + u8 IoaBus = (Register[0] & 0x00FF0000) >> 16; + u8 IoaDevFn = (Register[0] & 0x0000FF00) >> 8; + floppy_dev = pci_find_slot(IoaBus, IoaDevFn); + } + } + PPCDBG(PPCDBG_BUSWALK,"\tFloppy pci_dev\n"); + PPCDBGCALL(PPCDBG_BUSWALK, dumpPci_Dev(floppy_dev) ); + return floppy_dev; +} + +/*********************************************************************** + * ppc64_pcibios_init + * + * Chance to initialize and structures or variable before PCI Bus walk. + * + ***********************************************************************/ +void +pSeries_pcibios_init(void) { + PPCDBG(PPCDBG_PHBINIT, "\tppc64_pcibios_init Entry.\n"); + + if(get_property(find_path_device("/rtas"),"read-pc-config",NULL) != NULL) { + PPCDBG(PPCDBG_PHBINIT, "\tFound: read-pc-config\n"); + } + else PPCDBG(PPCDBG_PHBINIT, "\tNOT Found: read-pc-config\n"); + + + if(get_property(find_path_device("/rtas"),"ibm,read-pc-config",NULL) != NULL) { + PPCDBG(PPCDBG_PHBINIT, "\tFound: ibm,read-pc-config\n"); + Pci_Set_IOA_Address = 1; + } + if(get_property(find_path_device("/rtas"),"ibm,fw-phb-id",NULL) != NULL) { + PPCDBG(PPCDBG_PHBINIT, "\tFound: ibm,fw-phb-id\n"); + Pci_Large_Bus_System = 1; + } +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/pacaData.c linux.ac/arch/ppc64/kernel/pacaData.c --- linux.vanilla/arch/ppc64/kernel/pacaData.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/pacaData.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,132 @@ +/* + * c 2001 PPC 64 Team, 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. + */ + +#define __KERNEL__ 1 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* The Paca is an array with one entry per processor. Each contains an + * ItLpPaca, which contains the information shared between the + * hypervisor and Linux. Each also contains an ItLpRegSave area which + * is used by the hypervisor to save registers. + * On systems with hardware multi-threading, there are two threads + * per processor. The Paca array must contain an entry for each thread. + * The VPD Areas will give a max logical processors = 2 * max physical + * processors. The processor VPD array needs one entry per physical + * processor (not thread). + */ +#define PACAINITDATA(number,start,lpq,asrr,asrv) \ +{ (struct ItLpPaca *)(((char *)(&xPaca[number]))+offsetof(struct Paca, xLpPaca)), \ + (struct ItLpRegSave *)(((char *)(&xPaca[number]))+offsetof(struct Paca, xRegSav)), \ + 0, /* Current */ \ + 0, /* R21 Save */ \ + 0, /* R22 Save */ \ + 0, /* Kernel stack addr save */ \ + (number), /* Paca Index */ \ + 0, /* HW Proc Number */ \ + 0, /* CCR Save */ \ + 0, /* MSR Save */ \ + 0, /* LR Save */ \ + 0, /* Pointer to thread */ \ + {(asrr), /* Real pointer to segment table */ \ + (asrv), /* Virt pointer to segment table */ \ + REGION_COUNT-1}, /* Round robin index */ \ + {0,0,0,0,0,0,0,0},/* Segment cache */ \ + 0, /* Kernel TOC address */ \ + 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 */ \ + (start), /* Processor start */ \ + {0,0,0}, /* Resv */ \ + 0, /* Default Decrementer */ \ + 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] __page_aligned = { +#ifdef CONFIG_PPC_ISERIES + PACAINITDATA( 0, 1, &xItLpQueue, 0, 0xc000000000005000), +#else + PACAINITDATA( 0, 1, 0, 0x5000, 0xc000000000005000), +#endif + PACAINITDATA( 1, 0, 0, 0, 0), + PACAINITDATA( 2, 0, 0, 0, 0), + PACAINITDATA( 3, 0, 0, 0, 0), + PACAINITDATA( 4, 0, 0, 0, 0), + PACAINITDATA( 5, 0, 0, 0, 0), + PACAINITDATA( 6, 0, 0, 0, 0), + PACAINITDATA( 7, 0, 0, 0, 0), + PACAINITDATA( 8, 0, 0, 0, 0), + PACAINITDATA( 9, 0, 0, 0, 0), + PACAINITDATA(10, 0, 0, 0, 0), + PACAINITDATA(11, 0, 0, 0, 0), + PACAINITDATA(12, 0, 0, 0, 0), + PACAINITDATA(13, 0, 0, 0, 0), + PACAINITDATA(14, 0, 0, 0, 0), + PACAINITDATA(15, 0, 0, 0, 0), + PACAINITDATA(16, 0, 0, 0, 0), + PACAINITDATA(17, 0, 0, 0, 0), + PACAINITDATA(18, 0, 0, 0, 0), + PACAINITDATA(19, 0, 0, 0, 0), + PACAINITDATA(20, 0, 0, 0, 0), + PACAINITDATA(21, 0, 0, 0, 0), + PACAINITDATA(22, 0, 0, 0, 0), + PACAINITDATA(23, 0, 0, 0, 0), + PACAINITDATA(24, 0, 0, 0, 0), + PACAINITDATA(25, 0, 0, 0, 0), + PACAINITDATA(26, 0, 0, 0, 0), + PACAINITDATA(27, 0, 0, 0, 0), + PACAINITDATA(28, 0, 0, 0, 0), + PACAINITDATA(29, 0, 0, 0, 0), + PACAINITDATA(30, 0, 0, 0, 0), + PACAINITDATA(31, 0, 0, 0, 0), + PACAINITDATA(32, 0, 0, 0, 0), + PACAINITDATA(33, 0, 0, 0, 0), + PACAINITDATA(34, 0, 0, 0, 0), + PACAINITDATA(35, 0, 0, 0, 0), + PACAINITDATA(36, 0, 0, 0, 0), + PACAINITDATA(37, 0, 0, 0, 0), + PACAINITDATA(38, 0, 0, 0, 0), + PACAINITDATA(39, 0, 0, 0, 0), + PACAINITDATA(40, 0, 0, 0, 0), + PACAINITDATA(41, 0, 0, 0, 0), + PACAINITDATA(42, 0, 0, 0, 0), + PACAINITDATA(43, 0, 0, 0, 0), + PACAINITDATA(44, 0, 0, 0, 0), + PACAINITDATA(45, 0, 0, 0, 0), + PACAINITDATA(46, 0, 0, 0, 0), + PACAINITDATA(47, 0, 0, 0, 0) +}; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/pci.c linux.ac/arch/ppc64/kernel/pci.c --- linux.vanilla/arch/ppc64/kernel/pci.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/pci.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,816 @@ +/* + * + * + * Port for PPC64 David Engebretsen, IBM Corp. + * Contains common pci routines for ppc64 platform, pSeries and iSeries brands. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pci.h" + +unsigned long isa_io_base = 0; +unsigned long isa_mem_base = 0; +unsigned long pci_dram_offset = 0; + +/****************************************************************** + * Forward declare of prototypes + ******************************************************************/ +void pcibios_fixup_resources(struct pci_dev* dev); +void fixup_resources(struct pci_dev* dev); /* In brand pci code */ + +struct pci_dev *find_floppy(void); + +extern struct Naca *naca; + +int pci_assign_all_busses = 0; + +struct pci_controller* hose_head; +struct pci_controller** hose_tail = &hose_head; + +/****************************************************************** + * + ******************************************************************/ +int global_phb_number = 0; /* Global phb counter */ +int Pci_Large_Bus_System = 0; +int Pci_Set_IOA_Address = 0; +int Pci_Manage_Phb_Space = 0; + +static int pci_bus_count; + +/* Floppy dev for ppc64_fd_dma_setup(). May be null if no floppy in the system. */ +struct pci_dev *ppc64_floppy_dev = NULL; + +struct pci_fixup pcibios_fixups[] = { + { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources }, + { 0 } +}; + +void pcibios_fixup_pbus_ranges(struct pci_bus *pbus, + struct pbus_set_ranges_data *pranges) +{ +} + + +void +pcibios_update_resource(struct pci_dev *dev, struct resource *root, + struct resource *res, int resource) +{ + u32 new, check; + int reg; + struct pci_controller* hose = dev->sysdata; + + new = res->start; + if (hose && res->flags & IORESOURCE_MEM) + new -= hose->pci_mem_offset; + new |= (res->flags & PCI_REGION_FLAG_MASK); + if (resource < 6) { + reg = PCI_BASE_ADDRESS_0 + 4*resource; + } else if (resource == PCI_ROM_RESOURCE) { + res->flags |= PCI_ROM_ADDRESS_ENABLE; + reg = dev->rom_base_reg; + } else { + /* Somebody might have asked allocation of a non-standard resource */ + return; + } + + pci_write_config_dword(dev, reg, new); + pci_read_config_dword(dev, reg, &check); + if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { + printk(KERN_ERR "PCI: Error while updating region " + "%s/%d (%08x != %08x)\n", dev->slot_name, resource, + new, check); + } +} + +static void +pcibios_fixup_resources(struct pci_dev* dev) +{ + fixup_resources(dev); +} + +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might have be mirrored at 0x0100-0x03ff.. + */ +void +pcibios_align_resource(void *data, struct resource *res, unsigned long size) +{ + struct pci_dev *dev = data; + + if (res->flags & IORESOURCE_IO) { + unsigned long start = res->start; + + if (size > 0x100) { + printk(KERN_ERR "PCI: Can not align I/O Region %s %s because size %ld is too large.\n", + dev->slot_name, res->name, size); + } + + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + res->start = start; + } + } +} + + +/* + * Handle resources of PCI devices. If the world were perfect, we could + * just allocate all the resource regions and do nothing more. It isn't. + * On the other hand, we cannot just re-allocate all devices, as it would + * require us to know lots of host bridge internals. So we attempt to + * keep as much of the original configuration as possible, but tweak it + * when it's found to be wrong. + * + * Known BIOS problems we have to work around: + * - I/O or memory regions not configured + * - regions configured, but not enabled in the command register + * - bogus I/O addresses above 64K used + * - expansion ROMs left enabled (this may sound harmless, but given + * the fact the PCI specs explicitly allow address decoders to be + * shared between expansion ROMs and other resource regions, it's + * at least dangerous) + * + * Our solution: + * (1) Allocate resources for all buses behind PCI-to-PCI bridges. + * This gives us fixed barriers on where we can allocate. + * (2) Allocate resources for all enabled devices. If there is + * a collision, just mark the resource as unallocated. Also + * disable expansion ROMs during this step. + * (3) Try to allocate resources for disabled devices. If the + * resources were assigned correctly, everything goes well, + * if they weren't, they won't disturb allocation of other + * resources. + * (4) Assign new addresses to resources which were either + * not configured at all or misconfigured. If explicitly + * requested by the user, configure expansion ROM address + * as well. + */ + +static void __init +pcibios_allocate_bus_resources(struct list_head *bus_list) +{ + struct list_head *ln; + struct pci_bus *bus; + struct pci_dev *dev; + int idx; + struct resource *r, *pr; + + /* Depth-First Search on bus tree */ + for (ln=bus_list->next; ln != bus_list; ln=ln->next) { + bus = pci_bus_b(ln); + if ((dev = bus->self)) { + for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { + r = &dev->resource[idx]; + if (!r->start) + continue; + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) + printk(KERN_ERR "PCI: Cannot allocate resource region %d of bridge %s\n", idx, dev->slot_name); + } + } + pcibios_allocate_bus_resources(&bus->children); + } +} + +static void __init +pcibios_allocate_resources(int pass) +{ + struct pci_dev *dev; + int idx, disabled; + u16 command; + struct resource *r, *pr; + + pci_for_each_dev(dev) { + pci_read_config_word(dev, PCI_COMMAND, &command); + for(idx = 0; idx < 6; idx++) { + r = &dev->resource[idx]; + if (r->parent) /* Already allocated */ + continue; + if (!r->start) /* Address not assigned at all */ + continue; + + if (r->flags & IORESOURCE_IO) + disabled = !(command & PCI_COMMAND_IO); + else + disabled = !(command & PCI_COMMAND_MEMORY); + if (pass == disabled) { + PPCDBG(PPCDBG_PHBINIT, + "PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%d)\n", + r->start, r->end, r->flags, disabled, pass); + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) { + PPCDBG(PPCDBG_PHBINIT, + "PCI: Cannot allocate resource region %d of device %s, pr = 0x%lx\n", idx, dev->slot_name, pr); + if(pr) { + PPCDBG(PPCDBG_PHBINIT, + "PCI: Cannot allocate resource 0x%lx\n", request_resource(pr,r)); + } + /* We'll assign a new address later */ + r->end -= r->start; + r->start = 0; + } + } + } + if (!pass) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (r->flags & PCI_ROM_ADDRESS_ENABLE) { + /* Turn the ROM off, leave the resource region, but keep it unregistered. */ + u32 reg; + r->flags &= ~PCI_ROM_ADDRESS_ENABLE; + pci_read_config_dword(dev, dev->rom_base_reg, ®); + pci_write_config_dword(dev, dev->rom_base_reg, reg & ~PCI_ROM_ADDRESS_ENABLE); + } + } + } +} + +static void __init +pcibios_assign_resources(void) +{ + struct pci_dev *dev; + int idx; + struct resource *r; + + pci_for_each_dev(dev) { + int class = dev->class >> 8; + + /* Don't touch classless devices and host bridges */ + if (!class || class == PCI_CLASS_BRIDGE_HOST) + continue; + + for(idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + + /* + * Don't touch IDE controllers and I/O ports of video cards! + */ + if ((class == PCI_CLASS_STORAGE_IDE && idx < 4) || + (class == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO))) + continue; + + /* + * We shall assign a new address to this resource, either because + * the BIOS forgot to do so or because we have decided the old + * address was unusable for some reason. + */ + if (!r->start && r->end && ppc_md.pcibios_enable_device_hook && + !ppc_md.pcibios_enable_device_hook(dev, 1)) + pci_assign_resource(dev, idx); + } + + if (0) { /* don't assign ROMs */ + r = &dev->resource[PCI_ROM_RESOURCE]; + r->end -= r->start; + r->start = 0; + if (r->end) + pci_assign_resource(dev, PCI_ROM_RESOURCE); + } + } +} + + +int +pcibios_enable_resources(struct pci_dev *dev) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for(idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + if (!r->start && r->end) { + printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (dev->resource[PCI_ROM_RESOURCE].start) + cmd |= PCI_COMMAND_MEMORY; + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + +/* + * Allocate pci_controller(phb) initialized common variables. + */ +struct pci_controller * __init +pci_alloc_pci_controller(char *model, enum phb_types controller_type) +{ + struct pci_controller *hose; + PPCDBG(PPCDBG_PHBINIT, "PCI: Allocate pci_controller for %s\n",model); + hose = (struct pci_controller *)alloc_bootmem(sizeof(struct pci_controller)); + if(hose == NULL) { + printk(KERN_ERR "PCI: Allocate pci_controller failed.\n"); + return NULL; + } + memset(hose, 0, sizeof(struct pci_controller)); + if(strlen(model) < 8) strcpy(hose->what,model); + else memcpy(hose->what,model,7); + hose->type = controller_type; + hose->global_number = global_phb_number; + global_phb_number++; + + *hose_tail = hose; + hose_tail = &hose->next; + return hose; +} + +/* + * This fixup is arch independent and probably should go somewhere else. + */ +void __init +pcibios_generic_fixup(void) +{ + struct pci_dev *dev; + + /* Fix miss-identified vendor AMD pcnet32 adapters. */ + dev = NULL; + while ((dev = pci_find_device(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_AMD_LANCE, dev)) != NULL && + dev->class == (PCI_CLASS_NETWORK_ETHERNET << 8)) + dev->vendor = PCI_VENDOR_ID_AMD; +} + + + +/*********************************************************************** + * + * + * + ***********************************************************************/ +void __init +pcibios_init(void) +{ + struct pci_controller *hose; + struct pci_bus *bus; + int next_busno; + +#ifndef CONFIG_PPC_ISERIES + pSeries_pcibios_init(); +#endif + + printk("PCI: Probing PCI hardware\n"); + PPCDBG(PPCDBG_BUSWALK,"PCI: Probing PCI hardware\n"); + + + /* Scan all of the recorded PCI controllers. */ + for (next_busno = 0, hose = hose_head; hose; hose = hose->next) { + hose->last_busno = 0xff; + bus = pci_scan_bus(hose->first_busno, hose->ops, hose); + hose->bus = bus; + hose->last_busno = bus->subordinate; + if (pci_assign_all_busses || next_busno <= hose->last_busno) + next_busno = hose->last_busno+1; + } + pci_bus_count = next_busno; + + + /* Call machine dependant fixup */ + if (ppc_md.pcibios_fixup) { + ppc_md.pcibios_fixup(); + } + + /* Generic fixups */ + pcibios_generic_fixup(); + + /* Allocate and assign resources */ + pcibios_allocate_bus_resources(&pci_root_buses); + if(naca->io_subsystem == IOS_OPEN_PIC) { + pcibios_allocate_resources(0); // DRENG Condor removed. This will have to be reviewed!!! + } + if(naca->io_subsystem == IOS_OPEN_PIC) { + pcibios_allocate_resources(1); // DRENG Condor removed. This will have to be reviewed!!! + } + pcibios_assign_resources(); + + /* + * Set up TCE tables for each PHB. + */ + for (hose = hose_head; hose; hose = hose->next) { + create_pci_bus_tce_table(hose->global_number, + (unsigned long)hose); + } + + ppc64_floppy_dev = find_floppy(); + + printk("PCI: Probing PCI hardware done\n"); + PPCDBG(PPCDBG_BUSWALK,"PCI: Probing PCI hardware done.\n"); + +} + +int __init +pcibios_assign_all_busses(void) +{ + return pci_assign_all_busses; +} + +unsigned long resource_fixup(struct pci_dev * dev, struct resource * res, + unsigned long start, unsigned long size) +{ + return start; +} + +void __init pcibios_fixup_bus(struct pci_bus *bus) +{ + pci_read_bridge_bases(bus); + + if ( ppc_md.pcibios_fixup_bus ) + ppc_md.pcibios_fixup_bus(bus); +} + +char __init *pcibios_setup(char *str) +{ + return str; +} + +int pcibios_enable_device(struct pci_dev *dev) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + if (ppc_md.pcibios_enable_device_hook) + if (ppc_md.pcibios_enable_device_hook(dev, 0)) + return -EINVAL; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for (idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + if (!r->start && r->end) { + printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", + dev->slot_name, old_cmd, cmd); + PPCDBG(PPCDBG_BUSWALK,"PCI: Enabling device %s \n",dev->slot_name); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + +struct pci_controller* +pci_bus_to_hose(int bus) +{ + struct pci_controller* hose = hose_head; + + for (; hose; hose = hose->next) + if (bus >= hose->first_busno && bus <= hose->last_busno) + return hose; + return NULL; +} + +void* +pci_bus_io_base(unsigned int bus) +{ + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + if (!hose) + return NULL; + return hose->io_base_virt; +} + +unsigned long +pci_bus_io_base_phys(unsigned int bus) +{ + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + if (!hose) + return 0; + return hose->io_base_phys; +} + +unsigned long +pci_bus_mem_base_phys(unsigned int bus) +{ + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + if (!hose) + return 0; + return hose->pci_mem_offset; +} + +/* + * Return the index of the PCI controller for device pdev. + */ +int pci_controller_num(struct pci_dev *dev) +{ + struct pci_controller *hose = (struct pci_controller *) dev->sysdata; + + return hose->global_number; +} + +/* + * Platform support for /proc/bus/pci/X/Y mmap()s, + * modelled on the sparc64 implementation by Dave Miller. + * -- paulus. + */ + +/* + * Adjust vm_pgoff of VMA such that it is the physical page offset + * corresponding to the 32-bit pci bus offset for DEV requested by the user. + * + * Basically, the user finds the base address for his device which he wishes + * to mmap. They read the 32-bit value from the config space base register, + * add whatever PAGE_SIZE multiple offset they wish, and feed this into the + * offset parameter of mmap on /proc/bus/pci/XXX for that device. + * + * Returns negative error code on failure, zero on success. + */ +static __inline__ int +__pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state) +{ + struct pci_controller *hose = (struct pci_controller *) dev->sysdata; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long io_offset = 0; + int i, res_bit; + + if (hose == 0) + return -EINVAL; /* should never happen */ + + /* If memory, add on the PCI bridge address offset */ + if (mmap_state == pci_mmap_mem) { + offset += hose->pci_mem_offset; + res_bit = IORESOURCE_MEM; + } else { + io_offset = (unsigned long)hose->io_base_virt - isa_io_base; + offset += io_offset; + res_bit = IORESOURCE_IO; + } + + /* + * Check that the offset requested corresponds to one of the + * resources of the device. + */ + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { + struct resource *rp = &dev->resource[i]; + int flags = rp->flags; + + /* treat ROM as memory (should be already) */ + if (i == PCI_ROM_RESOURCE) + flags |= IORESOURCE_MEM; + + /* Active and same type? */ + if ((flags & res_bit) == 0) + continue; + + /* In the range of this resource? */ + if (offset < (rp->start & PAGE_MASK) || offset > rp->end) + continue; + + /* found it! construct the final physical address */ + if (mmap_state == pci_mmap_io) + offset += hose->io_base_phys - io_offset; + + vma->vm_pgoff = offset >> PAGE_SHIFT; + return 0; + } + + return -EINVAL; +} + +/* + * Set vm_flags of VMA, as appropriate for this architecture, for a pci device + * mapping. + */ +static __inline__ void +__pci_mmap_set_flags(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state) +{ + vma->vm_flags |= VM_SHM | VM_LOCKED | VM_IO; +} + +/* + * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci + * device mapping. + */ +static __inline__ void +__pci_mmap_set_pgprot(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + long prot = pgprot_val(vma->vm_page_prot); + + /* XXX would be nice to have a way to ask for write-through */ + prot |= _PAGE_NO_CACHE; + if (!write_combine) + prot |= _PAGE_GUARDED; + vma->vm_page_prot = __pgprot(prot); +} + +/* + * Perform the actual remap of the pages for a PCI device mapping, as + * appropriate for this architecture. The region in the process to map + * is described by vm_start and vm_end members of VMA, the base physical + * address is found in vm_pgoff. + * The pci device structure is provided so that architectures may make mapping + * decisions on a per-device or per-bus basis. + * + * Returns a negative error code on failure, zero on success. + */ +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, + int write_combine) +{ + int ret; + + ret = __pci_mmap_make_offset(dev, vma, mmap_state); + if (ret < 0) + return ret; + + __pci_mmap_set_flags(dev, vma, mmap_state); + __pci_mmap_set_pgprot(dev, vma, mmap_state, write_combine); + + ret = remap_page_range(vma->vm_start, vma->vm_pgoff << PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + + return ret; +} + +/* Provide information on locations of various I/O regions in physical + * memory. Do this on a per-card basis so that we choose the right + * root bridge. + * Note that the returned IO or memory base is a physical address + */ + +long +sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn) +{ + struct pci_controller* hose = pci_bus_to_hose(bus); + long result = -EOPNOTSUPP; + + if (!hose) + return -ENODEV; + + switch (which) { + case IOBASE_BRIDGE_NUMBER: + return (long)hose->first_busno; + case IOBASE_MEMORY: + return (long)hose->pci_mem_offset; + case IOBASE_IO: + return (long)hose->io_base_phys; + case IOBASE_ISA_IO: + return (long)isa_io_base; + case IOBASE_ISA_MEM: + return (long)isa_mem_base; + } + + return result; +} + +/***************************************************** + * Dump Resource information + *****************************************************/ +void dumpResources(struct resource* Resource) { + if(Resource != NULL) { + int Flags = 0x00000F00 & Resource->flags; + if(Resource->start == 0 && Resource->end == 0) return; + else if(Resource->start == Resource->end ) return; + else { + if (Flags == IORESOURCE_IO) udbg_printf("IO.:"); + else if(Flags == IORESOURCE_MEM) udbg_printf("MEM:"); + else if(Flags == IORESOURCE_IRQ) udbg_printf("IRQ:"); + else udbg_printf("0x%02X:",Resource->flags); + + } + udbg_printf("0x%016LX / 0x%016LX (0x%08X)\n", + Resource->start, Resource->end, Resource->end - Resource->start); + } +} +int resourceSize(struct resource* Resource) { + if(Resource->start == 0 && Resource->end == 0) return 0; + else if(Resource->start == Resource->end ) return 0; + else return (Resource->end-1)-Resource->start; +} + + +/***************************************************** + * Dump PHB information for Debug + *****************************************************/ +void dumpPci_Controller(struct pci_controller* phb) { + udbg_printf("\tpci_controller= 0x%016LX\n", phb); + if(phb != NULL) { + udbg_printf("\twhat & type = %s 0x%02X\n ",phb->what,phb->type); + udbg_printf("\tbus = "); + if(phb->bus != NULL) udbg_printf("0x%02X\n", phb->bus->number); + else udbg_printf("\n"); + udbg_printf("\tarch_data = 0x%016LX\n", phb->arch_data); + udbg_printf("\tfirst_busno = 0x%02X\n", phb->first_busno); + udbg_printf("\tlast_busno = 0x%02X\n", phb->last_busno); + udbg_printf("\tio_base_virt* = 0x%016LX\n", phb->io_base_virt); + udbg_printf("\tio_base_phys = 0x%016LX\n", phb->io_base_phys); + udbg_printf("\tpci_mem_offset= 0x%016LX\n", phb->pci_mem_offset); + udbg_printf("\tpci_io_offset = 0x%016LX\n", phb->pci_io_offset); + + udbg_printf("\tcfg_addr = 0x%016LX\n", phb->cfg_addr); + udbg_printf("\tcfg_data = 0x%016LX\n", phb->cfg_data); + udbg_printf("\tphb_regs = 0x%016LX\n", phb->phb_regs); + udbg_printf("\tchip_regs = 0x%016LX\n", phb->chip_regs); + + + udbg_printf("\tResources\n"); + dumpResources(&phb->io_resource); + if(phb->mem_resource_count > 0) dumpResources(&phb->mem_resources[0]); + if(phb->mem_resource_count > 1) dumpResources(&phb->mem_resources[1]); + if(phb->mem_resource_count > 2) dumpResources(&phb->mem_resources[2]); + + udbg_printf("\tglobal_num = 0x%02X\n", phb->global_number); + udbg_printf("\tlocal_num = 0x%02X\n", phb->local_number); + } +} +/***************************************************** + * Dump PHB information for Debug + *****************************************************/ +void dumpPci_Bus(struct pci_bus* Pci_Bus){ + int i; + udbg_printf("\tpci_bus = 0x%016LX \n",Pci_Bus); + if(Pci_Bus != NULL) { + + udbg_printf("\tnumber = 0x%02X \n",Pci_Bus->number); + udbg_printf("\tprimary = 0x%02X \n",Pci_Bus->primary); + udbg_printf("\tsecondary = 0x%02X \n",Pci_Bus->secondary); + udbg_printf("\tsubordinate = 0x%02X \n",Pci_Bus->subordinate); + + for(i=0;i<4;++i) { + if(Pci_Bus->resource[i] == NULL) continue; + if(Pci_Bus->resource[i]->start == 0 && Pci_Bus->resource[i]->end == 0) break; + udbg_printf("\tResources[%d]",i); + dumpResources(Pci_Bus->resource[i]); + } + } +} +/***************************************************** + * Dump Device information for Debug + *****************************************************/ +void dumpPci_Dev(struct pci_dev* Pci_Dev) { + int i; + udbg_printf("\tpci_dev = 0x%016LX \n",Pci_Dev); + if( Pci_Dev == NULL ) return; + udbg_printf("\tname = %s\n",Pci_Dev->name); + udbg_printf("\tpci_dev phb = 0x%016LX \n",PCI_GET_PHB_PTR(Pci_Dev) ); + udbg_printf("\tDevice = 0x%4X%02X:%02X.%02X 0x%04X:%04X\n", + PCI_GET_PHB_NUMBER(Pci_Dev), + PCI_GET_BUS_NUMBER(Pci_Dev), + PCI_SLOT(Pci_Dev->devfn), + PCI_FUNC(Pci_Dev->devfn), + Pci_Dev->vendor, + Pci_Dev->device); + udbg_printf("\tHdr/Irq = 0x%02X/0x%02X \n",Pci_Dev->hdr_type,Pci_Dev->irq); + for(i=0;iresource[i].start == 0 && Pci_Dev->resource[i].end == 0) continue; + udbg_printf("\tResources[%d] ",i); + dumpResources(&Pci_Dev->resource[i]); + } + dumpResources(&Pci_Dev->resource[i]); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/pci.h linux.ac/arch/ppc64/kernel/pci.h --- linux.vanilla/arch/ppc64/kernel/pci.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/pci.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,83 @@ +/* + * c 2001 PPC 64 Team, 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. + */ +#ifndef __PPC_KERNEL_PCI_H__ +#define __PPC_KERNEL_PCI_H__ + +/* Include these to prevent strange compile warnings if user forgets + * to include them in their .c file. */ +#include +#include + +extern unsigned long isa_io_base; +extern unsigned long isa_mem_base; +extern unsigned long pci_dram_offset; + +/******************************************************************* + * Platform independant variables referenced. + ******************************************************************* + * Set pci_assign_all_busses to 1 if you want the kernel to re-assign + * all PCI bus numbers. + *******************************************************************/ +extern int pci_assign_all_busses; + +extern struct pci_controller* pci_alloc_pci_controller(char *model, enum phb_types controller_type); +extern struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node); + +/******************************************************************* + * Platform functions that are brand specific implementation. + *******************************************************************/ +extern unsigned long find_and_init_phbs(void); + +extern int pci_read_irq_line(struct pci_dev* PciDev); +extern int pci_read_bar_registers(struct pci_dev* PciDev, int Count, int RomFlag); + +extern void fixup_resources(struct pci_dev *dev); +extern void ppc64_pcibios_init(void); + +extern int pci_reset_device(struct pci_dev*); +extern int pci_get_location(struct pci_dev*, char*, int); + +extern struct pci_dev* find_floppy(void); +extern struct pci_dev *ppc64_floppy_dev; +/******************************************************************* + * Platform configuration flags.. (Live in pci.c) + *******************************************************************/ +extern int Pci_Large_Bus_System; /* System has > 256 buses */ +extern int Pci_Set_IOA_Address; /* Set IOA BARs from OF */ +extern int Pci_Manage_Phb_Space; /* Manage Phb Space for IOAs*/ + +/******************************************************************* + * Helper macros for extracting data from pci structures. + * PCI_GET_PHB_PTR(struct pci_dev*) returns the Phb pointer. + * PCI_GET_PHB_NUMBER(struct pci_dev*) returns the Phb number. + * PCI_GET_BUS_NUMBER(struct pci_dev*) returns the bus number. + *******************************************************************/ +#define PCI_GET_PHB_PTR(PCIDEV) ((struct pci_controller*)##PCIDEV##->sysdata) +#define PCI_GET_PHB_NUMBER(PCIDEV) ((##PCIDEV##->bus->number&0x00FFFF00)>>8) +#define PCI_GET_BUS_NUMBER(PCIDEV) ( ##PCIDEV##->bus->number&0x0000FF) + +/******************************************************************* + * Pci Flight Recorder support. + *******************************************************************/ +#define PCIFR(...) fr_Log_Entry(PciFr,__VA_ARGS__); +extern struct flightRecorder* PciFr; + +/******************************************************************* + * Debugging Routines. + *******************************************************************/ +extern void dumpResources(struct resource* Resource); +extern void dumpPci_Controller(struct pci_controller* phb); +extern void dumpPci_Bus(struct pci_bus* Pci_Bus); +extern void dumpPci_Dev(struct pci_dev* Pci_Dev); + +extern void dump_Phb_tree(void); +extern void dump_Bus_tree(void); +extern void dump_Dev_tree(void); + +#endif /* __PPC_KERNEL_PCI_H__ */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/pci_dma.c linux.ac/arch/ppc64/kernel/pci_dma.c --- linux.vanilla/arch/ppc64/kernel/pci_dma.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/pci_dma.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,1383 @@ +/* + * pci_dma.c + * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation + * + * Dynamic DMA mapping support. + * + * Manages the TCE space assigned to this partition. + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DEBUG_TCE 1 + +/* Initialize so this guy does not end up in the BSS section. + * Only used to pass OF initialization data set in prom.c into the main + * kernel code -- data ultimately copied into tceTables[]. + */ +extern struct _of_tce_table of_tce_table[]; + +struct TceTable virtBusTceTable; /* Tce table for virtual bus */ + +struct TceTable * tceTables[256]; /* Tce tables for 256 busses + * Bus 255 is the virtual bus + * zero indicates no bus defined + */ + /* allocates a contiguous range of tces (power-of-2 size) */ +static long alloc_tce_range( struct TceTable *, + unsigned order ); + /* allocates a contiguous range of tces (power-of-2 size) + * assumes lock already held + */ +static long alloc_tce_range_nolock( struct TceTable *, + unsigned order ); + /* frees a contiguous range of tces (power-of-2 size) */ +static void free_tce_range( struct TceTable *, + long tcenum, + unsigned order ); + /* frees a contiguous rnage of tces (power-of-2 size) + * assumes lock already held + */ +static void free_tce_range_nolock( struct TceTable *, + long tcenum, + unsigned order ); + /* allocates a range of tces and sets them to the pages */ +static dma_addr_t get_tces( struct TceTable *, + unsigned order, + void *page, + unsigned numPages, + int tceType, + int direction ); +static void free_tces( struct TceTable *, + dma_addr_t tce, + unsigned order, + unsigned numPages ); +static long test_tce_range( struct TceTable *, + long tcenum, + unsigned order ); + +static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents, + dma_addr_t dma_addr, unsigned long numTces ); + +static unsigned long num_tces_sg( struct scatterlist *sg, + int nents ); + +static dma_addr_t create_tces_sg( struct TceTable *tbl, + struct scatterlist *sg, + int nents, + unsigned numTces, + int tceType, + int direction ); + +static void getTceTableParms( struct pci_controller *phb, + struct TceTable *tce_table_parms ); + + +static unsigned long setTce( unsigned long base, + unsigned long tce_num, + unsigned long tce_data); + +static long resv_tce_range_top_level( struct TceTable *tbl, + unsigned long dma_addr, + unsigned size ); + +u8 iSeries_Get_Bus( struct pci_dev * dv ) +{ + return 0; +} + +/* + * Given a pci device, return which tce table is assigned to it. + */ +unsigned long get_tce_table_index( struct pci_dev *dev) { + unsigned long index = + ((struct pci_controller *)dev->sysdata)->global_number; + PPCDBG(PPCDBG_TCE, "get_tce_table_index:\n"); + PPCDBG(PPCDBG_TCE, "\tdev = 0x%lx, index = 0x%lx\n", dev, index); + return(index); +} + +static unsigned __inline__ count_leading_zeros32( unsigned long x ) +{ + unsigned lz; + asm("cntlzw %0,%1" : "=r"(lz) : "r"(x)); + return lz; +} + +static void __inline__ build_tce( struct TceTable * tbl, long tcenum, + unsigned long uaddr, int tceType, int direction ) +{ + u64 setTceRc; + union Tce tce; + + PPCDBG(PPCDBG_TCE, "build_tce: uaddr = 0x%lx\n", uaddr); + tce.wholeTce = 0; + tce.tceBits.rpn = (virt_to_absolute(uaddr)) >> PAGE_SHIFT; + /* If for virtual bus */ + if ( tceType == TCE_VB ) { + tce.tceBits.valid = 1; + tce.tceBits.allIo = 1; + if ( direction != PCI_DMA_TODEVICE ) + tce.tceBits.readWrite = 1; + } + /* If for PCI bus */ + else { + tce.tceBits.readWrite = 1; // Read allowed + if ( direction != PCI_DMA_TODEVICE ) + tce.tceBits.pciWrite = 1; + } + +#ifdef CONFIG_PPC_ISERIES + setTceRc = HvCallXm_setTce( (u64)tbl->index, (u64)tcenum, tce.wholeTce ); +#else + setTceRc = setTce( (u64)tbl->base, (u64)tcenum, tce.wholeTce ); +#endif + + if ( setTceRc ) { + PPCDBG(PPCDBG_TCE, "build_tce: HvCallXm_setTce failed, rc=%ld, index=%ld, tcenum=%0lx, tce=%016lx\n", + (u64)tbl->index, (u64)tcenum, tce.wholeTce ); + } + +} + + + +/* + * Build a TceTable structure. This contains a multi-level bit map which + * is used to manage allocation of the tce space. + */ +struct TceTable * build_tce_table( struct TceTable * tbl ) +{ + unsigned long bits, bytes, totalBytes; + unsigned long numBits[NUM_TCE_LEVELS], numBytes[NUM_TCE_LEVELS]; + unsigned i, k, m; + unsigned char * pos, * p, b; + + spin_lock_init( &(tbl->lock) ); + + tbl->mlbm.maxLevel = 0; + + /* Compute number of bits and bytes for each level of the + * multi-level bit map + */ + totalBytes = 0; + bits = tbl->size * (PAGE_SIZE / sizeof( union Tce )); + + for ( i=0; imlbm.level[i].map = pos; + tbl->mlbm.maxLevel = i; + + if ( numBits[i] & 1 ) { + p = pos + numBytes[i] - 1; + m = (( numBits[i] % 8) - 1) & 7; + *p = 0x80 >> m; + PPCDBG(PPCDBG_TCE, "build_tce_table: level %d last bit %x\n", i, 0x80>>m ); + } + } + else + tbl->mlbm.level[i].map = 0; + pos += numBytes[i]; + tbl->mlbm.level[i].numBits = numBits[i]; + tbl->mlbm.level[i].numBytes = numBytes[i]; + + } + + /* For the highest level, turn on all the bits */ + + i = tbl->mlbm.maxLevel; + p = tbl->mlbm.level[i].map; + m = numBits[i]; + PPCDBG(PPCDBG_TCE, "build_tce_table: highest level (%d) has all bits set\n", i); + for (k=0; k= 8 ) { + /* handle full bytes */ + *p++ = 0xff; + m -= 8; + } + else { + /* handle the last partial byte */ + b = 0x80; + *p = 0; + while (m) { + *p |= b; + b >>= 1; + --m; + } + } + } + + + return tbl; + +} + +static long alloc_tce_range( struct TceTable *tbl, unsigned order ) +{ + long retval; + unsigned long flags; + + /* Lock the tce allocation bitmap */ + spin_lock_irqsave( &(tbl->lock), flags ); + + /* Do the actual work */ + retval = alloc_tce_range_nolock( tbl, order ); + + /* Unlock the tce allocation bitmap */ + spin_unlock_irqrestore( &(tbl->lock), flags ); + + return retval; +} + + + +static long alloc_tce_range_nolock( struct TceTable *tbl, unsigned order ) +{ + unsigned long numBits, numBytes; + unsigned long i, bit, block, mask; + long tcenum; + unsigned char * map; + + /* If the order (power of 2 size) requested is larger than our + * biggest, indicate failure + */ + if ( order > tbl->mlbm.maxLevel ) { + PPCDBG(PPCDBG_TCE, "alloc_tce_range_nolock: invalid order requested, order = %d\n", + order ); + return -1; + } + + numBits = tbl->mlbm.level[order].numBits; + numBytes = tbl->mlbm.level[order].numBytes; + map = tbl->mlbm.level[order].map; + + /* Initialize return value to -1 (failure) */ + tcenum = -1; + + /* Loop through the bytes of the bitmap */ + for (i=0; i> bit); + *map &= mask; + /* compute the index into our tce table for + * the first tce in the block + */ + PPCDBG(PPCDBG_TCE, "alloc_tce_range_nolock: allocating block %ld, (byte=%ld, bit=%ld) order %d\n", block, i, bit, order ); + tcenum = block << order; + break; + } + ++map; + } + +#ifdef DEBUG_TCE + if ( tcenum == -1 ) { + PPCDBG(PPCDBG_TCE, "alloc_tce_range_nolock: no available blocks of order = %d\n", order ); + if ( order < tbl->mlbm.maxLevel ) + PPCDBG(PPCDBG_TCE, "alloc_tce_range_nolock: trying next bigger size\n" ); + else + PPCDBG(PPCDBG_TCE, "alloc_tce_range_nolock: maximum size reached...failing\n"); + } +#endif + + /* If no block of the requested size was found, try the next + * size bigger. If one of those is found, return the second + * half of the block to freespace and keep the first half + */ + if ( ( tcenum == -1 ) && ( order < tbl->mlbm.maxLevel ) ) { + tcenum = alloc_tce_range_nolock( tbl, order+1 ); + if ( tcenum != -1 ) { + free_tce_range_nolock( tbl, tcenum+(1<lock), flags ); + + /* Do the actual work */ + free_tce_range_nolock( tbl, tcenum, order ); + + /* Unlock the tce allocation bitmap */ + spin_unlock_irqrestore( &(tbl->lock), flags ); + +} + +static void free_tce_range_nolock( struct TceTable *tbl, long tcenum, unsigned order ) +{ + unsigned long block; + unsigned byte, bit, mask, b; + unsigned char * map, * bytep; + + if ( order > tbl->mlbm.maxLevel ) { + PPCDBG(PPCDBG_TCE, "free_tce_range: order too large, order = %d, tcenum = %d\n", order, tcenum ); + return; + } + + block = tcenum >> order; + if ( tcenum != (block << order ) ) { + PPCDBG(PPCDBG_TCE, "free_tce_range: tcenum %lx is not on appropriate boundary for order %x\n", tcenum, order ); + return; + } + if ( block >= tbl->mlbm.level[order].numBits ) { + PPCDBG(PPCDBG_TCE, "free_tce_range: tcenum %lx is outside the range of this map (order %x, numBits %lx\n", tcenum, order, tbl->mlbm.level[order].numBits ); + return; + } +#ifdef DEBUG_TCE + if ( test_tce_range( tbl, tcenum, order ) ) { + PPCDBG(PPCDBG_TCE, "free_tce_range: freeing range not completely allocated.\n"); + PPCDBG(PPCDBG_TCE, "free_tce_range: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order ); + } +#endif + map = tbl->mlbm.level[order].map; + byte = block / 8; + bit = block % 8; + mask = 0x80 >> bit; + bytep = map + byte; +#ifdef DEBUG_TCE + PPCDBG(PPCDBG_TCE, "free_tce_range_nolock: freeing block %ld (byte=%d, bit=%d) of order %d\n",block, byte, bit, order); + if ( *bytep & mask ) + PPCDBG(PPCDBG_TCE, "free_tce_range: already free: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order ); +#endif + *bytep |= mask; + + /* If there is a higher level in the bit map than this we may be + * able to buddy up this block with its partner. + * If this is the highest level we can't buddy up + * If this level has an odd number of bits and + * we are freeing the last block we can't buddy up + */ + if ( ( order < tbl->mlbm.maxLevel ) && + ( ( 0 == ( tbl->mlbm.level[order].numBits & 1 ) ) || + ( block < tbl->mlbm.level[order].numBits-1 ) ) ) { + + /* See if we can buddy up the block we just freed */ + bit &= 6; /* get to the first of the buddy bits */ + mask = 0xc0 >> bit; /* build two bit mask */ + b = *bytep & mask; /* Get the two bits */ + if ( 0 == (b ^ mask) ) { /* If both bits are on */ + /* both of the buddy blocks are free we can combine them */ + *bytep ^= mask; /* turn off the two bits */ + block = ( byte * 8 ) + bit; /* block of first of buddies */ + tcenum = block << order; + /* free the buddied block */ + PPCDBG(PPCDBG_TCE, "free_tce_range: buddying up block %ld and block %ld\n", block, block+1); + free_tce_range_nolock( tbl, tcenum, order+1 ); + } + } +} + +static long test_tce_range( struct TceTable *tbl, long tcenum, unsigned order ) +{ + unsigned long block; + unsigned byte, bit, mask, b; + long retval, retLeft, retRight; + unsigned char * map; + + map = tbl->mlbm.level[order].map; + block = tcenum >> order; + byte = block / 8; /* Byte within bitmap */ + bit = block % 8; /* Bit within byte */ + mask = 0x80 >> bit; + b = (*(map+byte) & mask ); /* 0 if block is allocated, else free */ + if ( b ) + retval = 1; /* 1 == block is free */ + else + retval = 0; /* 0 == block is allocated */ + /* Test bits at all levels below this to ensure that all agree */ + + if (order) { + retLeft = test_tce_range( tbl, tcenum, order-1 ); + retRight = test_tce_range( tbl, tcenum+(1<<(order-1)), order-1 ); + if ( retLeft || retRight ) { + retval = 2; + } + } + + /* Test bits at all levels above this to ensure that all agree */ + + return retval; +} + +static dma_addr_t get_tces( struct TceTable *tbl, unsigned order, void *page, unsigned numPages, int tceType, int direction ) +{ + long tcenum; + unsigned long uaddr; + unsigned i; + dma_addr_t retTce = NO_TCE; + + uaddr = (unsigned long)page & PAGE_MASK; + + /* Allocate a range of tces */ + tcenum = alloc_tce_range( tbl, order ); + if ( tcenum != -1 ) { + /* We got the tces we wanted */ + tcenum += tbl->startOffset; /* Offset into real TCE table */ + retTce = tcenum << PAGE_SHIFT; /* Set the return dma address */ + /* Setup a tce for each page */ + for (i=0; isize * (PAGE_SIZE / sizeof(union Tce))) - 1; + + tcenum = dma_addr >> PAGE_SHIFT; + tcenum -= tbl->startOffset; + + if ( tcenum > maxTcenum ) { + PPCDBG(PPCDBG_TCE, "free_tces: tcenum > maxTcenum, tcenum = %ld, maxTcenum = %ld\n", + tcenum, maxTcenum ); + PPCDBG(PPCDBG_TCE, "free_tces: TCE Table at %16lx\n", (unsigned long)tbl ); + PPCDBG(PPCDBG_TCE, "free_tces: bus# %lu\n", (unsigned long)tbl->busNumber ); + PPCDBG(PPCDBG_TCE, "free_tces: size %lu\n", (unsigned long)tbl->size ); + PPCDBG(PPCDBG_TCE, "free_tces: startOff %lu\n", (unsigned long)tbl->startOffset ); + PPCDBG(PPCDBG_TCE, "free_tces: index %lu\n", (unsigned long)tbl->index ); + return; + } + + freeTce = tcenum; + + for (i=0; iindex, (u64)tcenum, tce.wholeTce ); +#else + setTceRc = setTce( (u64)tbl->base, (u64)tcenum, tce.wholeTce ); +#endif + + if ( setTceRc ) { + PPCDBG(PPCDBG_TCE, "free_tces: HvCallXm_setTce failed, rc=%ld, index=%ld, tcenum=%0lx, tce=%016lx\n", + (u64)tbl->index, (u64)tcenum, tce.wholeTce ); + } + + ++tcenum; + } + + free_tce_range( tbl, freeTce, order ); + +} + +void __init create_virtual_bus_tce_table(void) +{ + struct TceTable *t; + struct TceTableManagerCB virtBusTceTableParms; + u64 absParmsPtr; + + virtBusTceTableParms.busNumber = 255; /* Bus 255 is the virtual bus */ + virtBusTceTableParms.virtualBusFlag = 0xff; /* Ask for virtual bus */ + + absParmsPtr = virt_to_absolute( (u64)&virtBusTceTableParms ); + HvCallXm_getTceTableParms( absParmsPtr ); + + virtBusTceTable.size = virtBusTceTableParms.size; + virtBusTceTable.busNumber = virtBusTceTableParms.busNumber; + virtBusTceTable.startOffset = virtBusTceTableParms.startOffset; + virtBusTceTable.index = virtBusTceTableParms.index; + + t = build_tce_table( &virtBusTceTable ); + if ( t ) { + tceTables[255] = t; + PPCDBG(PPCDBG_TCE, "Virtual Bus TCE table built successfully.\n"); + PPCDBG(PPCDBG_TCE, " TCE table size = %ld entries\n", + (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); + PPCDBG(PPCDBG_TCE, " TCE table token = %d\n", + (unsigned)t->index ); + PPCDBG(PPCDBG_TCE, " TCE table start entry = 0x%lx\n", + (unsigned long)t->startOffset ); + } + else + PPCDBG(PPCDBG_TCE, "Virtual Bus TCE table failed.\n"); +} + +void create_pci_bus_tce_table( unsigned long busNumber, + unsigned long token ) +{ + struct TceTable * t; + struct TceTable * newTceTable; + struct TceTableManagerCB pciBusTceTableParms; +#ifdef CONFIG_PPC_ISERIES + u64 parmsPtr; +#endif + struct pci_controller *phb = (struct pci_controller *)token; + + PPCDBG(PPCDBG_TCE, "Entering create_pci_bus_tce_table.\n"); + PPCDBG(PPCDBG_TCE, "\tbusNumber = 0x%lx, token = 0x%lx.\n", + busNumber, token); + if ( busNumber > 254 ) { + PPCDBG(PPCDBG_TCE, "PCI Bus TCE table failed.\n"); + PPCDBG(PPCDBG_TCE, " Invalid bus number %u\n", busNumber ); + return; + } + + newTceTable = kmalloc( sizeof(struct TceTable), GFP_KERNEL ); + PPCDBG(PPCDBG_TCE, "\tnewTceTable = 0x%lx\n", newTceTable); + + pciBusTceTableParms.busNumber = busNumber; + pciBusTceTableParms.virtualBusFlag = 0; + +#ifdef CONFIG_PPC_ISERIES + parmsPtr = virt_to_absolute( (u64)&pciBusTceTableParms ); + + /* + * Call HV with the architected data structure to get TCE table info. + * Put the returned data into the Linux representation of the TCE + * table data. + */ + HvCallXm_getTceTableParms( parmsPtr ); + newTceTable->size = pciBusTceTableParms.size; + newTceTable->busNumber = pciBusTceTableParms.busNumber; + newTceTable->startOffset = pciBusTceTableParms.startOffset; + newTceTable->index = pciBusTceTableParms.index; +#else + getTceTableParms( phb, newTceTable ); +#endif + + t = build_tce_table( newTceTable ); + if ( t ) { + tceTables[busNumber] = t; + PPCDBG(PPCDBG_TCE, "PCI Bus TCE table built successfully.\n"); + PPCDBG(PPCDBG_TCE, " TCE table size = %ld entries\n", + (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); + PPCDBG(PPCDBG_TCE, " TCE table token = %d\n", + (unsigned)t->index ); + PPCDBG(PPCDBG_TCE, " TCE table start entry = 0x%lx\n", + (unsigned long)t->startOffset ); + } + else { + kfree( newTceTable ); + PPCDBG(PPCDBG_TCE, "PCI Bus TCE table failed.\n"); + return; + } + +#ifndef CONFIG_PPC_ISERIES + /* Do not allow DMA's to the 1st 16MB of PCI space in order + * to account for the I/O hole and aliases. + * + * DRENG: this only really needs to be done on PHB0 & should be + * validated against dma-ranges. This is all somewhat compilcated + * by some OF oddities: on a 260, there is no dma-ranges, but there + * is a io-hole. On an F80, there are dma-ranges, but no I/O hole. + * Just seems easier to simply map out the first 16MB in all cases. + * + * The RTAS case here ends up being redundant as it falls into + * the 16MB already mapped out. + */ + resv_tce_range_top_level(newTceTable, 0, 4096); + resv_tce_range_top_level(newTceTable, rtas.base, + (rtas.size) >> PAGE_SHIFT); + +#if 1 + /* Initialize the table to have a one-to-one mapping over RTAS */ + + /* DRENG strictly speaking, the RPA says this should be done. + * I do not see how it can really be a valid thing to require however. + */ + { + unsigned long tce_entry, *tce_entryp, i; + + tce_entryp = (unsigned long *)newTceTable->base; + tce_entryp += (rtas.base >> PAGE_SHIFT); + tce_entry = (rtas.base & PAGE_MASK) | 0x3; + + for(i = 0; i < (rtas.size >> PAGE_SHIFT); i++) { + *tce_entryp = tce_entry; + + tce_entryp++; + tce_entry += (1UL << PAGE_SHIFT); + } + } +#endif +#endif +} + +static void getTceTableParms( struct pci_controller *phb, + struct TceTable *newTceTable ) { +#if 0 + struct pci_dev *dev; + struct device_node *np; +#endif + phandle node; + unsigned long i; + + node = ((struct device_node *)(phb->arch_data))->node; + + PPCDBG(PPCDBG_TCE, "getTceTableParms: start\n"); + PPCDBG(PPCDBG_TCE, "\tof_tce_table = 0x%lx\n", of_tce_table); + PPCDBG(PPCDBG_TCE, "\tphb = 0x%lx\n", phb); + PPCDBG(PPCDBG_TCE, "\tnewTceTable = 0x%lx\n", newTceTable); + + i = 0; + while(of_tce_table[i].node) { + PPCDBG(PPCDBG_TCE, "\ttce_table[%d].node = 0x%lx\n", + i, of_tce_table[i].node); + PPCDBG(PPCDBG_TCE, "\ttce_table[%d].base = 0x%lx\n", + i, of_tce_table[i].base); + PPCDBG(PPCDBG_TCE, "\ttce_table[%d].size = 0x%lx\n", + i, of_tce_table[i].size >> PAGE_SHIFT); + PPCDBG(PPCDBG_TCE, "\tphb->arch_data->node = 0x%lx\n", node); + + if(of_tce_table[i].node == node) { + memset((void *)of_tce_table[i].base, + 0, of_tce_table[i].size); + newTceTable->busNumber = phb->bus->number; + newTceTable->size = + (of_tce_table[i].size) >> PAGE_SHIFT; + newTceTable->startOffset = 0; + newTceTable->base = + of_tce_table[i].base; + newTceTable->index = 0; + } + i++; + } + +#if 0 + pci_for_each_dev(dev) { + np = pci_device_to_OF_node(dev); + node = np->node; + bus_number = dev->bus->number; + create_pci_bus_tce_table( unsigned long bus_number ); + node = hose->arch_data->node; + } +#endif +} + +/* Allocates a contiguous real buffer and creates TCEs over it. + * Returns the virtual address of the buffer and sets dma_handle + * to the dma address (tce) of the first page. + */ +void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + struct TceTable * tbl; + void *ret = NULL; + unsigned order, nPages, bus; + dma_addr_t tce; + int tceType; + + PPCDBG(PPCDBG_TCE, "pci_alloc_consistent:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx\n", hwdev); + PPCDBG(PPCDBG_TCE, "\tsize = 0x%16.16lx\n", size); + PPCDBG(PPCDBG_TCE, "\tdma_handle = 0x%16.16lx\n", dma_handle); + + size = PAGE_ALIGN(size); + order = get_order(size); + nPages = 1 << order; + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = get_tce_table_index( hwdev ); + // bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return NULL; +#endif /* CONFIG_PCI */ + } + + tbl = tceTables[bus]; + if ( tbl ) { + /* Alloc enough pages (and possibly more) */ + ret = (void *)__get_free_pages( GFP_ATOMIC, order ); + if ( ret ) { + /* Page allocation succeeded */ + memset(ret, 0, nPages << PAGE_SHIFT); + /* Set up tces to cover the allocated range */ + tce = get_tces( tbl, order, ret, nPages, tceType, + PCI_DMA_BIDIRECTIONAL ); + if ( tce == NO_TCE ) { + PPCDBG(PPCDBG_TCE, "pci_alloc_consistent: get_tces failed\n" ); + free_pages( (unsigned long)ret, order ); + ret = NULL; + } + else + { + *dma_handle = tce; + } + } + else + PPCDBG(PPCDBG_TCE, "pci_alloc_consistent: __get_free_pages failed for order = %d\n", order); + } + + PPCDBG(PPCDBG_TCE, "\tpci_alloc_consistent: dma_handle = 0x%16.16lx\n", *dma_handle); + PPCDBG(PPCDBG_TCE, "\tpci_alloc_consistent: return = 0x%16.16lx\n", ret); + return ret; +} + +void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + struct TceTable * tbl; + unsigned order, nPages, bus; + + PPCDBG(PPCDBG_TCE, "pci_free_consistent:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, size = 0x%16.16lx, dma_handle = 0x%16.16lx, vaddr = 0x%16.16lx\n", hwdev, size, dma_handle, vaddr); + + size = PAGE_ALIGN(size); + order = get_order(size); + nPages = 1 << order; + + if ( order > 10 ) + PPCDBG(PPCDBG_TCE, "pci_free_consistent: order=%d, size=%d, nPages=%d, dma_handle=%016lx, vaddr=%016lx\n", + order, size, nPages, (unsigned long)dma_handle, (unsigned long)vaddr ); + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = get_tce_table_index( hwdev ); + // bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + PPCDBG(PPCDBG_TCE, "pci_free_consistent: invalid bus # %d\n", bus ); + PPCDBG(PPCDBG_TCE, "pci_free_consistent: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + tbl = tceTables[bus]; + + if ( tbl ) { + free_tces( tbl, dma_handle, order, nPages ); + free_pages( (unsigned long)vaddr, order ); + } +} + +/* Creates TCEs for a user provided buffer. The user buffer must be + * contiguous real kernel storage (not vmalloc). The address of the buffer + * passed here is the kernel (virtual) address of the buffer. The buffer + * need not be page aligned, the dma_addr_t returned will point to the same + * byte within the page as vaddr. + */ +dma_addr_t pci_map_single( struct pci_dev *hwdev, void *vaddr, size_t size, int direction ) +{ + struct TceTable * tbl; + dma_addr_t dma_handle; + unsigned long uaddr; + unsigned order, nPages, bus; + int tceType; + + PPCDBG(PPCDBG_TCE, "pci_map_single:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, size = 0x%16.16lx, direction = 0x%16.16lx, vaddr = 0x%16.16lx\n", hwdev, size, direction, vaddr); + if ( direction == PCI_DMA_NONE ) + BUG(); + + dma_handle = NO_TCE; + + uaddr = (unsigned long)vaddr; + nPages = PAGE_ALIGN( uaddr + size ) - ( uaddr & PAGE_MASK ); + order = get_order( nPages & PAGE_MASK ); + nPages >>= PAGE_SHIFT; + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = get_tce_table_index( hwdev ); + // bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return NO_TCE; +#endif /* CONFIG_PCI */ + + } + + tbl = tceTables[bus]; + + if ( tbl ) { + dma_handle = get_tces( tbl, order, vaddr, nPages, tceType, + direction ); + dma_handle |= ( uaddr & ~PAGE_MASK ); + } + + return dma_handle; +} + +void pci_unmap_single( struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction ) +{ + struct TceTable * tbl; + unsigned order, nPages, bus; + + PPCDBG(PPCDBG_TCE, "pci_unmap_single:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, size = 0x%16.16lx, direction = 0x%16.16lx, dma_handle = 0x%16.16lx\n", hwdev, size, direction, dma_handle); + if ( direction == PCI_DMA_NONE ) + BUG(); + + nPages = PAGE_ALIGN( dma_handle + size ) - ( dma_handle & PAGE_MASK ); + order = get_order( nPages & PAGE_MASK ); + nPages >>= PAGE_SHIFT; + + if ( order > 10 ) + PPCDBG(PPCDBG_TCE, "pci_unmap_single: order=%d, size=%d, nPages=%d, dma_handle=%016lx\n", + order, size, nPages, (unsigned long)dma_handle ); + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = get_tce_table_index( hwdev ); + // bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + PPCDBG(PPCDBG_TCE, "pci_unmap_single: invalid bus # %d\n", bus ); + PPCDBG(PPCDBG_TCE, "pci_unmap_single: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + tbl = tceTables[bus]; + + if ( tbl ) + free_tces( tbl, dma_handle, order, nPages ); + +} + +/* Figure out how many TCEs are actually going to be required + * to map this scatterlist. This code is not optimal. It + * takes into account the case where entry n ends in the same + * page in which entry n+1 starts. It does not handle the + * general case of entry n ending in the same page in which + * entry m starts. + */ +static unsigned long num_tces_sg( struct scatterlist *sg, int nents ) +{ + unsigned long nTces, numPages, startPage, endPage, prevEndPage; + unsigned i; + + prevEndPage = 0; + nTces = 0; + + for (i=0; iaddress >> PAGE_SHIFT; + endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT; + numPages = endPage - startPage + 1; + /* Simple optimization: if the previous entry ended + * in the same page in which this entry starts + * then we can reduce the required pages by one. + * This matches assumptions in fill_scatterlist_sg and + * create_tces_sg + */ + if ( startPage == prevEndPage ) + --numPages; + nTces += numPages; + prevEndPage = endPage; + sg++; + } + return nTces; +} + +/* Fill in the dma data in the scatterlist + * return the number of dma sg entries created + */ +static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents, + dma_addr_t dma_addr , unsigned long numTces) +{ + struct scatterlist *dma_sg; + u32 cur_start_dma; + unsigned long cur_len_dma, cur_end_virt, uaddr; + unsigned num_dma_ents; + + dma_sg = sg; + num_dma_ents = 1; + + /* Process the first sg entry */ + cur_start_dma = dma_addr + ((unsigned long)sg->address & (~PAGE_MASK)); + cur_len_dma = sg->length; + /* cur_end_virt holds the address of the byte immediately after the + * end of the current buffer. + */ + cur_end_virt = (unsigned long)sg->address + cur_len_dma; + /* Later code assumes that unused sg->dma_address and sg->dma_length + * fields will be zero. Other archs seem to assume that the user + * (device driver) guarantees that...I don't want to depend on that + */ + sg->dma_address = sg->dma_length = 0; + + /* Process the rest of the sg entries */ + while (--nents) { + ++sg; + /* Clear possibly unused fields. Note: sg >= dma_sg so + * this can't be clearing a field we've already set + */ + sg->dma_address = sg->dma_length = 0; + + /* Check if it is possible to make this next entry + * contiguous (in dma space) with the previous entry. + */ + + /* The entries can be contiguous in dma space if + * the previous entry ends immediately before the + * start of the current entry (in virtual space) + * or if the previous entry ends at a page boundary + * and the current entry starts at a page boundary. + */ + uaddr = (unsigned long)sg->address; + if ( ( uaddr != cur_end_virt ) && + ( ( ( uaddr | cur_end_virt ) & (~PAGE_MASK) ) || + ( ( uaddr & PAGE_MASK ) == ( ( cur_end_virt-1 ) & PAGE_MASK ) ) ) ) { + /* This entry can not be contiguous in dma space. + * save the previous dma entry and start a new one + */ + dma_sg->dma_address = cur_start_dma; + dma_sg->dma_length = cur_len_dma; + + ++dma_sg; + ++num_dma_ents; + + cur_start_dma += cur_len_dma-1; + /* If the previous entry ends and this entry starts + * in the same page then they share a tce. In that + * case don't bump cur_start_dma to the next page + * in dma space. This matches assumptions made in + * num_tces_sg and create_tces_sg. + */ + if ((uaddr & PAGE_MASK) == ((cur_end_virt-1) & PAGE_MASK)) + cur_start_dma &= PAGE_MASK; + else + cur_start_dma = PAGE_ALIGN(cur_start_dma+1); + cur_start_dma += ( uaddr & (~PAGE_MASK) ); + cur_len_dma = 0; + } + /* Accumulate the length of this entry for the next + * dma entry + */ + cur_len_dma += sg->length; + cur_end_virt = uaddr + sg->length; + } + /* Fill in the last dma entry */ + dma_sg->dma_address = cur_start_dma; + dma_sg->dma_length = cur_len_dma; + + if ((((cur_start_dma +cur_len_dma - 1)>> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1) != numTces) + { + PPCDBG(PPCDBG_TCE, "fill_scatterlist_sg: numTces %ld, used tces %d\n", + numTces, + (unsigned)(((cur_start_dma + cur_len_dma - 1) >> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1)); + } + + + return num_dma_ents; +} + +/* Call the hypervisor to create the TCE entries. + * return the number of TCEs created + */ +static dma_addr_t create_tces_sg( struct TceTable *tbl, struct scatterlist *sg, + int nents, unsigned numTces, int tceType, int direction ) +{ + unsigned order, i, j; + unsigned long startPage, endPage, prevEndPage, numPages, uaddr; + long tcenum, starttcenum; + dma_addr_t dmaAddr; + + dmaAddr = NO_TCE; + + order = get_order( numTces << PAGE_SHIFT ); + /* allocate a block of tces */ + tcenum = alloc_tce_range( tbl, order ); + if ( tcenum != -1 ) { + tcenum += tbl->startOffset; + starttcenum = tcenum; + dmaAddr = tcenum << PAGE_SHIFT; + prevEndPage = 0; + for (j=0; jaddress >> PAGE_SHIFT; + endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT; + numPages = endPage - startPage + 1; + + uaddr = (unsigned long)sg->address; + + /* If the previous entry ended in the same page that + * the current page starts then they share that + * tce and we reduce the number of tces we need + * by one. This matches assumptions made in + * num_tces_sg and fill_scatterlist_sg + */ + if ( startPage == prevEndPage ) { + --numPages; + uaddr += PAGE_SIZE; + } + + for (i=0; idma_address = pci_map_single( hwdev, sg->address, + sg->length, direction ); + sg->dma_length = sg->length; + return 1; + } + + if ( direction == PCI_DMA_NONE ) + BUG(); + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = get_tce_table_index( hwdev ); + // bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return 0; +#endif /* CONFIG_PCI */ + } + + tbl = tceTables[bus]; + + if ( tbl ) { + /* Compute the number of tces required */ + numTces = num_tces_sg( sg, nents ); + /* Create the tces and get the dma address */ + dma_handle = create_tces_sg( tbl, sg, nents, numTces, + tceType, direction ); + + /* Fill in the dma scatterlist */ + num_dma = fill_scatterlist_sg( sg, nents, dma_handle, numTces ); + } + + return num_dma; +} + +void pci_unmap_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nelms, int direction ) +{ + struct TceTable * tbl; + unsigned order, numTces, bus, i; + dma_addr_t dma_end_page, dma_start_page; + + PPCDBG(PPCDBG_TCE, "pci_unmap_sg:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, sg = 0x%16.16lx, direction = 0x%16.16lx, nelms = 0x%16.16lx\n", hwdev, sg, direction, nelms); + + if ( direction == PCI_DMA_NONE ) + BUG(); + + dma_start_page = sg->dma_address & PAGE_MASK; + for ( i=nelms; i>0; --i ) { + unsigned k = i - 1; + if ( sg[k].dma_length ) { + dma_end_page = ( sg[k].dma_address + + sg[k].dma_length - 1 ) & PAGE_MASK; + break; + } + } + + numTces = ((dma_end_page - dma_start_page ) >> PAGE_SHIFT) + 1; + order = get_order( numTces << PAGE_SHIFT ); + + if ( order > 10 ) + PPCDBG(PPCDBG_TCE, "pci_unmap_sg: order=%d, numTces=%d, nelms=%d, dma_start_page=%016lx, dma_end_page=%016lx\n", + order, numTces, nelms, (unsigned long)dma_start_page, (unsigned long)dma_end_page ); + + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = get_tce_table_index( hwdev ); + // bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + PPCDBG(PPCDBG_TCE, "pci_unmap_sg: invalid bus # %d\n", bus ); + PPCDBG(PPCDBG_TCE, "pci_unmap_sg: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + + tbl = tceTables[bus]; + + if ( tbl ) + free_tces( tbl, dma_start_page, order, numTces ); + +} + +static unsigned long setTce( unsigned long base, + unsigned long tce_num, + unsigned long tce_data) { + union Tce *tce_addr; + + PPCDBG(PPCDBG_TCE, "setTce:\n"); + PPCDBG(PPCDBG_TCE, "\tbase = 0x%lx\n", base); + PPCDBG(PPCDBG_TCE, "\ttce_num = 0x%lx\n", tce_num); + PPCDBG(PPCDBG_TCE, "\ttce_data = 0x%lx\n", tce_data); + + tce_addr = ((union Tce *)base) + tce_num; + + PPCDBG(PPCDBG_TCE, "\ttce_addr = 0x%lx\n", tce_addr); + + *tce_addr = (union Tce)tce_data; + + /* Make sure the update is visible to hardware. */ + __asm__ __volatile__ ("sync" : : : "memory"); + + return(0); +} + +unsigned long phb_tce_table_init(struct pci_controller *phb) { + unsigned int r, cfg_rw, i; + unsigned long r64; + phandle node; + + PPCDBG(PPCDBG_TCE, "phb_tce_table_init: start.\n"); + + node = ((struct device_node *)(phb->arch_data))->node; + + PPCDBG(PPCDBG_TCEINIT, "\tphb = 0x%lx\n", phb); + PPCDBG(PPCDBG_TCEINIT, "\tphb->type = 0x%lx\n", phb->type); + PPCDBG(PPCDBG_TCEINIT, "\tphb->phb_regs = 0x%lx\n", phb->phb_regs); + PPCDBG(PPCDBG_TCEINIT, "\tphb->chip_regs = 0x%lx\n", phb->chip_regs); + PPCDBG(PPCDBG_TCEINIT, "\tphb: node = 0x%lx\n", node); + PPCDBG(PPCDBG_TCEINIT, "\tphb->arch_data = 0x%lx\n", phb->arch_data); + + i = 0; + while(of_tce_table[i].node) { + if(of_tce_table[i].node == node) { + if(phb->type == phb_type_python) { + r = *(((unsigned int *)phb->phb_regs) + (0xf10>>2)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR(low) = 0x%x\n", r); + r = *(((unsigned int *)phb->phb_regs) + (0xf00>>2)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR(high) = 0x%x\n", r); + r = *(((unsigned int *)phb->phb_regs) + (0xfd0>>2)); + PPCDBG(PPCDBG_TCEINIT, "\tPHB cfg(rw) = 0x%x\n", r); + break; + } else if(phb->type == phb_type_speedwagon) { + r64 = *(((unsigned long *)phb->chip_regs) + + (0x800>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tNCFG = 0x%lx\n", r64); + r64 = *(((unsigned long *)phb->chip_regs) + + (0x580>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR0 = 0x%lx\n", r64); + r64 = *(((unsigned long *)phb->chip_regs) + + (0x588>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR1 = 0x%lx\n", r64); + r64 = *(((unsigned long *)phb->chip_regs) + + (0x590>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR2 = 0x%lx\n", r64); + r64 = *(((unsigned long *)phb->chip_regs) + + (0x598>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR3 = 0x%lx\n", r64); + cfg_rw = *(((unsigned int *)phb->chip_regs) + + ((0x160 + + (((phb->local_number)+8)<<12))>>2)); + PPCDBG(PPCDBG_TCEINIT, "\tcfg_rw = 0x%x\n", cfg_rw); + } + } + i++; + } + + PPCDBG(PPCDBG_TCEINIT, "phb_tce_table_init: done\n"); + // create_pci_bus_tce_table(phb->number, (unsigned long)phb); +} + +/* + * Reserve a range of TCE table entries. + * + * Inputs: + * tbl TCE Table. + * dma_addr PCI Base Address, must be aligned to 2^(NUM_TCE_LEVELS-1). + * size Size to reserve in pages. Value is rounded up to a multiple + * of (2^(NUM_TCE_LEVELS-1)); ie the min size of the allocation + * is 2^9 * 4096 = 2MB. + * + * This should only be called after the table has been initialized, but + * before it has been used. + */ +static long resv_tce_range_top_level( struct TceTable *tbl, + unsigned long dma_addr, + unsigned size ) +{ + unsigned long i, segments, block; + long tcenum, maxTcenum; + unsigned byte, bit, mask; + unsigned char *map, *bytep; + + PPCDBG(PPCDBG_TCEINIT, "resv_tce_range_top: start\n"); + PPCDBG(PPCDBG_TCEINIT, "\tdma_addr=0x%lx, size=0x%lx\n", + dma_addr, size); + maxTcenum = (tbl->size * (PAGE_SIZE / sizeof(union Tce))) - 1; + tcenum = (dma_addr >> PAGE_SHIFT) & + (~((1UL << (NUM_TCE_LEVELS - 1)) - 1)); + tcenum -= tbl->startOffset; + + PPCDBG(PPCDBG_TCEINIT, "\ttcenum=0x%lx, maxTcenum=0x%lx\n", + tcenum, maxTcenum); + + if ( tcenum > maxTcenum ) { + return(-1); + } + + segments = (size + (1UL << (NUM_TCE_LEVELS - 1)) - 1) >> + (NUM_TCE_LEVELS - 1); + map = tbl->mlbm.level[NUM_TCE_LEVELS-1].map; + block = tcenum >> (NUM_TCE_LEVELS -1); + + PPCDBG(PPCDBG_TCEINIT, "\tsegments = 0x%lx, map = 0x%lx\n", + segments, map); + + for(i = 0; i < segments; i++) { + byte = block / 8; + bit = block % 8; + mask = 0x80 >> bit; + bytep = map + byte; + + *bytep = *bytep & (~mask); + + block++; + } + + return(0); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/pmac_nvram.c linux.ac/arch/ppc64/kernel/pmac_nvram.c --- linux.vanilla/arch/ppc64/kernel/pmac_nvram.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/pmac_nvram.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,396 @@ +/* + * c 2001 PPC 64 Team, 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. + * + * Miscellaneous procedures for dealing with the PowerMac hardware. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +/* + * Read and write the non-volatile RAM on PowerMacs and CHRP machines. + */ +static int nvram_naddrs; +static volatile unsigned char *nvram_addr; +static volatile unsigned char *nvram_data; +static int nvram_mult, is_core_99; +static char* nvram_image; +static int core99_bank = 0; +sys_ctrler_t sys_ctrler = SYS_CTRLER_UNKNOWN; + +extern int pmac_newworld; + +#define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ + +#define CORE99_SIGNATURE 0x5a +#define CORE99_ADLER_START 0x14 + +/* Core99 nvram is a flash */ +#define CORE99_FLASH_STATUS_DONE 0x80 +#define CORE99_FLASH_STATUS_ERR 0x38 +#define CORE99_FLASH_CMD_ERASE_CONFIRM 0xd0 +#define CORE99_FLASH_CMD_ERASE_SETUP 0x20 +#define CORE99_FLASH_CMD_RESET 0xff +#define CORE99_FLASH_CMD_WRITE_SETUP 0x40 + +/* CHRP NVRAM header */ +struct chrp_header { + u8 signature; + u8 cksum; + u16 len; + char name[12]; + u8 data[0]; +}; + +struct core99_header { + struct chrp_header hdr; + u32 adler; + u32 generation; + u32 reserved[2]; +}; + +static int nvram_partitions[3]; + +static u8 +chrp_checksum(struct chrp_header* hdr) +{ + u8 *ptr; + u16 sum = hdr->signature; + for (ptr = (u8 *)&hdr->len; ptr < hdr->data; ptr++) + sum += *ptr; + while (sum > 0xFF) + sum = (sum & 0xFF) + (sum>>8); + return sum; +} + +static u32 +core99_calc_adler(u8 *buffer) +{ + int cnt; + u32 low, high; + + buffer += CORE99_ADLER_START; + low = 1; + high = 0; + for (cnt=0; cnt<(NVRAM_SIZE-CORE99_ADLER_START); cnt++) { + if ((cnt % 5000) == 0) { + high %= 65521UL; + high %= 65521UL; + } + low += buffer[cnt]; + high += low; + } + low %= 65521UL; + high %= 65521UL; + + return (high << 16) | low; +} + +static u32 +core99_check(u8* datas) +{ + struct core99_header* hdr99 = (struct core99_header*)datas; + + if (hdr99->hdr.signature != CORE99_SIGNATURE) { +#ifdef DEBUG + printk("Invalid signature\n"); +#endif + return 0; + } + if (hdr99->hdr.cksum != chrp_checksum(&hdr99->hdr)) { +#ifdef DEBUG + printk("Invalid checksum\n"); +#endif + return 0; + } + if (hdr99->adler != core99_calc_adler(datas)) { +#ifdef DEBUG + printk("Invalid adler\n"); +#endif + return 0; + } + return hdr99->generation; +} + +static int +core99_erase_bank(int bank) +{ + int stat, i; + + u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE; + + out_8(base, CORE99_FLASH_CMD_ERASE_SETUP); + out_8(base, CORE99_FLASH_CMD_ERASE_CONFIRM); + do { stat = in_8(base); } + while(!(stat & CORE99_FLASH_STATUS_DONE)); + out_8(base, CORE99_FLASH_CMD_RESET); + if (stat & CORE99_FLASH_STATUS_ERR) { + printk("nvram: flash error 0x%02x on erase !\n", stat); + return -ENXIO; + } + for (i=0; iname, "common")) + nvram_partitions[pmac_nvram_OF] = offset + 0x10; + if (!strcmp(hdr->name, "APL,MacOS75")) { + nvram_partitions[pmac_nvram_XPRAM] = offset + 0x10; + nvram_partitions[pmac_nvram_NR] = offset + 0x110; + } + offset += (hdr->len * 0x10); + } while(offset < NVRAM_SIZE); + } else { + nvram_partitions[pmac_nvram_OF] = 0x1800; + nvram_partitions[pmac_nvram_XPRAM] = 0x1300; + nvram_partitions[pmac_nvram_NR] = 0x1400; + } +#ifdef DEBUG + printk("nvram: OF partition at 0x%x\n", nvram_partitions[pmac_nvram_OF]); + printk("nvram: XP partition at 0x%x\n", nvram_partitions[pmac_nvram_XPRAM]); + printk("nvram: NR partition at 0x%x\n", nvram_partitions[pmac_nvram_NR]); +#endif +} + +__init +void pmac_nvram_init(void) +{ + struct device_node *dp; + + nvram_naddrs = 0; + + dp = find_devices("nvram"); + if (dp == NULL) { + printk(KERN_ERR "Can't find NVRAM device\n"); + return; + } + nvram_naddrs = dp->n_addrs; + is_core_99 = device_is_compatible(dp, "nvram,flash"); + if (is_core_99) { + int i; + u32 gen_bank0, gen_bank1; + + if (nvram_naddrs < 1) { + printk(KERN_ERR "nvram: no address\n"); + return; + } + nvram_image = kmalloc(NVRAM_SIZE, GFP_KERNEL); + if (!nvram_image) { + printk(KERN_ERR "nvram: can't allocate image\n"); + return; + } + nvram_data = ioremap(dp->addrs[0].address, NVRAM_SIZE*2); +#ifdef DEBUG + printk("nvram: Checking bank 0...\n"); +#endif + gen_bank0 = core99_check((u8 *)nvram_data); + gen_bank1 = core99_check((u8 *)nvram_data + NVRAM_SIZE); + core99_bank = (gen_bank0 < gen_bank1) ? 1 : 0; +#ifdef DEBUG + printk("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1); + printk("nvram: Active bank is: %d\n", core99_bank); +#endif + for (i=0; iaddrs[0].address, dp->addrs[0].size); + nvram_mult = 1; + } else if (nvram_naddrs == 1) { + nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); + nvram_mult = (dp->addrs[0].size + NVRAM_SIZE - 1) / NVRAM_SIZE; + } else if (nvram_naddrs == 2) { + nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size); + nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size); + } else if (nvram_naddrs == 0 && sys_ctrler == SYS_CTRLER_PMU) { + nvram_naddrs = -1; + } else { + printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n", + nvram_naddrs); + } +} + +void +pmac_nvram_update(void) +{ + struct core99_header* hdr99; + + if (!is_core_99 || !nvram_data || !nvram_image) + return; + if (!memcmp(nvram_image, (u8*)nvram_data + core99_bank*NVRAM_SIZE, + NVRAM_SIZE)) + return; +#ifdef DEBUG + printk("Updating nvram...\n"); +#endif + hdr99 = (struct core99_header*)nvram_image; + hdr99->generation++; + hdr99->hdr.signature = CORE99_SIGNATURE; + hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr); + hdr99->adler = core99_calc_adler(nvram_image); + core99_bank = core99_bank ? 0 : 1; + if (core99_erase_bank(core99_bank)) { + printk("nvram: Error erasing bank %d\n", core99_bank); + return; + } + if (core99_write_bank(core99_bank, nvram_image)) + printk("nvram: Error writing bank %d\n", core99_bank); +} + +__openfirmware +unsigned char nvram_read_byte(int addr) +{ + #ifdef CONFIG_ADB_PMU // -aglitke + struct adb_request req; + #endif + + switch (nvram_naddrs) { +#ifdef CONFIG_ADB_PMU + case -1: + if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM, + (addr >> 8) & 0xff, addr & 0xff)) + break; + while (!req.complete) + pmu_poll(); + return req.reply[1]; +#endif + case 1: + if (is_core_99) + return nvram_image[addr]; + return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]; + case 2: + *nvram_addr = addr >> 5; + eieio(); + return nvram_data[(addr & 0x1f) << 4]; + } + return 0; +} + +__openfirmware +void nvram_write_byte(unsigned char val, int addr) +{ + #ifdef CONFIG_ADB_PMU // -aglitke + struct adb_request req; + #endif + switch (nvram_naddrs) { +#ifdef CONFIG_ADB_PMU + case -1: + if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM, + (addr >> 8) & 0xff, addr & 0xff, val)) + break; + while (!req.complete) + pmu_poll(); + break; +#endif + case 1: + if (is_core_99) { + nvram_image[addr] = val; + break; + } + nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val; + break; + case 2: + *nvram_addr = addr >> 5; + eieio(); + nvram_data[(addr & 0x1f) << 4] = val; + break; + } + eieio(); +} + +int +pmac_get_partition(int partition) +{ + return nvram_partitions[partition]; +} + +u8 +pmac_xpram_read(int xpaddr) +{ + int offset = nvram_partitions[pmac_nvram_XPRAM]; + + if (offset < 0) + return 0; + + return nvram_read_byte(xpaddr + offset); +} + +void +pmac_xpram_write(int xpaddr, u8 data) +{ + int offset = nvram_partitions[pmac_nvram_XPRAM]; + + if (offset < 0) + return; + + nvram_write_byte(xpaddr + offset, data); +} + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/pmc.c linux.ac/arch/ppc64/kernel/pmc.c --- linux.vanilla/arch/ppc64/kernel/pmc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/pmc.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,165 @@ +/* + * pmc.c + * Copyright (C) 2001 Dave Engebretsen & 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: + * 2001/06/05 : engebret : Created. + * End Change Activity + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern struct Naca *naca; + +struct _pmc_sw pmc_sw_system = { + 0 +}; + +struct _pmc_sw pmc_sw_cpu[NR_CPUS] = { + {0 }, +}; + +/* + * Provide enough storage for either system level counters or + * one cpu's counters. + */ +struct _pmc_sw_text pmc_sw_text; +struct _pmc_hw_text pmc_hw_text; + +char * +ppc64_pmc_stab(int file) { + int n; + unsigned long stab_faults, stab_capacity_castouts, stab_invalidations; + unsigned long i; + + stab_faults = stab_capacity_castouts = stab_invalidations = n = 0; + + if(file == -1) { + for(i = 0; i < smp_num_cpus; i++) { + stab_faults += pmc_sw_cpu[i].stab_faults; + stab_capacity_castouts += pmc_sw_cpu[i].stab_capacity_castouts; + stab_invalidations += pmc_sw_cpu[i].stab_invalidations; + } + n += sprintf(pmc_sw_text.buffer + n, + "Faults 0x%lx\n", stab_faults); + n += sprintf(pmc_sw_text.buffer + n, + "Castouts 0x%lx\n", stab_capacity_castouts); + n += sprintf(pmc_sw_text.buffer + n, + "Invalidations 0x%lx\n", stab_invalidations); + } else { + n += sprintf(pmc_sw_text.buffer + n, + "Faults 0x%lx\n", + pmc_sw_cpu[file].stab_faults); + + n += sprintf(pmc_sw_text.buffer + n, + "Castouts 0x%lx\n", + pmc_sw_cpu[file].stab_capacity_castouts); + + n += sprintf(pmc_sw_text.buffer + n, + "Invalidations 0x%lx\n", + pmc_sw_cpu[file].stab_invalidations); + + for(i = 0; i < STAB_ENTRY_MAX; i++) { + if(pmc_sw_cpu[file].stab_entry_use[i]) { + n += sprintf(pmc_sw_text.buffer + n, + "Entry %02ld 0x%lx\n", i, + pmc_sw_cpu[file].stab_entry_use[i]); + } + } + + } + + return(pmc_sw_text.buffer); +} + +char * +ppc64_pmc_htab(int file) { + int n; + unsigned long htab_primary_overflows, htab_capacity_castouts; + unsigned long htab_read_to_write_faults; + unsigned long i; + + htab_primary_overflows = htab_capacity_castouts = 0; + htab_read_to_write_faults = n = 0; + + if(file == -1) { + n += sprintf(pmc_sw_text.buffer + n, + "Primary Overflows 0x%lx\n", + pmc_sw_system.htab_primary_overflows); + n += sprintf(pmc_sw_text.buffer + n, + "Castouts 0x%lx\n", + pmc_sw_system.htab_capacity_castouts); + } else { + n += sprintf(pmc_sw_text.buffer + n, + "Primary Overflows N/A\n"); + + n += sprintf(pmc_sw_text.buffer + n, + "Castouts N/A\n\n"); + + } + + return(pmc_sw_text.buffer); +} + +char * +ppc64_pmc_hw(int file) { + int n; + + n = 0; + if(file == -1) { + n += sprintf(pmc_hw_text.buffer + n, "Not Implemented\n"); + } else { + n += sprintf(pmc_hw_text.buffer + n, + "MMCR0 0x%lx\n", mfspr(MMCR0)); + n += sprintf(pmc_hw_text.buffer + n, + "MMCR1 0x%lx\n", mfspr(MMCR1)); +#if 0 + n += sprintf(pmc_hw_text.buffer + n, + "MMCRA 0x%lx\n", mfspr(MMCRA)); +#endif + + n += sprintf(pmc_hw_text.buffer + n, + "PMC1 0x%lx\n", mfspr(PMC1)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC2 0x%lx\n", mfspr(PMC2)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC3 0x%lx\n", mfspr(PMC3)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC4 0x%lx\n", mfspr(PMC4)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC5 0x%lx\n", mfspr(PMC5)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC6 0x%lx\n", mfspr(PMC6)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC7 0x%lx\n", mfspr(PMC7)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC8 0x%lx\n", mfspr(PMC8)); + } + + return(pmc_hw_text.buffer); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/ppc-stub.c linux.ac/arch/ppc64/kernel/ppc-stub.c --- linux.vanilla/arch/ppc64/kernel/ppc-stub.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/ppc-stub.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,747 @@ +/* + * ppc-stub.c: KGDB support for the Linux kernel. + * + * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC + * some stuff borrowed from Paul Mackerras' xmon + * Copyright (C) 1998 Michael AK Tesch (tesch@cs.wisc.edu) + * + * Modifications to run under Linux + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * + * This file originally came from the gdb sources, and the + * copyright notices have been retained below. + */ + +/**************************************************************************** + + THIS SOFTWARE IS NOT COPYRIGHTED + + HP offers the following for use in the public domain. HP makes no + warranty with regard to the software or its performance and the + user accepts the software "AS IS" with all faults. + + HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD + TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +****************************************************************************/ + +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * This code has been extensively tested on the Fujitsu SPARClite demo board. + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a trap #1. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +void breakinst(void); + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; + +static int initialized = 0; +static int kgdb_active = 0; +static int kgdb_started = 0; +static u_int fault_jmp_buf[100]; +static int kdebug; + +static const char hexchars[]="0123456789abcdef"; + +/* Place where we save old trap entries for restoration - sparc*/ +/* struct tt_entry kgdb_savettable[256]; */ +/* typedef void (*trapfunc_t)(void); */ + +#if 0 +/* Install an exception handler for kgdb */ +static void exceptionHandler(int tnum, unsigned int *tfunc) +{ + /* We are dorking with a live trap table, all irqs off */ +} +#endif + +int +kgdb_setjmp(long *buf) +{ + asm ("mflr 0; stw 0,0(%0);" + "stw 1,4(%0); stw 2,8(%0);" + "mfcr 0; stw 0,12(%0);" + "stmw 13,16(%0)" + : : "r" (buf)); + /* XXX should save fp regs as well */ + return 0; +} +void +kgdb_longjmp(long *buf, int val) +{ + if (val == 0) + val = 1; + asm ("lmw 13,16(%0);" + "lwz 0,12(%0); mtcrf 0x38,0;" + "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);" + "mtlr 0; mr 3,%1" + : : "r" (buf), "r" (val)); +} +/* Convert ch from a hex digit to an int */ +static int +hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + */ +static unsigned char * +mem2hex(char *mem, char *buf, int count) +{ + unsigned char ch; + + if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { + debugger_fault_handler = kgdb_fault_handler; + while (count-- > 0) { + ch = *mem++; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + } else { + /* error condition */ + } + debugger_fault_handler = 0; + *buf = 0; + return buf; +} + +/* convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written. +*/ +static char * +hex2mem(char *buf, char *mem, int count) +{ + int i; + unsigned char ch; + + if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { + debugger_fault_handler = kgdb_fault_handler; + for (i=0; i# */ +static void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* wait around for the start character, ignore all other + * characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); +} + +/* send the packet in buffer. */ +static void putpacket(unsigned char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch, recv; + + /* $#. */ + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + recv = getDebugChar(); + } while ((recv & 0x7f) != '+'); +} + +static void kgdb_flush_cache_all(void) +{ + flush_instruction_cache(); +} + + +/* Set up exception handlers for tracing and breakpoints + * [could be called kgdb_init()] + */ +void set_debug_traps(void) +{ +#if 0 + unsigned char c; + + save_and_cli(flags); + + /* In case GDB is started before us, ack any packets (presumably + * "$?#xx") sitting there. + * + * I've found this code causes more problems than it solves, + * so that's why it's commented out. GDB seems to work fine + * now starting either before or after the kernel -bwb + */ + + while((c = getDebugChar()) != '$'); + while((c = getDebugChar()) != '#'); + c = getDebugChar(); /* eat first csum byte */ + c = getDebugChar(); /* eat second csum byte */ + putDebugChar('+'); /* ack it */ +#endif + debugger = kgdb; + debugger_bpt = kgdb_bpt; + debugger_sstep = kgdb_sstep; + debugger_iabr_match = kgdb_iabr_match; + debugger_dabr_match = kgdb_dabr_match; + + initialized = 1; +} + +static void kgdb_fault_handler(struct pt_regs *regs) +{ + kgdb_longjmp((long*)fault_jmp_buf, 1); +} + +int kgdb_bpt(struct pt_regs *regs) +{ + handle_exception(regs); + return 1; +} + +int kgdb_sstep(struct pt_regs *regs) +{ + handle_exception(regs); + return 1; +} + +void kgdb(struct pt_regs *regs) +{ + handle_exception(regs); +} + +int kgdb_iabr_match(struct pt_regs *regs) +{ + printk("kgdb doesn't support iabr, what?!?\n"); + handle_exception(regs); + return 1; +} + +int kgdb_dabr_match(struct pt_regs *regs) +{ + printk("kgdb doesn't support dabr, what?!?\n"); + handle_exception(regs); + return 1; +} + +/* Convert the SPARC hardware trap type code to a unix signal number. */ +/* + * This table contains the mapping between PowerPC hardware trap types, and + * signals, which are primarily what GDB understands. + */ +static struct hard_trap_info +{ + unsigned int tt; /* Trap type code for powerpc */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 0x200, SIGSEGV }, /* machine check */ + { 0x300, SIGSEGV }, /* address error (store) */ + { 0x400, SIGBUS }, /* instruction bus error */ + { 0x500, SIGINT }, /* interrupt */ + { 0x600, SIGBUS }, /* alingment */ + { 0x700, SIGTRAP }, /* breakpoint trap */ + { 0x800, SIGFPE }, /* fpu unavail */ + { 0x900, SIGALRM }, /* decrementer */ + { 0xa00, SIGILL }, /* reserved */ + { 0xb00, SIGILL }, /* reserved */ + { 0xc00, SIGCHLD }, /* syscall */ + { 0xd00, SIGTRAP }, /* single-step/watch */ + { 0xe00, SIGFPE }, /* fp assist */ + { 0, 0} /* Must be last */ +}; + +static int computeSignal(unsigned int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +#define PC_REGNUM 64 +#define SP_REGNUM 1 + +/* + * This function does all command processing for interfacing to gdb. + */ +static void +handle_exception (struct pt_regs *regs) +{ + int sigval; + int addr; + int length; + char *ptr; + unsigned long msr; + + if (debugger_fault_handler) { + debugger_fault_handler(regs); + panic("kgdb longjump failed!\n"); + } + if (kgdb_active) { + printk("interrupt while in kgdb, returning\n"); + return; + } + kgdb_active = 1; + kgdb_started = 1; + +#ifdef KGDB_DEBUG + printk("kgdb: entering handle_exception; trap [0x%x]\n", + (unsigned int)regs->trap); +#endif + + kgdb_interruptible(0); + lock_kernel(); + msr = get_msr(); + set_msr(msr & ~MSR_EE); /* disable interrupts */ + + if (regs->nip == (unsigned long)breakinst) { + /* Skip over breakpoint trap insn */ + regs->nip += 4; + } + + /* reply to host that an exception has occurred */ + sigval = computeSignal(regs->trap); + ptr = remcomOutBuffer; + +#if 0 + *ptr++ = 'S'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; +#else + *ptr++ = 'T'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + *ptr++ = hexchars[PC_REGNUM >> 4]; + *ptr++ = hexchars[PC_REGNUM & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->nip, ptr, 4); + *ptr++ = ';'; + *ptr++ = hexchars[SP_REGNUM >> 4]; + *ptr++ = hexchars[SP_REGNUM & 0xf]; + *ptr++ = ':'; + ptr = mem2hex(((char *)®s) + SP_REGNUM*4, ptr, 4); + *ptr++ = ';'; +#endif + + *ptr++ = 0; + + putpacket(remcomOutBuffer); + + /* XXX We may want to add some features dealing with poking the + * XXX page tables, ... (look at sparc-stub.c for more info) + * XXX also required hacking to the gdb sources directly... + */ + + while (1) { + remcomOutBuffer[0] = 0; + + getpacket(remcomInBuffer); + switch (remcomInBuffer[0]) { + case '?': /* report most recent signal */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[sigval >> 4]; + remcomOutBuffer[2] = hexchars[sigval & 0xf]; + remcomOutBuffer[3] = 0; + break; +#if 0 + case 'q': /* this screws up gdb for some reason...*/ + { + extern long _start, sdata, __bss_start; + + ptr = &remcomInBuffer[1]; + if (strncmp(ptr, "Offsets", 7) != 0) + break; + + ptr = remcomOutBuffer; + sprintf(ptr, "Text=%8.8x;Data=%8.8x;Bss=%8.8x", + &_start, &sdata, &__bss_start); + break; + } +#endif + case 'd': + /* toggle debug flag */ + kdebug ^= 1; + break; + + case 'g': /* return the value of the CPU registers. + * some of them are non-PowerPC names :( + * they are stored in gdb like: + * struct { + * u32 gpr[32]; + * f64 fpr[32]; + * u32 pc, ps, cnd, lr; (ps=msr) + * u32 cnt, xer, mq; + * } + */ + { + int i; + ptr = remcomOutBuffer; + /* General Purpose Regs */ + ptr = mem2hex((char *)regs, ptr, 32 * 4); + /* Floating Point Regs - FIXME */ + /*ptr = mem2hex((char *), ptr, 32 * 8);*/ + for(i=0; i<(32*8*2); i++) { /* 2chars/byte */ + ptr[i] = '0'; + } + ptr += 32*8*2; + /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ + ptr = mem2hex((char *)®s->nip, ptr, 4); + ptr = mem2hex((char *)®s->msr, ptr, 4); + ptr = mem2hex((char *)®s->ccr, ptr, 4); + ptr = mem2hex((char *)®s->link, ptr, 4); + ptr = mem2hex((char *)®s->ctr, ptr, 4); + ptr = mem2hex((char *)®s->xer, ptr, 4); + } + break; + + case 'G': /* set the value of the CPU registers */ + { + ptr = &remcomInBuffer[1]; + + /* + * If the stack pointer has moved, you should pray. + * (cause only god can help you). + */ + + /* General Purpose Regs */ + hex2mem(ptr, (char *)regs, 32 * 4); + + /* Floating Point Regs - FIXME?? */ + /*ptr = hex2mem(ptr, ??, 32 * 8);*/ + ptr += 32*8*2; + + /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ + ptr = hex2mem(ptr, (char *)®s->nip, 4); + ptr = hex2mem(ptr, (char *)®s->msr, 4); + ptr = hex2mem(ptr, (char *)®s->ccr, 4); + ptr = hex2mem(ptr, (char *)®s->link, 4); + ptr = hex2mem(ptr, (char *)®s->ctr, 4); + ptr = hex2mem(ptr, (char *)®s->xer, 4); + + strcpy(remcomOutBuffer,"OK"); + } + break; + case 'H': + /* don't do anything, yet, just acknowledge */ + hexToInt(&ptr, &addr); + strcpy(remcomOutBuffer,"OK"); + break; + + case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + /* Try to read %x,%x. */ + + ptr = &remcomInBuffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, remcomOutBuffer,length)) + break; + strcpy (remcomOutBuffer, "E03"); + } else { + strcpy(remcomOutBuffer,"E01"); + } + break; + + case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + /* Try to read '%x,%x:'. */ + + ptr = &remcomInBuffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length)) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "E03"); + } + flush_icache_range(addr, addr+length); + } else { + strcpy(remcomOutBuffer, "E02"); + } + break; + + + case 'k': /* kill the program, actually just continue */ + case 'c': /* cAA..AA Continue; address AA..AA optional */ + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr)) { + regs->nip = addr; + } + +/* Need to flush the instruction cache here, as we may have deposited a + * breakpoint, and the icache probably has no way of knowing that a data ref to + * some location may have changed something that is in the instruction cache. + */ + kgdb_flush_cache_all(); + set_msr(msr); + kgdb_interruptible(1); + unlock_kernel(); + kgdb_active = 0; + return; + + case 's': + kgdb_flush_cache_all(); + regs->msr |= MSR_SE; +#if 0 + set_msr(msr | MSR_SE); +#endif + unlock_kernel(); + kgdb_active = 0; + return; + + case 'r': /* Reset (if user process..exit ???)*/ + panic("kgdb reset."); + break; + } /* switch */ + if (remcomOutBuffer[0] && kdebug) { + printk("remcomInBuffer: %s\n", remcomInBuffer); + printk("remcomOutBuffer: %s\n", remcomOutBuffer); + } + /* reply to the request */ + putpacket(remcomOutBuffer); + } /* while(1) */ +} + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ + +void +breakpoint(void) +{ + if (!initialized) { + printk("breakpoint() called b4 kgdb init\n"); + return; + } + + asm(" .globl breakinst + breakinst: .long 0x7d821008 + "); +} + +/* Output string in GDB O-packet format if GDB has connected. If nothing + output, returns 0 (caller must then handle output). */ +int +kgdb_output_string (const char* s, unsigned int count) +{ + char buffer[512]; + + if (!kgdb_started) + return 0; + + count = (count <= (sizeof(buffer) / 2 - 2)) + ? count : (sizeof(buffer) / 2 - 2); + + buffer[0] = 'O'; + mem2hex (s, &buffer[1], count); + putpacket(buffer); + + return 1; + } + +/* I don't know why other platforms don't need this. The function for + * the 8xx is found in arch/ppc/8xx_io/uart.c. -- Dan + */ +void +kgdb_map_scc(void) +{ +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/ppc_asm.h linux.ac/arch/ppc64/kernel/ppc_asm.h --- linux.vanilla/arch/ppc64/kernel/ppc_asm.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/ppc_asm.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,145 @@ +/* + * arch/ppc/kernel/ppc_asm.h + * + * Definitions used by various bits of low-level assembly code on PowerPC. + * + * Copyright (C) 1995-1999 Gary Thomas, Paul Mackerras, Cort Dougan. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +#include "ppc_asm.tmpl" +#include "ppc_defs.h" + +/* + * Macros for storing registers into and loading registers from + * exception frames. + */ +#define SAVE_GPR(n, base) std n,GPR0+8*(n)(base) +#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base) +#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base) +#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base) +#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base) +#define REST_GPR(n, base) ld n,GPR0+8*(n)(base) +#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base) +#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base) +#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base) +#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base) + +#define SAVE_FPR(n, base) stfd n,THREAD_FPR0+8*(n)(base) +#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base) +#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base) +#define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base) +#define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base) +#define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base) +#define REST_FPR(n, base) lfd n,THREAD_FPR0+8*(n)(base) +#define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base) +#define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base) +#define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) +#define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) +#define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) + +#define SYNC \ + sync; \ + isync + +/* iSeries LPAR uses an event queue to signal I/O events + This code checks the queue */ + +#define CHECKLPQUEUE(ra,rb,rc) \ + mfspr ra,SPRG3; /* Get PACA address */\ + ld ra,PACALPQUEUE(ra); /* Get LpQueue address */\ + cmpi 0,ra,0; /* Does LpQueue exist? */\ + beq 99f; /* No? - then skip rest */\ + ld rb,LPQCUREVENTPTR(ra); /* Get current LpEvent */\ + lbz rb,LPEVENTFLAGS(rb); /* Get valid bit */\ + lbz rc,LPQOVERFLOW(ra); /* Get LpQueue overflow */\ + andi. ra,rb,0x0080; /* Isolate valid bit */\ + or. ra,ra,rc; /* 0 == no pending events */\ +99: + +#define CHECKDECR(ra,rb) \ + mfspr rb,SPRG3; /* Get Paca address */\ + lbz ra,PACALPPACA+LPPACADECRINT(rb); /* Get DECR int flag */\ + cmpi 0,ra,0; /* DECR occurred in hypervisor ? */\ + beq 99f; /* If not, skip rest */\ + xor ra,ra,ra; \ + 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 +#define HMT_MEDIUM or 2,2,2 +#define HMT_HIGH or 3,3,3 + +/* Insert the high 32 bits of the MSR into what will be the new + MSR (via SRR1 and rfid) This preserves the MSR.SF and MSR.ISF + bits. */ + +#define FIX_SRR1(ra, rb) \ + mr rb,ra; \ + mfmsr ra; \ + rldimi ra,rb,0,32 + +#define CLR_TOP32(r) rlwinm (r),(r),0,0,31 /* clear top 32 bits */ + +/* + * LOADADDR( rn, name ) + * loads the address of 'name' into 'rn' + * + * LOADBASE( rn, name ) + * loads the address (less the low 16 bits) of 'name' into 'rn' + * suitable for base+disp addressing + */ +#define LOADADDR(rn,name) \ + lis rn,name##@highest; \ + ori rn,rn,name##@higher; \ + rldicr rn,rn,32,31; \ + oris rn,rn,name##@h; \ + ori rn,rn,name##@l + +#define LOADBASE(rn,name) \ + lis rn,name@highest; \ + ori rn,rn,name@higher; \ + rldicr rn,rn,32,31; \ + oris rn,rn,name@ha + + +#define SET_REG_TO_CONST(reg, value) \ + lis reg,(((value)>>48)&0xFFFF); \ + ori reg,reg,(((value)>>32)&0xFFFF); \ + rldicr reg,reg,32,31; \ + oris reg,reg,(((value)>>16)&0xFFFF); \ + ori reg,reg,((value)&0xFFFF); + +#define SET_REG_TO_LABEL(reg, label) \ + lis reg,(label)@highest; \ + ori reg,reg,(label)@higher; \ + rldicr reg,reg,32,31; \ + oris reg,reg,(label)@h; \ + ori reg,reg,(label)@l; + + +/* PPPBBB - DRENG If KERNELBASE is always 0xC0..., + * Then we can easily do this with one asm insn. -Peter + */ +#define tophys(rd,rs) \ + lis rd,((KERNELBASE>>48)&0xFFFF); \ + rldicr rd,rd,32,31; \ + sub rd,rs,rd + +#define tovirt(rd,rs) \ + lis rd,((KERNELBASE>>48)&0xFFFF); \ + rldicr rd,rd,32,31; \ + add rd,rs,rd + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/ppc_asm.tmpl linux.ac/arch/ppc64/kernel/ppc_asm.tmpl --- linux.vanilla/arch/ppc64/kernel/ppc_asm.tmpl Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/ppc_asm.tmpl Wed Oct 10 01:47:34 2001 @@ -0,0 +1,115 @@ +/* Condition Register Bit Fields */ + +#define cr0 0 +#define cr1 1 +#define cr2 2 +#define cr3 3 +#define cr4 4 +#define cr5 5 +#define cr6 6 +#define cr7 7 + + +/* General Purpose Registers (GPRs) */ + +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + + +/* Floating Point Registers (FPRs) */ + +#define fr0 0 +#define fr1 1 +#define fr2 2 +#define fr3 3 +#define fr4 4 +#define fr5 5 +#define fr6 6 +#define fr7 7 +#define fr8 8 +#define fr9 9 +#define fr10 10 +#define fr11 11 +#define fr12 12 +#define fr13 13 +#define fr14 14 +#define fr15 15 +#define fr16 16 +#define fr17 17 +#define fr18 18 +#define fr19 19 +#define fr20 20 +#define fr21 21 +#define fr22 22 +#define fr23 23 +#define fr24 24 +#define fr25 25 +#define fr26 26 +#define fr27 27 +#define fr28 28 +#define fr29 29 +#define fr30 30 +#define fr31 31 + +#define vr0 0 +#define vr1 1 +#define vr2 2 +#define vr3 3 +#define vr4 4 +#define vr5 5 +#define vr6 6 +#define vr7 7 +#define vr8 8 +#define vr9 9 +#define vr10 10 +#define vr11 11 +#define vr12 12 +#define vr13 13 +#define vr14 14 +#define vr15 15 +#define vr16 16 +#define vr17 17 +#define vr18 18 +#define vr19 19 +#define vr20 20 +#define vr21 21 +#define vr22 22 +#define vr23 23 +#define vr24 24 +#define vr25 25 +#define vr26 26 +#define vr27 27 +#define vr28 28 +#define vr29 29 +#define vr30 30 +#define vr31 31 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/ppc_defs.head linux.ac/arch/ppc64/kernel/ppc_defs.head --- linux.vanilla/arch/ppc64/kernel/ppc_defs.head Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/ppc_defs.head Wed Oct 10 01:47:34 2001 @@ -0,0 +1,3 @@ +/* + * WARNING! This file is automatically generated - DO NOT EDIT! + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/ppc_ksyms.c linux.ac/arch/ppc64/kernel/ppc_ksyms.c --- linux.vanilla/arch/ppc64/kernel/ppc_ksyms.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/ppc_ksyms.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,249 @@ +/* + * c 2001 PPC 64 Team, 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SMP +#include +#endif /* CONFIG_SMP */ + +/* Tell string.h we don't want memcpy etc. as cpp defines */ +#define EXPORT_SYMTAB_STROPS + +extern void transfer_to_handler(void); +extern void syscall_trace(void); +extern void do_IRQ(struct pt_regs *regs, int isfake); +extern void SystemResetException(struct pt_regs *regs); +extern void MachineCheckException(struct pt_regs *regs); +extern void AlignmentException(struct pt_regs *regs); +extern void ProgramCheckException(struct pt_regs *regs); +extern void SingleStepException(struct pt_regs *regs); +extern int sys_sigreturn(struct pt_regs *regs); +extern int do_signal(sigset_t *, struct pt_regs *); + +long long __ashrdi3(long long, int); +long long __ashldi3(long long, int); +long long __lshrdi3(long long, int); +int abs(int); +extern unsigned long ret_to_user_hook; + +EXPORT_SYMBOL(clear_page); +EXPORT_SYMBOL(do_signal); +EXPORT_SYMBOL(syscall_trace); +EXPORT_SYMBOL(transfer_to_handler); +EXPORT_SYMBOL(do_IRQ); +EXPORT_SYMBOL(SystemResetException); +EXPORT_SYMBOL(MachineCheckException); +EXPORT_SYMBOL(AlignmentException); +EXPORT_SYMBOL(ProgramCheckException); +EXPORT_SYMBOL(SingleStepException); +EXPORT_SYMBOL(sys_sigreturn); +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); + +#if !__INLINE_BITOPS +EXPORT_SYMBOL(set_bit); +EXPORT_SYMBOL(clear_bit); +EXPORT_SYMBOL(change_bit); +EXPORT_SYMBOL(test_and_set_bit); +EXPORT_SYMBOL(test_and_clear_bit); +EXPORT_SYMBOL(test_and_change_bit); +#endif /* __INLINE_BITOPS */ + +EXPORT_SYMBOL(strcpy); +EXPORT_SYMBOL(strncpy); +EXPORT_SYMBOL(strcat); +EXPORT_SYMBOL(strncat); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strrchr); +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(strtok); +EXPORT_SYMBOL(strstr); +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strnlen); +EXPORT_SYMBOL(strcmp); +EXPORT_SYMBOL(strncmp); + +/* EXPORT_SYMBOL(csum_partial); already in net/netsyms.c */ +EXPORT_SYMBOL(csum_partial_copy_generic); +EXPORT_SYMBOL(ip_fast_csum); +EXPORT_SYMBOL(csum_tcpudp_magic); + +EXPORT_SYMBOL(__copy_tofrom_user); +EXPORT_SYMBOL(__clear_user); +EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(__strnlen_user); + +/* +EXPORT_SYMBOL(inb); +EXPORT_SYMBOL(inw); +EXPORT_SYMBOL(inl); +EXPORT_SYMBOL(outb); +EXPORT_SYMBOL(outw); +EXPORT_SYMBOL(outl); +EXPORT_SYMBOL(outsl);*/ + +EXPORT_SYMBOL(_insb); +EXPORT_SYMBOL(_outsb); +EXPORT_SYMBOL(_insw); +EXPORT_SYMBOL(_outsw); +EXPORT_SYMBOL(_insl); +EXPORT_SYMBOL(_outsl); +EXPORT_SYMBOL(_insw_ns); +EXPORT_SYMBOL(_outsw_ns); +EXPORT_SYMBOL(_insl_ns); +EXPORT_SYMBOL(_outsl_ns); +EXPORT_SYMBOL(ioremap); +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); + +EXPORT_SYMBOL(ide_insw); +EXPORT_SYMBOL(ide_outsw); +EXPORT_SYMBOL(ppc_ide_md); +#ifdef CONFIG_BLK_DEV_IDE_MODULE +EXPORT_SYMBOL(chrp_ide_irq); +EXPORT_SYMBOL(chrp_ide_ports_known); +EXPORT_SYMBOL(chrp_ide_regbase); +EXPORT_SYMBOL(chrp_ide_probe); +#endif + +#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); +EXPORT_SYMBOL(kernel_thread); + +EXPORT_SYMBOL(flush_instruction_cache); +EXPORT_SYMBOL(_get_PVR); +EXPORT_SYMBOL(giveup_fpu); +EXPORT_SYMBOL(enable_kernel_fp); +EXPORT_SYMBOL(flush_icache_range); +#ifdef CONFIG_SMP +EXPORT_SYMBOL(__global_cli); +EXPORT_SYMBOL(__global_sti); +EXPORT_SYMBOL(__global_save_flags); +EXPORT_SYMBOL(__global_restore_flags); +#endif + +#ifndef CONFIG_MACH_SPECIFIC +EXPORT_SYMBOL(_machine); +#endif +EXPORT_SYMBOL(ppc_md); + +EXPORT_SYMBOL(find_devices); +EXPORT_SYMBOL(find_type_devices); +EXPORT_SYMBOL(find_compatible_devices); +EXPORT_SYMBOL(find_path_device); +EXPORT_SYMBOL(find_phandle); +EXPORT_SYMBOL(device_is_compatible); +EXPORT_SYMBOL(machine_is_compatible); +EXPORT_SYMBOL(find_pci_device_OFnode); +EXPORT_SYMBOL(find_all_nodes); +EXPORT_SYMBOL(get_property); + +EXPORT_SYMBOL(kd_mksound); +EXPORT_SYMBOL_NOVERS(sys_ctrler); /* tibit */ +#ifdef CONFIG_NVRAM +EXPORT_SYMBOL(nvram_read_byte); +EXPORT_SYMBOL(nvram_write_byte); +#endif /* CONFIG_NVRAM */ + +EXPORT_SYMBOL_NOVERS(__ashrdi3); +EXPORT_SYMBOL_NOVERS(__ashldi3); +EXPORT_SYMBOL_NOVERS(__lshrdi3); +EXPORT_SYMBOL_NOVERS(memcpy); +EXPORT_SYMBOL_NOVERS(memset); +EXPORT_SYMBOL_NOVERS(memmove); +EXPORT_SYMBOL_NOVERS(memscan); +EXPORT_SYMBOL_NOVERS(memcmp); + +EXPORT_SYMBOL(abs); + +#ifdef CONFIG_VT +EXPORT_SYMBOL(screen_info); +#endif + +EXPORT_SYMBOL(timer_interrupt); +EXPORT_SYMBOL(irq_desc); +void ppc_irq_dispatch_handler(struct pt_regs *, int); +EXPORT_SYMBOL(ppc_irq_dispatch_handler); +EXPORT_SYMBOL(get_wchan); +EXPORT_SYMBOL(console_drivers); +#ifdef CONFIG_XMON +EXPORT_SYMBOL(xmon); +#endif + +#if defined(CONFIG_KGDB) || defined(CONFIG_XMON) +extern void (*debugger)(struct pt_regs *regs); +extern int (*debugger_bpt)(struct pt_regs *regs); +extern int (*debugger_sstep)(struct pt_regs *regs); +extern int (*debugger_iabr_match)(struct pt_regs *regs); +extern int (*debugger_dabr_match)(struct pt_regs *regs); +extern void (*debugger_fault_handler)(struct pt_regs *regs); + +EXPORT_SYMBOL(debugger); +EXPORT_SYMBOL(debugger_bpt); +EXPORT_SYMBOL(debugger_sstep); +EXPORT_SYMBOL(debugger_iabr_match); +EXPORT_SYMBOL(debugger_dabr_match); +EXPORT_SYMBOL(debugger_fault_handler); +#endif + +EXPORT_SYMBOL(ret_to_user_hook); + +EXPORT_SYMBOL(tb_ticks_per_usec); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/proc_pcifr.c linux.ac/arch/ppc64/kernel/proc_pcifr.c --- linux.vanilla/arch/ppc64/kernel/proc_pcifr.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/proc_pcifr.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,108 @@ +/************************************************************************/ +/* File pcifr_proc.c created by Allan Trautman on Thu Aug 2 2001. */ +/************************************************************************/ +/* Supports the ../proc/ppc64/pcifr for the pci flight recorder. */ +/* 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 */ +/************************************************************************/ +#include +#include +#include + +#include +#include +#include + +//#include +//#include + +#include +#include "pci.h" + +void pci_Fr_TestCode(void); + +static spinlock_t proc_pcifr_lock; +struct flightRecorder* PciFr = NULL; + +/************************************************************************/ +/* Forward declares. */ +/************************************************************************/ +static struct proc_dir_entry *pciFr_proc_root = NULL; +int proc_pciFr_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pciFr_write_proc(struct file *file, const char *buffer, unsigned long count, void *data); + +/************************************************************************/ +/* Create entry ../proc/ppc64/pcifr */ +/************************************************************************/ +void proc_pciFr_init(struct proc_dir_entry *proc_ppc64_root) { + if (proc_ppc64_root == NULL) return; + + /* Read = User,Group,Other, Write User */ + printk("PCI: Creating ../proc/ppc64/pcifr \n"); + spin_lock(&proc_pcifr_lock); + pciFr_proc_root = create_proc_entry("pcifr", S_IFREG | S_IRUGO | S_IWUSR, proc_ppc64_root); + spin_unlock(&proc_pcifr_lock); + + if (pciFr_proc_root == NULL) return; + + pciFr_proc_root->nlink = 1; + pciFr_proc_root->data = (void *)0; + pciFr_proc_root->read_proc = proc_pciFr_read_proc; + pciFr_proc_root->write_proc = proc_pciFr_write_proc; + + PciFr = alloc_Flight_Recorder(NULL,"PciFr", 4096); + + pci_Fr_TestCode(); + +} +/*******************************************************************************/ +/* Get called when client reads the ../proc/ppc64/pcifr. */ +/*******************************************************************************/ +int proc_pciFr_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { + int BufferLen; + printk("PCI: Dump Flight Recorder!\n"); + BufferLen = fr_Dump(PciFr, page, count); + *eof = BufferLen; + return BufferLen; +} +/*******************************************************************************/ +/* Gets called when client writes to ../proc/ppc64/pcifr */ +/*******************************************************************************/ +int proc_pciFr_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { + pci_Fr_TestCode(); + return count; +} + +/******************************************************************************* + * Test Code + *******************************************************************************/ +void pci_Fr_TestCode(void) { + int StackVariable = 10; + + fr_Log_Entry(PciFr,"1. First Log Entry"); + fr_Log_Entry(PciFr,"2. Stack Variable(10) %d",StackVariable); + fr_Log_Entry(PciFr,"%d. Pointer 0x%16LX",3,PciFr); + + LOGFR(PciFr,"4. Test Log Hi there."); + LOGFR(PciFr,"5. Stack Variable(10) %d",StackVariable); + LOGFR(PciFr,"%d. Pointer 0x%16LX",6,PciFr); + + PCIFR("8. Hi there."); + PCIFR("9. Stack Variable(10) %d",StackVariable); + PCIFR("%d. Variable and pointer 0x%16LX",10,PciFr); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/proc_pmc.c linux.ac/arch/ppc64/kernel/proc_pmc.c --- linux.vanilla/arch/ppc64/kernel/proc_pmc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/proc_pmc.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,722 @@ +/* + * proc_pmc.c + * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen 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: + * 2001 : mikec : Created + * 2001/06/05 : engebret : Software event count support. + * 2001/08/03 : trautman : Added PCI Flight Recorder + * End Change Activity + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* pci Flight Recorder AHT */ +extern proc_pciFr_init(struct proc_dir_entry *proc_ppc64_root); + +static int proc_pmc_control_mode = 0; + +static struct proc_dir_entry *proc_ppc64_root = NULL; +static struct proc_dir_entry *proc_ppc64_pmc_root = NULL; +static struct proc_dir_entry *proc_ppc64_pmc_system_root = NULL; +static struct proc_dir_entry *proc_ppc64_pmc_cpu_root[NR_CPUS] = {NULL, }; + +static spinlock_t proc_ppc64_lock; + +extern struct Naca *naca; + +int proc_ppc64_pmc_find_file(void *data); +int proc_ppc64_pmc_read(char *page, char **start, off_t off, + int count, int *eof, char *buffer); +int proc_ppc64_pmc_stab_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +int proc_ppc64_pmc_htab_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +int proc_ppc64_pmc_hw_read(char *page, char **start, off_t off, + int count, int *eof, void *data); + +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_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 proc_ppc64_init(void) +{ + unsigned long i; + struct proc_dir_entry *ent = NULL; + char buf[256]; + + printk("proc_ppc64: Creating /proc/ppc64/pmc\n"); + + /* + * Create the root, system, and cpu directories as follows: + * /proc/ppc64/pmc/system + * /proc/ppc64/pmc/cpu0 + */ + spin_lock(&proc_ppc64_lock); + proc_ppc64_root = proc_mkdir("ppc64", 0); + if (!proc_ppc64_root) return; + spin_unlock(&proc_ppc64_lock); + + /* Create the /proc/ppc64/pcifr for the Pci Flight Recorder. */ + proc_pciFr_init(proc_ppc64_root); + + + proc_ppc64_pmc_root = proc_mkdir("pmc", proc_ppc64_root); + + proc_ppc64_pmc_system_root = proc_mkdir("system", proc_ppc64_pmc_root); + for(i = 0; i < naca->processorCount; i++) { + sprintf(buf, "cpu%ld", i); + proc_ppc64_pmc_cpu_root[i] = proc_mkdir(buf, proc_ppc64_pmc_root); + } + + + /* Create directories for the software counters. */ + for(i = 0; i < naca->processorCount; i++) { + ent = create_proc_entry("stab", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_cpu_root[i]); + if(ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; + ent->read_proc = proc_ppc64_pmc_stab_read; + ent->write_proc = proc_ppc64_pmc_stab_read; + } + + ent = create_proc_entry("htab", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_cpu_root[i]); + if(ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; + ent->read_proc = proc_ppc64_pmc_htab_read; + ent->write_proc = proc_ppc64_pmc_htab_read; + } + } + + ent = create_proc_entry("stab", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_system_root); + if(ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_system_root; + ent->read_proc = proc_ppc64_pmc_stab_read; + ent->write_proc = proc_ppc64_pmc_stab_read; + } + + ent = create_proc_entry("htab", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_system_root); + if(ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_system_root; + ent->read_proc = proc_ppc64_pmc_htab_read; + ent->write_proc = proc_ppc64_pmc_htab_read; + } + + /* Create directories for the hardware counters. */ + for(i = 0; i < naca->processorCount; i++) { + ent = create_proc_entry("hardware", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_cpu_root[i]); + if(ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; + ent->read_proc = proc_ppc64_pmc_hw_read; + ent->write_proc = proc_ppc64_pmc_hw_read; + } + } + + ent = create_proc_entry("hardware", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_system_root); + if(ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_system_root; + ent->read_proc = proc_ppc64_pmc_hw_read; + ent->write_proc = proc_ppc64_pmc_hw_read; + } +} + +/* + * Find the requested 'file' given a proc token. + * + * Inputs: void * data: proc token + * Output: int : (0, ..., +N) = CPU number. + * -1 = System. + */ +int proc_ppc64_pmc_find_file(void *data) { + int i; + + if((unsigned long)data == + (unsigned long) proc_ppc64_pmc_system_root) { + return(-1); + } else { + for(i = 0; i < naca->processorCount; i++) { + if((unsigned long)data == + (unsigned long)proc_ppc64_pmc_cpu_root[i]) { + return(i); + } + } + } + + /* On error, just default to a type of system. */ + printk("proc_ppc64_pmc_find_file: failed to find file token.\n"); + return(-1); +} + +int +proc_ppc64_pmc_read(char *page, char **start, off_t off, + int count, int *eof, char *buffer) { + int buffer_size, n, i; + unsigned long flags; + + if (count < 0) return 0; + + if(buffer == NULL) { + *eof = 1; + return 0; + } + + /* Check for read beyond EOF */ + buffer_size = n = strlen(buffer); + if(off >= buffer_size) { + *eof = 1; + return 0; + } + if (n > (buffer_size - off)) n = buffer_size - off; + + /* Never return more than was requested */ + if (n > count) { + n = count; + } else { + *eof = 1; + } + + memcpy(page, buffer + off, n); + + *start = page; + + return n; +} + +int +proc_ppc64_pmc_stab_read(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int n, file; + char *buffer = NULL; + + if (count < 0) return 0; + spin_lock(&proc_ppc64_lock); + + /* Figure out which file is being request. */ + file = proc_ppc64_pmc_find_file(data); + + /* Update the counters and the text buffer representation. */ + buffer = ppc64_pmc_stab(file); + + /* Put the data into the requestor's buffer. */ + n = proc_ppc64_pmc_read(page, start, off, count, eof, buffer); + + spin_unlock(&proc_ppc64_lock); + return n; +} + +int +proc_ppc64_pmc_htab_read(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int n, file; + char *buffer = NULL; + + if (count < 0) return 0; + spin_lock(&proc_ppc64_lock); + + /* Figure out which file is being request. */ + file = proc_ppc64_pmc_find_file(data); + + /* Update the counters and the text buffer representation. */ + buffer = ppc64_pmc_htab(file); + + /* Put the data into the requestor's buffer. */ + n = proc_ppc64_pmc_read(page, start, off, count, eof, buffer); + + spin_unlock(&proc_ppc64_lock); + return n; +} + +int +proc_ppc64_pmc_hw_read(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int n, file; + char *buffer = NULL; + + if (count < 0) return 0; + spin_lock(&proc_ppc64_lock); + + /* Figure out which file is being request. */ + file = proc_ppc64_pmc_find_file(data); + + /* Update the counters and the text buffer representation. */ + buffer = ppc64_pmc_hw(file); + + /* Put the data into the requestor's buffer. */ + n = proc_ppc64_pmc_read(page, start, off, count, eof, buffer); + + spin_unlock(&proc_ppc64_lock); + return n; +} + +/* + * DRENG the remainder of these functions still need work ... + */ +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; + +} + +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 ); +} + +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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/process.c linux.ac/arch/ppc64/kernel/process.c --- linux.vanilla/arch/ppc64/kernel/process.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/process.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,742 @@ +/* + * + * + * linux/arch/ppc/kernel/process.c + * + * Derived from "arch/i386/kernel/process.c" + * Copyright (C) 1995 Linus Torvalds + * + * Updated and modified by Cort Dougan (cort@cs.nmt.edu) and + * Paul Mackerras (paulus@cs.anu.edu.au) + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs); +extern unsigned long _get_SP(void); + +struct task_struct *last_task_used_math = NULL; +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); + + +/* **** THE FOLLOWING IS AN AWFUL HACK **** + * We define swapper_pg_dir to be ioremap_dir + * and init_mmap to be ioremap_mmap + * so that the ioremap_mm will get initialized + * correctly by the INIT_MM macro (from linux/sched.h) + + * ioremap_mm is really a dummy mm which is only used + * to contain the ioremap_dir pointer and the + * page_table_lock for the ioremap page table + * This is similar to the way init_mm is used + */ + +#define swapper_pg_dir ioremap_dir +#define init_mmap ioremap_mmap +extern struct vm_area_struct ioremap_mmap; +struct mm_struct ioremap_mm = INIT_MM(ioremap_mm); +struct vm_area_struct ioremap_mmap = IOREMAP_MMAP; +#undef swapper_pg_dir +#undef init_mmap +// **** END OF HACK **** + +/* this is 16-byte aligned because it has a stack in it */ +union task_union __attribute((aligned(16))) init_task_union = { + INIT_TASK(init_task_union.task) +}; + +#ifdef CONFIG_SMP +struct current_set_struct current_set[NR_CPUS] = {{&init_task, 0}, }; +#endif + +char *sysmap = NULL; +unsigned long sysmap_size = 0; + +extern char __toc_start; + +#undef SHOW_TASK_SWITCHES +#undef CHECK_STACK + +#if defined(CHECK_STACK) +unsigned long +kernel_stack_top(struct task_struct *tsk) +{ + return ((unsigned long)tsk) + sizeof(union task_union); +} + +unsigned long +task_top(struct task_struct *tsk) +{ + return ((unsigned long)tsk) + sizeof(struct task_struct); +} + +/* check to make sure the kernel stack is healthy */ +int check_stack(struct task_struct *tsk) +{ + unsigned long stack_top = kernel_stack_top(tsk); + unsigned long tsk_top = task_top(tsk); + int ret = 0; + +#if 0 + /* check thread magic */ + if ( tsk->thread.magic != THREAD_MAGIC ) + { + ret |= 1; + printk("thread.magic bad: %08x\n", tsk->thread.magic); + } +#endif + + if ( !tsk ) + printk("check_stack(): tsk bad tsk %p\n",tsk); + + /* check if stored ksp is bad */ + if ( (tsk->thread.ksp > stack_top) || (tsk->thread.ksp < tsk_top) ) + { + printk("stack out of bounds: %s/%d\n" + " tsk_top %08lx ksp %08lx stack_top %08lx\n", + tsk->comm,tsk->pid, + tsk_top, tsk->thread.ksp, stack_top); + ret |= 2; + } + + /* check if stack ptr RIGHT NOW is bad */ + if ( (tsk == current) && ((_get_SP() > stack_top ) || (_get_SP() < tsk_top)) ) + { + printk("current stack ptr out of bounds: %s/%d\n" + " tsk_top %08lx sp %08lx stack_top %08lx\n", + current->comm,current->pid, + tsk_top, _get_SP(), stack_top); + ret |= 4; + } + +#if 0 + /* check amount of free stack */ + for ( i = (unsigned long *)task_top(tsk) ; i < kernel_stack_top(tsk) ; i++ ) + { + if ( !i ) + printk("check_stack(): i = %p\n", i); + if ( *i != 0 ) + { + /* only notify if it's less than 900 bytes */ + if ( (i - (unsigned long *)task_top(tsk)) < 900 ) + printk("%d bytes free on stack\n", + i - task_top(tsk)); + break; + } + } +#endif + + if (ret) + { + panic("bad kernel stack"); + } + return(ret); +} +#endif /* defined(CHECK_STACK) */ + +void +enable_kernel_fp(void) +{ +#ifdef CONFIG_SMP + if (current->thread.regs && (current->thread.regs->msr & MSR_FP)) + giveup_fpu(current); + else + giveup_fpu(NULL); /* just enables FP for kernel */ +#else + giveup_fpu(last_task_used_math); +#endif /* CONFIG_SMP */ +} + +int +dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) +{ + if (regs->msr & MSR_FP) + giveup_fpu(current); + memcpy(fpregs, ¤t->thread.fpr[0], sizeof(*fpregs)); + return 1; +} + +void +_switch_to(struct task_struct *prev, struct task_struct *new, + struct task_struct **last) +{ + struct thread_struct *new_thread, *old_thread; + unsigned long s; + + __save_flags(s); + __cli(); +#if CHECK_STACK + check_stack(prev); + check_stack(new); +#endif + +#ifdef SHOW_TASK_SWITCHES + printk("%s/%d -> %s/%d NIP %08lx cpu %d root %x/%x\n", + prev->comm,prev->pid, + new->comm,new->pid,new->thread.regs->nip,new->processor, + new->fs->root,prev->fs->root); +#endif +#ifdef CONFIG_SMP + /* avoid complexity of lazy save/restore of fpu + * by just saving it every time we switch out if + * this task used the fpu during the last quantum. + * + * If it tries to use the fpu again, it'll trap and + * reload its fp regs. So we don't have to do a restore + * every switch, just a save. + * -- Cort + */ + if ( prev->thread.regs && (prev->thread.regs->msr & MSR_FP) ) + giveup_fpu(prev); + + /* prev->last_processor = prev->processor; */ + current_set[smp_processor_id()].task = new; +#endif /* CONFIG_SMP */ + new_thread = &new->thread; + old_thread = ¤t->thread; + *last = _switch(old_thread, new_thread); + __restore_flags(s); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printk("NIP: %016lX XER: %016lX LR: %016lX REGS: %p TRAP: %04lx %s\n", + regs->nip, regs->xer, regs->link, regs,regs->trap, print_tainted()); + printk("MSR: %016lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + printk("TASK = %p[%d] '%s' ", + current, current->pid, current->comm); + printk("Last syscall: %ld ", current->thread.last_syscall); + printk("\nlast math %p ", last_task_used_math); + +#ifdef CONFIG_SMP + /* printk(" CPU: %d last CPU: %d", current->processor,current->last_processor); */ +#endif /* CONFIG_SMP */ + + printk("\n"); + for (i = 0; i < 32; i++) + { + long r; + if ((i % 4) == 0) + { + printk("GPR%02d: ", i); + } + + if ( __get_user(r, &(regs->gpr[i])) ) + goto out; + printk("%016lX ", r); + if ((i % 4) == 3) + { + printk("\n"); + } + } +out: +} + +void exit_thread(void) +{ + if (last_task_used_math == current) + last_task_used_math = NULL; +} + +void flush_thread(void) +{ + if (last_task_used_math == current) + last_task_used_math = NULL; +} + +void +release_thread(struct task_struct *t) +{ +} + +/* + * Copy a thread.. + */ +int +copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + unsigned long unused, + struct task_struct * p, struct pt_regs * regs) +{ + unsigned long msr; + struct pt_regs * childregs, *kregs; +#ifdef CONFIG_SMP + extern void ret_from_smpfork(void); +#else + extern void ret_from_except(void); +#endif + /* Copy registers */ + childregs = ((struct pt_regs *) + ((unsigned long)p + sizeof(union task_union) + - STACK_FRAME_OVERHEAD)) - 2; + *childregs = *regs; + childregs->gpr[3] = 0; /* Result from fork() */ + p->thread.regs = childregs; + p->thread.ksp = (unsigned long) childregs - STACK_FRAME_OVERHEAD; + p->thread.ksp -= sizeof(struct pt_regs ) + STACK_FRAME_OVERHEAD; + kregs = (struct pt_regs *)(p->thread.ksp + STACK_FRAME_OVERHEAD); +#ifdef CONFIG_SMP + kregs->nip = *((unsigned long *)ret_from_smpfork); +#else + /* The PPC64 compiler makes use of a TOC to contain function + * pointers. The function (ret_from_except) is actually a pointer + * to the TOC entry. The first entry is a pointer to the actual + * function. + */ + kregs->nip = *((unsigned long *)ret_from_except); +#endif + asm volatile("mfmsr %0" : "=r" (msr):); + kregs->msr = msr; + kregs->gpr[1] = (unsigned long)childregs - STACK_FRAME_OVERHEAD; + kregs->gpr[2] = (((unsigned long)&__toc_start) + 0x8000); + + if (usp >= (unsigned long) regs) { + /* Stack is in kernel space - must adjust */ + childregs->gpr[1] = (unsigned long)(childregs + 1); + *((unsigned long *) childregs->gpr[1]) = 0; + } else { + /* Provided stack is in user space */ + childregs->gpr[1] = usp; + } + p->thread.last_syscall = -1; + + /* + * copy fpu info - assume lazy fpu switch now always + * -- Cort + */ + if (regs->msr & MSR_FP) + giveup_fpu(current); + memcpy(&p->thread.fpr, ¤t->thread.fpr, sizeof(p->thread.fpr)); + p->thread.fpscr = current->thread.fpscr; + childregs->msr &= ~MSR_FP; + +#ifdef CONFIG_SMP + // p->last_processor = NO_PROC_ID; +#endif /* CONFIG_SMP */ + return 0; +} + +/* + * XXX ld.so expects the auxiliary table to start on + * a 16-byte boundary, so we have to find it and + * move it up. :-( + */ +static inline void shove_aux_table(unsigned long sp) +{ + int argc; + char *p; + unsigned long e; + unsigned long aux_start, offset; + + sp += sizeof(int); + if (__get_user(argc, (int *)sp)) + return; + sp += sizeof(int) + (argc + 1) * sizeof(char *); + /* skip over the environment pointers */ + do { + if (__get_user(p, (char **)sp)) + return; + sp += sizeof(char *); + } while (p != NULL); + aux_start = sp; + /* skip to the end of the auxiliary table */ + do { + if (__get_user(e, (unsigned long *)sp)) + return; + sp += 2 * sizeof(unsigned long); + } while (e != AT_NULL); + offset = ((aux_start + 15) & ~15) - aux_start; + if (offset != 0) { + do { + sp -= sizeof(unsigned long); + if (__get_user(e, (unsigned long *)sp) + || __put_user(e, (unsigned long *)(sp + offset))) + return; + } while (sp > aux_start); + } +} + +/* + * Set up a thread for executing a new program + */ +void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) +{ + /* NIP is *really* a pointer to the function descriptor for + * the elf _start routine. The first entry in the function + * descriptor is the entry address of _start and the second + * entry is the TOC value we need to use. + */ + unsigned long *entry = (unsigned long *)nip; + unsigned long *toc = entry + 1; + + PPCDBG(PPCDBG_SYS64, "start_thread - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + PPCDBG(PPCDBG_SYS64, " pt_regs=0x%lx, nip=0x%lx, sp=0x%lx\n", regs, nip, sp); + + set_fs(USER_DS); + __get_user(regs->nip, entry); + regs->gpr[1] = sp; + __get_user(regs->gpr[2], toc); + regs->msr = MSR_USER64; + shove_aux_table(sp); + if (last_task_used_math == current) + last_task_used_math = 0; + current->thread.fpscr = 0; +} + +asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + unsigned long clone_flags = p1; + int res; + + PPCDBG(PPCDBG_SYS64, "sys_clone - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + res = do_fork(clone_flags, regs->gpr[1], regs, 0); +#ifdef CONFIG_SMP + /* When we clone the idle task we keep the same pid but + * the return value of 0 for both causes problems. + * -- Cort + */ + if ((current->pid == 0) && (current == &init_task)) + res = 1; +#endif /* CONFIG_SMP */ + + PPCDBG(PPCDBG_SYS64, "sys_clone - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return res; +} + +asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + int res; + + PPCDBG(PPCDBG_SYS64, "sys_fork - entered - pid=%ld comm=%s \n", current->pid, current->comm); + + res = do_fork(SIGCHLD, regs->gpr[1], regs, 0); + + #ifdef CONFIG_SMP + /* When we clone the idle task we keep the same pid but + * the return value of 0 for both causes problems. + * -- Cort + */ + if ((current->pid == 0) && (current == &init_task)) + res = 1; + #endif /* CONFIG_SMP */ + + PPCDBG(PPCDBG_SYS64, "sys_fork - exited - pid=%ld comm=%s \n", current->pid, current->comm); + + return res; +} + +asmlinkage int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + PPCDBG(PPCDBG_SYS64, "sys_vfork - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs, 0); +} + +asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs *regs) +{ + int error; + char * filename; + + PPCDBG(PPCDBG_SYS64, "sys_execve - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + filename = getname((char *) a0); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + if (regs->msr & MSR_FP) + giveup_fpu(current); + + PPCDBG(PPCDBG_SYS64, "sys_execve - before do_execve : filename = %s\n", filename); + + error = do_execve(filename, (char **) a1, (char **) a2, regs); + + if (error == 0) + current->ptrace &= ~PT_DTRACE; + putname(filename); + + out: + PPCDBG(PPCDBG_SYS64, "sys_execve - exited - pid=%ld current=%lx comm=%s error = %lx\n", current->pid, current, current->comm, error); + + return error; +} + + +unsigned long find_hpte(unsigned long); + +static __inline__ void set_pp_bit(unsigned long pp, HPTE *addr) +{ + unsigned long old; + unsigned long *p = (unsigned long *)(&(addr->dw1)); + + __asm__ __volatile__( +"1: ldarx %0,0,%3\n\ + rldimi %0,%2,0,62\n\ + stdcx. %0,0,%3\n\ + bne 1b" + : "=&r" (old), "=m" (*p) + : "r" (pp), "r" (p), "m" (*p) + : "cc"); +} + + +static void updateStackHptePP( unsigned long newpp, unsigned long ea ) +{ + unsigned long vsid,va,vpn; + long slot; + + + vsid = get_kernel_vsid( ea ); + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + vpn = va >> PAGE_SHIFT; + + slot = find_hpte( vpn ); + + + if ( _machine == _MACH_iSeries ) { + HvCallHpt_setPp( slot, newpp ); + } + else { + + HPTE * hptep = htab_data.htab + slot; + + set_pp_bit(newpp , hptep ); + + /* Ensure it is out of the tlb too */ + _tlbie( va ); + + + /* Ensure it is visible before validating */ + __asm__ __volatile__ ("eieio" : : : "memory"); + + + __asm__ __volatile__ ("ptesync" : : : "memory"); + + + } +} + + +struct task_struct * alloc_task_struct(void) { + struct task_struct * new_task_ptr; + + new_task_ptr = ((struct task_struct *) __get_free_pages(GFP_KERNEL, get_order(THREAD_SIZE))); + + /* Set the second page to be write protected - prevent kernel stack overflow */ + updateStackHptePP( PP_RXRX , (unsigned long)new_task_ptr + PAGE_SIZE ); + + return new_task_ptr; +} + +void free_task_struct(struct task_struct * task_ptr) { + + if (atomic_read(&(virt_to_page(task_ptr)->count)) == 1) { + updateStackHptePP( PP_RWXX , (unsigned long)task_ptr + PAGE_SIZE ); + } + free_pages((unsigned long)(task_ptr), get_order(THREAD_SIZE)); +} + + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printk("Call backtrace: "); + while (sp) { + if (__get_user( i, &sp[2] )) + break; + if (cnt++ % 4 == 0) + printk("\n"); + printk("%016lX ", i); + if (cnt > 32) break; + if (__get_user(sp, (unsigned long **)sp)) + break; + } + printk("\n"); +} + +#if 0 +/* + * Low level print for debugging - Cort + */ +int __init ll_printk(const char *fmt, ...) +{ + va_list args; + char buf[256]; + int i; + + va_start(args, fmt); + i=vsprintf(buf,fmt,args); + ll_puts(buf); + va_end(args); + return i; +} + +int lines = 24, cols = 80; +int orig_x = 0, orig_y = 0; + +void puthex(unsigned long val) +{ + unsigned char buf[10]; + int i; + for (i = 7; i >= 0; i--) + { + buf[i] = "0123456789ABCDEF"[val & 0x0F]; + val >>= 4; + } + buf[8] = '\0'; + prom_print(buf); +} + +void __init ll_puts(const char *s) +{ + int x,y; + char *vidmem = (char *)/*(_ISA_MEM_BASE + 0xB8000) */0xD00B8000; + char c; + extern int mem_init_done; + + if ( mem_init_done ) /* assume this means we can printk */ + { + printk(s); + return; + } + + /* + * can't ll_puts on chrp without openfirmware yet. + * vidmem just needs to be setup for it. + * -- Cort + */ + if ( _machine != _MACH_prep ) + return; + x = orig_x; + y = orig_y; + + while ( ( c = *s++ ) != '\0' ) { + if ( c == '\n' ) { + x = 0; + if ( ++y >= lines ) { + /*scroll();*/ + /*y--;*/ + y = 0; + } + } else { + vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) { + x = 0; + if ( ++y >= lines ) { + /*scroll();*/ + /*y--;*/ + y = 0; + } + } + } + } + + orig_x = x; + orig_y = y; +} +#endif + +/* + * These bracket the sleeping functions.. + */ +extern void scheduling_functions_start_here(void); +extern void scheduling_functions_end_here(void); +#define first_sched (*(unsigned long *)scheduling_functions_start_here) +#define last_sched (*(unsigned long *)scheduling_functions_end_here) + +unsigned long get_wchan(struct task_struct *p) +{ + unsigned long ip, sp; + unsigned long stack_page = (unsigned long)p; + int count = 0; + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + sp = p->thread.ksp; + do { + sp = *(unsigned long *)sp; + if (sp < (stack_page + sizeof(struct task_struct)) || + sp >= (stack_page + (4 * PAGE_SIZE))) + return 0; + if (count > 0) { + ip = *(unsigned long *)(sp + 16); + if (ip < first_sched || ip >= last_sched) + return (ip & 0xFFFFFFFF); + } + } while (count++ < 16); + return 0; +} + +void show_trace_task(struct task_struct *p) +{ + unsigned long ip, sp; + unsigned long stack_page = (unsigned long)p; + int count = 0; + + if (!p) + return; + + printk("Call Trace: "); + sp = p->thread.ksp; + do { + sp = *(unsigned long *)sp; + if (sp < (stack_page + sizeof(struct task_struct)) || + sp >= (stack_page + (4 * PAGE_SIZE))) + break; + if (count > 0) { + ip = *(unsigned long *)(sp + 16); + printk("[%016lx] ", ip); + } + } while (count++ < 16); + printk("\n"); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/prom.c linux.ac/arch/ppc64/kernel/prom.c --- linux.vanilla/arch/ppc64/kernel/prom.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/prom.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,2350 @@ +/* + * + * + * Procedures for interfacing to Open Firmware. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras. + * + * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. + * {engebret|bergner}@us.ibm.com + * + * 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. + */ + +#if 0 +#define DEBUG_YABOOT +#endif + +#if 0 +#define DEBUG_PROM +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG_YABOOT +#define call_yaboot(FUNC,...) \ + do { \ + if (FUNC) { \ + struct prom_t *_prom = PTRRELOC(&prom); \ + unsigned long prom_entry = _prom->entry;\ + _prom->entry = (unsigned long)(FUNC); \ + enter_prom(__VA_ARGS__); \ + _prom->entry = prom_entry; \ + } \ + } while(0) +#else +#define call_yaboot(FUNC,...) do { ; } while(0) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "open_pic.h" +#include + +#ifdef CONFIG_FB +#include +#endif + +/* + * prom_init() is called very early on, before the kernel text + * and data have been mapped to KERNELBASE. At this point the code + * is running at whatever address it has been loaded at, so + * references to extern and static variables must be relocated + * explicitly. The procedure reloc_offset() returns the address + * we're currently running at minus the address we were linked at. + * (Note that strings count as static variables.) + * + * Because OF may have mapped I/O devices into the area starting at + * KERNELBASE, particularly on CHRP machines, we can't safely call + * OF once the kernel has been mapped to KERNELBASE. Therefore all + * OF calls should be done within prom_init(), and prom_init() + * and all routines called within it must be careful to relocate + * references as necessary. + * + * Note that the bss is cleared *after* prom_init runs, so we have + * to make sure that any static or extern variables it accesses + * are put in the data segment. + */ + + +#define PROM_BUG() do { \ + prom_print(RELOC("kernel BUG at ")); \ + prom_print(RELOC(__FILE__)); \ + prom_print(RELOC(":")); \ + prom_print_hex(__LINE__); \ + prom_print(RELOC("!\n")); \ + __asm__ __volatile__(".long " BUG_ILLEGAL_INSTR); \ +} while (0) + + + +struct pci_reg_property { + struct pci_address addr; + u32 size_hi; + u32 size_lo; +}; + + +struct isa_reg_property { + u32 space; + u32 address; + u32 size; +}; + +struct pci_intr_map { + struct pci_address addr; + u32 dunno; + phandle int_ctrler; + u32 intr; +}; + + +typedef unsigned long interpret_func(struct device_node *, unsigned long, + int, int); +#if 0 +static interpret_func interpret_pci_props; +#endif +static unsigned long interpret_pci_props(struct device_node *, unsigned long, + int, int); + +static interpret_func interpret_dbdma_props; +static interpret_func interpret_isa_props; +static interpret_func interpret_macio_props; +static interpret_func interpret_root_props; + +#ifndef FB_MAX /* avoid pulling in all of the fb stuff */ +#define FB_MAX 8 +#endif + + +struct prom_t prom = { + 0, /* entry */ + 0, /* chosen */ + 0, /* cpu */ + 0, /* stdout */ + 0, /* disp_node */ + {0,0,0,{0},NULL}, /* args */ + 0, /* version */ + 32 /* encode_phys_size */ +#ifdef DEBUG_YABOOT + ,NULL /* yaboot */ +#endif +}; + +int cpu_hw_index[NR_CPUS] = {0}; + +char *prom_display_paths[FB_MAX] __initdata = { 0, }; +unsigned int prom_num_displays = 0; +char *of_stdout_device = 0; + +extern struct rtas_t rtas; +extern unsigned long klimit; +extern struct Naca *naca; +extern struct lmb lmb; +#ifdef CONFIG_MSCHUNKS +extern struct msChunks msChunks; +#endif /* CONFIG_MSCHUNKS */ + +#define MAX_PHB 16 * 3 // 16 Towers * 3 PHBs/tower +struct _of_tce_table of_tce_table[MAX_PHB + 1] = {{0, 0, 0}}; + +char *bootpath = 0; +char *bootdevice = 0; + +/* Set for a newworld machine */ +int use_of_interrupt_tree = 0; +int pmac_newworld = 0; + +struct device_node *allnodes = 0; + +static unsigned long call_prom(const char *service, int nargs, int nret, ...); +static void prom_exit(void); +static unsigned long copy_device_tree(unsigned long); +static unsigned long inspect_node(phandle, struct device_node *, unsigned long, + unsigned long, struct device_node ***); +static unsigned long finish_node(struct device_node *, unsigned long, + interpret_func *, int, int); +static unsigned long finish_node_interrupts(struct device_node *, unsigned long); +static unsigned long check_display(unsigned long); +static int prom_next_node(phandle *); + +#ifdef CONFIG_MSCHUNKS +static unsigned long prom_initialize_mschunks(unsigned long); +#endif /* CONFIG_MSCHUNKS */ + +extern unsigned long reloc_offset(void); + +extern void enter_prom(void *dummy,...); + +void cacheable_memzero(void *, unsigned int); + +extern char cmd_line[512]; /* XXX */ +unsigned long dev_tree_size; + +char testString[] = "LINUX\n"; + + +/* This is the one and *ONLY* place where we actually call open + * firmware from, since we need to make sure we're running in 32b + * mode when we do. We switch back to 64b mode upon return. + */ + +__init +static unsigned long +call_prom(const char *service, int nargs, int nret, ...) +{ + int i; + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + va_list list; + + _prom->args.service = (u32)LONG_LSW(service); + _prom->args.nargs = nargs; + _prom->args.nret = nret; + _prom->args.rets = (prom_arg_t *)&(_prom->args.args[nargs]); + + va_start(list, nret); + for (i=0; i < nargs ;i++) + _prom->args.args[i] = (prom_arg_t)LONG_LSW(va_arg(list, unsigned long)); + va_end(list); + + for (i=0; i < nret ;i++) + _prom->args.rets[i] = 0; + + enter_prom(&_prom->args); + + return (unsigned long)((nret > 0) ? _prom->args.rets[0] : 0); +} + + +__init +static void +prom_exit() +{ + unsigned long offset = reloc_offset(); + + call_prom(RELOC("exit"), 0, 0); + + for (;;) /* should never get here */ + ; +} + +__init +void +prom_enter(void) +{ + unsigned long offset = reloc_offset(); + + call_prom(RELOC("enter"), 0, 0); +} + + +__init +void +prom_print(const char *msg) +{ + const char *p, *q; + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + + if (_prom->stdout == 0) + return; + + for (p = msg; *p != 0; p = q) { + for (q = p; *q != 0 && *q != '\n'; ++q) + ; + if (q > p) + call_prom(RELOC("write"), 3, 1, _prom->stdout, + p, q - p); + if (*q != 0) { + ++q; + call_prom(RELOC("write"), 3, 1, _prom->stdout, + RELOC("\r\n"), 2); + } + } +} + +void +prom_print_hex(unsigned long val) +{ + int i, nibbles = sizeof(val)*2; + char buf[sizeof(val)*2+1]; + + for (i = nibbles-1; i >= 0; i--) { + buf[i] = (val & 0xf) + '0'; + if (buf[i] > '9') + buf[i] += ('a'-'0'-10); + val >>= 4; + } + buf[nibbles] = '\0'; + prom_print(buf); +} + +void +prom_print_nl(void) +{ + unsigned long offset = reloc_offset(); + prom_print(RELOC("\n")); +} + + +static unsigned long +prom_initialize_naca(unsigned long mem) +{ + phandle node; + char type[64]; + unsigned long num_cpus = 0; + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + struct lmb *_lmb = PTRRELOC(&lmb); + struct Naca *_naca = RELOC(naca); + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_initialize_naca: start...\n")); +#endif + + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + + if (!strcmp(type, RELOC("cpu"))) { + num_cpus += 1; + + /* We're assuming *all* of the CPUs have the same + * d-cache and i-cache sizes... -Peter + */ + if ( num_cpus == 1 ) { + u32 size; + + call_prom(RELOC("getprop"), 4, 1, node, + RELOC("d-cache-line-size"), + &size, sizeof(size)); + + _naca->dCacheL1LineSize = size; + _naca->dCacheL1LogLineSize = __ilog2(size); + _naca->dCacheL1LinesPerPage = PAGE_SIZE / size; + + call_prom(RELOC("getprop"), 4, 1, node, + RELOC("i-cache-line-size"), + &size, sizeof(size)); + + _naca->iCacheL1LineSize = size; + _naca->iCacheL1LogLineSize = __ilog2(size); + _naca->iCacheL1LinesPerPage = PAGE_SIZE / size; + } + } else if (!strcmp(type, RELOC("serial"))) { + phandle isa, pci; + struct isa_reg_property reg; + union pci_range ranges; + + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, + RELOC("ibm,aix-loc"), type, sizeof(type)); + + if (strcmp(type, RELOC("S1"))) + continue; + + call_prom(RELOC("getprop"), 4, 1, node, RELOC("reg"), + ®, sizeof(reg)); + + isa = call_prom(RELOC("parent"), 1, 1, node); + if (!isa) + PROM_BUG(); + pci = call_prom(RELOC("parent"), 1, 1, isa); + if (!pci) + PROM_BUG(); + + call_prom(RELOC("getprop"), 4, 1, pci, RELOC("ranges"), + &ranges, sizeof(ranges)); + + if ( _prom->encode_phys_size == 32 ) + _naca->serialPortAddr = ranges.pci32.phys+reg.address; + else { + _naca->serialPortAddr = + ((((unsigned long)ranges.pci64.phys_hi) << 32) | + (ranges.pci64.phys_lo)) + reg.address; + } + } + } + + _naca->interrupt_controller = IC_INVALID; + _naca->io_subsystem = IOS_INVALID; + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("name"), + type, sizeof(type)); + if (strcmp(type, RELOC("interrupt-controller"))) { + continue; + } + call_prom(RELOC("getprop"), 4, 1, node, RELOC("compatible"), + type, sizeof(type)); + if (strstr(type, RELOC("open-pic"))) { + _naca->interrupt_controller = IC_OPEN_PIC; + _naca->io_subsystem = IOS_OPEN_PIC; + } else if (strstr(type, RELOC("ppc-xicp"))) { + _naca->interrupt_controller = IC_PPC_XIC; + _naca->io_subsystem = IOS_PPC_XIC; + } else { + prom_print(RELOC("prom: failed to recognize interrupt-controller\n")); + } + break; + } + + if(_naca->interrupt_controller == IC_INVALID) { + prom_print(RELOC("prom: failed to find interrupt-controller\n")); + PROM_BUG(); + } + + /* We gotta have at least 1 cpu... */ + if ( (_naca->processorCount = num_cpus) < 1 ) + PROM_BUG(); + + _naca->physicalMemorySize = lmb_phys_mem_size(); + + /* + * Hardcode to GP size. I am not sure where to get this info + * in general, as there does not appear to be a slb-size OF + * entry. At least in Condor and earlier. DRENG + */ + _naca->slb_size = 64; + +#ifdef DEBUG_PROM + prom_print(RELOC("naca->processorCount = 0x")); + prom_print_hex(_naca->processorCount); + prom_print_nl(); + + prom_print(RELOC("naca->physicalMemorySize = 0x")); + prom_print_hex(_naca->physicalMemorySize); + prom_print_nl(); + + prom_print(RELOC("naca->dCacheL1LineSize = 0x")); + prom_print_hex(_naca->dCacheL1LineSize); + prom_print_nl(); + + prom_print(RELOC("naca->dCacheL1LogLineSize = 0x")); + prom_print_hex(_naca->dCacheL1LogLineSize); + prom_print_nl(); + + prom_print(RELOC("naca->dCacheL1LinesPerPage = 0x")); + prom_print_hex(_naca->dCacheL1LinesPerPage); + prom_print_nl(); + + prom_print(RELOC("naca->iCacheL1LineSize = 0x")); + prom_print_hex(_naca->iCacheL1LineSize); + prom_print_nl(); + + prom_print(RELOC("naca->iCacheL1LogLineSize = 0x")); + prom_print_hex(_naca->iCacheL1LogLineSize); + prom_print_nl(); + + prom_print(RELOC("naca->iCacheL1LinesPerPage = 0x")); + prom_print_hex(_naca->iCacheL1LinesPerPage); + prom_print_nl(); + + prom_print(RELOC("naca->serialPortAddr = 0x")); + prom_print_hex(_naca->serialPortAddr); + prom_print_nl(); + + prom_print(RELOC("naca->interrupt_controller = 0x")); + prom_print_hex(_naca->interrupt_controller); + prom_print_nl(); + + prom_print(RELOC("prom_initialize_naca: end...\n")); +#endif + + return mem; +} + + +__init +static unsigned long +prom_initialize_lmb(unsigned long mem) +{ + phandle node; + char type[64]; + unsigned long i, offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + union lmb_reg_property reg; + 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); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + + if (strcmp(type, RELOC("memory"))) + continue; + + num_regs = call_prom(RELOC("getprop"), 4, 1, node, RELOC("reg"), + ®, sizeof(reg)) / bytes_per_reg; + + for(i=0; i < num_regs ;i++) { + if (_prom->encode_phys_size == 32) { + lmb_base = reg.addr32[i].address; + lmb_size = reg.addr32[i].size; + } else { + lmb_base = reg.addr64[i].address; + 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(); + +#ifdef CONFIG_MSCHUNKS + mem = prom_initialize_mschunks(mem); +#endif /* CONFIG_MSCHUNKS */ + + return mem; +} + + +__init +static unsigned long +prom_instantiate_rtas(unsigned long mem) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + struct rtas_t *_rtas = PTRRELOC(&rtas); + ihandle prom_rtas; + u32 getprop_rval; + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_instantiate_rtas: start...\n")); +#endif + prom_rtas = (ihandle)call_prom(RELOC("finddevice"), 1, 1, RELOC("/rtas")); + if (prom_rtas != (ihandle) -1) { + call_prom(RELOC("getprop"), + 4, 1, prom_rtas, + RELOC("rtas-size"), + &getprop_rval, + sizeof(getprop_rval)); + _rtas->size = getprop_rval; + prom_print(RELOC("instantiating rtas")); + if (_rtas->size != 0) { + /* + * Ask OF for some space for RTAS. + * Actually OF has bugs so we just arbitrarily + * use memory at the 6MB point. + */ + // The new code... + mem = PAGE_ALIGN(mem); + _rtas->base = mem + offset - KERNELBASE; + + mem += _rtas->size; + prom_print(RELOC(" at 0x")); + prom_print_hex(_rtas->base); + + _rtas->event_scan.log = (struct rtas_error_log *)(mem + offset); + call_prom(RELOC("getprop"), + 4, 1, prom_rtas, + RELOC("rtas-error-log-max"), + &getprop_rval, + sizeof(getprop_rval)); + _rtas->event_scan.log_size = getprop_rval; + mem += getprop_rval; + + call_prom(RELOC("getprop"), + 4, 1, prom_rtas, + RELOC("rtas-event-scan-rate"), + &getprop_rval, + sizeof(getprop_rval)); + _rtas->event_scan.rate = getprop_rval; + + prom_rtas = (ihandle)call_prom(RELOC("open"), + 1, 1, RELOC("/rtas")); + prom_print(RELOC("...")); + + if ((long)call_prom(RELOC("call-method"), 3, 2, + RELOC("instantiate-rtas"), + prom_rtas, + _rtas->base) >= 0) { + _rtas->entry = (long)_prom->args.rets[1]; + } + } + + if(_rtas->entry <= 0) { + prom_print(RELOC(" failed\n")); + } else { + prom_print(RELOC(" done\n")); + } + +#ifdef DEBUG_PROM + prom_print(RELOC("rtas->base = 0x")); + prom_print_hex(_rtas->base); + prom_print_nl(); + prom_print(RELOC("rtas->entry = 0x")); + prom_print_hex(_rtas->entry); + prom_print_nl(); + prom_print(RELOC("rtas->size = 0x")); + prom_print_hex(_rtas->size); + prom_print_nl(); + prom_print(RELOC("rtas->event_scan.rate = 0x")); + prom_print_hex(_rtas->event_scan.rate); + prom_print_nl(); + prom_print(RELOC("rtas->event_scan.log = 0x")); + prom_print_hex((unsigned long)_rtas->event_scan.log); + prom_print_nl(); + prom_print(RELOC("rtas->event_scan.log_size = 0x")); + prom_print_hex(_rtas->event_scan.log_size); + prom_print_nl(); +#endif + } +#ifdef DEBUG_PROM + prom_print(RELOC("prom_instantiate_rtas: end...\n")); +#endif + + return mem; +} + +unsigned long prom_strtoul(const char *cp) +{ + unsigned long result = 0,value; + + while (*cp) { + value = *cp-'0'; + result = result*10 + value; + cp++; + } + + return result; +} + + +#ifdef CONFIG_MSCHUNKS +static unsigned long +prom_initialize_mschunks(unsigned long mem) +{ + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct msChunks *_msChunks = PTRRELOC(&msChunks); + unsigned long i, pchunk = 0; + unsigned long mem_size = _lmb->memory.size; + unsigned long chunk_size = _lmb->memory.lcd_size; + +#if 1 + /* Fix me: 630 3G-4G IO hack here... -Peter (PPPBBB) */ + unsigned long io_base = 3UL<<30; + unsigned long io_size = 1UL<<30; + + for(i=0; i < _lmb->memory.cnt ;i++) { + unsigned long base = _lmb->memory.region[i].base; + unsigned long size = _lmb->memory.region[i].size; + if ( lmb_addrs_overlap(base,size,io_base,io_size) ) { + /* If we really have dram here, then we don't + * have a 630! -Peter + */ + io_base = mem_size; + io_size = 1; + break; + } + } +#else + unsigned long io_base = ; + unsigned long io_size = ; +#endif + + if ( lmb_addrs_overlap(0,mem_size,io_base,io_size) ) { + lmb_add(io_base, io_size); + lmb_reserve(io_base, io_size); + } + + mem = msChunks_alloc(mem, mem_size / chunk_size, chunk_size); + + for(i=0; i < _lmb->memory.cnt ;i++) { + unsigned long base = _lmb->memory.region[i].base; + unsigned long size = _lmb->memory.region[i].size; + unsigned long achunk = addr_to_chunk(base); + unsigned long end_achunk = addr_to_chunk(base+size); + _lmb->memory.region[i].physbase = chunk_to_addr(pchunk); + for(; achunk < end_achunk ;) { + PTRRELOC(_msChunks->abs)[pchunk++] = achunk++; + } + } + + return mem; +} +#endif /* CONFIG_MSCHUNKS */ + +void +prom_initialize_tce_table(void) +{ + phandle node; + ihandle phb_node; + unsigned long offset = reloc_offset(); + char compatible[64], path[64], type[64]; + unsigned long i, table = 0; + unsigned long base, vbase, align; + unsigned int minalign, minsize; + struct _of_tce_table *prom_tce_table = RELOC(of_tce_table); + unsigned long tce_entry, *tce_entryp; + +#ifdef DEBUG_PROM + prom_print(RELOC("starting prom_initialize_tce_table\n")); +#endif + + /* Search all nodes looking for PHBs. */ + for (node = 0; prom_next_node(&node); ) { + compatible[0] = 0; + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("compatible"), + compatible, sizeof(compatible)); + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + + if((compatible[0] == 0) || + ((strstr(compatible, RELOC("python")) == NULL) && + (strstr(compatible, RELOC("Speedwagon")) == NULL))) { + continue; + } + if((type[0] == 0) || (strstr(type, RELOC("pci")) == NULL)) { + continue; + } + + if(call_prom(RELOC("getprop"), 4, 1, node, + RELOC("tce-table-minalign"), &minalign, + sizeof(minalign)) < 0) { + minalign = 0; + } + + if(call_prom(RELOC("getprop"), 4, 1, node, + RELOC("tce-table-minsize"), &minsize, + sizeof(minsize)) < 0) { + minsize = 4UL << 20; + } + + /* Even though we read what OF wants, we just set the table + * size to 4 MB. This is enough to map 2GB of PCI DMA space. + * By doing this, we avoid the pitfalls of trying to DMA to + * MMIO space and the DMA alias hole. + */ + minsize = 4UL << 20; + + /* Align to the greater of the align or size */ + align = (minalign < minsize) ? minsize : minalign; + + /* Carve out storage for the TCE table. */ + base = lmb_alloc(minsize, align); + + if ( !base ) { + prom_print(RELOC("ERROR, cannot find space for TCE table.\n")); + prom_exit(); + } + + vbase = absolute_to_virt(base); + + /* Save away the TCE table attributes for later use. */ + prom_tce_table[table].node = node; + prom_tce_table[table].base = vbase; + prom_tce_table[table].size = minsize; + +#ifdef DEBUG_PROM + prom_print(RELOC("TCE table: 0x")); + prom_print_hex(table); + prom_print_nl(); + + prom_print(RELOC("\tnode = 0x")); + prom_print_hex(node); + prom_print_nl(); + + prom_print(RELOC("\tbase = 0x")); + prom_print_hex(vbase); + prom_print_nl(); + + prom_print(RELOC("\tsize = 0x")); + prom_print_hex(minsize); + prom_print_nl(); +#endif + + /* Initialize the table to have a one-to-one mapping + * over the allocated size. + */ + tce_entryp = (unsigned long *)base; + for(i = 0; i < (minsize >> 3) ;tce_entryp++, i++) { + tce_entry = (i << PAGE_SHIFT); + tce_entry |= 0x3; + *tce_entryp = tce_entry; + } + + /* Call OF to setup the TCE hardware */ + if(call_prom(RELOC("package-to-path"), 3, 1, node, + path, 255) <= 0) { + prom_print(RELOC("package-to-path failed\n")); + } else { + prom_print(RELOC("opened ")); + prom_print(path); + prom_print_nl(); + } + + phb_node = (ihandle)call_prom(RELOC("open"), 1, 1, path); + if( (long)phb_node <= 0) { + prom_print(RELOC("open failed\n")); + } else { + prom_print(RELOC("open success\n")); + } + call_prom(RELOC("call-method"), 6, 0, + RELOC("set-64-bit-addressing"), + phb_node, + -1, + minsize, + base & 0xffffffff, + (base >> 32) & 0xffffffff); + call_prom(RELOC("close"), 1, 0, phb_node); + + table++; + } + + /* Flag the first invalid entry */ + prom_tce_table[table].node = 0; +#ifdef DEBUG_PROM + prom_print(RELOC("ending prom_initialize_tce_table\n")); +#endif +} + +/* + * With CHRP SMP we need to use the OF to start the other + * processors so we can't wait until smp_boot_cpus (the OF is + * trashed by then) so we have to put the processors into + * a holding pattern controlled by the kernel (not OF) before + * we destroy the OF. + * + * This uses a chunk of low memory, puts some holding pattern + * code there and sends the other processors off to there until + * smp_boot_cpus tells them to do something. The holding pattern + * checks that address until its cpu # is there, when it is that + * cpu jumps to __secondary_start(). smp_boot_cpus() takes care + * of setting those values. + * + * We also use physical address 0x4 here to tell when a cpu + * is in its holding pattern code. + * + * Fixup comment... DRENG / PPPBBB - Peter + * + * -- Cort + */ +static void +prom_hold_cpus(unsigned long mem) +{ + unsigned long i; + unsigned int reg; + phandle node; + unsigned long offset = reloc_offset(); + char type[64], *path; + int cpuid = 0; + extern void __secondary_hold(void); + extern unsigned long __secondary_hold_spinloop; + extern unsigned long __secondary_hold_acknowledge; + unsigned long *spinloop = __v2a(&__secondary_hold_spinloop); + unsigned long *acknowledge = __v2a(&__secondary_hold_acknowledge); + unsigned long secondary_hold = __v2a(*PTRRELOC((unsigned long *)__secondary_hold)); + struct Naca *_naca = RELOC(naca); + struct prom_t *_prom = PTRRELOC(&prom); + + /* Initially, we must have one active CPU. */ + _naca->processorCount = 1; + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_hold_cpus: start...\n")); + prom_print(RELOC(" 1) spinloop = 0x")); + prom_print_hex(spinloop); + prom_print_nl(); + prom_print(RELOC(" 1) *spinloop = 0x")); + prom_print_hex(*spinloop); + prom_print_nl(); + prom_print(RELOC(" 1) acknowledge = 0x")); + prom_print_hex(acknowledge); + prom_print_nl(); + prom_print(RELOC(" 1) *acknowledge = 0x")); + prom_print_hex(*acknowledge); + prom_print_nl(); + prom_print(RELOC(" 1) secondary_hold = 0x")); + prom_print_hex(secondary_hold); + prom_print_nl(); +#endif + + /* Set the common spinloop variable, so all of the secondary cpus + * will block when they are awakened from their OF spinloop. + * This must occur for both SMP and non SMP kernels, since OF will + * be trashed when we move the kernel. + */ + *spinloop = 0; + +#ifdef DEBUG_PROM + prom_print(RELOC(" 2) spinloop = 0x")); + prom_print_hex(spinloop); + prom_print_nl(); + prom_print(RELOC(" 2) *spinloop = 0x")); + prom_print_hex(*spinloop); + prom_print_nl(); + prom_print(RELOC(" 2) acknowledge = 0x")); + prom_print_hex(acknowledge); + prom_print_nl(); + prom_print(RELOC(" 2) *acknowledge = 0x")); + prom_print_hex(*acknowledge); + prom_print_nl(); + prom_print(RELOC(" 2) secondary_hold = 0x")); + prom_print_hex(secondary_hold); + prom_print_nl(); +#endif + /* look for cpus */ + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + if (strcmp(type, RELOC("cpu")) != 0) + continue; + + /* Skip non-configured cpus. */ + call_prom(RELOC("getprop"), 4, 1, node, RELOC("status"), + type, sizeof(type)); + if (strcmp(type, RELOC("okay")) != 0) + continue; + + reg = -1; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("reg"), + ®, sizeof(reg)); + + /* Only need to start secondary procs, not ourself. */ + if ( reg == _prom->cpu ) + continue; + + path = (char *) mem; + memset(path, 0, 256); + if ((long) call_prom(RELOC("package-to-path"), 3, 1, + node, path, 255) < 0) + continue; + + cpuid++; + +#ifdef DEBUG_PROM + prom_print_nl(); + prom_print(RELOC("cpuid = 0x")); + prom_print_hex(cpuid); + prom_print_nl(); + prom_print(RELOC("cpu hw idx = 0x")); + prom_print_hex(reg); + prom_print_nl(); +#endif + RELOC(cpu_hw_index)[cpuid] = reg; + + prom_print(RELOC("starting cpu ")); + prom_print(path); + + /* Init the acknowledge var which will be reset by + * the secondary cpu when it awakens from its OF + * spinloop. + */ + *acknowledge = (unsigned long)-1; + +#ifdef DEBUG_PROM + prom_print(RELOC(" 3) spinloop = 0x")); + prom_print_hex(spinloop); + prom_print_nl(); + prom_print(RELOC(" 3) *spinloop = 0x")); + prom_print_hex(*spinloop); + prom_print_nl(); + prom_print(RELOC(" 3) acknowledge = 0x")); + prom_print_hex(acknowledge); + prom_print_nl(); + prom_print(RELOC(" 3) *acknowledge = 0x")); + prom_print_hex(*acknowledge); + prom_print_nl(); + prom_print(RELOC(" 3) secondary_hold = 0x")); + prom_print_hex(secondary_hold); + prom_print_nl(); + prom_print(RELOC(" 3) cpuid = 0x")); + prom_print_hex(cpuid); + prom_print_nl(); +#endif + call_prom(RELOC("start-cpu"), 3, 0, node, secondary_hold, cpuid); + prom_print(RELOC("...")); + for ( i = 0 ; (i < 100000000) && + (*acknowledge == ((unsigned long)-1)); i++ ) ; +#ifdef DEBUG_PROM + { + unsigned long *p = 0x0; + prom_print(RELOC(" 4) 0x0 = 0x")); + prom_print_hex(*p); + prom_print_nl(); + } +#endif + if (*acknowledge == cpuid) { + prom_print(RELOC("ok\n")); + /* Set the number of active processors. */ + _naca->processorCount++; + } else { + prom_print(RELOC("failed: ")); + prom_print_hex(*acknowledge); + prom_print_nl(); + } + } + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_hold_cpus: end...\n")); +#endif +} + + +/* + * We enter here early on, when the Open Firmware prom is still + * handling exceptions and the MMU hash table for us. + */ + +__init +unsigned long +prom_init(unsigned long r3, unsigned long r4, unsigned long pp, + unsigned long r6, unsigned long r7, yaboot_debug_t *yaboot) +{ + int chrp = 0; + unsigned long mem; + ihandle prom_mmu, prom_op, prom_root, prom_cpu; + phandle cpu_pkg; + unsigned long offset = reloc_offset(); + long l; + char *p, *d; + unsigned long phys; + u32 getprop_rval; + struct Naca *_naca = RELOC(naca); + struct prom_t *_prom = PTRRELOC(&prom); + + /* Get a handle to the prom entry point before anything else */ + _prom->entry = pp; + +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->dummy,offset>>32,offset&0xffffffff); + call_yaboot(yaboot->printf, RELOC("offset = 0x%08x%08x\n"), LONG_MSW(offset), LONG_LSW(offset)); +#endif + + /* Default */ + phys = KERNELBASE - offset; + +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->printf, RELOC("phys = 0x%08x%08x\n"), LONG_MSW(phys), LONG_LSW(phys)); +#endif + + +#ifdef DEBUG_YABOOT + _prom->yaboot = yaboot; + call_yaboot(yaboot->printf, RELOC("pp = 0x%08x%08x\n"), LONG_MSW(pp), LONG_LSW(pp)); + call_yaboot(yaboot->printf, RELOC("prom = 0x%08x%08x\n"), LONG_MSW(_prom->entry), LONG_LSW(_prom->entry)); +#endif + + /* First get a handle for the stdout device */ + _prom->chosen = (ihandle)call_prom(RELOC("finddevice"), 1, 1, + RELOC("/chosen")); + +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->printf, RELOC("prom->chosen = 0x%08x%08x\n"), LONG_MSW(_prom->chosen), LONG_LSW(_prom->chosen)); +#endif + + if ((long)_prom->chosen <= 0) + prom_exit(); + + if ((long)call_prom(RELOC("getprop"), 4, 1, _prom->chosen, + RELOC("stdout"), &getprop_rval, + sizeof(getprop_rval)) <= 0) + prom_exit(); + + _prom->stdout = (ihandle)(unsigned long)getprop_rval; + +#ifdef DEBUG_YABOOT + if (_prom->stdout == 0) { + call_yaboot(yaboot->printf, RELOC("prom->stdout = 0x%08x%08x\n"), LONG_MSW(_prom->stdout), LONG_LSW(_prom->stdout)); + } + + call_yaboot(yaboot->printf, RELOC("prom->stdout = 0x%08x%08x\n"), LONG_MSW(_prom->stdout), LONG_LSW(_prom->stdout)); +#endif + +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->printf, RELOC("Location: 0x11\n")); +#endif +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->printf, RELOC("Location: 0x11a\n")); +#endif + + mem = RELOC(klimit) - offset; +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->printf, RELOC("Location: 0x11b\n")); +#endif + + /* Get the full OF pathname of the stdout device */ + p = (char *) mem; + memset(p, 0, 256); + call_prom(RELOC("instance-to-path"), 3, 1, _prom->stdout, p, 255); + RELOC(of_stdout_device) = PTRUNRELOC(p); + mem += strlen(p) + 1; + + getprop_rval = 1; + prom_root = (ihandle)call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); + if (prom_root != (ihandle)-1) { + call_prom(RELOC("getprop"), 4, 1, + prom_root, RELOC("#size-cells"), + &getprop_rval, sizeof(getprop_rval)); + } + _prom->encode_phys_size = (getprop_rval==1) ? 32 : 64; + +#ifdef DEBUG_PROM + prom_print(RELOC("DRENG: Detect OF version...\n")); +#endif + /* Find the OF version */ + prom_op = (ihandle)call_prom(RELOC("finddevice"), 1, 1, RELOC("/openprom")); + if (prom_op != (ihandle)-1) { + char model[64]; + long sz; + sz = (long)call_prom(RELOC("getprop"), 4, 1, prom_op, + RELOC("model"), model, 64); + if (sz > 0) { + char *c; + /* hack to skip the ibm chrp firmware # */ + if ( strncmp(model,RELOC("IBM"),3) ) { + for (c = model; *c; c++) + if (*c >= '0' && *c <= '9') { + _prom->version = *c - '0'; + break; + } + } + else + chrp = 1; + } + } + if (_prom->version >= 3) + prom_print(RELOC("OF Version 3 detected.\n")); + + + /* Determine which cpu is actually running right _now_ */ + if ((long)call_prom(RELOC("getprop"), 4, 1, _prom->chosen, + RELOC("cpu"), &getprop_rval, + sizeof(getprop_rval)) <= 0) + prom_exit(); + + prom_cpu = (ihandle)(unsigned long)getprop_rval; + cpu_pkg = call_prom(RELOC("instance-to-package"), 1, 1, prom_cpu); + call_prom(RELOC("getprop"), 4, 1, + cpu_pkg, RELOC("reg"), + &getprop_rval, sizeof(getprop_rval)); + _prom->cpu = (int)(unsigned long)getprop_rval; + cpu_hw_index[0] = _prom->cpu; + +#ifdef DEBUG_PROM + prom_print(RELOC("Booting CPU hw index = 0x")); + prom_print_hex(_prom->cpu); + prom_print_nl(); +#endif + + /* Get the boot device and translate it to a full OF pathname. */ + p = (char *) mem; + l = (long) call_prom(RELOC("getprop"), 4, 1, _prom->chosen, + RELOC("bootpath"), p, 1<<20); + if (l > 0) { + p[l] = 0; /* should already be null-terminated */ + RELOC(bootpath) = PTRUNRELOC(p); + mem += l + 1; + d = (char *) mem; + *d = 0; + call_prom(RELOC("canon"), 3, 1, p, d, 1<<20); + RELOC(bootdevice) = PTRUNRELOC(d); + mem = DOUBLEWORD_ALIGN(mem + strlen(d) + 1); + } + + mem = prom_initialize_lmb(mem); + + mem = prom_instantiate_rtas(mem); + + /* Initialize some system info into the Naca early... */ + mem = prom_initialize_naca(mem); + + /* If we are on an SMP machine, then we *MUST* do the + * following, regardless of whether we have an SMP + * kernel or not. + */ + if ( _naca->processorCount > 1 ) + prom_hold_cpus(mem); + + mem = check_display(mem); + +#ifdef DEBUG_PROM + prom_print(RELOC("copying OF device tree...\n")); +#endif + mem = copy_device_tree(mem); + + RELOC(klimit) = mem + offset; + + lmb_reserve(0, __pa(RELOC(klimit))); + + prom_initialize_tce_table(); + + if ((long) call_prom(RELOC("getprop"), 4, 1, + _prom->chosen, + RELOC("mmu"), + &getprop_rval, + sizeof(getprop_rval)) <= 0) { + prom_print(RELOC(" no MMU found\n")); + prom_exit(); + } + + /* We assume the phys. address size is 3 cells */ + RELOC(prom_mmu) = (ihandle)(unsigned long)getprop_rval; + + if ((long)call_prom(RELOC("call-method"), 4, 4, + RELOC("translate"), + prom_mmu, + (void *)(KERNELBASE - offset), + (void *)1) != 0) { + prom_print(RELOC(" (translate failed) ")); + } else { + prom_print(RELOC(" (translate ok) ")); + phys = (unsigned long)_prom->args.rets[3]; + } + + /* If OpenFirmware version >= 3, then use quiesce call */ + if (_prom->version >= 3) { + prom_print(RELOC("Calling quiesce ...\n")); + call_prom(RELOC("quiesce"), 0, 0); + phys = KERNELBASE - offset; + } + + prom_print(RELOC("returning from prom_init\n")); + return phys; +} + + +static int +prom_set_color(ihandle ih, int i, int r, int g, int b) +{ + unsigned long offset = reloc_offset(); + + return (int)(long)call_prom(RELOC("call-method"), 6, 1, + RELOC("color!"), + ih, + (void *)(long) i, + (void *)(long) b, + (void *)(long) g, + (void *)(long) r ); +} + +/* + * If we have a display that we don't know how to drive, + * we will want to try to execute OF's open method for it + * later. However, OF will probably fall over if we do that + * we've taken over the MMU. + * So we check whether we will need to open the display, + * and if so, open it now. + */ +__init +static unsigned long +check_display(unsigned long mem) +{ + phandle node; + ihandle ih; + int i; + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + char type[64], *path; + static unsigned char default_colors[] = { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0xaa, + 0x00, 0xaa, 0x00, + 0x00, 0xaa, 0xaa, + 0xaa, 0x00, 0x00, + 0xaa, 0x00, 0xaa, + 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, + 0x55, 0x55, 0x55, + 0x55, 0x55, 0xff, + 0x55, 0xff, 0x55, + 0x55, 0xff, 0xff, + 0xff, 0x55, 0x55, + 0xff, 0x55, 0xff, + 0xff, 0xff, 0x55, + 0xff, 0xff, 0xff + }; + + _prom->disp_node = 0; + + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + if (strcmp(type, RELOC("display")) != 0) + continue; + /* It seems OF doesn't null-terminate the path :-( */ + path = (char *) mem; + memset(path, 0, 256); + if ((long) call_prom(RELOC("package-to-path"), 3, 1, + node, path, 255) < 0) + continue; + prom_print(RELOC("opening display ")); + prom_print(path); + ih = (ihandle)call_prom(RELOC("open"), 1, 1, path); + if (ih == (ihandle)0 || ih == (ihandle)-1) { + prom_print(RELOC("... failed\n")); + continue; + } + prom_print(RELOC("... ok\n")); + + if (_prom->disp_node == 0) + _prom->disp_node = (ihandle)node; + + /* Setup a useable color table when the appropriate + * method is available. Should update this to set-colors */ + for (i = 0; i < 32; i++) + if (prom_set_color(ih, i, RELOC(default_colors)[i*3], + RELOC(default_colors)[i*3+1], + RELOC(default_colors)[i*3+2]) != 0) + break; + +#ifdef CONFIG_FB + for (i = 0; i < LINUX_LOGO_COLORS; i++) + if (prom_set_color(ih, i + 32, + RELOC(linux_logo_red)[i], + RELOC(linux_logo_green)[i], + RELOC(linux_logo_blue)[i]) != 0) + break; +#endif /* CONFIG_FB */ + + /* + * If this display is the device that OF is using for stdout, + * move it to the front of the list. + */ + mem += strlen(path) + 1; + i = RELOC(prom_num_displays)++; + if (RELOC(of_stdout_device) != 0 && i > 0 + && strcmp(PTRRELOC(RELOC(of_stdout_device)), path) == 0) { + for (; i > 0; --i) + RELOC(prom_display_paths[i]) = RELOC(prom_display_paths[i-1]); + } + RELOC(prom_display_paths[i]) = PTRUNRELOC(path); + if (RELOC(prom_num_displays) >= FB_MAX) + break; + } + return DOUBLEWORD_ALIGN(mem); +} + +__init +static int +prom_next_node(phandle *nodep) +{ + phandle node; + unsigned long offset = reloc_offset(); + + if ((node = *nodep) != 0 + && (*nodep = call_prom(RELOC("child"), 1, 1, node)) != 0) + return 1; + if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0) + return 1; + for (;;) { + if ((node = call_prom(RELOC("parent"), 1, 1, node)) == 0) + return 0; + if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0) + return 1; + } +} + +/* + * Make a copy of the device tree from the PROM. + */ +__init +static unsigned long +copy_device_tree(unsigned long mem_start) +{ + phandle root; + unsigned long new_start; + struct device_node **allnextp; + unsigned long offset = reloc_offset(); + unsigned long mem_end = mem_start + (8<<20); + + root = call_prom(RELOC("peer"), 1, 1, (phandle)0); + if (root == (phandle)0) { + prom_print(RELOC("couldn't get device tree root\n")); + prom_exit(); + } + allnextp = &RELOC(allnodes); + mem_start = DOUBLEWORD_ALIGN(mem_start); + new_start = inspect_node(root, 0, mem_start, mem_end, &allnextp); + *allnextp = 0; + return new_start; +} + +__init +static unsigned long +inspect_node(phandle node, struct device_node *dad, + unsigned long mem_start, unsigned long mem_end, + struct device_node ***allnextpp) +{ + int l; + phandle child; + struct device_node *np; + struct property *pp, **prev_propp; + char *prev_name, *namep; + unsigned char *valp; + unsigned long offset = reloc_offset(); + + np = (struct device_node *) mem_start; + mem_start += sizeof(struct device_node); + memset(np, 0, sizeof(*np)); + np->node = node; + **allnextpp = PTRUNRELOC(np); + *allnextpp = &np->allnext; + if (dad != 0) { + np->parent = PTRUNRELOC(dad); + /* we temporarily use the `next' field as `last_child'. */ + if (dad->next == 0) + dad->child = PTRUNRELOC(np); + else + dad->next->sibling = PTRUNRELOC(np); + dad->next = np; + } + + /* get and store all properties */ + prev_propp = &np->properties; + prev_name = RELOC(""); + for (;;) { + pp = (struct property *) mem_start; + namep = (char *) (pp + 1); + pp->name = PTRUNRELOC(namep); + if ((long) call_prom(RELOC("nextprop"), 3, 1, node, prev_name, + namep) <= 0) + break; + mem_start = DOUBLEWORD_ALIGN((unsigned long)namep + strlen(namep) + 1); + prev_name = namep; + valp = (unsigned char *) mem_start; + pp->value = PTRUNRELOC(valp); + pp->length = (int)(long) + call_prom(RELOC("getprop"), 4, 1, node, namep, + valp, mem_end - mem_start); + if (pp->length < 0) + continue; + mem_start = DOUBLEWORD_ALIGN(mem_start + pp->length); + *prev_propp = PTRUNRELOC(pp); + prev_propp = &pp->next; + } + *prev_propp = 0; + + /* get the node's full name */ + l = (long) call_prom(RELOC("package-to-path"), 3, 1, node, + (char *) mem_start, mem_end - mem_start); + if (l >= 0) { + np->full_name = PTRUNRELOC((char *) mem_start); + *(char *)(mem_start + l) = 0; + mem_start = DOUBLEWORD_ALIGN(mem_start + l + 1); + } + + /* do all our children */ + child = call_prom(RELOC("child"), 1, 1, node); + while (child != (phandle)0) { + mem_start = inspect_node(child, np, mem_start, mem_end, + allnextpp); + child = call_prom(RELOC("peer"), 1, 1, child); + } + + return mem_start; +} + +/* + * finish_device_tree is called once things are running normally + * (i.e. with text and data mapped to the address they were linked at). + * It traverses the device tree and fills in the name, type, + * {n_}addrs and {n_}intrs fields of each node. + */ +__init +void +finish_device_tree(void) +{ + unsigned long mem = klimit; + + /* All newworld machines now use the interrupt tree */ + struct device_node *np = allnodes; + + while(np) { + if (get_property(np, "interrupt-parent", 0)) { + pmac_newworld = 1; // This is true on the RS6000-260 + break; + } + np = np->allnext; + } + if (pmac_newworld) use_of_interrupt_tree = 1; + udbg_printf("use_of_interrupt_tree = 0x%lx\n", use_of_interrupt_tree); + + mem = finish_node(allnodes, mem, NULL, 0, 0); + dev_tree_size = mem - (unsigned long) allnodes; + + mem = _ALIGN(mem, PAGE_SIZE); + lmb_reserve(__pa(klimit), mem-klimit); + + klimit = mem; + + rtas.dev = find_devices("rtas"); +} + + +__init +static unsigned long +finish_node(struct device_node *np, unsigned long mem_start, + interpret_func *ifunc, int naddrc, int nsizec) +{ + struct device_node *child; + int *ip; + + np->name = get_property(np, "name", 0); + np->type = get_property(np, "device_type", 0); + + /* get the device addresses and interrupts */ + if (ifunc != NULL) { + mem_start = ifunc(np, mem_start, naddrc, nsizec); + } + if (use_of_interrupt_tree) { + mem_start = finish_node_interrupts(np, mem_start); + } + + /* Look for #address-cells and #size-cells properties. */ + ip = (int *) get_property(np, "#address-cells", 0); + if (ip != NULL) + naddrc = *ip; + ip = (int *) get_property(np, "#size-cells", 0); + if (ip != NULL) + nsizec = *ip; + + /* the f50 sets the name to 'display' and 'compatible' to what we + * expect for the name -- Cort + */ + ifunc = NULL; + if (!strcmp(np->name, "display")) + np->name = get_property(np, "compatible", 0); + + if (!strcmp(np->name, "device-tree") || np->parent == NULL) + ifunc = interpret_root_props; + else if (np->type == 0) + ifunc = NULL; + else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci")) + ifunc = interpret_pci_props; + else if (!strcmp(np->type, "dbdma")) + ifunc = interpret_dbdma_props; + else if (!strcmp(np->type, "mac-io")) + ifunc = interpret_macio_props; + else if (!strcmp(np->type, "isa")) + ifunc = interpret_isa_props; + + for (child = np->child; child != NULL; child = child->sibling) + mem_start = finish_node(child, mem_start, ifunc, + naddrc, nsizec); + + return mem_start; +} + +/* This routine walks the interrupt tree for a given device node and gather + * all necessary informations according to the draft interrupt mapping + * for CHRP. The current version was only tested on Apple "Core99" machines + * and may not handle cascaded controllers correctly. + */ +__init +static unsigned long +finish_node_interrupts(struct device_node *np, unsigned long mem_start) +{ + /* Finish this node */ + unsigned int *isizep, *asizep, *interrupts, *map, *map_mask, *reg; + phandle *parent; + struct device_node *node, *parent_node; + int l, isize, ipsize, asize, map_size, regpsize; + + /* Currently, we don't look at all nodes with no "interrupts" property */ + + interrupts = (unsigned int *)get_property(np, "interrupts", &l); + if (interrupts == NULL) + return mem_start; + ipsize = l>>2; + + reg = (unsigned int *)get_property(np, "reg", &l); + regpsize = l>>2; + + /* We assume default interrupt cell size is 1 (bugus ?) */ + isize = 1; + node = np; + + do { + /* We adjust the cell size if the current parent contains an #interrupt-cells + * property */ + isizep = (unsigned int *)get_property(node, "#interrupt-cells", &l); + if (isizep) + isize = *isizep; + + /* We don't do interrupt cascade (ISA) for now, we stop on the first + * controller found + */ + if (get_property(node, "interrupt-controller", &l)) { + int i,j; + int cvt_irq; + + /* XXX on chrp, offset interrupt numbers for the + 8259 by 0, those for the openpic by 16 */ + // DRENG Condor -- this info is not actually known yet ... + cvt_irq = _machine == _MACH_chrp + && get_property(node, "interrupt-parent", NULL) == 0; + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = ipsize / isize; + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *interrupts++; + if (cvt_irq) + np->intrs[i].line = openpic_to_irq(np->intrs[i].line); + np->intrs[i].sense = 1; + if (isize > 1) + np->intrs[i].sense = *interrupts++; + for (j=2; j>2; + map_mask = (unsigned int *)get_property(node, "interrupt-map-mask", &l); + asizep = (unsigned int *)get_property(node, "#address-cells", &l); + if (asizep && l == sizeof(unsigned int)) + asize = *asizep; + else + asize = 0; + found = 0; + while(map_size>0 && !found) { + found = 1; + for (i=0; i=regpsize) || ((mask & *map) != (mask & reg[i]))) + found = 0; + map++; + map_size--; + } + for (i=0; iparent; + } while(node); + + return mem_start; +} + +int +prom_n_addr_cells(struct device_node* np) +{ + int* ip; + do { + if (np->parent) + np = np->parent; + ip = (int *) get_property(np, "#address-cells", 0); + if (ip != NULL) + return *ip; + } while(np->parent); + return 0; +} + +int +prom_n_size_cells(struct device_node* np) +{ + int* ip; + do { + if (np->parent) + np = np->parent; + ip = (int *) get_property(np, "#size-cells", 0); + if (ip != NULL) + return *ip; + } while(np->parent); + return 0; +} + +__init +static unsigned long +interpret_pci_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct address_range *adr; + struct pci_reg_property *pci_addrs; + int i, l, *ip, ml; + struct pci_intr_map *imp; + + pci_addrs = (struct pci_reg_property *) + get_property(np, "assigned-addresses", &l); + if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct pci_reg_property)) >= 0) { + /* XXX assumes PCI addresses mapped 1-1 to physical */ + adr[i].space = pci_addrs[i].addr.a_hi; + adr[i].address = pci_addrs[i].addr.a_lo; + adr[i].size = pci_addrs[i].size_lo; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + /* + * If the pci host bridge has an interrupt-map property, + * look for our node in it. + */ + if (np->parent != 0 && pci_addrs != 0 + && (imp = (struct pci_intr_map *) + get_property(np->parent, "interrupt-map", &ml)) != 0 + && (ip = (int *) get_property(np, "interrupts", &l)) != 0) { + unsigned int devfn = pci_addrs[0].addr.a_hi & 0xff00; + unsigned int cell_size; + struct device_node* np2; + /* This is hackish, but is only used for BootX booting */ + cell_size = sizeof(struct pci_intr_map); + np2 = np->parent; + while(np2) { + if (device_is_compatible(np2, "uni-north")) { + cell_size += 4; + break; + } + np2 = np2->parent; + } + np->n_intrs = 0; + np->intrs = (struct interrupt_info *) mem_start; + for (i = 0; (ml -= cell_size) >= 0; ++i) { + if (imp->addr.a_hi == devfn) { + np->intrs[np->n_intrs].line = imp->intr; + np->intrs[np->n_intrs].sense = 1; /* FIXME */ + ++np->n_intrs; + } + imp = (struct pci_intr_map *)(((unsigned long)imp) + + cell_size); + } + if (np->n_intrs == 0) + np->intrs = 0; + mem_start += np->n_intrs * sizeof(struct interrupt_info); + return mem_start; + } + + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 1; + } + } + + return mem_start; +} + +__init +static unsigned long +interpret_dbdma_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct reg_property *rp; + struct address_range *adr; + unsigned long base_address; + int i, l, *ip; + struct device_node *db; + + base_address = 0; + for (db = np->parent; db != NULL; db = db->parent) { + if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) { + base_address = db->addrs[0].address; + break; + } + } + + rp = (struct reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = 0; + adr[i].address = rp[i].address + base_address; + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 1; + } + } + + return mem_start; +} + +__init +static unsigned long +interpret_macio_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct reg_property *rp; + struct address_range *adr; + unsigned long base_address; + int i, l, keylargo, *ip; + struct device_node *db; + + base_address = 0; + for (db = np->parent; db != NULL; db = db->parent) { + if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { + base_address = db->addrs[0].address; + keylargo = device_is_compatible(db, "Keylargo"); + break; + } + } + + rp = (struct reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = 0; + adr[i].address = rp[i].address + base_address; + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + ip = (int *) get_property(np, "interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + if (_machine == _MACH_Pmac) { + /* for the iMac */ + np->n_intrs = l / sizeof(int); + /* Hack for BootX on Core99 */ + if (keylargo) + np->n_intrs = np->n_intrs/2; + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + if (keylargo) + np->intrs[i].sense = *ip++; + else + np->intrs[i].sense = 1; + } + } else { + /* CHRP machines */ + np->n_intrs = l / (2 * sizeof(int)); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = openpic_to_irq(*ip++); + np->intrs[i].sense = *ip++; + } + } + mem_start += np->n_intrs * sizeof(struct interrupt_info); + } + + return mem_start; +} + +__init +static unsigned long +interpret_isa_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct isa_reg_property *rp; + struct address_range *adr; + int i, l, *ip; + + rp = (struct isa_reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct isa_reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = rp[i].space; + adr[i].address = rp[i].address + + (adr[i].space? 0: _ISA_MEM_BASE); + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / (2 * sizeof(int)); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = *ip++; + } + } + + return mem_start; +} + +__init +static unsigned long +interpret_root_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct address_range *adr; + int i, l, *ip; + unsigned int *rp; + int rpsize = (naddrc + nsizec) * sizeof(unsigned int); + + rp = (unsigned int *) get_property(np, "reg", &l); + if (rp != 0 && l >= rpsize) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= rpsize) >= 0) { + adr[i].space = 0; + adr[i].address = rp[naddrc - 1]; + adr[i].size = rp[naddrc + nsizec - 1]; + ++i; + rp += naddrc + nsizec; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 1; + } + } + + return mem_start; +} + +/* + * Work out the sense (active-low level / active-high edge) + * of each interrupt from the device tree. + */ +void __init +prom_get_irq_senses(unsigned char *senses, int off, int max) +{ + struct device_node *np; + int i, j; + + /* default to level-triggered */ + memset(senses, 1, max - off); + if (!use_of_interrupt_tree) + return; + + for (np = allnodes; np != 0; np = np->allnext) { + for (j = 0; j < np->n_intrs; j++) { + i = np->intrs[j].line; + if (i >= off && i < max) + senses[i-off] = np->intrs[j].sense; + } + } +} + +/* + * Construct and return a list of the device_nodes with a given name. + */ +__openfirmware +struct device_node * +find_devices(const char *name) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (np->name != 0 && strcasecmp(np->name, name) == 0) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = 0; + return head; +} + +/* + * Construct and return a list of the device_nodes with a given type. + */ +__openfirmware +struct device_node * +find_type_devices(const char *type) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (np->type != 0 && strcasecmp(np->type, type) == 0) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = 0; + return head; +} + +/* Finds a device node given its PCI bus number, device number + * and function number + */ +__openfirmware +struct device_node * +find_pci_device_OFnode(unsigned char bus, unsigned char dev_fn) +{ + struct device_node* np; + unsigned int *reg; + int l; + + for (np = allnodes; np != 0; np = np->allnext) { + if (np->parent == NULL || np->parent->type == NULL + || strcmp(np->parent->type, "pci") != 0) + continue; + reg = (unsigned int *) get_property(np, "reg", &l); + if (reg == 0 || l < sizeof(struct reg_property)) + continue; + if (((reg[0] >> 8) & 0xff) == dev_fn && ((reg[0] >> 16) & 0xff) == bus) + break; + } + return np; +} + +/* + * Returns all nodes linked together + */ +__openfirmware +struct device_node * +find_all_nodes(void) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + *prevp = np; + prevp = &np->next; + } + *prevp = 0; + return head; +} + +/* Checks if the given "compat" string matches one of the strings in + * the device's "compatible" property + */ +__openfirmware +int +device_is_compatible(struct device_node *device, const char *compat) +{ + const char* cp; + int cplen, l; + + cp = (char *) get_property(device, "compatible", &cplen); + if (cp == NULL) + return 0; + while (cplen > 0) { + if (strncasecmp(cp, compat, strlen(compat)) == 0) + return 1; + l = strlen(cp) + 1; + cp += l; + cplen -= l; + } + + return 0; +} + + +/* + * Indicates whether the root node has a given value in its + * compatible property. + */ +__openfirmware +int +machine_is_compatible(const char *compat) +{ + struct device_node *root; + + root = find_path_device("/"); + if (root == 0) + return 0; + return device_is_compatible(root, compat); +} + +/* + * Construct and return a list of the device_nodes with a given type + * and compatible property. + */ +__openfirmware +struct device_node * +find_compatible_devices(const char *type, const char *compat) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (type != NULL + && !(np->type != 0 && strcasecmp(np->type, type) == 0)) + continue; + if (device_is_compatible(np, compat)) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = 0; + return head; +} + +/* + * Find the device_node with a given full_name. + */ +__openfirmware +struct device_node * +find_path_device(const char *path) +{ + struct device_node *np; + + for (np = allnodes; np != 0; np = np->allnext) + if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0) + return np; + return NULL; +} + +/* + * Find the device_node with a given phandle. + */ +__openfirmware +struct device_node * +find_phandle(phandle ph) +{ + struct device_node *np; + + for (np = allnodes; np != 0; np = np->allnext) + if (np->node == ph) + return np; + return NULL; +} + +/* + * Find a property with a given name for a given node + * and return the value. + */ +__openfirmware +unsigned char * +get_property(struct device_node *np, const char *name, int *lenp) +{ + struct property *pp; + + for (pp = np->properties; pp != 0; pp = pp->next) + if (strcmp(pp->name, name) == 0) { + if (lenp != 0) + *lenp = pp->length; + return pp->value; + } + return 0; +} + +/* + * Add a property to a node + */ +__openfirmware +void +prom_add_property(struct device_node* np, struct property* prop) +{ + struct property **next = &np->properties; + + prop->next = NULL; + while (*next) + next = &(*next)->next; + *next = prop; +} + +#if 0 +__openfirmware +void +print_properties(struct device_node *np) +{ + struct property *pp; + char *cp; + int i, n; + + for (pp = np->properties; pp != 0; pp = pp->next) { + printk(KERN_INFO "%s", pp->name); + for (i = strlen(pp->name); i < 16; ++i) + printk(" "); + cp = (char *) pp->value; + for (i = pp->length; i > 0; --i, ++cp) + if ((i > 1 && (*cp < 0x20 || *cp > 0x7e)) + || (i == 1 && *cp != 0)) + break; + if (i == 0 && pp->length > 1) { + /* looks like a string */ + printk(" %s\n", (char *) pp->value); + } else { + /* dump it in hex */ + n = pp->length; + if (n > 64) + n = 64; + if (pp->length % 4 == 0) { + unsigned int *p = (unsigned int *) pp->value; + + n /= 4; + for (i = 0; i < n; ++i) { + if (i != 0 && (i % 4) == 0) + printk("\n "); + printk(" %08x", *p++); + } + } else { + unsigned char *bp = pp->value; + + for (i = 0; i < n; ++i) { + if (i != 0 && (i % 16) == 0) + printk("\n "); + printk(" %02x", *bp++); + } + } + printk("\n"); + if (pp->length > 64) + printk(" ... (length = %d)\n", + pp->length); + } + } +} +#endif + + +__init +void +abort() +{ +#ifdef CONFIG_XMON + xmon(NULL); +#endif + for (;;) + prom_exit(); +} + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/ptrace.c linux.ac/arch/ppc64/kernel/ptrace.c --- linux.vanilla/arch/ppc64/kernel/ptrace.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/ptrace.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,342 @@ +/* + * linux/arch/ppc/kernel/ptrace.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/m68k/kernel/ptrace.c" + * Copyright (C) 1994 by Hamish Macdonald + * Taken from linux/kernel/ptrace.c and modified for M680x0. + * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds + * + * Modified by Cort Dougan (cort@hq.fsmlabs.com) + * and Paul Mackerras (paulus@linuxcare.com.au). + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file README.legal in the main directory of + * this archive for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Set of msr bits that gdb can change on behalf of a process. + */ +#define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1) + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* + * Get contents of register REGNO in task TASK. + */ +static inline unsigned long get_reg(struct task_struct *task, int regno) +{ + if (regno < sizeof(struct pt_regs) / sizeof(unsigned long)) + return ((unsigned long *)task->thread.regs)[regno]; + return (0); +} + +/* + * Write contents of register REGNO in task TASK. + */ +static inline int put_reg(struct task_struct *task, int regno, + unsigned long data) +{ + if (regno <= PT_MQ) { + if (regno == PT_MSR) + data = (data & MSR_DEBUGCHANGE) + | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); + ((unsigned long *)task->thread.regs)[regno] = data; + return 0; + } + return -EIO; +} + +static inline void +set_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + regs->msr |= MSR_SE; +} + +static inline void +clear_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + regs->msr &= ~MSR_SE; +} + +int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret = -EPERM; + + lock_kernel(); + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + ret = -ESRCH; + if (!(child->ptrace & PT_PTRACED)) + goto out_tsk; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out_tsk; + } + if (child->p_pptr != current) + goto out_tsk; + + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long index, tmp; + + ret = -EIO; + /* convert to index and check */ + index = (unsigned long) addr >> 3; + if ((addr & 7) || index > PT_FPSCR) + break; + + if (index < PT_FPR0) { + tmp = get_reg(child, (int) index); + } else { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + tmp = ((unsigned long *)child->thread.fpr)[index - PT_FPR0]; + } + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* If I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) + break; + ret = -EIO; + break; + + /* write the word at location addr in the USER area */ + case PTRACE_POKEUSR: { + unsigned long index; + + ret = -EIO; + /* convert to index and check */ + index = (unsigned long) addr >> 3; + if ((addr & 7) || index > PT_FPSCR) + break; + + if (index == PT_ORIG_R3) + break; + if (index < PT_FPR0) { + ret = put_reg(child, index, data); + } else { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + ((unsigned long *)child->thread.fpr)[index - PT_FPR0] = data; + ret = 0; + } + break; + } + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + child->ptrace |= PT_TRACESYS; + else + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~PT_TRACESYS; + set_single_step(child); + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: { /* detach a process that was attached. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~(PT_PTRACED|PT_TRACESYS); + child->exit_code = data; + write_lock_irq(&tasklist_lock); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irq(&tasklist_lock); + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + } + + case PPC_PTRACE_GETREGS: + { /* Get GPRs 0 - 31. */ + u64 tmp; + u64 cntr; + ret = 0; + for (cntr=0; cntr<32 && ret==0; ++cntr) + { + tmp = ((u64*)child->thread.regs)[cntr]; + ret = put_user(tmp, (u64*)(data+cntr)); + } + break; + } + + case PPC_PTRACE_SETREGS: + { /* Set GPRs 0 - 31. */ + u64 cntr; + ret = 0; + for (cntr=0; cntr<32 && ret==0; ++cntr) + { + ret = put_reg(child, cntr, *(u64*)(data+cntr)); + } + break; + } + + case PPC_PTRACE_GETFPREGS: + { /* Get FPRs 0 - 31. */ + u64 tmp; + u64 cntr; + ret = -EIO; + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + ret = 0; + for (cntr=0; cntr<32 && ret==0; ++cntr) + { + tmp = ((u64*)child->thread.fpr)[cntr]; + ret = put_user(tmp, (u64*)(data+cntr)); + } + break; + } + + case PPC_PTRACE_SETFPREGS: + { /* Get FPRs 0 - 31. */ + u64 cntr; + ret = -EIO; + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + for (cntr=0; cntr<32; ++cntr) + { + ((u64*)child->thread.fpr)[cntr] = *(u64*)(data+cntr); + } + ret = 0; + break; + } + + default: + ret = -EIO; + break; + } +out_tsk: + free_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +void syscall_trace(void) +{ + if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) + != (PT_PTRACED|PT_TRACESYS)) + return; + current->exit_code = SIGTRAP; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/ptrace32.c linux.ac/arch/ppc64/kernel/ptrace32.c --- linux.vanilla/arch/ppc64/kernel/ptrace32.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/ptrace32.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,413 @@ +/* + * linux/arch/ppc/kernel/ptrace32.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/m68k/kernel/ptrace.c" + * Copyright (C) 1994 by Hamish Macdonald + * Taken from linux/kernel/ptrace.c and modified for M680x0. + * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds + * + * Modified by Cort Dougan (cort@hq.fsmlabs.com) + * and Paul Mackerras (paulus@linuxcare.com.au). + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file README.legal in the main directory of + * this archive for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Set of msr bits that gdb can change on behalf of a process. + */ +#define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1) + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* + * Get contents of register REGNO in task TASK. + */ +static inline unsigned long get_reg(struct task_struct *task, int regno) +{ + if (regno < sizeof(struct pt_regs) / sizeof(unsigned long)) + return ((unsigned long *)task->thread.regs)[regno]; + return (0); +} + +/* + * Write contents of register REGNO in task TASK. + * (Put DATA into task TASK's register REGNO.) + */ +static inline int put_reg(struct task_struct *task, int regno, unsigned long data) +{ + if (regno <= PT_MQ) + { + if (regno == PT_MSR) + data = (data & MSR_DEBUGCHANGE) | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); + ((unsigned long *)task->thread.regs)[regno] = data; + return 0; + } + return -EIO; +} + +static inline void +set_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + regs->msr |= MSR_SE; +} + +static inline void +clear_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + regs->msr &= ~MSR_SE; +} + +int sys32_ptrace(long request, long pid, unsigned long addr, unsigned long data) +{ + struct task_struct *child; + int ret = -EPERM; + + lock_kernel(); + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + ret = -ESRCH; + if (!(child->ptrace & PT_PTRACED)) + goto out_tsk; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out_tsk; + } + if (child->p_pptr != current) + goto out_tsk; + + switch (request) + { + /* Read word at location ADDR */ + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: + { + unsigned int tmp_mem_value; + int copied; + + copied = access_process_vm(child, addr, &tmp_mem_value, sizeof(tmp_mem_value), 0); + ret = -EIO; + if (copied != sizeof(tmp_mem_value)) + break; + ret = put_user(tmp_mem_value, (u32*)data); // copy 4 bytes of data into the user location specified by the 8 byte pointer in "data". + break; + } + + /* Read 4 bytes of the other process' storage */ + /* data is a pointer specifying where the user wants the 4 bytes copied into */ + /* addr is a pointer in the user's storage that contains an 8 byte address in the other process of the 4 bytes that is to be read */ + /* (this is run in a 32-bit process looking at a 64-bit process) */ + /* when I and D space are separate, these will need to be fixed. */ + case PPC_PTRACE_PEEKTEXT_3264: + case PPC_PTRACE_PEEKDATA_3264: + { + u32 tmp_mem_value; + int copied; + u32* addrOthers; + + ret = -EIO; + + /* Get the addr in the other process that we want to read */ + if (get_user(addrOthers,(u32**)addr) != 0) + break; + + copied = access_process_vm(child, (u64)addrOthers, &tmp_mem_value, sizeof(tmp_mem_value), 0); + if (copied != sizeof(tmp_mem_value)) + break; + ret = put_user(tmp_mem_value, (u32*)data); // copy 4 bytes of data into the user location specified by the 8 byte pointer in "data". + break; + } + + /* Read a register (specified by ADDR) out of the "user area" */ + case PTRACE_PEEKUSR: { + int index; + unsigned int reg32bits; + unsigned long tmp_reg_value; + + ret = -EIO; + /* convert to index and check */ + index = (unsigned long) addr >> 2; + if ((addr & 3) || index > PT_FPSCR32) + break; + + if (index < PT_FPR0) { + tmp_reg_value = get_reg(child, index); + } else { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + /* the user space code considers the floating point to be + * an array of unsigned int (32 bits) - the index passed + * in is based on this assumption. + */ + tmp_reg_value = ((unsigned int *)child->thread.fpr)[index - PT_FPR0]; + } + reg32bits = tmp_reg_value; + ret = put_user(reg32bits, (u32*)data); // copy 4 bytes of data into the user location specified by the 8 byte pointer in "data". + break; + } + + /* Read 4 bytes out of the other process' pt_regs area */ + /* data is a pointer specifying where the user wants the 4 bytes copied into */ + /* addr is the offset into the other process' pt_regs structure that is to be read */ + /* (this is run in a 32-bit process looking at a 64-bit process) */ + case PPC_PTRACE_PEEKUSR_3264: + { + u32 index; + u32 reg32bits; + u64 tmp_reg_value; + u32 numReg; + u32 part; + + ret = -EIO; + /* Determine which register the user wants */ + index = (u64)addr >> 2; /* Divide addr by 4 */ + numReg = index / 2; + /* Determine which part of the register the user wants */ + if (index % 2) + part = 1; /* want the 2nd half of the register (right-most). */ + else + part = 0; /* want the 1st half of the register (left-most). */ + + /* Validate the input - check to see if address is on the wrong boundary or beyond the end of the user area */ + if ((addr & 3) || numReg > PT_FPSCR) + break; + + if (numReg >= PT_FPR0) + { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + } + tmp_reg_value = get_reg(child, numReg); + reg32bits = ((u32*)&tmp_reg_value)[part]; + ret = put_user(reg32bits, (u32*)data); /* copy 4 bytes of data into the user location specified by the 8 byte pointer in "data". */ + break; + } + + /* Write the word at location ADDR */ + /* If I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: { + unsigned int tmp_value_to_write; + tmp_value_to_write = data; + ret = 0; + if (access_process_vm(child, addr, &tmp_value_to_write, sizeof(tmp_value_to_write), 1) == sizeof(tmp_value_to_write)) + break; + ret = -EIO; + break; + } + + /* Write 4 bytes into the other process' storage */ + /* data is the 4 bytes that the user wants written */ + /* addr is a pointer in the user's storage that contains an 8 byte address in the other process where the 4 bytes that is to be written */ + /* (this is run in a 32-bit process looking at a 64-bit process) */ + /* when I and D space are separate, these will need to be fixed. */ + case PPC_PTRACE_POKETEXT_3264: + case PPC_PTRACE_POKEDATA_3264: + { + u32 tmp_value_to_write = data; + u32* addrOthers; + int bytesWritten; + + /* Get the addr in the other process that we want to write into */ + ret = -EIO; + if (get_user(addrOthers,(u32**)addr) != 0) + break; + + ret = 0; + bytesWritten = access_process_vm(child, (u64)addrOthers, &tmp_value_to_write, sizeof(tmp_value_to_write), 1); + if (bytesWritten == sizeof(tmp_value_to_write)) + break; + ret = -EIO; + break; + } + + /* Write DATA into location ADDR within the USER area */ + case PTRACE_POKEUSR: { + unsigned long index; + + ret = -EIO; + + /* convert to index and check */ + index = (unsigned long) addr >> 2; + if ((addr & 3) || index > PT_FPSCR32) + break; + + if (index == PT_ORIG_R3) + break; + + + if (index < PT_FPR0) { + ret = put_reg(child, index, data); + } else { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + /* the user space code considers the floating point to be + * an array of unsigned int (32 bits) - the index passed + * in is based on this assumption. + */ + + ((unsigned int *)child->thread.fpr)[index - PT_FPR0] = data; + ret = 0; + } + break; + } + + /* Write 4 bytes into the other process' pt_regs area */ + /* data is the 4 bytes that the user wants written */ + /* addr is the offset into the other process' pt_regs structure that is to be written into */ + /* (this is run in a 32-bit process looking at a 64-bit process) */ + case PPC_PTRACE_POKEUSR_3264: + { + u32 index; + u32 numReg; + + ret = -EIO; + + /* Determine which register the user wants */ + index = (u64)addr >> 2; /* Divide addr by 4 */ + numReg = index / 2; + + /* Validate the input - check to see if address is on the wrong boundary or beyond the end of the user area */ + if ((addr & 3) || numReg > PT_FPSCR) + break; + /* Insure it is a register we let them change */ + if ((numReg == PT_ORIG_R3) || ((numReg > PT_MQ) && (numReg < PT_FPR0))) + break; + + if (numReg >= PT_FPR0) + { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + } + + if (numReg == PT_MSR) + data = (data & MSR_DEBUGCHANGE) | (child->thread.regs->msr & ~MSR_DEBUGCHANGE); + + ((u32*)child->thread.regs)[index] = data; + ret = 0; + break; + } + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + child->ptrace |= PT_TRACESYS; + else + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~PT_TRACESYS; + set_single_step(child); + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: { /* detach a process that was attached. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~(PT_PTRACED|PT_TRACESYS); + child->exit_code = data; + write_lock_irq(&tasklist_lock); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irq(&tasklist_lock); + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + } + + default: + ret = -EIO; + break; + } +out_tsk: + free_task_struct(child); +out: + unlock_kernel(); + return ret; +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/rtas-eventscan.c linux.ac/arch/ppc64/kernel/rtas-eventscan.c --- linux.vanilla/arch/ppc64/kernel/rtas-eventscan.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/rtas-eventscan.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,217 @@ +/* + * arch/ppc64/kernel/rtas-eventscan.c + * + * Copyright (c) 2000 Tilmann Bitterberg + * (tilmann@bitterberg.de) + * + * Error processing of errors found by rtas even-scan routine + * which is done with every heartbeat. (chrp_setup.c) + */ + +#include +#include + +#include /* for ppc_md */ +#include +#include + +static long rtas_handle_error_log(struct rtas_error_log *, long, char *); +static const char *rtas_errorlog_check_type (struct rtas_error_log *); + +/* ****************************************************************** */ + +#define rtas_errorlog_check_severity(x) \ + (_errlog_severity[x->severity]) +#define rtas_errorlog_check_target(x) \ + (_errlog_target[x->target]) +#define rtas_errorlog_check_initiator(x) \ + (_errlog_initiator[x->initiator]) +#define rtas_errorlog_check_extended(x) \ + (_errlog_extended[x->extended]) +#define rtas_errorlog_disect_extended(x) \ + 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; + long cnt = 0; + char *buf; + + buf = rtas_event_buffer[smp_processor_id()]; + + for(;;) { + long status = 1; + call_rtas( "event-scan", 4, 1, &status, EVENT_SCAN_ALL_EVENTS, + 0, __pa(log), size); + + if ( status == 1 ) /* No errors/events found */ + break; + + /* New error log returned */ + cnt += 1; + if (rtas_handle_error_log(log, status, buf) != 0) + printk("%s", buf); + + if (status == -1) { + /* Hardware error */ + panic("RTAS: event-scan reported hardware error\n"); + return 0; + } + } + ppc_md.heartbeat_count = ppc_md.heartbeat_reset; + return cnt; +} + + +/* ****************************************************************** */ +/* + * EVENT-SCAN + * The whole stuff below here doesn't take any action when it found + * an error, it just prints as much information as possible and + * then its up to the user to decide what to do. + * + * Returns 0 if no errors were found + * Returns 1 if there may be more errors + */ +static long +rtas_handle_error_log(struct rtas_error_log *log, long status, char *buffer) +{ +const char *_errlog_severity[] = { +#ifdef VERBOSE_ERRORS + "No Error\n\t\ +Should require no further information", + "Event\n\t\ +This is not really an error, it is an event. I use events\n\t\ +to communicate with RTAS back and forth.", + "Warning\n\t\ +Indicates a non-state-losing error, either fully recovered\n\t\ +by RTAS or not needing recovery. Ignore it.", + "Error sync\n\t\ +May only be fatal to a certain program or thread. Recovery\n\t\ +and continuation is possible, if I only had a handler for\n\t\ +this. Less serious", + "Error\n\t\ +Less serious, but still causing a loss of data and state.\n\t\ +I can't tell you exactly what to do, You have to decide\n\t\ +with help from the target and initiator field, what kind\n\t\ +of further actions may take place.", + "Fatal\n\t\ +Represent a permanent hardware failure and I believe this\n\t\ +affects my overall performance and behaviour. I would not\n\t\ +attempt to continue normal operation." +#else + "No Error", + "Event", + "Warning", + "Error sync", + "Error", + "Fatal" +#endif /* VERBOSE_ERRORS */ +}; + +const char *_errlog_extended[] = { +#ifdef VERBOSE_ERRORS + "Not present\n\t\ +Sad, the RTAS call didn't return an extended error log.", + "Present\n\t\ +The extended log is present and hopefully it contains a lot of\n\t\ +useful information, which leads to the solution of the problem." +#else + "Not present", + "Present" +#endif /* VERBOSE_ERRORS */ +}; + +const char *_errlog_initiator[] = { + "Unknown or not applicable", + "CPU", + "PCI", + "ISA", + "Memory", + "Power management" +}; + +const char *_errlog_target[] = { + "Unknown or not applicable", + "CPU", + "PCI", + "ISA", + "Memory", + "Power management" +}; + int n = 0; + + if (status == -1) { + n += sprintf ( buffer+n, KERN_WARNING "event-scan: " + "Unable to get errors. Do you a " + "favor and throw this box away\n"); + return n; + } + n += sprintf ( buffer+n, KERN_INFO "event-scan: " + "Error Log version %u\n", log->version); + + switch (log->disposition) { + case DISP_FULLY_RECOVERED: + /* there was an error, but everything is fine now */ + break; + case DISP_NOT_RECOVERED: + n += sprintf (buffer+n, KERN_WARNING "event-scan: " + "We have a really serious problem!\n"); + case DISP_LIMITED_RECOVERY: + n += sprintf (buffer+n, KERN_WARNING "event-scan: " + "Error classification\n"); + n += sprintf (buffer+n, KERN_WARNING "event-scan: " + "Severity : %s\n", + rtas_errorlog_check_severity (log)); + n += sprintf (buffer+n, KERN_WARNING "event-scan: " + "Initiator : %s\n", + rtas_errorlog_check_initiator (log)); + n += sprintf (buffer+n, KERN_WARNING "event-scan: " + "Target : %s\n", + rtas_errorlog_check_target (log)); + n += sprintf (buffer+n, KERN_WARNING "event-scan: " + "Type : %s\n", + rtas_errorlog_check_type (log)); + n += sprintf (buffer+n, KERN_WARNING "event-scan: " + "Ext. log : %s\n", + rtas_errorlog_check_extended (log)); + if (log->extended) + rtas_errorlog_disect_extended (log); + break; + default: + /* nothing */ + break; + } + return n; +} + +/* ****************************************************************** */ + +static const char * +rtas_errorlog_check_type (struct rtas_error_log *log) +{ + const char *_errlog_type[] = { + "unknown type", + "too many tries failed", + "TCE error", + "RTAS device failed", + "target timed out", + "parity error on data", /* 5 */ + "parity error on address", + "parity error on external cache", + "access to invalid address", + "uncorrectable ECC error", + "corrected ECC error" /* 10 */ + }; + if (log->type == TYPE_EPOW) + return "EPOW"; + if (log->type >= TYPE_PMGM_POWER_SW_ON) + return "PowerMGM Event (not handled right now)"; + return _errlog_type[log->type]; +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/rtas-proc.c linux.ac/arch/ppc64/kernel/rtas-proc.c --- linux.vanilla/arch/ppc64/kernel/rtas-proc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/rtas-proc.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,795 @@ +/* + * arch/ppc64/kernel/rtas-proc.c + * Copyright (C) 2000 Tilmann Bitterberg + * (tilmann@bitterberg.de) + * + * RTAS (Runtime Abstraction Services) stuff + * Intention is to provide a clean user interface + * to use the RTAS. + * + * TODO: + * Split off a header file and maybe move it to a different + * location. Write Documentation on what the /proc/rtas/ entries + * actually do. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include /* for ppc_md */ +#include + +/* Token for Sensors */ +#define KEY_SWITCH 0x0001 +#define ENCLOSURE_SWITCH 0x0002 +#define THERMAL_SENSOR 0x0003 +#define LID_STATUS 0x0004 +#define POWER_SOURCE 0x0005 +#define BATTERY_VOLTAGE 0x0006 +#define BATTERY_REMAINING 0x0007 +#define BATTERY_PERCENTAGE 0x0008 +#define EPOW_SENSOR 0x0009 +#define BATTERY_CYCLESTATE 0x000a +#define BATTERY_CHARGING 0x000b + +/* IBM specific sensors */ +#define IBM_SURVEILLANCE 0x2328 /* 9000 */ +#define IBM_FANRPM 0x2329 /* 9001 */ +#define IBM_VOLTAGE 0x232a /* 9002 */ +#define IBM_DRCONNECTOR 0x232b /* 9003 */ +#define IBM_POWERSUPPLY 0x232c /* 9004 */ +#define IBM_INTQUEUE 0x232d /* 9005 */ + +/* Status return values */ +#define SENSOR_CRITICAL_HIGH 13 +#define SENSOR_WARNING_HIGH 12 +#define SENSOR_NORMAL 11 +#define SENSOR_WARNING_LOW 10 +#define SENSOR_CRITICAL_LOW 9 +#define SENSOR_SUCCESS 0 +#define SENSOR_HW_ERROR -1 +#define SENSOR_BUSY -2 +#define SENSOR_NOT_EXIST -3 +#define SENSOR_DR_ENTITY -9000 + +/* Location Codes */ +#define LOC_SCSI_DEV_ADDR 'A' +#define LOC_SCSI_DEV_LOC 'B' +#define LOC_CPU 'C' +#define LOC_DISKETTE 'D' +#define LOC_ETHERNET 'E' +#define LOC_FAN 'F' +#define LOC_GRAPHICS 'G' +/* reserved / not used 'H' */ +#define LOC_IO_ADAPTER 'I' +/* reserved / not used 'J' */ +#define LOC_KEYBOARD 'K' +#define LOC_LCD 'L' +#define LOC_MEMORY 'M' +#define LOC_NV_MEMORY 'N' +#define LOC_MOUSE 'O' +#define LOC_PLANAR 'P' +#define LOC_OTHER_IO 'Q' +#define LOC_PARALLEL 'R' +#define LOC_SERIAL 'S' +#define LOC_DEAD_RING 'T' +#define LOC_RACKMOUNTED 'U' /* for _u_nit is rack mounted */ +#define LOC_VOLTAGE 'V' +#define LOC_SWITCH_ADAPTER 'W' +#define LOC_OTHER 'X' +#define LOC_FIRMWARE 'Y' +#define LOC_SCSI 'Z' + +/* Tokens for indicators */ +#define TONE_FREQUENCY 0x0001 /* 0 - 1000 (HZ)*/ +#define TONE_VOLUME 0x0002 /* 0 - 100 (%) */ +#define SYSTEM_POWER_STATE 0x0003 +#define WARNING_LIGHT 0x0004 +#define DISK_ACTIVITY_LIGHT 0x0005 +#define HEX_DISPLAY_UNIT 0x0006 +#define BATTERY_WARNING_TIME 0x0007 +#define CONDITION_CYCLE_REQUEST 0x0008 +#define SURVEILLANCE_INDICATOR 0x2328 /* 9000 */ +#define DR_ACTION 0x2329 /* 9001 */ +#define DR_INDICATOR 0x232a /* 9002 */ +/* 9003 - 9004: Vendor specific */ +#define GLOBAL_INTERRUPT_QUEUE 0x232d /* 9005 */ +/* 9006 - 9999: Vendor specific */ + +/* other */ +#define MAX_SENSORS 17 /* I only know of 17 sensors */ +#define MAX_LINELENGTH 256 +#define SENSOR_PREFIX "ibm,sensor-" +#define cel_to_fahr(x) ((x*9/5)+32) + + +/* Globals */ +static struct proc_dir_entry *proc_rtas; +static struct rtas_sensors sensors; +static struct device_node *rtas_node; +static unsigned long power_on_time = 0; /* Save the time the user set */ +static char progress_led[MAX_LINELENGTH]; + +static unsigned long rtas_tone_frequency = 1000; +static unsigned long rtas_tone_volume = 0; + +/* ****************STRUCTS******************************************* */ +struct individual_sensor { + unsigned int token; + unsigned int quant; +}; + +struct rtas_sensors { + struct individual_sensor sensor[MAX_SENSORS]; + unsigned int quant; +}; + +/* ****************************************************************** */ +/* Declarations */ +static int ppc_rtas_sensor_read(char * buf, char ** start, off_t off, + int count, int *eof, void *data); +static ssize_t ppc_rtas_clock_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_clock_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_progress_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_progress_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_poweron_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_poweron_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); + +static ssize_t ppc_rtas_tone_freq_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_tone_freq_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_tone_volume_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_tone_volume_read(struct file * file, char * buf, + size_t count, loff_t *ppos); + +struct file_operations ppc_rtas_poweron_operations = { + read: ppc_rtas_poweron_read, + write: ppc_rtas_poweron_write +}; +struct file_operations ppc_rtas_progress_operations = { + read: ppc_rtas_progress_read, + write: ppc_rtas_progress_write +}; + +struct file_operations ppc_rtas_clock_operations = { + read: ppc_rtas_clock_read, + write: ppc_rtas_clock_write +}; + +struct file_operations ppc_rtas_tone_freq_operations = { + read: ppc_rtas_tone_freq_read, + write: ppc_rtas_tone_freq_write +}; +struct file_operations ppc_rtas_tone_volume_operations = { + read: ppc_rtas_tone_volume_read, + write: ppc_rtas_tone_volume_write +}; + +int ppc_rtas_find_all_sensors (void); +int ppc_rtas_process_sensor(struct individual_sensor s, int state, + int error, char * buf); +char * ppc_rtas_process_error(int error); +int get_location_code(struct individual_sensor s, char * buf); +int check_location_string (char *c, char * buf); +int check_location (char *c, int idx, char * buf); + +/* ****************************************************************** */ +/* MAIN */ +/* ****************************************************************** */ +void proc_rtas_init(void) +{ + struct proc_dir_entry *entry; + + rtas_node = find_devices("rtas"); + if ((rtas_node == 0) || (_machine != _MACH_chrp)) { + return; + } + + proc_rtas = proc_mkdir("rtas", 0); + if (proc_rtas == 0) + return; + + /* /proc/rtas entries */ + + entry = create_proc_entry("progress", S_IRUGO|S_IWUSR, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_progress_operations; + + entry = create_proc_entry("clock", S_IRUGO|S_IWUSR, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_clock_operations; + + entry = create_proc_entry("poweron", S_IWUSR|S_IRUGO, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_poweron_operations; + + create_proc_read_entry("sensors", S_IRUGO, proc_rtas, + ppc_rtas_sensor_read, NULL); + + entry = create_proc_entry("frequency", S_IWUSR|S_IRUGO, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_tone_freq_operations; + + entry = create_proc_entry("volume", S_IWUSR|S_IRUGO, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_tone_volume_operations; +} + +/* ****************************************************************** */ +/* POWER-ON-TIME */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_poweron_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + struct rtc_time tm; + unsigned long nowtime; + char *dest; + int error; + + nowtime = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_poweron_write: Invalid time\n"); + return count; + } + power_on_time = nowtime; /* save the time */ + + to_tm(nowtime, &tm); + + error = call_rtas("set-time-for-power-on", 7, 1, NULL, + tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, 0 /* nano */); + if (error != 0) + printk(KERN_WARNING "error: setting poweron time returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_poweron_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n; + if (power_on_time == 0) + n = sprintf(buf, "Power on time not set\n"); + else + n = sprintf(buf, "%lu\n", power_on_time); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} + +/* ****************************************************************** */ +/* PROGRESS */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_progress_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + unsigned long hex; + + strcpy(progress_led, buf); /* save the string */ + /* Lets see if the user passed hexdigits */ + hex = simple_strtoul(buf, NULL, 10); + + ppc_md.progress ((char *)buf, hex); + return count; + + /* clear the line */ /* ppc_md.progress(" ", 0xffff);*/ +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_progress_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n = 0; + if (progress_led != NULL) + n = sprintf (buf, "%s\n", progress_led); + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} + +/* ****************************************************************** */ +/* CLOCK */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_clock_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + struct rtc_time tm; + unsigned long nowtime; + char *dest; + int error; + + nowtime = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_clock_write: Invalid time\n"); + return count; + } + + to_tm(nowtime, &tm); + error = call_rtas("set-time-of-day", 7, 1, NULL, + tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, 0); + if (error != 0) + printk(KERN_WARNING "error: setting the clock returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_clock_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + unsigned int year, mon, day, hour, min, sec; + unsigned long *ret = kmalloc(4*8, GFP_KERNEL); + int n, error; + + error = call_rtas("get-time-of-day", 0, 8, ret); + + year = ret[0]; mon = ret[1]; day = ret[2]; + hour = ret[3]; min = ret[4]; sec = ret[5]; + + if (error != 0){ + printk(KERN_WARNING "error: reading the clock returned: %s\n", + ppc_rtas_process_error(error)); + n = sprintf (buf, "0"); + } else { + n = sprintf (buf, "%lu\n", mktime(year, mon, day, hour, min, sec)); + } + kfree(ret); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} + +/* ****************************************************************** */ +/* SENSOR STUFF */ +/* ****************************************************************** */ +static int ppc_rtas_sensor_read(char * buf, char ** start, off_t off, + int count, int *eof, void *data) +{ + int i,j,n; + unsigned long ret; + int state, error; + char *buffer; + + if (count < 0) + return -EINVAL; + + /* May not be enough */ + buffer = kmalloc(buffer, MAX_LINELENGTH*MAX_SENSORS); + + if (!buffer) + return -ENOMEM; + + memset(buffer, 0, MAX_LINELENGTH*MAX_SENSORS); + + n = sprintf ( buffer , "RTAS (RunTime Abstraction Services) Sensor Information\n"); + n += sprintf ( buffer+n, "Sensor\t\tValue\t\tCondition\tLocation\n"); + n += sprintf ( buffer+n, "********************************************************\n"); + + if (ppc_rtas_find_all_sensors() != 0) { + n += sprintf ( buffer+n, "\nNo sensors are available\n"); + goto return_string; + } + + for (i=0; i= 0) { + error = call_rtas("get-sensor-state", 2, 2, &ret, + sensors.sensor[i].token, sensors.sensor[i].quant-j); + state = (int) ret; + n += ppc_rtas_process_sensor(sensors.sensor[i], state, error, buffer+n ); + n += sprintf (buffer+n, "\n"); + j--; + } /* while */ + } /* for */ + +return_string: + if (off >= strlen(buffer)) { + *eof = 1; + kfree(buffer); + return 0; + } + if (n > strlen(buffer) - off) + n = strlen(buffer) - off; + if (n > count) + n = count; + else + *eof = 1; + memcpy(buf, buffer + off, n); + *start = buf; + kfree(buffer); + return n; +} + +/* ****************************************************************** */ + +int ppc_rtas_find_all_sensors (void) +{ + unsigned long *utmp; + int len, i, j; + + utmp = (unsigned long *) get_property(rtas_node, "rtas-sensors", &len); + if (utmp == NULL) { + printk (KERN_ERR "error: could not get rtas-sensors\n"); + return 1; + } + + sensors.quant = len / 8; /* int + int */ + + for (i=0, j=0; j= llen) pos=0; + } + return n; +} +/* ****************************************************************** */ +/* INDICATORS - Tone Frequency */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_freq_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + unsigned long freq; + char *dest; + int error; + freq = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_tone_freq_write: Invalid tone freqency\n"); + return count; + } + if (freq < 0) freq = 0; + rtas_tone_frequency = freq; /* save it for later */ + error = call_rtas("set-indicator", 3, 1, NULL, + TONE_FREQUENCY, 0, freq); + if (error != 0) + printk(KERN_WARNING "error: setting tone frequency returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_freq_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n; + n = sprintf(buf, "%lu\n", rtas_tone_frequency); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} +/* ****************************************************************** */ +/* INDICATORS - Tone Volume */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_volume_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + unsigned long volume; + char *dest; + int error; + volume = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_tone_volume_write: Invalid tone volume\n"); + return count; + } + if (volume < 0) volume = 0; + if (volume > 100) volume = 100; + + rtas_tone_volume = volume; /* save it for later */ + error = call_rtas("set-indicator", 3, 1, NULL, + TONE_VOLUME, 0, volume); + if (error != 0) + printk(KERN_WARNING "error: setting tone volume returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_volume_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n; + n = sprintf(buf, "%lu\n", rtas_tone_volume); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/rtas.c linux.ac/arch/ppc64/kernel/rtas.c --- linux.vanilla/arch/ppc64/kernel/rtas.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/rtas.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,211 @@ +/* + * + * Procedures for interfacing to the RTAS on CHRP machines. + * + * Peter Bergner, IBM March 2001. + * Copyright (C) 2001 IBM. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * prom_init() is called very early on, before the kernel text + * and data have been mapped to KERNELBASE. At this point the code + * is running at whatever address it has been loaded at, so + * references to extern and static variables must be relocated + * explicitly. The procedure reloc_offset() returns the address + * we're currently running at minus the address we were linked at. + * (Note that strings count as static variables.) + * + * Because OF may have mapped I/O devices into the area starting at + * KERNELBASE, particularly on CHRP machines, we can't safely call + * OF once the kernel has been mapped to KERNELBASE. Therefore all + * OF calls should be done within prom_init(), and prom_init() + * and all routines called within it must be careful to relocate + * references as necessary. + * + * Note that the bss is cleared *after* prom_init runs, so we have + * to make sure that any static or extern variables it accesses + * are put in the data segment. + */ + + +struct rtas_t rtas = { 0, 0, 0, SPIN_LOCK_UNLOCKED, NULL, {0, 0, NULL}}; + +extern unsigned long reloc_offset(void); + +void +phys_call_rtas(int token, int nargs, int nret, ...) +{ + va_list list; + unsigned long offset = reloc_offset(); + struct rtas_args *rtas = PTRRELOC(&(get_paca()->xRtas)); + int i; + + rtas->token = token; + rtas->nargs = nargs; + rtas->nret = nret; + rtas->rets = (rtas_arg_t *)PTRRELOC(&(rtas->args[nargs])); + + va_start(list, nret); + for (i = 0; i < nargs; i++) + rtas->args[i] = (rtas_arg_t)LONG_LSW(va_arg(list, ulong)); + va_end(list); + + enter_rtas(rtas); +} + +void +phys_call_rtas_display_status(char c) +{ + unsigned long offset = reloc_offset(); + struct rtas_args *rtas = PTRRELOC(&(get_paca()->xRtas)); + + rtas->token = 10; + rtas->nargs = 1; + rtas->nret = 1; + rtas->rets = (rtas_arg_t *)PTRRELOC(&(rtas->args[1])); + rtas->args[0] = (int)c; + + enter_rtas(rtas); +} + +void +call_rtas_display_status(char c) +{ + struct rtas_args *rtas = &(get_paca()->xRtas); + + rtas->token = 10; + rtas->nargs = 1; + rtas->nret = 1; + rtas->rets = (rtas_arg_t *)&(rtas->args[1]); + rtas->args[0] = (int)c; + + enter_rtas((void *)__pa((unsigned long)rtas)); +} + + +#if 0 +#define DEBUG_RTAS +#endif +__openfirmware +long +call_rtas(const char *service, int nargs, int nret, + unsigned long *outputs, ...) +{ + va_list list; + int i; + unsigned long s; + struct rtas_args *rtas_args = &(get_paca()->xRtas); + int *tokp; + +#ifdef DEBUG_RTAS + udbg_printf("Entering call_rtas\n"); + udbg_printf("\tservice = '%s'\n", service); + udbg_printf("\tnargs = %d\n", nargs); + udbg_printf("\tnret = %d\n", nret); + udbg_printf("\t&outputs = 0x%lx\n", outputs); +#endif /* DEBUG_RTAS */ + + if (rtas.dev == NULL) { +#ifdef DEBUG_RTAS + udbg_printf("\tNo rtas device in device-tree...\n"); +#endif /* DEBUG_RTAS */ + return -1; + } + tokp = (int *) get_property(rtas.dev, service, NULL); +#ifdef DEBUG_RTAS + udbg_printf("\ttokp = 0x%lx\n", tokp); + udbg_printf("\ttok = 0x%lx\n", (tokp) ? *tokp : -1); +#endif /* DEBUG_RTAS */ + if (tokp == NULL) { + printk(KERN_ERR "No RTAS service called %s\n", service); + return -1; + } + rtas_args->token = *tokp; + rtas_args->nargs = nargs; + rtas_args->nret = nret; + rtas_args->rets = (rtas_arg_t *)&(rtas_args->args[nargs]); + va_start(list, outputs); + for (i = 0; i < nargs; ++i) { + rtas_args->args[i] = (rtas_arg_t)LONG_LSW(va_arg(list, ulong)); +#ifdef DEBUG_RTAS + udbg_printf("\tnarg[%d] = 0x%lx\n", i, rtas_args->args[i]); +#endif /* DEBUG_RTAS */ + } + va_end(list); + + for (i = 0; i < nret; ++i) + rtas_args->rets[i] = 0; + +#if 0 /* Gotta do something different here, use global lock for now... */ + spin_lock_irqsave(&rtas_args->lock, s); +#else + spin_lock_irqsave(&rtas.lock, s); +#endif +#ifdef DEBUG_RTAS + udbg_printf("\tentering rtas with 0x%lx\n", (void *)__pa((unsigned long)rtas_args)); +#endif /* DEBUG_RTAS */ + enter_rtas((void *)__pa((unsigned long)rtas_args)); +#ifdef DEBUG_RTAS + udbg_printf("\treturned from rtas ...\n"); +#endif /* DEBUG_RTAS */ +#if 0 /* Gotta do something different here, use global lock for now... */ + spin_unlock_irqrestore(&rtas_args->lock, s); +#else + spin_unlock_irqrestore(&rtas.lock, s); +#endif +#ifdef DEBUG_RTAS + for(i=0; i < nret ;i++) + udbg_printf("\tnret[%d] = 0x%lx\n", i, (ulong)rtas_args->rets[i]); +#endif /* DEBUG_RTAS */ + + if (nret > 1 && outputs != NULL) + for (i = 0; i < nret-1; ++i) + outputs[i] = rtas_args->rets[i+1]; + return (ulong)((nret > 0) ? rtas_args->rets[0] : 0); +} + + +void __chrp +rtas_restart(char *cmd) +{ + printk("RTAS system-reboot returned %d\n", + call_rtas("system-reboot", 0, 1, NULL)); + for (;;); +} + +void __chrp +rtas_power_off(void) +{ + /* allow power on only with power button press */ + printk("RTAS power-off returned %d\n", + call_rtas("power-off", 2, 1, NULL,0xffffffff,0xffffffff)); + for (;;); +} + +void __chrp +rtas_halt(void) +{ + rtas_power_off(); +} + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/semaphore.c linux.ac/arch/ppc64/kernel/semaphore.c --- linux.vanilla/arch/ppc64/kernel/semaphore.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/semaphore.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,130 @@ +/* + * + * + * PowerPC-specific semaphore code. + * + * Copyright (C) 1999 Cort Dougan + * + * 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. + * + * April 2001 - Reworked by Paul Mackerras + * to eliminate the SMP races in the old version between the updates + * of `count' and `waking'. Now we use negative `count' values to + * indicate that some process(es) are waiting for the semaphore. + */ + +#include +#include +#include + +/* + * Atomically update sem->count. + * This does the equivalent of the following: + * + * old_count = sem->count; + * tmp = MAX(old_count, 0) + incr; + * sem->count = tmp; + * return old_count; + */ +static inline int __sem_update_count(struct semaphore *sem, int incr) +{ + int old_count, tmp; + + __asm__ __volatile__("\n" +"1: lwarx %0,0,%3\n" +" srawi %1,%0,31\n" +" andc %1,%0,%1\n" +" add %1,%1,%4\n" +" stwcx. %1,0,%3\n" +" bne 1b" + : "=&r" (old_count), "=&r" (tmp), "=m" (sem->count) + : "r" (&sem->count), "r" (incr), "m" (sem->count) + : "cc"); + + return old_count; +} + +void __up(struct semaphore *sem) +{ + /* + * Note that we incremented count in up() before we came here, + * but that was ineffective since the result was <= 0, and + * any negative value of count is equivalent to 0. + * This ends up setting count to 1, unless count is now > 0 + * (i.e. because some other cpu has called up() in the meantime), + * in which case we just increment count. + */ + __sem_update_count(sem, 1); + wake_up(&sem->wait); +} + +/* + * Note that when we come in to __down or __down_interruptible, + * we have already decremented count, but that decrement was + * ineffective since the result was < 0, and any negative value + * of count is equivalent to 0. + * Thus it is only when we decrement count from some value > 0 + * that we have actually got the semaphore. + */ +void __down(struct semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + tsk->state = TASK_UNINTERRUPTIBLE; + add_wait_queue_exclusive(&sem->wait, &wait); + smp_wmb(); + + /* + * Try to get the semaphore. If the count is > 0, then we've + * got the semaphore; we decrement count and exit the loop. + * If the count is 0 or negative, we set it to -1, indicating + * that we are asleep, and then sleep. + */ + while (__sem_update_count(sem, -1) <= 0) { + schedule(); + tsk->state = TASK_UNINTERRUPTIBLE; + } + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + + /* + * If there are any more sleepers, wake one of them up so + * that it can either get the semaphore, or set count to -1 + * indicating that there are still processes sleeping. + */ + wake_up(&sem->wait); +} + +int __down_interruptible(struct semaphore * sem) +{ + int retval = 0; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + tsk->state = TASK_INTERRUPTIBLE; + add_wait_queue_exclusive(&sem->wait, &wait); + smp_wmb(); + + while (__sem_update_count(sem, -1) <= 0) { + if (signal_pending(current)) { + /* + * A signal is pending - give up trying. + * Set sem->count to 0 if it is negative, + * since we are no longer sleeping. + */ + __sem_update_count(sem, 0); + retval = -EINTR; + break; + } + schedule(); + tsk->state = TASK_INTERRUPTIBLE; + } + tsk->state = TASK_RUNNING; + remove_wait_queue(&sem->wait, &wait); + wake_up(&sem->wait); + return retval; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/setup.c linux.ac/arch/ppc64/kernel/setup.c --- linux.vanilla/arch/ppc64/kernel/setup.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/setup.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,723 @@ +/* + * + * Common prep/pmac/chrp boot and setup code. + * + * Copyright (C) 2001 PPC64 Team, 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern unsigned long klimit; +/* extern void *stab; */ +extern HTAB htab_data; +extern unsigned long loops_per_jiffy; + +int have_of = 1; + +extern void chrp_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); + +extern void chrp_init_map_io_space( void ); +extern void iSeries_init( void ); +extern void iSeries_init_early( void ); +extern void mm_init_ppc64( void ); + +unsigned long decr_overclock = 1; +unsigned long decr_overclock_proc0 = 1; +unsigned long decr_overclock_set = 0; +unsigned long decr_overclock_proc0_set = 0; + +#ifdef CONFIG_XMON +extern void xmon_map_scc(void); +#endif + +char saved_command_line[256]; +unsigned char aux_device_present; +struct int_control_struct int_control = +{ + __no_use_cli, + __no_use_sti, + __no_use_restore_flags, + __no_use_save_flags +}; +struct ide_machdep_calls ppc_ide_md; +int parse_bootinfo(void); + +void parse_cmd_line(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7); + +unsigned long DMA_MODE_READ, DMA_MODE_WRITE; +int _machine = _MACH_chrp; /* DRENG prom.c needs this assumption, a better way needs to be found. */ + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned long SYSRQ_KEY; +#endif /* CONFIG_MAGIC_SYSRQ */ + +struct machdep_calls ppc_md; +struct Naca *naca; + +/* + * Perhaps we can put the pmac screen_info[] here + * on pmac as well so we don't need the ifdef's. + * Until we get multiple-console support in here + * that is. -- Cort + * Maybe tie it to serial consoles, since this is really what + * these processors use on existing boards. -- Dan + */ +struct screen_info screen_info = { + 0, 25, /* orig-x, orig-y */ + 0, /* unused */ + 0, /* orig-video-page */ + 0, /* orig-video-mode */ + 80, /* orig-video-cols */ + 0,0,0, /* ega_ax, ega_bx, ega_cx */ + 25, /* orig-video-lines */ + 1, /* orig-video-isVGA */ + 16 /* orig-video-points */ +}; + +/* + * These are used in binfmt_elf.c to put aux entries on the stack + * for each elf executable being started. + */ +int dcache_bsize; +int icache_bsize; +int ucache_bsize; + +/* + * Initialize the PPCDBG state. Called before relocation has been enabled. + */ +void ppcdbg_initialize(void) { + unsigned long offset = reloc_offset(); + struct Naca *_naca = RELOC(naca); + + _naca->debug_switch = PPC_DEBUG_DEFAULT; +} + +void naca_init(void) { + naca->xRamDisk = NULL; + naca->xRamDiskSize = 0; /* in pages */ +} + +/* + * Initialize a set of PACA's, one for each processor. + * + * At this point, relocation is on, but we have not done any other + * setup of the mm subsystem. + */ +void paca_init(void) { +#if 0 + int processorCount = naca->processorCount, i; + struct Paca *paca[]; + + /* Put the array of paca's on a page boundary & allocate 1/2 page of */ + /* storage for each. */ + klimit += (PAGE_SIZE-1) & PAGE_MASK; + naca->xPaca = paca[0] = klimit; + klimit += ((PAGE_SIZE>>1) * processorCount); + + for(i=0; ixPacaIndex = i; + } +#endif +} + +/* + * Determine the type of system this we are running on. + * For the PPC64 kernel, we assume chrp. + */ +void identify_machine(void) { + if ( itLpNaca.xLparInstalled == 1 ) + _machine = _MACH_iSeries; + else + _machine = _MACH_chrp; +} + +/* + * Do some initial setup of the system. The paramters are those which + * were passed in from the bootloader. + */ +void setup_system(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) { + + identify_machine(); + + switch (_machine) { + case _MACH_chrp: + parse_bootinfo(); + chrp_init_map_io_space(); + break; + case _MACH_iSeries: + iSeries_init_early(); + break; + default: + } + + udbg_init(); + + udbg_puts("\n-----------------------------------------------------\n"); + udbg_puts("Naca Info...\n\n"); + udbg_puts("naca = 0x"); + udbg_puthex((unsigned long)naca); + udbg_putc('\n'); + + udbg_puts("naca->processorCount = 0x"); + udbg_puthex(naca->processorCount); + udbg_putc('\n'); + + udbg_puts("naca->physicalMemorySize = 0x"); + udbg_puthex(naca->physicalMemorySize); + udbg_putc('\n'); + + udbg_puts("naca->dCacheL1LineSize = 0x"); + udbg_puthex(naca->dCacheL1LineSize); + udbg_putc('\n'); + + udbg_puts("naca->dCacheL1LogLineSize = 0x"); + udbg_puthex(naca->dCacheL1LogLineSize); + udbg_putc('\n'); + + udbg_puts("naca->dCacheL1LinesPerPage = 0x"); + udbg_puthex(naca->dCacheL1LinesPerPage); + udbg_putc('\n'); + + udbg_puts("naca->iCacheL1LineSize = 0x"); + udbg_puthex(naca->iCacheL1LineSize); + udbg_putc('\n'); + + udbg_puts("naca->iCacheL1LogLineSize = 0x"); + udbg_puthex(naca->iCacheL1LogLineSize); + udbg_putc('\n'); + + udbg_puts("naca->iCacheL1LinesPerPage = 0x"); + udbg_puthex(naca->iCacheL1LinesPerPage); + udbg_putc('\n'); + + udbg_puts("naca->serialPortAddr = 0x"); + udbg_puthex(naca->serialPortAddr); + udbg_putc('\n'); + + udbg_puts("naca->interrupt_controller = 0x"); + udbg_puthex(naca->interrupt_controller); + udbg_putc('\n'); + + udbg_printf("\nHTAB Info ...\n\n"); + udbg_printf("htab_data.htab = 0x%lx\n", htab_data.htab); + udbg_printf("htab_data.num_ptegs = 0x%lx\n", htab_data.htab_num_ptegs); + + udbg_puts("\n-----------------------------------------------------\n"); + + + if ( _machine == _MACH_chrp ) { + finish_device_tree(); + chrp_init(r3, r4, r5, r6, r7); + } + + mm_init_ppc64(); + + switch (_machine) { + case _MACH_chrp: // DRENG _MACH_chrp needs to be _MACH_pSeries now + // The following relies on the device tree being fully configured. + parse_cmd_line(r3, r4, r5, r6, r7); + + break; + case _MACH_iSeries: + iSeries_init(); + break; + default: + // Need a way to die here-prob have to display something to the panel DRENG + } +} + +/* + * I really need to add multiple-console support... -- Cort + */ +int __init pmac_display_supported(char *name) +{ + return 0; +} +void __init pmac_find_display(void) +{ +} + +void machine_restart(char *cmd) +{ + ppc_md.restart(cmd); +} + +void machine_power_off(void) +{ + ppc_md.power_off(); +} + +void machine_halt(void) +{ + ppc_md.halt(); +} + +/* + * get_cpuinfo - Get information on one CPU for use by procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. + */ + +int get_cpuinfo(char *buffer, unsigned *cpu_np) +{ + unsigned long len = 0; + unsigned long i; + unsigned int pvr; + unsigned short maj, min; + +#ifdef CONFIG_SMP +#define CPU_PRESENT(x) (cpu_callin_map[(x)]) +#else +#define CPU_PRESENT(x) ((x)==0) +#define smp_num_cpus 1 +#endif + + i = *cpu_np; + while (i < NR_CPUS && (cpu_online_map & (1 << i)) == 0) { + ++i; + } + if (n >= NR_CPUS) { + /* Insert summary data as a fake CPU at NR_CPUS. */ + if (n > NR_CPUS) { + *cpu_np = NR_CPUS + 1; + return (0); + } +#ifdef CONFIG_SMP + { unsigned long bogosum = 0; + + for ( i = 0; i < smp_num_cpus ; i++ ) + { + if ( !CPU_PRESENT(i) ) + continue; + bogosum += naca->loops_per_jiffy; + } + len += sprintf(buffer+len,"total bogomips\t: %lu.%02lu\n", + (bogosum+2500)/(500000/HZ), + (bogosum+2500)/(5000/HZ) % 100); + } +#endif /* CONFIG_SMP */ + if (ppc_md.get_cpuinfo != NULL) + { + len += ppc_md.get_cpuinfo(buffer+len); + } + *cpu_np = NR_CPUS; + return (len); + } + *cpu_np = i + 1; + + len += sprintf(len+buffer,"processor\t: %lu\n",i); + len += sprintf(len+buffer,"cpu\t\t: "); + + pvr = naca->paca[i].pvr; + + switch (PVR_VER(pvr)) + { + case PV_PULSAR: + len += sprintf(len+buffer, "RS64-III (pulsar)\n"); + break; + case PV_POWER4: + len += sprintf(len+buffer, "POWER4 (gp)\n"); + break; + case PV_ICESTAR: + len += sprintf(len+buffer, "RS64-III (icestar)\n"); + break; + case PV_SSTAR: + len += sprintf(len+buffer, "RS64-IV (sstar)\n"); + break; + case PV_630: + len += sprintf(len+buffer, "POWER3 (630)\n"); + break; + case PV_630p: + len += sprintf(len+buffer, "POWER3 (630+)\n"); + break; + default: + len += sprintf(len+buffer, "Unknown (%08x)\n", pvr); + break; + } + + /* + * Assume here that all clock rates are the same in a + * smp system. -- Cort + */ + if ( _machine != _MACH_iSeries ) { + struct device_node *cpu_node; + int *fp; + + cpu_node = find_type_devices("cpu"); + if ( !cpu_node ) break; + { + int s; + for ( s = 0; (s < i) && cpu_node->next ; + s++, cpu_node = cpu_node->next ) + /* nothing */ ; + } + fp = (int *) get_property(cpu_node, "clock-frequency", NULL); + if ( !fp ) break; + len += sprintf(len+buffer, "clock\t\t: %dMHz\n", + *fp / 1000000); + } + + if (ppc_md.setup_residual != NULL) + { + len += ppc_md.setup_residual(buffer + len); + } + + maj = (pvr >> 8) & 0xFF; + min = pvr & 0xFF; + + len += sprintf(len+buffer, "revision\t: %hd.%hd\n", maj, min); + + len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n\n", + ((naca->loops_per_jiffy)+2500)/(500000/HZ), + ((naca->loops_per_jiffy)+2500)/(5000/HZ) % 100); + + return len; +} + +/* + * Fetch the cmd_line from open firmware. */ +void parse_cmd_line(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) { + struct device_node *chosen; + char *p; + +#ifdef CONFIG_BLK_DEV_INITRD + if (r3 && r4 && r4 != 0xdeadbeef) + { + if (r3 < KERNELBASE) + r3 += KERNELBASE; + initrd_start = r3; + initrd_end = r3 + r4; + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + initrd_below_start_ok = 1; + } +#endif + + cmd_line[0] = 0; + chosen = find_devices("chosen"); + if (chosen != NULL) { + p = get_property(chosen, "bootargs", NULL); + if (p != NULL) + strncpy(cmd_line, p, sizeof(cmd_line)); + } + cmd_line[sizeof(cmd_line) - 1] = 0; + + /* Look for mem= option on command line */ + if (strstr(cmd_line, "mem=")) { + char *p, *q; + unsigned long maxmem = 0; + extern unsigned long __max_memory; + + for (q = cmd_line; (p = strstr(q, "mem=")) != 0; ) { + q = p + 4; + if (p > cmd_line && p[-1] != ' ') + continue; + maxmem = simple_strtoul(q, &q, 0); + if (*q == 'k' || *q == 'K') { + maxmem <<= 10; + ++q; + } else if (*q == 'm' || *q == 'M') { + maxmem <<= 20; + ++q; + } + } + __max_memory = maxmem; + } + ppc_md.progress("id mach: done", 0x200); +} + +int parse_bootinfo(void) +{ +#if 0 + /* DRENG this stuff is needs to be completly redone in the context of + * yaboot. Remove for now - I think we can wait on this for some time. + */ + struct bi_record *rec; + extern char __bss_start[]; + extern char *sysmap; + extern unsigned long sysmap_size; + + rec = (struct bi_record *)_ALIGN((ulong)__bss_start+(1<<20)-1,(1<<20)); + if ( rec->tag != BI_FIRST ) + { + /* + * This 0x10000 offset is a terrible hack but it will go away when + * we have the bootloader handle all the relocation and + * prom calls -- Cort + */ + rec = (struct bi_record *)_ALIGN((ulong)__bss_start+0x10000+(1<<20)-1,(1<<20)); + if ( rec->tag != BI_FIRST ) + return -1; + } + for ( ; rec->tag != BI_LAST ; + rec = (struct bi_record *)((ulong)rec + rec->size) ) + { + ulong *data = rec->data; + switch (rec->tag) + { + case BI_CMD_LINE: + memcpy(cmd_line, (void *)data, rec->size); + break; + case BI_SYSMAP: + sysmap = (char *)((data[0] >= (KERNELBASE)) ? data[0] : + (data[0]+KERNELBASE)); + sysmap_size = data[1]; + break; +#ifdef CONFIG_BLK_DEV_INITRD + case BI_INITRD: + initrd_start = data[0]; + initrd_end = data[0] + rec->size; + break; +#endif /* CONFIG_BLK_DEV_INITRD */ + } + } +#endif + return 0; +} + +void __init ppc_init(void) +{ + /* clear the progress line */ + ppc_md.progress(" ", 0xffff); + + if (ppc_md.init != NULL) { + ppc_md.init(); + } +} + +/* + * Called into from start_kernel, after lock_kernel has been called. + * Initializes bootmem, which is unsed to manage page allocation until + * mem_init is called. + */ +void __init setup_arch(char **cmdline_p) +{ + extern int panic_timeout; + extern char _etext[], _edata[]; + extern void do_init_bootmem(void); + +#ifdef CONFIG_XMON + xmon_map_scc(); + if (strstr(cmd_line, "xmon")) + xmon(0); +#endif /* CONFIG_XMON */ +#ifdef CONFIG_KDB + xmon_map_scc(); /* in kdb/start.c --need to rename TAI */ +#endif + ppc_md.progress("setup_arch:enter", 0x3eab); + +#if defined(CONFIG_KGDB) + kgdb_map_scc(); + set_debug_traps(); + breakpoint(); +#endif + /* + * Set cache line size based on type of cpu as a default. + * Systems with OF can look in the properties on the cpu node(s) + * for a possibly more accurate value. + */ + dcache_bsize = naca->dCacheL1LineSize; + icache_bsize = naca->iCacheL1LineSize; + + /* reboot on panic */ + panic_timeout = 180; + + init_mm.start_code = PAGE_OFFSET; + init_mm.end_code = (unsigned long) _etext; + init_mm.end_data = (unsigned long) _edata; + init_mm.brk = (unsigned long) klimit; + + /* Save unparsed command line copy for /proc/cmdline */ + strcpy(saved_command_line, cmd_line); + *cmdline_p = cmd_line; + + /* set up the bootmem stuff with available memory */ + do_init_bootmem(); + ppc_md.progress("setup_arch:bootmem", 0x3eab); + + ppc_md.setup_arch(); + + paging_init(); + ppc_md.progress("setup_arch: exit", 0x3eab); +} + +void ppc_generic_ide_fix_driveid(struct hd_driveid *id) +{ + int i; + unsigned short *stringcast; + + id->config = __le16_to_cpu(id->config); + id->cyls = __le16_to_cpu(id->cyls); + id->reserved2 = __le16_to_cpu(id->reserved2); + id->heads = __le16_to_cpu(id->heads); + id->track_bytes = __le16_to_cpu(id->track_bytes); + id->sector_bytes = __le16_to_cpu(id->sector_bytes); + id->sectors = __le16_to_cpu(id->sectors); + id->vendor0 = __le16_to_cpu(id->vendor0); + id->vendor1 = __le16_to_cpu(id->vendor1); + id->vendor2 = __le16_to_cpu(id->vendor2); + stringcast = (unsigned short *)&id->serial_no[0]; + for (i = 0; i < (20/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + id->buf_type = __le16_to_cpu(id->buf_type); + id->buf_size = __le16_to_cpu(id->buf_size); + id->ecc_bytes = __le16_to_cpu(id->ecc_bytes); + stringcast = (unsigned short *)&id->fw_rev[0]; + for (i = 0; i < (8/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + stringcast = (unsigned short *)&id->model[0]; + for (i = 0; i < (40/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + id->dword_io = __le16_to_cpu(id->dword_io); + id->reserved50 = __le16_to_cpu(id->reserved50); + id->field_valid = __le16_to_cpu(id->field_valid); + id->cur_cyls = __le16_to_cpu(id->cur_cyls); + id->cur_heads = __le16_to_cpu(id->cur_heads); + id->cur_sectors = __le16_to_cpu(id->cur_sectors); + id->cur_capacity0 = __le16_to_cpu(id->cur_capacity0); + id->cur_capacity1 = __le16_to_cpu(id->cur_capacity1); + id->lba_capacity = __le32_to_cpu(id->lba_capacity); + id->dma_1word = __le16_to_cpu(id->dma_1word); + id->dma_mword = __le16_to_cpu(id->dma_mword); + id->eide_pio_modes = __le16_to_cpu(id->eide_pio_modes); + id->eide_dma_min = __le16_to_cpu(id->eide_dma_min); + id->eide_dma_time = __le16_to_cpu(id->eide_dma_time); + id->eide_pio = __le16_to_cpu(id->eide_pio); + id->eide_pio_iordy = __le16_to_cpu(id->eide_pio_iordy); + for (i = 0; i < 2; i++) + id->words69_70[i] = __le16_to_cpu(id->words69_70[i]); + for (i = 0; i < 4; i++) + id->words71_74[i] = __le16_to_cpu(id->words71_74[i]); + id->queue_depth = __le16_to_cpu(id->queue_depth); + for (i = 0; i < 4; i++) + id->words76_79[i] = __le16_to_cpu(id->words76_79[i]); + id->major_rev_num = __le16_to_cpu(id->major_rev_num); + id->minor_rev_num = __le16_to_cpu(id->minor_rev_num); + id->command_set_1 = __le16_to_cpu(id->command_set_1); + id->command_set_2 = __le16_to_cpu(id->command_set_2); + id->cfsse = __le16_to_cpu(id->cfsse); + id->cfs_enable_1 = __le16_to_cpu(id->cfs_enable_1); + id->cfs_enable_2 = __le16_to_cpu(id->cfs_enable_2); + id->csf_default = __le16_to_cpu(id->csf_default); + id->dma_ultra = __le16_to_cpu(id->dma_ultra); + id->word89 = __le16_to_cpu(id->word89); + id->word90 = __le16_to_cpu(id->word90); + id->CurAPMvalues = __le16_to_cpu(id->CurAPMvalues); + id->word92 = __le16_to_cpu(id->word92); + id->hw_config = __le16_to_cpu(id->hw_config); + for (i = 0; i < 32; i++) + id->words94_125[i] = __le16_to_cpu(id->words94_125[i]); + id->last_lun = __le16_to_cpu(id->last_lun); + id->word127 = __le16_to_cpu(id->word127); + id->dlf = __le16_to_cpu(id->dlf); + id->csfo = __le16_to_cpu(id->csfo); + for (i = 0; i < 26; i++) + id->words130_155[i] = __le16_to_cpu(id->words130_155[i]); + id->word156 = __le16_to_cpu(id->word156); + for (i = 0; i < 3; i++) + id->words157_159[i] = __le16_to_cpu(id->words157_159[i]); + for (i = 0; i < 96; i++) + id->words160_255[i] = __le16_to_cpu(id->words160_255[i]); +} + +void exception_trace(unsigned long trap) { + unsigned long x, srr0, srr1, reg20, reg1, reg21; + + asm("mflr %0" : "=r" (x) :); + asm("mfspr %0,0x1a" : "=r" (srr0) :); + asm("mfspr %0,0x1b" : "=r" (srr1) :); + asm("mr %0,1" : "=r" (reg1) :); + asm("mr %0,20" : "=r" (reg20) :); + asm("mr %0,21" : "=r" (reg21) :); + + udbg_puts("\n"); + udbg_puts("Took an exception : "); udbg_puthex(x); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(reg1); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(reg20); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(reg21); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(srr0); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(srr1); udbg_puts("\n"); +} + +/* This should only be called on processor 0 during calibrate decr */ +void setup_default_decr(void) +{ + struct Paca * paca = (struct Paca *)mfspr(SPRG3); + + if ( decr_overclock_set && !decr_overclock_proc0_set ) + decr_overclock_proc0 = decr_overclock; + + paca->default_decr = tb_ticks_per_jiffy / decr_overclock_proc0; + paca->next_jiffy_update_tb = get_tb() + tb_ticks_per_jiffy; +} + +int set_decr_overclock_proc0( char * str ) +{ + unsigned long val = simple_strtoul( str, NULL, 0 ); + if ( ( val >= 1 ) && ( val <= 48 ) ) { + decr_overclock_proc0_set = 1; + decr_overclock_proc0 = val; + printk("proc 0 decrementer overclock factor of %ld\n", val); + } + else + printk("invalid proc 0 decrementer overclock factor of %ld\n", val); + return 1; +} + +int set_decr_overclock( char * str ) +{ + unsigned long val = simple_strtoul( str, NULL, 0 ); + if ( ( val >= 1 ) && ( val <= 48 ) ) { + decr_overclock_set = 1; + decr_overclock = val; + printk("decrementer overclock factor of %ld\n", val); + } + else + printk("invalid decrementer overclock factor of %ld\n", val); + return 1; + +} + +__setup("decr_overclock_proc0=", set_decr_overclock_proc0 ); +__setup("decr_overclock=", set_decr_overclock ); + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/signal.c linux.ac/arch/ppc64/kernel/signal.c --- linux.vanilla/arch/ppc64/kernel/signal.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/signal.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,823 @@ +/* + * linux/arch/ppc64/kernel/signal.c + * + * + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/kernel/signal.c" + * Copyright (C) 1991, 1992 Linus Torvalds + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define GP_REGS_SIZE MIN(sizeof(elf_gregset_t), sizeof(struct pt_regs)) + +/* + * These are the flags in the MSR that the user is allowed to change + * by modifying the saved value of the MSR on the stack. SE and BE + * should not be in this list since gdb may want to change these. I.e, + * you should be able to step out of a signal handler to see what + * instruction executes next after the signal handler completes. + * Alternately, if you stepped into a signal handler, you should be + * able to continue 'til the next breakpoint from within the signal + * handler, even if the handler returns. + */ +#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) + +int do_signal(sigset_t *oldset, struct pt_regs *regs); +extern long sys_wait4(pid_t pid, unsigned int *stat_addr, + int options, /*unsigned long*/ struct rusage *ru); + +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +long sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7, + struct pt_regs *regs) +{ + sigset_t saveset; + + PPCDBG(PPCDBG_SYS64X, "sys_sigsuspend - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->gpr[3] = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + /* + * If a signal handler needs to be called, + * do_signal() has set R3 to the signal number (the + * first argument of the signal handler), so don't + * overwrite that with EINTR ! + * In the other cases, do_signal() doesn't touch + * R3, so it's still set to -EINTR (see above). + */ + return regs->gpr[3]; + } +} + +long sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, int p3, int p4, int p6, + int p7, struct pt_regs *regs) +{ + sigset_t saveset, newset; + + + PPCDBG(PPCDBG_SYS64X, "sys_rt_sigsuspend - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->gpr[3] = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + return regs->gpr[3]; + } +} + + + +asmlinkage long sys_sigaltstack(const stack_t *uss, stack_t *uoss) +{ + struct pt_regs *regs = (struct pt_regs *) &uss; + + PPCDBG(PPCDBG_SYS64X, "sys_sigaltstack - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + return do_sigaltstack(uss, uoss, regs->gpr[1]); +} + +long sys_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + PPCDBG(PPCDBG_SYS64X, "sys_sigaction - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + + + + return ret; +} + +/* + * When we have signals to deliver, we set up on the + * user stack, going down from the original stack pointer: + * a sigregs struct + * one or more sigcontext structs + * a gap of __SIGNAL_FRAMESIZE bytes + * + * Each of these things must be a multiple of 16 bytes in size. + * + * XXX ultimately we will have to stack up a siginfo and ucontext + * for each rt signal. + */ +struct sigregs { + elf_gregset_t gp_regs; + double fp_regs[ELF_NFPREG]; + unsigned int tramp[2]; + /* 64 bit API allows for 288 bytes below sp before + decrementing it. */ + int abigap[72]; +}; + + + +struct rt_sigframe +{ + unsigned long _unused[2]; + struct siginfo *pinfo; + void *puc; + struct siginfo info; + struct ucontext uc; +}; + + +/* + * When we have rt signals to deliver, we set up on the + * user stack, going down from the original stack pointer: + * a sigregs struct + * one rt_sigframe struct (siginfo + ucontext) + * a gap of __SIGNAL_FRAMESIZE bytes + * + * Each of these things must be a multiple of 16 bytes in size. + * + */ + +extern long sys32_rt_sigreturn(struct pt_regs *regs); + +int sys_rt_sigreturn(struct pt_regs *regs) +{ + struct rt_sigframe *rt_sf; + struct sigcontext_struct sigctx; + struct sigregs *sr; + int ret; + elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */ + sigset_t set; + stack_t st; + unsigned long prevsp; + + + PPCDBG(PPCDBG_SYS64X, "sys_rt_sigreturn - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + + /* If runnining a 32 bit process, then execute a 32 bit signal return */ + if (current->thread.flags & PPC_FLAG_32BIT) + { + ret = sys32_rt_sigreturn(regs); + PPCDBG(PPCDBG_SYS64NI, "sys_sigreturn - returned from sys32_sigreturn w/ %ld \n", ret); + return ret; + } + + rt_sf = (struct rt_sigframe *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); + if (copy_from_user(&sigctx, &rt_sf->uc.uc_mcontext, sizeof(sigctx)) + || copy_from_user(&set, &rt_sf->uc.uc_sigmask, sizeof(set)) + || copy_from_user(&st, &rt_sf->uc.uc_stack, sizeof(st))) + goto badframe; + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + rt_sf++; /* Look at next rt_sigframe */ + if (rt_sf == (struct rt_sigframe *)(sigctx.regs)) { + /* Last stacked signal - restore registers - + * sigctx is initialized to point to the + * preamble frame (where registers are stored) + * see handle_signal() + */ + sr = (struct sigregs *) sigctx.regs; + if (regs->msr & MSR_FP ) + giveup_fpu(current); + if (copy_from_user(saved_regs, &sr->gp_regs, + sizeof(sr->gp_regs))) + goto badframe; + saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) + | (saved_regs[PT_MSR] & MSR_USERCHANGE); + memcpy(regs, saved_regs, GP_REGS_SIZE); + if (copy_from_user(current->thread.fpr, &sr->fp_regs, + sizeof(sr->fp_regs))) + goto badframe; + /* This function sets back the stack flags into + the current task structure. */ + sys_sigaltstack(&st, NULL); + + ret = regs->result; + } else { + /* More signals to go */ + /* Set up registers for next signal handler */ + regs->gpr[1] = (unsigned long)rt_sf - __SIGNAL_FRAMESIZE; + if (copy_from_user(&sigctx, &rt_sf->uc.uc_mcontext, sizeof(sigctx))) + goto badframe; + sr = (struct sigregs *) sigctx.regs; + regs->gpr[3] = ret = sigctx.signal; + /* Get the siginfo */ + get_user(regs->gpr[4], (unsigned long *)&rt_sf->pinfo); + /* Get the ucontext */ + get_user(regs->gpr[5], (unsigned long *)&rt_sf->puc); + regs->gpr[6] = (unsigned long) rt_sf; + + regs->link = (unsigned long) &sr->tramp; + regs->nip = sigctx.handler; + if (get_user(prevsp, &sr->gp_regs[PT_R1]) + || put_user(prevsp, (unsigned long *) regs->gpr[1])) + goto badframe; + } + return ret; + +badframe: + do_exit(SIGSEGV); +} + +static void +setup_rt_frame(struct pt_regs *regs, struct sigregs *frame, + signed long newsp) +{ + struct rt_sigframe *rt_sf = (struct rt_sigframe *) newsp; + /* Handler is *really* a pointer to the function descriptor for + * the signal routine. The first entry in the function + * descriptor is the entry address of signal and the second + * entry is the TOC value we need to use. + */ + struct funct_descr_entry { + unsigned long entry; + unsigned long toc; + }; + + + struct funct_descr_entry * funct_desc_ptr; + unsigned long temp_ptr; + + + /* Set up preamble frame */ + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto badframe; + if (regs->msr & MSR_FP) + giveup_fpu(current); + if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) + || __copy_to_user(&frame->fp_regs, current->thread.fpr, + ELF_NFPREG * sizeof(double)) + /* Set up to return from user space. + It calls the sc exception at offset 0x9999 + for sys_rt_sigreturn(). + */ + || __put_user(0x38006666UL, &frame->tramp[0]) /* li r0,0x6666 */ + || __put_user(0x44000002UL, &frame->tramp[1])) /* sc */ + goto badframe; + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + + /* Retrieve rt_sigframe from stack and + set up registers for signal handler + */ + newsp -= __SIGNAL_FRAMESIZE; + + if ( get_user(temp_ptr, &rt_sf->uc.uc_mcontext.handler)) + { + goto badframe; + } + + funct_desc_ptr = ( struct funct_descr_entry *) temp_ptr; + + if (put_user(regs->gpr[1], (unsigned long *)newsp) + || get_user(regs->nip, &funct_desc_ptr->entry) + || get_user(regs->gpr[2], &funct_desc_ptr->toc) + || get_user(regs->gpr[3], &rt_sf->uc.uc_mcontext.signal) + || get_user(regs->gpr[4], (unsigned long *)&rt_sf->pinfo) + || get_user(regs->gpr[5], (unsigned long *)&rt_sf->puc)) + goto badframe; + + regs->gpr[1] = newsp; + regs->gpr[6] = (unsigned long) rt_sf; + regs->link = (unsigned long) frame->tramp; + + + return; + +badframe: +#if DEBUG_SIG + printk("badframe in setup_rt_frame, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + do_exit(SIGSEGV); +} + +/* + * Do a signal return; undo the signal stack. + */ +extern long sys32_sigreturn(struct pt_regs *regs); +asmlinkage long sys_sigreturn(struct pt_regs *regs) +{ + struct sigcontext_struct *sc, sigctx; + struct sigregs *sr; + long ret; + elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */ + sigset_t set; + unsigned long prevsp; + + PPCDBG(PPCDBG_SYS64NI, "sys_sigreturn - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + /* If runnining a 32 bit process, then execute a 32 bit signal return */ + if (current->thread.flags & PPC_FLAG_32BIT) + { + ret = sys32_sigreturn(regs); + PPCDBG(PPCDBG_SYS64NI, "sys_sigreturn - returned from sys32_sigreturn w/ %ld \n", ret); + return ret; + } + + sc = (struct sigcontext_struct *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + + set.sig[0] = sigctx.oldmask; +#if _NSIG_WORDS > 1 + set.sig[1] = sigctx._unused[3]; +#endif + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + sc++; /* Look at next sigcontext */ + if (sc == (struct sigcontext_struct *)(sigctx.regs)) { + /* Last stacked signal - restore registers */ + sr = (struct sigregs *) sigctx.regs; + if (regs->msr & MSR_FP ) + giveup_fpu(current); + if (copy_from_user(saved_regs, &sr->gp_regs, + sizeof(sr->gp_regs))) + goto badframe; + saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) + | (saved_regs[PT_MSR] & MSR_USERCHANGE); + memcpy(regs, saved_regs, GP_REGS_SIZE); + + if (copy_from_user(current->thread.fpr, &sr->fp_regs, + sizeof(sr->fp_regs))) + goto badframe; + + ret = regs->result; + + } else { + /* More signals to go */ + regs->gpr[1] = (unsigned long)sc - __SIGNAL_FRAMESIZE; + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + sr = (struct sigregs *) sigctx.regs; + regs->gpr[3] = ret = sigctx.signal; + regs->gpr[4] = (unsigned long) sc; + regs->link = (unsigned long) &sr->tramp; + regs->nip = sigctx.handler; + + if (get_user(prevsp, &sr->gp_regs[PT_R1]) + || put_user(prevsp, (unsigned long *) regs->gpr[1])) + goto badframe; + } + return ret; + +badframe: + do_exit(SIGSEGV); +} + +/* + * Set up a signal frame. + */ +static void +setup_frame(struct pt_regs *regs, struct sigregs *frame, + unsigned long newsp) +{ + + /* Handler is *really* a pointer to the function descriptor for + * the signal routine. The first entry in the function + * descriptor is the entry address of signal and the second + * entry is the TOC value we need to use. + */ + struct funct_descr_entry { + unsigned long entry; + unsigned long toc; + }; + + + struct funct_descr_entry * funct_desc_ptr; + unsigned long temp_ptr; + + struct sigcontext_struct *sc = (struct sigcontext_struct *) newsp; + + PPCDBG(PPCDBG_SIGNAL, "setup_frame - entered - regs=%p, frame=%p, newsp=%lx \n", regs, frame, newsp); + + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto badframe; + if (regs->msr & MSR_FP) + giveup_fpu(current); + if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) + || __copy_to_user(&frame->fp_regs, current->thread.fpr, + ELF_NFPREG * sizeof(double)) + || __put_user(0x38007777UL, &frame->tramp[0]) /* li r0,0x7777 */ + || __put_user(0x44000002UL, &frame->tramp[1])) /* sc */ + goto badframe; + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + + newsp -= __SIGNAL_FRAMESIZE; + if ( get_user(temp_ptr, &sc->handler)) + goto badframe; + + funct_desc_ptr = ( struct funct_descr_entry *) temp_ptr; + + if (put_user(regs->gpr[1], (unsigned long *)newsp) + || get_user(regs->nip, & funct_desc_ptr ->entry) + || get_user(regs->gpr[2],& funct_desc_ptr->toc) + || get_user(regs->gpr[3], &sc->signal)) + goto badframe; + regs->gpr[1] = newsp; + regs->gpr[4] = (unsigned long) sc; + regs->link = (unsigned long) frame->tramp; + + + PPCDBG(PPCDBG_SIGNAL, "setup_frame - returning - regs->gpr[1]=%lx, regs->gpr[4]=%lx, regs->link=%lx \n", + regs->gpr[1], regs->gpr[4], regs->link); + + return; + + badframe: + PPCDBG(PPCDBG_SIGNAL, "setup_frame - badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", regs, frame, newsp); PPCDBG_ENTER_DEBUGGER(); +#if DEBUG_SIG + printk("badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + do_exit(SIGSEGV); +} + +/* + * OK, we're invoking a handler + */ +static void +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, + unsigned long *newspp, unsigned long frame) +{ + struct sigcontext_struct *sc; + struct rt_sigframe *rt_sf; + + if (regs->trap == 0x0C00 /* System Call! */ + && ((int)regs->result == -ERESTARTNOHAND || + ((int)regs->result == -ERESTARTSYS && + !(ka->sa.sa_flags & SA_RESTART)))) + regs->result = -EINTR; + /* Set up Signal Frame */ + + if (ka->sa.sa_flags & SA_SIGINFO) { + /* Put a Real Time Context onto stack */ + *newspp -= sizeof(*rt_sf); + rt_sf = (struct rt_sigframe *) *newspp; + if (verify_area(VERIFY_WRITE, rt_sf, sizeof(*rt_sf))) + goto badframe; + + + if (__put_user((unsigned long) ka->sa.sa_handler, &rt_sf->uc.uc_mcontext.handler) + || __put_user(&rt_sf->info, &rt_sf->pinfo) + || __put_user(&rt_sf->uc, &rt_sf->puc) + /* Put the siginfo */ + || __copy_to_user(&rt_sf->info, info, sizeof(*info)) + /* Create the ucontext */ + || __put_user(0, &rt_sf->uc.uc_flags) + || __put_user(0, &rt_sf->uc.uc_link) + || __put_user(current->sas_ss_sp, &rt_sf->uc.uc_stack.ss_sp) + || __put_user(sas_ss_flags(regs->gpr[1]), + &rt_sf->uc.uc_stack.ss_flags) + || __put_user(current->sas_ss_size, &rt_sf->uc.uc_stack.ss_size) + || __copy_to_user(&rt_sf->uc.uc_sigmask, oldset, sizeof(*oldset)) + /* mcontext.regs points to preamble register frame */ + || __put_user((struct pt_regs *)frame, &rt_sf->uc.uc_mcontext.regs) + || __put_user(sig, &rt_sf->uc.uc_mcontext.signal)) + goto badframe; + + } else { + /* Put another sigcontext on the stack */ + *newspp -= sizeof(*sc); + sc = (struct sigcontext_struct *) *newspp; + if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) + goto badframe; + + if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler) + || __put_user(oldset->sig[0], &sc->oldmask) +#if _NSIG_WORDS > 1 + || __put_user(oldset->sig[1], &sc->_unused[3]) +#endif + || __put_user((struct pt_regs *)frame, &sc->regs) + || __put_user(sig, &sc->signal)) + goto badframe; + } + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } + return; + +badframe: +#if DEBUG_SIG + printk("badframe in handle_signal, regs=%p frame=%lx newsp=%lx\n", + regs, frame, *newspp); + printk("sc=%p sig=%d ka=%p info=%p oldset=%p\n", sc, sig, ka, info, oldset); +#endif + do_exit(SIGSEGV); +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +extern int do_signal32(sigset_t *oldset, struct pt_regs *regs); +int do_signal(sigset_t *oldset, struct pt_regs *regs) +{ + siginfo_t info; + struct k_sigaction *ka; + unsigned long frame, newsp; + + /* + * If the current thread is 32 bit - invoke the + * 32 bit signal handling code + */ + if (current->thread.flags & PPC_FLAG_32BIT) + return do_signal32(oldset, regs); + + PPCDBG(PPCDBG_SIGNAL, "do_signal - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + + + if (!oldset) + oldset = ¤t->blocked; + + newsp = frame = 0; + + for (;;) { + unsigned long signr; + + PPCDBG(PPCDBG_SIGNAL, "do_signal - (pre) dequeueing signal - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + PPCDBG(PPCDBG_SIGNAL, "do_signal - (aft) dequeueing signal - signal=%lx - pid=%ld current=%lx comm=%s \n", signr, current->pid, current, current->comm); + + if (!signr) + break; + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + + + PPCDBG(PPCDBG_SIGNAL, "do_signal - ka=%p, action handler=%lx \n", ka, ka->sa.sa_handler); + + if (ka->sa.sa_handler == SIG_IGN) { + PPCDBG(PPCDBG_SIGNAL, "do_signal - into SIG_IGN logic \n"); + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + PPCDBG(PPCDBG_SIGNAL, "do_signal - into SIG_DFL logic \n"); + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if (do_coredump(signr, regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + sigaddset(¤t->pending.signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + if ( (ka->sa.sa_flags & SA_ONSTACK) + && (! on_sig_stack(regs->gpr[1]))) + newsp = (current->sas_ss_sp + current->sas_ss_size); + else + newsp = regs->gpr[1]; + newsp = frame = newsp - sizeof(struct sigregs); + + /* Whee! Actually deliver the signal. */ + + PPCDBG(PPCDBG_SIGNAL, "do_signal - GOING TO RUN SIGNAL HANDLER - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + handle_signal(signr, ka, &info, oldset, regs, &newsp, frame); + PPCDBG(PPCDBG_SIGNAL, "do_signal - after running signal handler - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + break; + } + + if (regs->trap == 0x0C00 /* System Call! */ && + ((int)regs->result == -ERESTARTNOHAND || + (int)regs->result == -ERESTARTSYS || + (int)regs->result == -ERESTARTNOINTR)) { + PPCDBG(PPCDBG_SIGNAL, "do_signal - going to back up & retry system call \n"); + regs->gpr[3] = regs->orig_gpr3; + regs->nip -= 4; /* Back up & retry system call */ + regs->result = 0; + } + + if (newsp == frame) + { + PPCDBG(PPCDBG_SIGNAL, "do_signal - returning w/ no signal delivered \n"); + return 0; /* no signals delivered */ + } + + + + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(regs, (struct sigregs *) frame, newsp); + else + setup_frame(regs, (struct sigregs *) frame, newsp); + PPCDBG(PPCDBG_SIGNAL, "do_signal - returning a signal was delivered \n"); + return 1; +} + + + + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/signal32.c linux.ac/arch/ppc64/kernel/signal32.c --- linux.vanilla/arch/ppc64/kernel/signal32.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/signal32.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,1579 @@ +/* + * signal32.c: Support 32bit signal syscalls. + * + * Copyright (C) 2001 IBM + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * + * These routines maintain argument size conversion between 32bit and 64bit + * environment. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) +/* + * These are the flags in the MSR that the user is allowed to change + * by modifying the saved value of the MSR on the stack. SE and BE + * should not be in this list since gdb may want to change these. I.e, + * you should be able to step out of a signal handler to see what + * instruction executes next after the signal handler completes. + * Alternately, if you stepped into a signal handler, you should be + * able to continue 'til the next breakpoint from within the signal + * handler, even if the handler returns. + */ +#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) + + + + +/* Use this to get at 32-bit user passed pointers. */ +/* Things to consider: the low-level assembly stub does + srl x, 0, x for first four arguments, so if you have + pointer to something in the first four arguments, just + declare it as a pointer, not u32. On the other side, + arguments from 5th onwards should be declared as u32 + for pointers, and need AA() around each usage. + A() macro should be used for places where you e.g. + have some internal variable u32 and just want to get + rid of a compiler warning. AA() has to be used in + places where you want to convert a function argument + to 32bit pointer or when you e.g. access pt_regs + structure and want to consider 32bit registers only. + - + */ +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) \ +({ unsigned long __ret; \ + __asm__ ("clrldi %0, %0, 32" \ + : "=r" (__ret) \ + : "0" (__x)); \ + __ret; \ +}) + + + +struct timespec32 { + s32 tv_sec; + s32 tv_nsec; +}; + + + + +struct sigregs32 { + /***********************************************************************/ + /* the gp_regs array is 32 bit representation of the pt_regs structure */ + /* that was stored on the kernle stack during the system call that */ + /* was interrupted for the signal. */ + /* */ + /* Note that the entire pt_regs regs structure will fit in the gp_regs */ + /* structure because the ELF_NREG value is 48 for PPC and the pt_regs*/ + /* structure contains 44 registers */ + /* */ + /***********************************************************************/ + elf_gregset_t32 gp_regs; + double fp_regs[ELF_NFPREG]; + unsigned int tramp[2]; + /* Programs using the rs6000/xcoff abi can save up to 19 gp regs + and 18 fp regs below sp before decrementing it. */ + int abigap[56]; +}; + + +struct rt_sigframe_32 { + /* Unused space at start of frame to allow for storing of stack pointers */ + unsigned long _unused; + /* This is a 32 bit pointer in user address space + * it is a pointer to the siginfo stucture in the rt stack frame + */ + u32 pinfo; + /* This is a 32 bit pointer in user address space */ + /* it is a pointer to the user context in the rt stack frame */ + u32 puc; + struct siginfo32 info; + struct ucontext32 uc; +}; + + + + + +extern asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru); + + +/****************************************************************************/ +/* Start of nonRT signal support */ +/* */ +/* sigset_t is 32 bits for non-rt signals */ +/* */ +/* System Calls */ +/* sigaction sys32_sigaction */ +/* sigpending sys32_sigpending */ +/* sigprocmask sys32_sigprocmask */ +/* sigreturn sys32_sigreturn */ +/* */ +/* Note sigsuspend has no special 32 bit routine - uses the 64 bit routine */ +/* */ +/* Other routines */ +/* setup_frame32 */ +/* */ +/****************************************************************************/ + + +asmlinkage long sys32_sigaction(int sig, struct old_sigaction32 *act, struct old_sigaction32 *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + PPCDBG(PPCDBG_SYS32, "sys32_sigaction - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + if (sig < 0) + { + sig = -sig; + } + + if (act) + { + old_sigset_t32 mask; + + ret = get_user((long)new_ka.sa.sa_handler, &act->sa_handler); + ret |= __get_user((long)new_ka.sa.sa_restorer, &act->sa_restorer); + ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); + ret |= __get_user(mask, &act->sa_mask); + if (ret) + return ret; + PPCDBG(PPCDBG_SIGNAL, "sys32_sigaction flags =%lx \n", new_ka.sa.sa_flags); + + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) + { + ret = put_user((long)old_ka.sa.sa_handler, &oact->sa_handler); + ret |= __put_user((long)old_ka.sa.sa_restorer, &oact->sa_restorer); + ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + ret |= __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + + PPCDBG(PPCDBG_SYS32, "sys32_sigaction - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return ret; +} + + + + +extern asmlinkage long sys_sigpending(old_sigset_t *set); + +asmlinkage long sys32_sigpending(old_sigset_t32 *set) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SYS32, "sys32_sigpending - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_sigpending(&s); + set_fs (old_fs); + if (put_user (s, set)) return -EFAULT; + + PPCDBG(PPCDBG_SYS32, "sys32_sigpending - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return ret; +} + + + + +extern asmlinkage long sys_sigprocmask(int how, old_sigset_t *set, old_sigset_t *oset); + +/* Note: it is necessary to treat how as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sigprocmask(u32 how, old_sigset_t32 *set, old_sigset_t32 *oset) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SYS32, "sys32_sigprocmask - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + if (set && get_user (s, set)) return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_sigprocmask((int)how, set ? &s : NULL, oset ? &s : NULL); + set_fs (old_fs); + if (ret) return ret; + if (oset && put_user (s, oset)) return -EFAULT; + + PPCDBG(PPCDBG_SYS32, "sys32_sigprocmask - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return 0; +} + + + +/* + * When we have signals to deliver, we set up on the + * user stack, going down from the original stack pointer: + * a sigregs struct + * one or more sigcontext structs + * a gap of __SIGNAL_FRAMESIZE32 bytes + * + * Each of these things must be a multiple of 16 bytes in size. + * +*/ + + +/* + * Do a signal return; undo the signal stack. + */ +long sys32_sigreturn(struct pt_regs *regs) +{ + struct sigcontext32_struct *sc, sigctx; + struct sigregs32 *sr; + int ret; + elf_gregset_t32 saved_regs; /* an array of ELF_NGREG unsigned ints (32 bits) */ + sigset_t set; + unsigned int prevsp; + + + PPCDBG(PPCDBG_SIGNAL, "sys32_sigreturn - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + PPCDBG(PPCDBG_SIGNAL, "sys32_sigreturn - comm=%s reg1 =%lx \n", + current->comm, regs->gpr[1]); + + + sc = (struct sigcontext32_struct *)(regs->gpr[1] + __SIGNAL_FRAMESIZE32); + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + + /* Note that PPC32 puts the upper 32 bits of the sigmask in the */ + /* unused part of the signal stackframe */ + set.sig[0] = sigctx.oldmask + ((long)(sigctx._unused[3])<< 32); + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + sc++; /* Look at next sigcontext */ + /* If the next sigcontext is actually the sigregs (frame) */ + /* - then no more sigcontexts on the user stack */ + if (sc == (struct sigcontext32_struct*)(u64)sigctx.regs) + { + PPCDBG(PPCDBG_SIGNAL, "sys32_sigreturn - no more sigcontexts found \n"); + /* Last stacked signal - restore registers */ + sr = (struct sigregs32*)(u64)sigctx.regs; + if (regs->msr & MSR_FP ) + giveup_fpu(current); + /* copy the 32 bit register values off the user stack */ + /* into the 32 bit register area */ + if (copy_from_user(saved_regs, &sr->gp_regs,sizeof(sr->gp_regs))) + goto badframe; + /**********************************************************************/ + /* The saved reg structure in the frame is an elf_grepset_t32, it is */ + /* a 32 bit register save of the registers in the pt_regs structure */ + /* that was stored on the kernel stack during the system call */ + /* when the system call was interrupted for the signal. Only 32 bits*/ + /* are saved because the sigcontext contains a pointer to the regs */ + /* and the sig context address is passed as a pointer to the signal */ + /* handler. */ + /* */ + /* The entries in the elf_grepset have the same index as the elements */ + /* in the pt_regs structure. */ + /* */ + /**********************************************************************/ + + saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) + | (saved_regs[PT_MSR] & MSR_USERCHANGE); + regs->gpr[0] = (u64)(saved_regs[0]) & 0xFFFFFFFF; + regs->gpr[1] = (u64)(saved_regs[1]) & 0xFFFFFFFF; + /**********************************************************************/ + /* Register 2 is the kernel toc - should be reset on any calls into */ + /* the kernel */ + /**********************************************************************/ + regs->gpr[2] = (u64)(saved_regs[2]) & 0xFFFFFFFF; + + regs->gpr[3] = (u64)(saved_regs[3]) & 0xFFFFFFFF; + regs->gpr[4] = (u64)(saved_regs[4]) & 0xFFFFFFFF; + regs->gpr[5] = (u64)(saved_regs[5]) & 0xFFFFFFFF; + regs->gpr[6] = (u64)(saved_regs[6]) & 0xFFFFFFFF; + regs->gpr[7] = (u64)(saved_regs[7]) & 0xFFFFFFFF; + regs->gpr[8] = (u64)(saved_regs[8]) & 0xFFFFFFFF; + regs->gpr[9] = (u64)(saved_regs[9]) & 0xFFFFFFFF; + regs->gpr[10] = (u64)(saved_regs[10]) & 0xFFFFFFFF; + regs->gpr[11] = (u64)(saved_regs[11]) & 0xFFFFFFFF; + regs->gpr[12] = (u64)(saved_regs[12]) & 0xFFFFFFFF; + regs->gpr[13] = (u64)(saved_regs[13]) & 0xFFFFFFFF; + regs->gpr[14] = (u64)(saved_regs[14]) & 0xFFFFFFFF; + regs->gpr[15] = (u64)(saved_regs[15]) & 0xFFFFFFFF; + regs->gpr[16] = (u64)(saved_regs[16]) & 0xFFFFFFFF; + regs->gpr[17] = (u64)(saved_regs[17]) & 0xFFFFFFFF; + regs->gpr[18] = (u64)(saved_regs[18]) & 0xFFFFFFFF; + regs->gpr[19] = (u64)(saved_regs[19]) & 0xFFFFFFFF; + regs->gpr[20] = (u64)(saved_regs[20]) & 0xFFFFFFFF; + regs->gpr[21] = (u64)(saved_regs[21]) & 0xFFFFFFFF; + regs->gpr[22] = (u64)(saved_regs[22]) & 0xFFFFFFFF; + regs->gpr[23] = (u64)(saved_regs[23]) & 0xFFFFFFFF; + regs->gpr[24] = (u64)(saved_regs[24]) & 0xFFFFFFFF; + regs->gpr[25] = (u64)(saved_regs[25]) & 0xFFFFFFFF; + regs->gpr[26] = (u64)(saved_regs[26]) & 0xFFFFFFFF; + regs->gpr[27] = (u64)(saved_regs[27]) & 0xFFFFFFFF; + regs->gpr[28] = (u64)(saved_regs[28]) & 0xFFFFFFFF; + regs->gpr[29] = (u64)(saved_regs[29]) & 0xFFFFFFFF; + regs->gpr[30] = (u64)(saved_regs[30]) & 0xFFFFFFFF; + regs->gpr[31] = (u64)(saved_regs[31]) & 0xFFFFFFFF; + /****************************************************/ + /* restore the non gpr registers */ + /****************************************************/ + regs->msr = (u64)(saved_regs[PT_MSR]) & 0xFFFFFFFF; + /* Insure that the interrupt mode is 64 bit, during 32 bit execution. + * (This is necessary because we only saved lower 32 bits of msr.) + */ + regs->msr = regs->msr | MSR_ISF; /* When this thread is interrupted it should run in 64 bit mode. */ + + regs->nip = (u64)(saved_regs[PT_NIP]) & 0xFFFFFFFF; + regs->orig_gpr3 = (u64)(saved_regs[PT_ORIG_R3]) & 0xFFFFFFFF; + regs->ctr = (u64)(saved_regs[PT_CTR]) & 0xFFFFFFFF; + regs->link = (u64)(saved_regs[PT_LNK]) & 0xFFFFFFFF; + regs->xer = (u64)(saved_regs[PT_XER]) & 0xFFFFFFFF; + regs->ccr = (u64)(saved_regs[PT_CCR]) & 0xFFFFFFFF; + regs->mq = (u64)(saved_regs[PT_MQ]) & 0xFFFFFFFF; + /******************************************************/ + /* the DAR and the DSISR are only relevant during a */ + /* data or instruction storage interrupt. The value */ + /* will be set to zero. */ + /******************************************************/ + regs->dar = 0; + regs->dsisr = 0; + regs->result = (u64)(saved_regs[PT_RESULT]) & 0xFFFFFFFF; + + if (copy_from_user(current->thread.fpr, &sr->fp_regs, sizeof(sr->fp_regs))) + goto badframe; + + ret = regs->result; + } + else + { + PPCDBG(PPCDBG_SIGNAL, "sys32_sigreturn - additional sigcontexts found \n"); + /* More signals to go */ + regs->gpr[1] = (unsigned long)sc - __SIGNAL_FRAMESIZE32; + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + sr = (struct sigregs32*)(u64)sigctx.regs; + regs->gpr[3] = ret = sigctx.signal; + regs->gpr[4] = (unsigned long) sc; + regs->link = (unsigned long) &sr->tramp; + regs->nip = sigctx.handler; + + if (get_user(prevsp, &sr->gp_regs[PT_R1]) + || put_user(prevsp, (unsigned int*) regs->gpr[1])) + goto badframe; + } + + PPCDBG(PPCDBG_SIGNAL, "sys32_sigreturn - normal exit returning %ld - pid=%ld current=%lx comm=%s \n", ret, current->pid, current, current->comm); + return ret; + +badframe: + PPCDBG(PPCDBG_SYS32NI, "sys32_sigreturn - badframe - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + do_exit(SIGSEGV); +} + +/* + * Set up a signal frame. + */ +static void +setup_frame32(struct pt_regs *regs, struct sigregs32 *frame, + unsigned int newsp) +{ + struct sigcontext32_struct *sc = (struct sigcontext32_struct *)(u64)newsp; + + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto badframe; + if (regs->msr & MSR_FP) + giveup_fpu(current); + + PPCDBG(PPCDBG_SIGNAL, "setup32 - entered - newsp =%lx \n", newsp); + /***************************************************************/ + /* */ + /* Copy the register contents for the pt_regs structure on the */ + /* kernel stack to the elf_gregset_t32 structure on the user */ + /* stack. This is a copy of 64 bit register values to 32 bit */ + /* register values. The high order 32 bits of the 64 bit */ + /* registers are not needed since a 32 bit application is */ + /* running and the saved registers are the contents of the */ + /* user registers at the time of a system call. */ + /* */ + /* The values saved on the user stack will be restored into */ + /* the registers during the signal return processing */ + /* */ + /* Note the +1 is needed in order to get the lower 32 bits */ + /* of 64 bit register */ + /***************************************************************/ + if (__copy_to_user(&frame->gp_regs[0], (u32*)(®s->gpr[0])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[1], (u32*)(®s->gpr[1])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[2], (u32*)(®s->gpr[2])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[3], (u32*)(®s->gpr[3])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[4], (u32*)(®s->gpr[4])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[5], (u32*)(®s->gpr[5])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[6], (u32*)(®s->gpr[6])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[7], (u32*)(®s->gpr[7])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[8], (u32*)(®s->gpr[8])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[9], (u32*)(®s->gpr[9])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[10], (u32*)(®s->gpr[10])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[11], (u32*)(®s->gpr[11])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[12], (u32*)(®s->gpr[12])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[13], (u32*)(®s->gpr[13])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[14], (u32*)(®s->gpr[14])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[15], (u32*)(®s->gpr[15])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[16], (u32*)(®s->gpr[16])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[17], (u32*)(®s->gpr[17])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[18], (u32*)(®s->gpr[18])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[19], (u32*)(®s->gpr[19])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[20], (u32*)(®s->gpr[20])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[21], (u32*)(®s->gpr[21])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[22], (u32*)(®s->gpr[22])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[23], (u32*)(®s->gpr[23])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[24], (u32*)(®s->gpr[24])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[25], (u32*)(®s->gpr[25])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[26], (u32*)(®s->gpr[26])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[27], (u32*)(®s->gpr[27])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[28], (u32*)(®s->gpr[28])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[29], (u32*)(®s->gpr[29])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[30], (u32*)(®s->gpr[30])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[31], (u32*)(®s->gpr[31])+1, sizeof(u32))) + goto badframe; + + /*****************************************************************************/ + /* Copy the non gpr registers to the user stack */ + /*****************************************************************************/ + + if (__copy_to_user(&frame->gp_regs[PT_NIP], (u32*)(®s->gpr[PT_NIP])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_MSR], (u32*)(®s->gpr[PT_MSR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_ORIG_R3], (u32*)(®s->gpr[PT_ORIG_R3])+1, + sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_CTR], (u32*)(®s->gpr[PT_CTR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_LNK], (u32*)(®s->gpr[PT_LNK])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_XER], (u32*)(®s->gpr[PT_XER])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_CCR], (u32*)(®s->gpr[PT_CCR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_MQ], (u32*)(®s->gpr[PT_MQ])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_RESULT], (u32*)(®s->gpr[PT_RESULT])+1, + sizeof(u32))) + goto badframe; + + + /*****************************************************************************/ + /* Now copy the floating point registers onto the user stack */ + /* */ + /* Also set up so on the completion of the signal handler, the sys_sigreturn */ + /* will get control to reset the stack */ + /*****************************************************************************/ + + + + PPCDBG(PPCDBG_SIGNAL, "setup32 - after copy to user \n"); + + if (__copy_to_user(&frame->fp_regs, current->thread.fpr, + ELF_NFPREG * sizeof(double)) + || __put_user(0x38007777U, &frame->tramp[0]) /* li r0,0x7777 */ + || __put_user(0x44000002U, &frame->tramp[1])) /* sc */ + goto badframe; + + PPCDBG(PPCDBG_SIGNAL, "setup32 - after copy to user2 \n"); + + + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + + PPCDBG(PPCDBG_SIGNAL, "setup32 - after flush icache range\n"); + + newsp -= __SIGNAL_FRAMESIZE32; + if (put_user(regs->gpr[1], (u32*)(u64)newsp) + || get_user(regs->nip, &sc->handler) + || get_user(regs->gpr[3], &sc->signal)) + goto badframe; + + PPCDBG(PPCDBG_SIGNAL, "setup32 - after copy to user 3 \n"); + + regs->gpr[1] = newsp & 0xFFFFFFFF; + /**************************************************************/ + /* first parameter to the signal handler is the signal number */ + /* - the value is in gpr3 */ + /* second parameter to the signal handler is the sigcontext */ + /* - set the value into gpr4 */ + /**************************************************************/ + regs->gpr[4] = (unsigned long) sc; + regs->link = (unsigned long) frame->tramp; + PPCDBG(PPCDBG_SIGNAL, "setup32 - before return trap address = %lx \n", frame->tramp); + return; + + badframe: + udbg_printf("setup_frame32 - badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", regs, frame, newsp); PPCDBG_ENTER_DEBUGGER(); +#if DEBUG_SIG + printk("badframe in setup_frame32, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + do_exit(SIGSEGV); +} + + + + + +/****************************************************************************/ +/* Start of RT signal support */ +/* */ +/* sigset_t is 64 bits for rt signals */ +/* */ +/* System Calls */ +/* sigaction sys32_rt_sigaction */ +/* sigpending sys32_rt_sigpending */ +/* sigprocmask sys32_rt_sigprocmask */ +/* sigreturn sys32_rt_sigreturn */ +/* sigtimedwait sys32_rt_sigtimedwait */ +/* sigqueueinfo sys32_rt_sigqueueinfo */ +/* sigsuspend sys32_rt_sigsuspend */ +/* */ +/* Other routines */ +/* setup_rt_frame32 */ +/* siginfo64to32 */ +/* siginfo32to64 */ +/* */ +/* */ +/****************************************************************************/ + + +// This code executes after the rt signal handler in 32 bit mode has completed and +// returned +long sys32_rt_sigreturn(struct pt_regs * regs) +{ + struct rt_sigframe_32 *rt_stack_frame; + struct sigcontext32_struct sigctx; + struct sigregs32 *signalregs; + + int ret; + elf_gregset_t32 saved_regs; /* an array of 32 bit register values */ + sigset_t signal_set; + stack_t stack; + unsigned int previous_stack; + + + + ret = 0; + /* Adjust the inputted reg1 to point to the first rt signal frame */ + rt_stack_frame = (struct rt_sigframe_32 *)(regs->gpr[1] + __SIGNAL_FRAMESIZE32); + /* Copy the information from the user stack */ + if (copy_from_user(&sigctx, &rt_stack_frame->uc.uc_mcontext,sizeof(sigctx)) + || copy_from_user(&signal_set, &rt_stack_frame->uc.uc_sigmask,sizeof(signal_set)) + || copy_from_user(&stack,&rt_stack_frame->uc.uc_stack,sizeof(stack))) + { + /* unable to copy from user storage */ + goto badframe; + } + + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigreturn - copied from user - signal_set=%lx \n", *(u64*)&signal_set); + + /* Unblock the signal that was processed + * After a signal handler runs - + * if the signal is blockable - the signal will be unblocked + * ( sigkill and sigstop are not blockable) + */ + sigdelsetmask(&signal_set, ~_BLOCKABLE); + /* update the current based on the sigmask found in the rt_stackframe */ + spin_lock_irq(¤t->sigmask_lock); + current->blocked = signal_set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + /* Set to point to the next rt_sigframe - this is used to determine whether this + * is the last signal to process + */ + rt_stack_frame ++; + + if (rt_stack_frame == (struct rt_sigframe_32 *)(u64)(sigctx.regs)) + { + signalregs = (struct sigregs32 *) (u64)sigctx.regs; + /* If currently owning the floating point - give them up */ + if (regs->msr & MSR_FP) + { + giveup_fpu(current); + } + if (copy_from_user(saved_regs,&signalregs->gp_regs,sizeof(signalregs->gp_regs))) + { + goto badframe; + } + /**********************************************************************/ + /* The saved reg structure in the frame is an elf_grepset_t32, it is */ + /* a 32 bit register save of the registers in the pt_regs structure */ + /* that was stored on the kernel stack during the system call */ + /* when the system call was interrupted for the signal. Only 32 bits*/ + /* are saved because the sigcontext contains a pointer to the regs */ + /* and the sig context address is passed as a pointer to the signal */ + /* handler. */ + /* */ + /* The entries in the elf_grepset have the same index as the elements */ + /* in the pt_regs structure. */ + /* */ + /**********************************************************************/ + + saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) + | (saved_regs[PT_MSR] & MSR_USERCHANGE); + regs->gpr[0] = (u64)(saved_regs[0]) & 0xFFFFFFFF; + regs->gpr[1] = (u64)(saved_regs[1]) & 0xFFFFFFFF; + /**********************************************************************/ + /* Register 2 is the kernel toc - should be reset on any calls into */ + /* the kernel */ + /**********************************************************************/ + regs->gpr[2] = (u64)(saved_regs[2]) & 0xFFFFFFFF; + + regs->gpr[3] = (u64)(saved_regs[3]) & 0xFFFFFFFF; + regs->gpr[4] = (u64)(saved_regs[4]) & 0xFFFFFFFF; + regs->gpr[5] = (u64)(saved_regs[5]) & 0xFFFFFFFF; + regs->gpr[6] = (u64)(saved_regs[6]) & 0xFFFFFFFF; + regs->gpr[7] = (u64)(saved_regs[7]) & 0xFFFFFFFF; + regs->gpr[8] = (u64)(saved_regs[8]) & 0xFFFFFFFF; + regs->gpr[9] = (u64)(saved_regs[9]) & 0xFFFFFFFF; + regs->gpr[10] = (u64)(saved_regs[10]) & 0xFFFFFFFF; + regs->gpr[11] = (u64)(saved_regs[11]) & 0xFFFFFFFF; + regs->gpr[12] = (u64)(saved_regs[12]) & 0xFFFFFFFF; + regs->gpr[13] = (u64)(saved_regs[13]) & 0xFFFFFFFF; + regs->gpr[14] = (u64)(saved_regs[14]) & 0xFFFFFFFF; + regs->gpr[15] = (u64)(saved_regs[15]) & 0xFFFFFFFF; + regs->gpr[16] = (u64)(saved_regs[16]) & 0xFFFFFFFF; + regs->gpr[17] = (u64)(saved_regs[17]) & 0xFFFFFFFF; + regs->gpr[18] = (u64)(saved_regs[18]) & 0xFFFFFFFF; + regs->gpr[19] = (u64)(saved_regs[19]) & 0xFFFFFFFF; + regs->gpr[20] = (u64)(saved_regs[20]) & 0xFFFFFFFF; + regs->gpr[21] = (u64)(saved_regs[21]) & 0xFFFFFFFF; + regs->gpr[22] = (u64)(saved_regs[22]) & 0xFFFFFFFF; + regs->gpr[23] = (u64)(saved_regs[23]) & 0xFFFFFFFF; + regs->gpr[24] = (u64)(saved_regs[24]) & 0xFFFFFFFF; + regs->gpr[25] = (u64)(saved_regs[25]) & 0xFFFFFFFF; + regs->gpr[26] = (u64)(saved_regs[26]) & 0xFFFFFFFF; + regs->gpr[27] = (u64)(saved_regs[27]) & 0xFFFFFFFF; + regs->gpr[28] = (u64)(saved_regs[28]) & 0xFFFFFFFF; + regs->gpr[29] = (u64)(saved_regs[29]) & 0xFFFFFFFF; + regs->gpr[30] = (u64)(saved_regs[30]) & 0xFFFFFFFF; + regs->gpr[31] = (u64)(saved_regs[31]) & 0xFFFFFFFF; + /****************************************************/ + /* restore the non gpr registers */ + /****************************************************/ + regs->msr = (u64)(saved_regs[PT_MSR]) & 0xFFFFFFFF; + + regs->nip = (u64)(saved_regs[PT_NIP]) & 0xFFFFFFFF; + regs->orig_gpr3 = (u64)(saved_regs[PT_ORIG_R3]) & 0xFFFFFFFF; + regs->ctr = (u64)(saved_regs[PT_CTR]) & 0xFFFFFFFF; + regs->link = (u64)(saved_regs[PT_LNK]) & 0xFFFFFFFF; + regs->xer = (u64)(saved_regs[PT_XER]) & 0xFFFFFFFF; + regs->ccr = (u64)(saved_regs[PT_CCR]) & 0xFFFFFFFF; + regs->mq = (u64)(saved_regs[PT_MQ]) & 0xFFFFFFFF; + /******************************************************/ + /* the DAR and the DSISR are only relevant during a */ + /* data or instruction storage interrupt. The value */ + /* will be set to zero. */ + /******************************************************/ + regs->dar = 0; + regs->dsisr = 0; + regs->result = (u64)(saved_regs[PT_RESULT]) & 0xFFFFFFFF; + + + } + else /* more signals to go */ + { + regs->gpr[1] = (u64)rt_stack_frame - __SIGNAL_FRAMESIZE32; + if (copy_from_user(&sigctx, &rt_stack_frame->uc.uc_mcontext,sizeof(sigctx))) + { + goto badframe; + } + signalregs = (struct sigregs32 *) (u64)sigctx.regs; + /* first parm to signal handler is the signal number */ + regs->gpr[3] = ret = sigctx.signal; + /* second parm is a pointer to sig info */ + get_user(regs->gpr[4], &rt_stack_frame->pinfo); + /* third parm is a pointer to the ucontext */ + get_user(regs->gpr[5], &rt_stack_frame->puc); + /* fourth parm is the stack frame */ + regs->gpr[6] = (u64)rt_stack_frame; + /* Set up link register to return to sigreturn when the */ + /* signal handler completes */ + regs->link = (u64)&signalregs->tramp; + /* Set next instruction to the start fo the signal handler */ + regs->nip = sigctx.handler; + /* Set the reg1 to look like a call to the signal handler */ + if (get_user(previous_stack,&signalregs->gp_regs[PT_R1]) + || put_user(previous_stack, (unsigned long *)regs->gpr[1])) + { + goto badframe; + } + + } + + return ret; + +badframe: + do_exit(SIGSEGV); +} + + + +asmlinkage long sys32_rt_sigaction(int sig, const struct sigaction32 *act, struct sigaction32 *oact, size_t sigsetsize) +{ + struct k_sigaction new_ka, old_ka; + int ret; + sigset32_t set32; + + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigaction - entered - sig=%x \n", sig); + + if (act) + PPCDBG(PPCDBG_SYS32NI, " sys32_rt_sigaction flags = %lx \n" ,act->sa_flags); + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset32_t)) + return -EINVAL; + + if (act) { + ret = get_user((long)new_ka.sa.sa_handler, &act->sa_handler); + ret |= __copy_from_user(&set32, &act->sa_mask, + sizeof(sigset32_t)); + switch (_NSIG_WORDS) { + case 4: new_ka.sa.sa_mask.sig[3] = set32.sig[6] + | (((long)set32.sig[7]) << 32); + case 3: new_ka.sa.sa_mask.sig[2] = set32.sig[4] + | (((long)set32.sig[5]) << 32); + case 2: new_ka.sa.sa_mask.sig[1] = set32.sig[2] + | (((long)set32.sig[3]) << 32); + case 1: new_ka.sa.sa_mask.sig[0] = set32.sig[0] + | (((long)set32.sig[1]) << 32); + } + + + ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); + + PPCDBG(PPCDBG_SIGNAL, " sys32_rt_sigaction flags(kernel) = %lx \n" ,new_ka.sa.sa_flags); + + if (ret) + return -EFAULT; + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + switch (_NSIG_WORDS) { + case 4: + set32.sig[7] = (old_ka.sa.sa_mask.sig[3] >> 32); + set32.sig[6] = old_ka.sa.sa_mask.sig[3]; + case 3: + set32.sig[5] = (old_ka.sa.sa_mask.sig[2] >> 32); + set32.sig[4] = old_ka.sa.sa_mask.sig[2]; + case 2: + set32.sig[3] = (old_ka.sa.sa_mask.sig[1] >> 32); + set32.sig[2] = old_ka.sa.sa_mask.sig[1]; + case 1: + set32.sig[1] = (old_ka.sa.sa_mask.sig[0] >> 32); + set32.sig[0] = old_ka.sa.sa_mask.sig[0]; + } + ret = put_user((long)old_ka.sa.sa_handler, &oact->sa_handler); + ret |= __copy_to_user(&oact->sa_mask, &set32, + sizeof(sigset32_t)); + ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + } + + + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigaction - exiting - sig=%x \n", sig); + return ret; +} + + +extern asmlinkage long sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, + size_t sigsetsize); + +/* Note: it is necessary to treat how as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_rt_sigprocmask(u32 how, sigset32_t *set, sigset32_t *oset, size_t sigsetsize) +{ + sigset_t s; + sigset32_t s32; + int ret; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigprocmask - entered how=%x \n", (int)how); + + if (set) { + if (copy_from_user (&s32, set, sizeof(sigset32_t))) + return -EFAULT; + + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigprocmask - *set=%lx \n", *(u64*)&s32); + + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + } + else + { + PPCDBG(PPCDBG_SIGNAL, " sys32_rt_sigprocmask mask set = NULL \n" ); + } + set_fs (KERNEL_DS); + ret = sys_rt_sigprocmask((int)how, set ? &s : NULL, oset ? &s : NULL, + sigsetsize); + set_fs (old_fs); + if (ret) return ret; + if (oset) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user (oset, &s32, sizeof(sigset32_t))) + return -EFAULT; + } + return 0; +} + + +extern asmlinkage long sys_rt_sigpending(sigset_t *set, size_t sigsetsize); + + + +asmlinkage long sys32_rt_sigpending(sigset32_t *set, __kernel_size_t32 sigsetsize) +{ + + sigset_t s; + sigset32_t s32; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_rt_sigpending(&s, sigsetsize); + set_fs (old_fs); + if (!ret) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user (set, &s32, sizeof(sigset32_t))) + return -EFAULT; + } + return ret; +} + + + +siginfo_t32 * +siginfo64to32(siginfo_t32 *d, siginfo_t *s) +{ + memset (d, 0, sizeof(siginfo_t32)); + d->si_signo = s->si_signo; + d->si_errno = s->si_errno; + d->si_code = s->si_code; + if (s->si_signo >= SIGRTMIN) { + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + + d->si_int = s->si_int; + } else switch (s->si_signo) { + /* XXX: What about POSIX1.b timers */ + case SIGCHLD: + d->si_pid = s->si_pid; + d->si_status = s->si_status; + d->si_utime = s->si_utime; + d->si_stime = s->si_stime; + break; + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + d->si_addr = (long)(s->si_addr); + break; + case SIGPOLL: + d->si_band = s->si_band; + d->si_fd = s->si_fd; + break; + default: + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + break; + } + return d; +} + +extern asmlinkage long +sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo, + const struct timespec *uts, size_t sigsetsize); + +asmlinkage long +sys32_rt_sigtimedwait(sigset32_t *uthese, siginfo_t32 *uinfo, + struct timespec32 *uts, __kernel_size_t32 sigsetsize) +{ + sigset_t s; + sigset32_t s32; + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs(); + siginfo_t info; + siginfo_t32 info32; + + if (copy_from_user (&s32, uthese, sizeof(sigset32_t))) + return -EFAULT; + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + if (uts) { + ret = get_user (t.tv_sec, &uts->tv_sec); + ret |= __get_user (t.tv_nsec, &uts->tv_nsec); + if (ret) + return -EFAULT; + } + set_fs (KERNEL_DS); + if (uts) + { + ret = sys_rt_sigtimedwait(&s, &info, &t, sigsetsize); + } + else + { + ret = sys_rt_sigtimedwait(&s, &info, (struct timespec *)uts, sigsetsize); + } + + set_fs (old_fs); + if (ret >= 0 && uinfo) { + if (copy_to_user (uinfo, siginfo64to32(&info32, &info), + sizeof(siginfo_t32))) + return -EFAULT; + } + return ret; +} + + + +siginfo_t * +siginfo32to64(siginfo_t *d, siginfo_t32 *s) +{ + d->si_signo = s->si_signo; + d->si_errno = s->si_errno; + d->si_code = s->si_code; + if (s->si_signo >= SIGRTMIN) { + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + d->si_int = s->si_int; + + } else switch (s->si_signo) { + /* XXX: What about POSIX1.b timers */ + case SIGCHLD: + d->si_pid = s->si_pid; + d->si_status = s->si_status; + d->si_utime = s->si_utime; + d->si_stime = s->si_stime; + break; + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + d->si_addr = (void *)A(s->si_addr); + break; + case SIGPOLL: + d->si_band = s->si_band; + d->si_fd = s->si_fd; + break; + default: + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + break; + } + return d; +} + + +extern asmlinkage long sys_rt_sigqueueinfo(int pid, int sig, siginfo_t *uinfo); + +/* Note: it is necessary to treat pid and sig as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_rt_sigqueueinfo(u32 pid, u32 sig, siginfo_t32 *uinfo) +{ + siginfo_t info; + siginfo_t32 info32; + int ret; + mm_segment_t old_fs = get_fs(); + + if (copy_from_user (&info32, uinfo, sizeof(siginfo_t32))) + return -EFAULT; + /* XXX: Is this correct? */ + siginfo32to64(&info, &info32); + + set_fs (KERNEL_DS); + ret = sys_rt_sigqueueinfo((int)pid, (int)sig, &info); + set_fs (old_fs); + return ret; +} + + +int do_signal(sigset_t *oldset, struct pt_regs *regs); +int sys32_rt_sigsuspend(sigset32_t* unewset, size_t sigsetsize, int p3, int p4, int p6, int p7, struct pt_regs *regs) +{ + sigset_t saveset, newset; + + sigset32_t s32; + + PPCDBG(PPCDBG_SYS64X, "sys32_rt_sigsuspend - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigsuspend - current block mask = %lx \n",current->blocked); + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&s32, unewset, sizeof(s32))) + return -EFAULT; + + /* Swap the 2 words of the 64-bit sigset_t (they are stored in the "wrong" endian in 32-bit user storage). */ + switch (_NSIG_WORDS) + { + case 4: newset.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: newset.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: newset.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: newset.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->gpr[3] = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + return regs->gpr[3]; + } +} + + + + + + + + +/* + * Set up a rt signal frame. + */ +static void +setup_rt_frame32(struct pt_regs *regs, struct sigregs32 *frame, + unsigned int newsp) +{ + + unsigned int copyreg4,copyreg5; + struct rt_sigframe_32 * rt_sf = (struct rt_sigframe_32 *) (u64)newsp; + + + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto badframe; + if (regs->msr & MSR_FP) + giveup_fpu(current); + /***************************************************************/ + /* */ + /* Copy the register contents for the pt_regs structure on the */ + /* kernel stack to the elf_gregset_t32 structure on the user */ + /* stack. This is a copy of 64 bit register values to 32 bit */ + /* register values. The high order 32 bits of the 64 bit */ + /* registers are not needed since a 32 bit application is */ + /* running and the saved registers are the contents of the */ + /* user registers at the time of a system call. */ + /* */ + /* The values saved on the user stack will be restored into */ + /* the registers during the signal return processing */ + /* */ + /* Note the +1 is needed in order to get the lower 32 bits */ + /* of 64 bit register */ + /***************************************************************/ + if (__copy_to_user(&frame->gp_regs[0], (u32*)(®s->gpr[0])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[1], (u32*)(®s->gpr[1])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[2], (u32*)(®s->gpr[2])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[3], (u32*)(®s->gpr[3])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[4], (u32*)(®s->gpr[4])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[5], (u32*)(®s->gpr[5])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[6], (u32*)(®s->gpr[6])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[7], (u32*)(®s->gpr[7])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[8], (u32*)(®s->gpr[8])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[9], (u32*)(®s->gpr[9])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[10], (u32*)(®s->gpr[10])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[11], (u32*)(®s->gpr[11])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[12], (u32*)(®s->gpr[12])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[13], (u32*)(®s->gpr[13])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[14], (u32*)(®s->gpr[14])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[15], (u32*)(®s->gpr[15])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[16], (u32*)(®s->gpr[16])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[17], (u32*)(®s->gpr[17])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[18], (u32*)(®s->gpr[18])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[19], (u32*)(®s->gpr[19])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[20], (u32*)(®s->gpr[20])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[21], (u32*)(®s->gpr[21])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[22], (u32*)(®s->gpr[22])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[23], (u32*)(®s->gpr[23])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[24], (u32*)(®s->gpr[24])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[25], (u32*)(®s->gpr[25])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[26], (u32*)(®s->gpr[26])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[27], (u32*)(®s->gpr[27])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[28], (u32*)(®s->gpr[28])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[29], (u32*)(®s->gpr[29])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[30], (u32*)(®s->gpr[30])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[31], (u32*)(®s->gpr[31])+1, sizeof(u32))) + goto badframe; + + /*****************************************************************************/ + /* Copy the non gpr registers to the user stack */ + /*****************************************************************************/ + + if (__copy_to_user(&frame->gp_regs[PT_NIP], (u32*)(®s->gpr[PT_NIP])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_MSR], (u32*)(®s->gpr[PT_MSR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_ORIG_R3], (u32*)(®s->gpr[PT_ORIG_R3])+1, + sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_CTR], (u32*)(®s->gpr[PT_CTR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_LNK], (u32*)(®s->gpr[PT_LNK])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_XER], (u32*)(®s->gpr[PT_XER])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_CCR], (u32*)(®s->gpr[PT_CCR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_MQ], (u32*)(®s->gpr[PT_MQ])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_RESULT], (u32*)(®s->gpr[PT_RESULT])+1, + sizeof(u32))) + goto badframe; + + + /*****************************************************************************/ + /* Now copy the floating point registers onto the user stack */ + /* */ + /* Also set up so on the completion of the signal handler, the sys_sigreturn */ + /* will get control to reset the stack */ + /*****************************************************************************/ + + + if (__copy_to_user(&frame->fp_regs, current->thread.fpr, + ELF_NFPREG * sizeof(double)) + || __put_user(0x38006666U, &frame->tramp[0]) /* li r0,0x6666 */ + || __put_user(0x44000002U, &frame->tramp[1])) /* sc */ + goto badframe; + + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + + + /* Retrieve rt_sigframe from stack and + set up registers for signal handler + */ + newsp -= __SIGNAL_FRAMESIZE32; + + + if (put_user((u32)(regs->gpr[1]), (unsigned int *)(u64)newsp) + || get_user(regs->nip, &rt_sf->uc.uc_mcontext.handler) + || get_user(regs->gpr[3], &rt_sf->uc.uc_mcontext.signal) + || get_user(copyreg4, &rt_sf->pinfo) + || get_user(copyreg5, &rt_sf->puc)) + goto badframe; + + regs->gpr[4] = copyreg4; + regs->gpr[5] = copyreg5; + + + regs->gpr[1] = newsp; + regs->gpr[6] = (unsigned long) rt_sf; + + + regs->link = (unsigned long) frame->tramp; + + return; + + +badframe: + udbg_printf("setup_frame32 - badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", regs, frame, newsp); PPCDBG_ENTER_DEBUGGER(); +#if DEBUG_SIG + printk("badframe in setup_frame32, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + do_exit(SIGSEGV); +} + + +/* + * OK, we're invoking a handler + */ +static void +handle_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, + unsigned int *newspp, unsigned int frame) +{ + struct sigcontext32_struct *sc; + struct rt_sigframe_32 *rt_stack_frame; + siginfo_t32 siginfo32bit; + + if (regs->trap == 0x0C00 /* System Call! */ + && ((int)regs->result == -ERESTARTNOHAND || + ((int)regs->result == -ERESTARTSYS && + !(ka->sa.sa_flags & SA_RESTART)))) + regs->result = -EINTR; + + /* Set up the signal frame */ + /* Determine if an real time frame - siginfo required */ + if (ka->sa.sa_flags & SA_SIGINFO) + { + siginfo64to32(&siginfo32bit,info); + *newspp -= sizeof(*rt_stack_frame); + rt_stack_frame = (struct rt_sigframe_32 *) (u64)(*newspp) ; + + if (verify_area(VERIFY_WRITE, rt_stack_frame, sizeof(*rt_stack_frame))) + { + goto badframe; + } + if (__put_user((u32)(u64)ka->sa.sa_handler, &rt_stack_frame->uc.uc_mcontext.handler) + || __put_user((u32)(u64)&rt_stack_frame->info, &rt_stack_frame->pinfo) + || __put_user((u32)(u64)&rt_stack_frame->uc, &rt_stack_frame->puc) + /* put the siginfo on the user stack */ + || __copy_to_user(&rt_stack_frame->info,&siginfo32bit,sizeof(siginfo32bit)) + /* set the ucontext on the user stack */ + || __put_user(0,&rt_stack_frame->uc.uc_flags) + || __put_user(0,&rt_stack_frame->uc.uc_link) + || __put_user(current->sas_ss_sp, &rt_stack_frame->uc.uc_stack.ss_sp) + || __put_user(sas_ss_flags(regs->gpr[1]), + &rt_stack_frame->uc.uc_stack.ss_flags) + || __put_user(current->sas_ss_size, &rt_stack_frame->uc.uc_stack.ss_size) + || __copy_to_user(&rt_stack_frame->uc.uc_sigmask, oldset,sizeof(*oldset)) + /* point the mcontext.regs to the pramble register frame */ + || __put_user(frame, &rt_stack_frame->uc.uc_mcontext.regs) + || __put_user(sig,&rt_stack_frame->uc.uc_mcontext.signal)) + { + goto badframe; + } + } + else + { + /* Put another sigcontext on the stack */ + *newspp -= sizeof(*sc); + sc = (struct sigcontext32_struct *)(u64)*newspp; + if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) + goto badframe; + + /* Note the upper 32 bits of the signal mask are stored in the */ + /* unused part of the signal stack frame */ + if (__put_user((u32)(u64)ka->sa.sa_handler, &sc->handler) + || __put_user(oldset->sig[0], &sc->oldmask) + || __put_user((oldset->sig[0] >> 32), &sc->_unused[3]) + || __put_user((unsigned int)frame, &sc->regs) + || __put_user(sig, &sc->signal)) + goto badframe; + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } + } + return; + +badframe: +#if DEBUG_SIG + printk("badframe in handle_signal32, regs=%p frame=%lx newsp=%lx\n", + regs, frame, *newspp); + printk("sc=%p sig=%d ka=%p info=%p oldset=%p\n", sc, sig, ka, info, oldset); +#endif + do_exit(SIGSEGV); +} + + +/****************************************************************************/ +/* Start Alternate signal stack support */ +/* */ +/* */ +/* */ +/* System Calls */ +/* sigaltatck sys32_sigaltstack */ +/* */ +/****************************************************************************/ + + +asmlinkage int sys32_sigaltstack(u32 newstack, u32 oldstack, int p3, int p4, int p6, + int p7, struct pt_regs *regs) + +{ + stack_t uss, uoss; + int ret; + mm_segment_t old_fs; + unsigned long sp; + + + /* set sp to the user stack on entry to the system call */ + /* the system call router sets R9 to the saved registers */ + sp = regs->gpr[1]; + + PPCDBG(PPCDBG_SIGNAL," sys32 alt stack entered sp = %lx \n", sp); + + + /* Put new stack info in local 64 bit stack struct */ + if (newstack && (get_user((long)uss.ss_sp, &((stack_32_t *)(long)newstack)->ss_sp) || + __get_user(uss.ss_flags, &((stack_32_t *)(long)newstack)->ss_flags) || + __get_user(uss.ss_size, &((stack_32_t *)(long)newstack)->ss_size))) + return -EFAULT; + + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = do_sigaltstack(newstack ? &uss : NULL, oldstack ? &uoss : NULL, sp); + set_fs(old_fs); + /* Copy the stack information to the user output buffer */ + if (!ret && oldstack && (put_user((long)uoss.ss_sp, &((stack_32_t *)(long)oldstack)->ss_sp) || + __put_user(uoss.ss_flags, &((stack_32_t *)(long)oldstack)->ss_flags) || + __put_user(uoss.ss_size, &((stack_32_t *)(long)oldstack)->ss_size))) + return -EFAULT; + return ret; +} + + + +/****************************************************************************/ +/* Start of do_signal32 routine */ +/* */ +/* This routine gets control when a pemding signal needs to be processed */ +/* in the 32 bit target thread - */ +/* */ +/* It handles both rt and non-rt signals */ +/* */ +/****************************************************************************/ + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ + +int do_signal32(sigset_t *oldset, struct pt_regs *regs) +{ + siginfo_t info; + struct k_sigaction *ka; + unsigned int frame, newsp; + + if (!oldset) + oldset = ¤t->blocked; + + newsp = frame = 0; + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + ifppcdebug(PPCDBG_SYS32) + { + if (signr) + udbg_printf("do_signal32 - processing signal=%2lx - pid=%ld, comm=%s \n", signr, current->pid, current->comm); + } + + if (!signr) + break; + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if (do_coredump(signr, regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + sigaddset(¤t->pending.signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + PPCDBG(PPCDBG_SIGNAL, " do signal :sigaction flags = %lx \n" ,ka->sa.sa_flags); + PPCDBG(PPCDBG_SIGNAL, " do signal :on sig stack = %lx \n" ,on_sig_stack(regs->gpr[1])); + PPCDBG(PPCDBG_SIGNAL, " do signal :reg1 = %lx \n" ,regs->gpr[1]); + PPCDBG(PPCDBG_SIGNAL, " do signal :alt stack = %lx \n" ,current->sas_ss_sp); + PPCDBG(PPCDBG_SIGNAL, " do signal :alt stack size = %lx \n" ,current->sas_ss_size); + + + + if ( (ka->sa.sa_flags & SA_ONSTACK) + && (! on_sig_stack(regs->gpr[1]))) + { + newsp = (current->sas_ss_sp + current->sas_ss_size); + } + else + newsp = regs->gpr[1]; + newsp = frame = newsp - sizeof(struct sigregs32); + + /* Whee! Actually deliver the signal. */ + handle_signal32(signr, ka, &info, oldset, regs, &newsp, frame); + break; + } + + if (regs->trap == 0x0C00 /* System Call! */ && + ((int)regs->result == -ERESTARTNOHAND || + (int)regs->result == -ERESTARTSYS || + (int)regs->result == -ERESTARTNOINTR)) { + regs->gpr[3] = regs->orig_gpr3; + regs->nip -= 4; /* Back up & retry system call */ + regs->result = 0; + } + + if (newsp == frame) + { + return 0; /* no signals delivered */ + } + // Invoke correct stack setup routine + if (ka->sa.sa_flags & SA_SIGINFO) + { + PPCDBG(PPCDBG_SIGNAL, " do sig - invoke setup rt signal newsp = %lx frame = %lx \n", + newsp, frame); + setup_rt_frame32(regs, (struct sigregs32*)(u64)frame, newsp); + } + else + { + PPCDBG(PPCDBG_SIGNAL, " do sig - invoke setup non rt signal newsp = %lx frame = %lx \n", + newsp, frame); + + setup_frame32(regs, (struct sigregs32*)(u64)frame, newsp); + } + + return 1; + +} + + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/smp.c linux.ac/arch/ppc64/kernel/smp.c --- linux.vanilla/arch/ppc64/kernel/smp.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/smp.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,797 @@ +/* + * + * + * SMP support for ppc. + * + * Written by Cort Dougan (cort@cs.nmt.edu) borrowing a great + * deal of code from the sparc and intel versions. + * + * Copyright (C) 1999 Cort Dougan + * + * PowerPC-64 Support added by Dave Engebretsen, Peter Bergner, and + * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#define __KERNEL_SYSCALLS__ +#include +#include +/* #include */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "open_pic.h" + +int smp_threads_ready = 0; +volatile int smp_commenced = 0; +int smp_num_cpus = 1; +int smp_tb_synchronized = 0; +extern atomic_t ipi_recv; +extern atomic_t ipi_sent; +spinlock_t kernel_flag __cacheline_aligned = SPIN_LOCK_UNLOCKED; +cycles_t cacheflush_time; +static int max_cpus __initdata = NR_CPUS; + +unsigned long cpu_online_map; + +volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; + +#define TB_SYNC_PASSES 4 +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); +void smp_message_pass(int target, int msg, unsigned long data, int wait); +u_int openpic_read(volatile u_int *addr); +static unsigned long iSeries_smp_message[NR_CPUS]; +extern struct Naca *naca; +extern struct Paca xPaca[]; + +/* Forward declarations */ +static void smp_iSeries_message_pass(int target, int msg, unsigned long data, int wait); +static int smp_iSeries_probe(void); +static void smp_iSeries_kick_cpu(int nr); +static void smp_iSeries_setup_cpu(int nr); + +static void smp_openpic_message_pass(int target, int msg, unsigned long data, int wait); +static int smp_chrp_probe(void); +static void smp_chrp_kick_cpu(int nr); +static void smp_chrp_setup_cpu(int cpu_nr); + +static void smp_xics_message_pass(int target, int msg, unsigned long data, int wait); +static int smp_xics_probe(void); +void xics_setup_cpu(void); +void xics_cause_IPI(int cpu); + +/* + * XICS only has a single IPI, so encode the messages per CPU + */ +volatile unsigned long xics_ipi_message[NR_CPUS] = {0}; + +#define smp_message_pass(t,m,d,w) \ + do { if (smp_ops) \ + atomic_inc(&ipi_sent); \ + smp_ops->message_pass((t),(m),(d),(w)); \ + } while(0) + +static struct smp_ops_t { + void (*message_pass)(int target, int msg, unsigned long data, int wait); + int (*probe)(void); + void (*kick_cpu)(int nr); + void (*setup_cpu)(int nr); + +} *smp_ops; + +/* iSeries (iSeries) */ +static struct smp_ops_t iSeries_smp_ops = { + smp_iSeries_message_pass, + smp_iSeries_probe, + smp_iSeries_kick_cpu, + smp_iSeries_setup_cpu +}; + +/* CHRP with openpic */ +static struct smp_ops_t chrp_smp_ops = { + smp_openpic_message_pass, + smp_chrp_probe, + smp_chrp_kick_cpu, + smp_chrp_setup_cpu, +}; + +/* CHRP with new XICS interrupt controller */ +static struct smp_ops_t xics_smp_ops = { + smp_xics_message_pass, + smp_xics_probe, + smp_chrp_kick_cpu, + smp_chrp_setup_cpu, +}; + +#ifdef CONFIG_KDB +void smp_kdb_stop(void) +{ +} +#endif + +static inline void set_tb(unsigned int upper, unsigned int lower) +{ + mtspr(SPRN_TBWL, 0); + mtspr(SPRN_TBWU, upper); + mtspr(SPRN_TBWL, lower); +} + +void iSeries_smp_message_recv( struct pt_regs * regs ) +{ + int cpu = smp_processor_id(); + int msg; + + if ( smp_num_cpus < 2 ) + return; + + for ( msg = 0; msg < 4; ++msg ) + if ( test_and_clear_bit( msg, &iSeries_smp_message[cpu] ) ) + smp_message_recv( msg, regs ); + +} + +static void smp_iSeries_message_pass(int target, int msg, unsigned long data, int wait) +{ + int i; + for (i = 0; i < smp_num_cpus; ++i) { + if ( (target == MSG_ALL) || + (target == i) || + ((target == MSG_ALL_BUT_SELF) && (i != smp_processor_id())) ) { + set_bit( msg, &iSeries_smp_message[i] ); + HvCall_sendIPI(&(xPaca[i])); + } + } +} + +static int smp_iSeries_probe(void) +{ + unsigned i; + unsigned np; + struct ItLpPaca * lpPaca; + + np = 0; + for (i=0; i < maxPacas; ++i) { + lpPaca = xPaca[i].xLpPacaPtr; + if ( lpPaca->xDynProcStatus < 2 ) { + ++np; + xPaca[i].next_jiffy_update_tb = xPaca[0].next_jiffy_update_tb; + } + } + + smp_tb_synchronized = 1; + return np; +} + +static void smp_iSeries_kick_cpu(int nr) +{ + struct ItLpPaca * lpPaca; + /* Verify we have a Paca for processor nr */ + if ( ( nr <= 0 ) || + ( nr >= maxPacas ) ) + return; + /* Verify that our partition has a processor nr */ + lpPaca = xPaca[nr].xLpPacaPtr; + if ( lpPaca->xDynProcStatus >= 2 ) + return; + /* The processor is currently spinning, waiting + * for the xProcStart field to become non-zero + * After we set xProcStart, the processor will + * continue on to secondary_start in iSeries_head.S + */ + xPaca[nr].xProcStart = 1; +} + +static void smp_iSeries_setup_cpu(int nr) +{ +} + +static void +smp_openpic_message_pass(int target, int msg, unsigned long data, int wait) +{ + /* make sure we're sending something that translates to an IPI */ + if ( msg > 0x3 ){ + printk("SMP %d: smp_message_pass: unknown msg %d\n", + smp_processor_id(), msg); + return; + } + switch ( target ) + { + case MSG_ALL: + openpic_cause_IPI(msg, 0xffffffff); + break; + case MSG_ALL_BUT_SELF: + openpic_cause_IPI(msg, + 0xffffffff & ~(1 << smp_processor_id())); + break; + default: + openpic_cause_IPI(msg, 1<processorCount > 1) + openpic_request_IPIs(); + + return naca->processorCount; +} + +static void +smp_chrp_kick_cpu(int nr) +{ + /* Verify we have a Paca for processor nr */ + if ( ( nr <= 0 ) || + ( nr >= maxPacas ) ) + return; + + /* The processor is currently spinning, waiting + * for the xProcStart field to become non-zero + * After we set xProcStart, the processor will + * continue on to secondary_start in iSeries_head.S + */ + xPaca[nr].xProcStart = 1; +} + +static void +smp_chrp_setup_cpu(int cpu_nr) +{ + static atomic_t ready = ATOMIC_INIT(1); + static volatile int frozen = 0; + + if (cpu_nr == 0) { + /* wait for all the others */ + while (atomic_read(&ready) < smp_num_cpus) + barrier(); + atomic_set(&ready, 1); + /* freeze the timebase */ + call_rtas("freeze-time-base", 0, 1, NULL); + mb(); + frozen = 1; + set_tb(0, 0); + xPaca[0].next_jiffy_update_tb = 0; + while (atomic_read(&ready) < smp_num_cpus) + barrier(); + /* thaw the timebase again */ + call_rtas("thaw-time-base", 0, 1, NULL); + mb(); + frozen = 0; + smp_tb_synchronized = 1; + } else { + atomic_inc(&ready); + while (!frozen) + barrier(); + set_tb(0, 0); + xPaca[cpu_nr].next_jiffy_update_tb = 0; + mb(); + atomic_inc(&ready); + while (frozen) + barrier(); + } + + if (OpenPIC_Addr) { + do_openpic_setup_cpu(); + } else { + if (cpu_nr > 0) + xics_setup_cpu(); + } +} + +static void +smp_xics_message_pass(int target, int msg, unsigned long data, int wait) +{ + int i; + + for (i = 0; i < smp_num_cpus; ++i) { + if (target == MSG_ALL || target == i + || (target == MSG_ALL_BUT_SELF + && i != smp_processor_id())) { + set_bit(msg, &xics_ipi_message[i]); + mb(); + xics_cause_IPI(i); + } + } +} + +static int +smp_xics_probe(void) +{ + return naca->processorCount; +} + +#if 0 +static void +smp_xics_setup_cpu(int cpu_nr) +{ + if (cpu_nr > 0) + xics_setup_cpu(); +} +#endif + + +void smp_local_timer_interrupt(struct pt_regs * regs) +{ + if (!--(get_paca()->prof_counter)) { + update_process_times(user_mode(regs)); + (get_paca()->prof_counter)=get_paca()->prof_multiplier; + } +} + +void smp_message_recv(int msg, struct pt_regs *regs) +{ + atomic_inc(&ipi_recv); + + switch( msg ) { + case PPC_MSG_CALL_FUNCTION: + smp_call_function_interrupt(); + break; + case PPC_MSG_RESCHEDULE: + current->need_resched = 1; + break; +#ifdef CONFIG_XMON + case PPC_MSG_XMON_BREAK: + xmon(regs); + break; +#endif /* CONFIG_XMON */ +#ifdef CONFIG_KDB + case PPC_MSG_XMON_BREAK: + /* This isn't finished yet, obviously -TAI */ + kdb(KDB_REASON_KEYBOARD,0, (kdb_eframe_t) regs); + break; +#endif + default: + printk("SMP %d: smp_message_recv(): unknown msg %d\n", + smp_processor_id(), msg); + break; + } +} + +void smp_send_reschedule(int cpu) +{ + /* + * This is only used if `cpu' is running an idle task, + * so it will reschedule itself anyway... + * + * This isn't the case anymore since the other CPU could be + * sleeping and won't reschedule until the next interrupt (such + * as the timer). + * -- Cort + */ + /* This is only used if `cpu' is running an idle task, + so it will reschedule itself anyway... */ + smp_message_pass(cpu, PPC_MSG_RESCHEDULE, 0, 0); +} + +#ifdef CONFIG_XMON +void smp_send_xmon_break(int cpu) +{ + smp_message_pass(cpu, PPC_MSG_XMON_BREAK, 0, 0); +} +#endif /* CONFIG_XMON */ + +static void stop_this_cpu(void *dummy) +{ + __cli(); + while (1) + ; +} + +void smp_send_stop(void) +{ + smp_call_function(stop_this_cpu, NULL, 1, 0); + smp_num_cpus = 1; +} + +/* + * Structure and data for smp_call_function(). This is designed to minimise + * static memory requirements. It also looks cleaner. + * Stolen from the i386 version. + */ +static spinlock_t call_lock = SPIN_LOCK_UNLOCKED; + +static struct call_data_struct { + void (*func) (void *info); + void *info; + atomic_t started; + atomic_t finished; + int wait; +} *call_data; + +/* + * This function sends a 'generic call function' IPI to all other CPUs + * in the system. + * + * [SUMMARY] Run a function on all other CPUs. + * The function to run. This must be fast and non-blocking. + * An arbitrary pointer to pass to the function. + * currently unused. + * If true, wait (atomically) until function has completed on other CPUs. + * [RETURNS] 0 on success, else a negative status code. Does not return until + * remote CPUs are nearly ready to execute <> or are or have executed. + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler, you may call it from a bottom half handler. + */ +int smp_call_function (void (*func) (void *info), void *info, int nonatomic, + int wait) + +{ + struct call_data_struct data; + int ret = -1, cpus = smp_num_cpus-1; + int timeout; + + if (!cpus) + return 0; + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + spin_lock_bh(&call_lock); + call_data = &data; + /* Send a message to all other CPUs and wait for them to respond */ + smp_message_pass(MSG_ALL_BUT_SELF, PPC_MSG_CALL_FUNCTION, 0, 0); + + /* 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)); + goto out; + } + barrier(); + udelay(1); +#ifdef CONFIG_PPC_ISERIES + HvCallCfg_getLps(); +#endif + } + } + ret = 0; + + out: + HMT_medium(); + spin_unlock_bh(&call_lock); + return ret; +} + +void smp_call_function_interrupt(void) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + /* + * Notify initiating CPU that I've grabbed the data and am + * about to execute the function + */ + atomic_inc(&call_data->started); + /* + * At this point the info structure may be out of scope unless wait==1 + */ + (*func)(info); + if (wait) + atomic_inc(&call_data->finished); +} + +static void smp_space_timers( unsigned nr ) +{ + unsigned long offset, i; + + offset = tb_ticks_per_jiffy / nr; + for ( i=1; iloops_per_jiffy=loops_per_jiffy; + + /* + * assume for now that the first cpu booted is + * cpu 0, the master -- Cort + */ + cpu_callin_map[0] = 1; + current->processor = 0; + + init_idle(); + + for (i = 0; i < NR_CPUS; i++) { + paca = &(naca->paca[i]); + paca->prof_counter=1; + paca->prof_multiplier = 1; + if(i != 0) { + /* + * Processor 0's segment table is statically + * initialized to real address 0x5000. The + * Other processor's tables are created and + * initialized here. + */ + + 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 = cpu_hw_index[i]; + memset((void *)paca->xStab_data.virt, 0, PAGE_SIZE); + paca->default_decr = tb_ticks_per_jiffy / decr_overclock; + } + PPCDBG(PPCDBG_SMP, + "\tProcessor %d, stab virt = 0x%lx, stab real = 0x%lx\n", + i, paca->xStab_data.virt, paca->xStab_data.real); + } + + /* + * XXX very rough, assumes 20 bus cycles to read a cache line, + * timebase increments every 4 bus cycles, 32kB L1 data cache. + */ + cacheflush_time = 5 * 1024; + + switch ( _machine ) { + case _MACH_chrp: + if (OpenPIC_Addr) { + smp_ops = &chrp_smp_ops; + } else { + smp_ops = &xics_smp_ops; + } + break; + case _MACH_iSeries: + smp_ops = &iSeries_smp_ops; + break; + default: + printk("SMP not supported on this machine.\n"); + return; + } + + /* 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; + + smp_space_timers( cpu_nr ); + + printk("Waiting for %d CPUs\n", cpu_nr-1); + + for ( i = 1 ; i < cpu_nr; i++ ) { + int c; + struct pt_regs regs; + + /* create a process for the processor */ + /* we don't care about the values in regs since we'll + never reschedule the forked task. */ + /* We DO care about one bit in the pt_regs we + pass to do_fork. That is the MSR_FP bit in + regs.msr. If that bit is on, then do_fork + (via copy_thread) will call giveup_fpu. + giveup_fpu will get a pointer to our (current's) + last register savearea via current->thread.regs + and using that pointer will turn off the MSR_FP, + MSR_FE0 and MSR_FE1 bits. At this point, this + pointer is pointing to some arbitrary point within + our stack */ + + memset(®s, 0, sizeof(struct pt_regs)); + + if (do_fork(CLONE_VM|CLONE_PID, 0, ®s, 0) < 0) + panic("failed fork for CPU %d", i); + p = init_task.prev_task; + if (!p) + panic("No idle task for CPU %d", i); + + PPCDBG(PPCDBG_SMP,"\tProcessor %d, task = 0x%lx\n", i, p); + + del_from_runqueue(p); + unhash_process(p); + init_tasks[i] = p; + + p->processor = i; + p->has_cpu = 1; + current_set[i].task = p; + 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 = (void *)___pa(sp); + PPCDBG(PPCDBG_SMP,"\tstack pointer real = 0x%lx\n", + current_set[i].sp_real); + + /* wake up cpus */ + smp_ops->kick_cpu(i); + + /* + * wait to see if the cpu made a callin (is actually up). + * use this value that I found through experimentation. + * -- Cort + */ + for ( c = 5000; c && !cpu_callin_map[i] ; c-- ) { +#ifdef CONFIG_PPC_ISERIES + HvCallCfg_getLps(); +#endif + udelay(100); + } + + if ( cpu_callin_map[i] ) + { + printk("Processor %d found.\n", i); + PPCDBG(PPCDBG_SMP, "\tProcessor %d found.\n", i); + /* this sync's the decr's -- Cort */ + smp_num_cpus++; + } else { + printk("Processor %d is stuck.\n", i); + PPCDBG(PPCDBG_SMP, "\tProcessor %d is stuck.\n", i); + } + } + + /* Setup CPU 0 last (important) */ + smp_ops->setup_cpu(0); + + if (smp_num_cpus < 2) + smp_tb_synchronized = 1; +} + +void __init smp_commence(void) +{ + /* + * Lets the callin's below out of their loop. + */ + PPCDBG(PPCDBG_SMP, "smp_commence: start\n"); + wmb(); + smp_commenced = 1; +} + +void __init smp_callin(void) +{ + int cpu = current->processor; + + smp_store_cpu_info(cpu); + set_dec(xPaca[cpu].default_decr); + cpu_callin_map[cpu] = 1; + + smp_ops->setup_cpu(cpu); + + init_idle(); + + /* + * This cpu is now "online". Only set them online + * before they enter the loop below since write access + * to the below variable is _not_ guaranteed to be + * atomic. + * -- Cort + */ + cpu_callin_map[cpu] = 1; + + while(!smp_commenced) { +#ifdef CONFIG_PPC_ISERIES + HvCallCfg_getLps(); +#endif + barrier(); + } + __sti(); +} + +/* intel needs this */ +void __init initialize_secondary(void) +{ +} + +/* Activate a secondary processor. */ +int start_secondary(void *unused) +{ + int cpu; + + cpu = current->processor; + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + smp_callin(); + + /* Go into the idle loop. */ + return cpu_idle(NULL); +} + +void __init smp_setup(char *str, int *ints) +{ +} + +int __init setup_profiling_timer(unsigned int multiplier) +{ + return 0; +} + +/* this function is called for each processor + */ +void __init smp_store_cpu_info(int id) +{ + naca->paca[id].pvr = _get_PVR(); +} + +static int __init maxcpus(char *str) +{ + get_option(&str, &max_cpus); + return 1; +} + +__setup("maxcpus=", maxcpus); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/stab.c linux.ac/arch/ppc64/kernel/stab.c --- linux.vanilla/arch/ppc64/kernel/stab.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/stab.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,369 @@ +/* + * PowerPC64 Segment Translation Support. + * + * Dave Engebretsen and Mike Corrigan {engebret|mikejc}@us.ibm.com + * Copyright (c) 2001 Dave Engebretsen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +inline int make_ste(unsigned long stab, + unsigned long esid, unsigned long vsid); +inline void make_slbe(unsigned long esid, unsigned long vsid); +extern unsigned long reloc_offset(void); +extern struct Naca *naca; + +/* + * Initialize the segment table for this processor with the set + * of kernel addresses. + */ + +void +stab_initialize(unsigned long skip_esid_c, unsigned long stab) { + unsigned long offset = reloc_offset(); + unsigned long esid, last_esid, vsid; + + /* + * Put in STEs for the 0xC00000000 - 0xF00000000 segments. + */ + esid = GET_ESID(KERNELBASE); + + /* + * If asked to skip loading esid 0xC00000000 then skip it. + */ + if ( skip_esid_c ) + esid += (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. */ + vsid = get_kernel_vsid(esid << SID_SHIFT); + if(!__is_processor(PV_POWER4)) { + make_ste(stab, esid, vsid); + } else { + make_slbe(esid, vsid ); + } + } +} + +/* + * Create a segment table entry for the given esid/vsid pair. + */ +inline int +make_ste(unsigned long stab, unsigned long esid, unsigned long vsid) { + unsigned long entry, group, old_esid, castout_entry, i; + unsigned int global_entry; + STE *ste, *castout_ste; + + /* Search the primary group first. */ + global_entry = (esid & 0x1f) << 3; + ste = (STE *)(stab | ((esid & 0x1f) << 7)); + + /* + * Find an empty entry, if one exists. + */ + for(group = 0; group < 2; group++) { + for(entry = 0; entry < 8; entry++, ste++) { + if(!(ste->dw0.dw0.v)) { + ste->dw1.dw1.vsid = vsid; + /* Order VSID updte */ + __asm__ __volatile__ ("eieio" : : : "memory"); + ste->dw0.dw0.esid = esid; + ste->dw0.dw0.v = 1; + ste->dw0.dw0.kp = 1; + /* Order update */ + __asm__ __volatile__ ("sync" : : : "memory"); + + return(global_entry | entry); + } + } + /* Now search the secondary group. */ + global_entry = ((~esid) & 0x1f) << 3; + ste = (STE *)(stab | (((~esid) & 0x1f) << 7)); + } + + /* + * Could not find empty entry, pick one with a round robin selection. + * Search all entries in the two groups. Note that the first time + * we get here, we start with entry REGION_COUNT - 1 so the initializer + * can be common with the SLB castout code. + */ + + /* This assumes we never castout when initializing the stab. */ + PMC_SW_PROCESSOR(stab_capacity_castouts); + + castout_entry = get_paca()->xStab_data.next_round_robin; + for(i = 0; i < 16; i++) { + if(castout_entry < 8) { + global_entry = (esid & 0x1f) << 3; + ste = (STE *)(stab | ((esid & 0x1f) << 7)); + castout_ste = ste + castout_entry; + } else { + global_entry = ((~esid) & 0x1f) << 3; + ste = (STE *)(stab | (((~esid) & 0x1f) << 7)); + castout_ste = ste + (castout_entry - 8); + } + + 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; + } + + castout_entry = (castout_entry + 1) & 0xf; + } + + get_paca()->xStab_data.next_round_robin = (castout_entry + 1) & 0xf; + + /* Modify the old entry to the new value. */ + + /* Force previous translations to complete. DRENG */ + __asm__ __volatile__ ("isync" : : : "memory" ); + + castout_ste->dw0.dw0.v = 0; + __asm__ __volatile__ ("sync" : : : "memory" ); /* Order update */ + castout_ste->dw1.dw1.vsid = vsid; + __asm__ __volatile__ ("eieio" : : : "memory" ); /* Order update */ + old_esid = castout_ste->dw0.dw0.esid; + castout_ste->dw0.dw0.esid = esid; + castout_ste->dw0.dw0.v = 1; + castout_ste->dw0.dw0.kp = 1; + __asm__ __volatile__ ("slbie %0" : : "r" (old_esid << SID_SHIFT)); + /* Ensure completion of slbie */ + __asm__ __volatile__ ("sync" : : : "memory" ); + + return(global_entry | (castout_entry & 0x7)); +} + +/* + * Create a segment buffer entry for the given esid/vsid pair. + */ +inline void make_slbe(unsigned long esid, unsigned long vsid) { + unsigned long entry, castout_entry; + slb_dword0 castout_esid_data; + union { + unsigned long word0; + slb_dword0 data; + } esid_data; + union { + unsigned long word0; + slb_dword1 data; + } vsid_data; + + /* + * Find an empty entry, if one exists. + */ + for(entry = 0; entry < naca->slb_size; entry++) { + __asm__ __volatile__("slbmfee %0,%1" + : "=r" (esid_data) : "r" (entry)); + if(!esid_data.data.v) { + /* + * Write the new SLB entry. + */ + vsid_data.word0 = 0; + vsid_data.data.vsid = vsid; + vsid_data.data.kp = 1; + + esid_data.word0 = 0; + esid_data.data.esid = esid; + esid_data.data.v = 1; + esid_data.data.index = entry; + + /* slbie not needed as no previous mapping existed. */ + /* Order update */ + __asm__ __volatile__ ("isync" : : : "memory"); + __asm__ __volatile__ ("slbmte %0,%1" + : : "r" (vsid_data), + "r" (esid_data)); + /* Order update */ + __asm__ __volatile__ ("isync" : : : "memory"); + return; + } + } + + /* + * Could not find empty entry, pick one with a round robin selection. + */ + + PMC_SW_PROCESSOR(stab_capacity_castouts); + + castout_entry = get_paca()->xStab_data.next_round_robin; + __asm__ __volatile__("slbmfee %0,%1" + : "=r" (castout_esid_data) + : "r" (castout_entry)); + + entry = castout_entry; + castout_entry++; + if(castout_entry >= naca->slb_size) { + castout_entry = REGION_COUNT - 1; + } + get_paca()->xStab_data.next_round_robin = castout_entry; + + /* Invalidate the old entry. */ + castout_esid_data.v = 0; /* Set the class to 0 */ + /* slbie not needed as the previous mapping is still valid. */ + __asm__ __volatile__("slbie %0" : : "r" (castout_esid_data)); + + /* + * Write the new SLB entry. + */ + vsid_data.word0 = 0; + vsid_data.data.vsid = vsid; + vsid_data.data.kp = 1; + + esid_data.word0 = 0; + esid_data.data.esid = esid; + esid_data.data.v = 1; + esid_data.data.index = entry; + + __asm__ __volatile__ ("isync" : : : "memory"); /* Order update */ + __asm__ __volatile__ ("slbmte %0,%1" + : : "r" (vsid_data), "r" (esid_data)); + __asm__ __volatile__ ("isync" : : : "memory" ); /* Order update */ +} + +/* + * Allocate a segment table entry for the given ea. + */ +int ste_allocate ( unsigned long ea, + unsigned long trap) +{ + unsigned long vsid, esid; + + PMC_SW_PROCESSOR(stab_faults); + + /* Check for invalid effective addresses. */ + if (!IS_VALID_EA(ea)) { + return 1; + } + + /* Kernel or user address? */ + if (REGION_ID(ea) >= KERNEL_REGION_ID) { + vsid = get_kernel_vsid( ea ); + } else { + struct mm_struct *mm = current->mm; + if ( mm ) { + vsid = get_vsid(mm->context, ea ); + } else { + return 1; + } + } + + esid = GET_ESID(ea); + if (trap == 0x380 || trap == 0x480) { + make_slbe(esid, vsid); + } else { + unsigned char top_entry, stab_entry, *segments, i; + + stab_entry = make_ste(get_paca()->xStab_data.virt, esid, vsid); + PMC_SW_PROCESSOR_A(stab_entry_use, stab_entry & 0xf); + + segments = get_paca()->xSegments; + top_entry = segments[0]; + if(top_entry < (STAB_CACHE_SIZE - 1)) { + top_entry++; + segments[top_entry] = stab_entry; + if(top_entry == STAB_CACHE_SIZE - 1) top_entry = 0xff; + segments[0] = top_entry; + } + } + + return(0); +} + +/* + * Flush all entries from the segment table of the current processor. + * Kernel and Bolted entries are not removed as we cannot tolerate + * faults on those addresses. + */ +void flush_stab(void) { + STE *stab = (STE *) get_paca()->xStab_data.virt; + unsigned char *segments = get_paca()->xSegments; + unsigned long flags, i; + + if(!__is_processor(PV_POWER4)) { + unsigned long entry; + STE *ste; + + /* Force previous translations to complete. DRENG */ + __asm__ __volatile__ ("isync" : : : "memory"); + + __save_and_cli(flags); + if(segments[0] != 0xff) { + for(i = 1; i <= segments[0]; i++) { + ste = stab + segments[i]; + ste->dw0.dw0.v = 0; + PMC_SW_PROCESSOR(stab_invalidations); + } + } else { + /* 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; + PMC_SW_PROCESSOR(stab_invalidations); + } + } + + *((unsigned long *)segments) = 0; + __restore_flags(flags); + + /* Invalidate the SLB. */ + /* Force invals to complete. */ + __asm__ __volatile__ ("sync" : : : "memory"); + /* Flush the SLB. */ + __asm__ __volatile__ ("slbia" : : : "memory"); + /* Force flush to complete. */ + __asm__ __volatile__ ("sync" : : : "memory"); + } else { + /* Invalidate the entire SLB (except entry 1), and then put */ + /* back in the bolted range translation. */ + /* This only does the first bolted segment presently. DRENG */ + unsigned long msr, tmp; + + /* esid v idx */ + slb_dword0 esid_data = {GET_ESID(BOLTEDBASE), 1, 0, 1}; + + /* vsid ks kp */ + slb_dword1 vsid_data = {0, 0, 1, 0, 0, 0, 0}; + vsid_data.vsid = get_kernel_vsid(BOLTEDBASE); + + PMC_SW_PROCESSOR(stab_invalidations); + + __asm__ __volatile__("\n\ + mfmsr %0 \n\ + rldicl %1,%0,48,1 \n\ + rldicl %1,%1,16,0 \n\ + mtmsrd %1 \n\ + isync \n\ + slbia \n\ + isync \n\ + slbmte %2,%3 \n\ + isync \n\ + mtmsrd %0" + : "=&r"(msr), "=&r"(tmp) + : "r" (vsid_data), "r" (esid_data) + : "memory"); + } +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/sys32.S linux.ac/arch/ppc64/kernel/sys32.S --- linux.vanilla/arch/ppc64/kernel/sys32.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/sys32.S Wed Oct 10 01:47:34 2001 @@ -0,0 +1,243 @@ +/* + * sys32.S: I-cache tricks for 32-bit compatability layer simple + * conversions. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) + * Copyright (C) 2000 Ken Aaker (kdaaker@rchland.vnet.ibm.com) + * For PPC ABI convention is parms in Regs 3-10. + * The router in entry.S clears the high 32 bits in the first + * 4 arguments (R3-R6). + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "ppc_asm.h" +#include +#include + +/* NOTE: call as jump breaks return stack, we have to avoid that */ + + .text + +_GLOBAL(sys32_mmap) + clrldi r7, r7, 32 /* int fd parm */ + clrldi r8, r8, 32 /* off_t offset parm */ + b .sys_mmap + +_GLOBAL(sys32_lseek) + extsw r4,r4 /* sign extend off_t offset parm */ + b .sys_lseek + +_GLOBAL(sys32_chmod) +/* Ken Aaker.. hmmm maybe I don't need to do anything here */ + b .sys_chmod + +_GLOBAL(sys32_mknod) +/* Ken Aaker.. hmmm maybe I don't need to do anything here */ + b .sys_mknod + +_GLOBAL(sys32_sendto) + clrldi r7, r7, 32 /* struct sockaddr *addr parm */ + clrldi r8, r8, 32 /* int addr_len parm */ + b .sys_sendto + +_GLOBAL(sys32_recvfrom) + clrldi r7, r7, 32 /* struct sockaddr *addr parm */ + clrldi r8, r8, 32 /* int *addr_len parm */ + b .sys_recvfrom + +_GLOBAL(sys32_getsockopt) + clrldi r7, r7, 32 /* int *optlen parm */ + b .sys_getsockopt + +_GLOBAL(sys32_bdflush) + extsw r4,r4 /* sign extend long data parm */ + b .sys_bdflush + +_GLOBAL(sys32_mmap2) + clrldi r7, r7, 32 /* unsigned long fd parm */ + extsw r8, r8 /* off_t offset */ + b .sys_mmap + +_GLOBAL(sys32_socketcall) /* r3=call, r4=args */ + cmpwi r3, 1 + blt- .do_einval + cmpwi r3, 17 + bgt- .do_einval + subi r3, r3, 1 /* index into socketcall_table vectors and jmp */ + sldi r3, r3, 3 /* each entry is 8 bytes */ + LOADADDR(r10,.socketcall_table_begin) + ldx r10, r10, r3 + mtctr r10 + bctr + +/* Socket function vectored fix ups for 32 bit */ +_STATIC(do_sys_socket) /* sys_socket(int, int, int) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + lwa r5,8(r10) + b .sys_socket + +_STATIC(do_sys_bind) /* sys_bind(int fd, struct sockaddr *, int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwa r5,8(r10) + b .sys_bind + +_STATIC(do_sys_connect) /* sys_connect(int, struct sockaddr *, int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwa r5,8(r10) + b .sys_connect + +_STATIC(do_sys_listen) /* sys_listen(int, int) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + b .sys_listen + +_STATIC(do_sys_accept) /* sys_accept(int, struct sockaddr *, int *) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + b .sys_accept + +_STATIC(do_sys_getsockname) /* sys_getsockname(int, struct sockaddr *, int *) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + b .sys_getsockname + +_STATIC(do_sys_getpeername) /* sys_getpeername(int, struct sockaddr *, int *) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + b .sys_getpeername + +_STATIC(do_sys_socketpair) /* sys_socketpair(int, int, int, int *) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + lwa r5,8(r10) + lwz r6,12(r10) + b .sys_socketpair + +_STATIC(do_sys_send) /* sys_send(int, void *, size_t, unsigned int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + lwz r6,12(r10) + b .sys_send + +_STATIC(do_sys_recv) /* sys_recv(int, void *, size_t, unsigned int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + lwz r6,12(r10) + b .sys_recv + +_STATIC(do_sys_sendto) /* sys32_sendto(int, u32, __kernel_size_t32, unsigned int, u32, int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + lwz r6,12(r10) + lwz r7,16(r10) + lwa r8,20(r10) + b .sys32_sendto + +_STATIC(do_sys_recvfrom) /* sys32_recvfrom(int, u32, __kernel_size_t32, unsigned int, u32, u32) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + lwz r6,12(r10) + lwz r7,16(r10) + lwz r8,20(r10) + b .sys32_recvfrom + +_STATIC(do_sys_shutdown) /* sys_shutdown(int, int) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + b .sys_shutdown + +_STATIC(do_sys_setsockopt) /* sys32_setsockopt(int, int, int, char *, int) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + lwa r5,8(r10) + lwz r6,12(r10) + lwa r7,16(r10) + b .sys32_setsockopt + +_STATIC(do_sys_getsockopt) /* sys32_getsockopt(int, int, int, u32, u32) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + lwa r5,8(r10) + lwz r6,12(r10) + lwz r7,16(r10) + b .sys32_getsockopt + +_STATIC(do_sys_sendmsg) /* sys32_sendmsg(int, struct msghdr32 *, unsigned int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwa r5,8(r10) + b .sys32_sendmsg + +_STATIC(do_sys_recvmsg) /* sys32_recvmsg(int, struct msghdr32 *, unsigned int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwa r5,8(r10) + b .sys32_recvmsg + +_STATIC(do_einval) + li r3,-EINVAL + b .ret_from_syscall_1 +_STATIC(do_efault) + li r3,-EFAULT + b .ret_from_syscall_1 + + .data + .align 8 +_GLOBAL(socketcall_table_begin) + .llong .do_sys_socket + .llong .do_sys_bind + .llong .do_sys_connect + .llong .do_sys_listen + .llong .do_sys_accept + .llong .do_sys_getsockname + .llong .do_sys_getpeername + .llong .do_sys_socketpair + .llong .do_sys_send + .llong .do_sys_recv + .llong .do_sys_sendto + .llong .do_sys_recvfrom + .llong .do_sys_shutdown + .llong .do_sys_setsockopt + .llong .do_sys_getsockopt + .llong .do_sys_sendmsg + .llong .do_sys_recvmsg +_GLOBAL(socketcall_table_end) + .section __ex_table,"a" + .align 3 + .llong .socketcall_table_begin + .llong 0 + .llong .socketcall_table_end + .llong .do_efault + .previous diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/sys_ppc32.c linux.ac/arch/ppc64/kernel/sys_ppc32.c --- linux.vanilla/arch/ppc64/kernel/sys_ppc32.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/sys_ppc32.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,4880 @@ +/* + * sys_ppc32.c: Conversion between 32bit and 64bit native syscalls. + * + * Copyright (C) 2001 IBM + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * + * These routines maintain argument size conversion between 32bit and 64bit + * environment. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +/* + * These are the flags in the MSR that the user is allowed to change + * by modifying the saved value of the MSR on the stack. SE and BE + * should not be in this list since gdb may want to change these. I.e, + * you should be able to step out of a signal handler to see what + * instruction executes next after the signal handler completes. + * Alternately, if you stepped into a signal handler, you should be + * able to continue 'til the next breakpoint from within the signal + * handler, even if the handler returns. + */ +#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) + + + +/* For this source file, we want overflow handling. */ +#undef high2lowuid +#undef high2lowgid +#undef low2highuid +#undef low2highgid + +#define high2lowuid(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid) +#define high2lowgid(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid) +#define low2highuid(uid) ((uid) == (u16)-1) ? (uid_t)-1 : (uid_t)(uid) +#define low2highgid(gid) ((gid) == (u16)-1) ? (gid_t)-1 : (gid_t)(gid) + + + +/* Use this to get at 32-bit user passed pointers. */ +/* Things to consider: the low-level assembly stub does + srl x, 0, x for first four arguments, so if you have + pointer to something in the first four arguments, just + declare it as a pointer, not u32. On the other side, + arguments from 5th onwards should be declared as u32 + for pointers, and need AA() around each usage. + A() macro should be used for places where you e.g. + have some internal variable u32 and just want to get + rid of a compiler warning. AA() has to be used in + places where you want to convert a function argument + to 32bit pointer or when you e.g. access pt_regs + structure and want to consider 32bit registers only. + - + */ +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) \ +({ unsigned long __ret; \ + __asm__ ("clrldi %0, %0, 32" \ + : "=r" (__ret) \ + : "0" (__x)); \ + __ret; \ +}) + + + +/* In order to reduce some races, while at the same time doing additional + * checking and hopefully speeding things up, we copy filenames to the + * kernel data space before using them.. + * + * POSIX.1 2.4: an empty pathname is invalid (ENOENT). + */ +static inline int do_getname32(const char *filename, char *page) +{ + int retval; + + /* 32bit pointer will be always far below TASK_SIZE :)) */ + retval = strncpy_from_user((char *)page, (char *)filename, PAGE_SIZE); + if (retval > 0) { + if (retval < PAGE_SIZE) + return 0; + return -ENAMETOOLONG; + } else if (!retval) + retval = -ENOENT; + return retval; +} + +char * getname32(const char *filename) +{ + char *tmp, *result; + + result = ERR_PTR(-ENOMEM); + tmp = __getname(); + if (tmp) { + int retval = do_getname32(filename, tmp); + + result = tmp; + if (retval < 0) { + putname(tmp); + result = ERR_PTR(retval); + } + } + return result; +} + + + +extern asmlinkage long sys_utime(char * filename, struct utimbuf * times); + +struct utimbuf32 { + __kernel_time_t32 actime, modtime; +}; + +asmlinkage long sys32_utime(char * filename, struct utimbuf32 *times) +{ + struct utimbuf t; + mm_segment_t old_fs; + int ret; + char *filenam; + + PPCDBG(PPCDBG_SYS32NI, "sys32_utime - running - filename=%s, times=%p - pid=%ld, comm=%s \n", filename, times, current->pid, current->comm); + + if (!times) + return sys_utime(filename, NULL); + if (get_user(t.actime, ×->actime) || __get_user(t.modtime, ×->modtime)) + return -EFAULT; + filenam = getname32(filename); + + ret = PTR_ERR(filenam); + if (!IS_ERR(filenam)) { + old_fs = get_fs(); + set_fs (KERNEL_DS); + ret = sys_utime(filenam, &t); + set_fs (old_fs); + putname (filenam); + } + + return ret; +} + + + +struct iovec32 { u32 iov_base; __kernel_size_t32 iov_len; }; + +typedef ssize_t (*IO_fn_t)(struct file *, char *, size_t, loff_t *); + +static long do_readv_writev32(int type, struct file *file, + const struct iovec32 *vector, u32 count) +{ + unsigned long tot_len; + struct iovec iovstack[UIO_FASTIOV]; + struct iovec *iov=iovstack, *ivp; + struct inode *inode; + long retval, i; + IO_fn_t fn; + + /* First get the "struct iovec" from user memory and + * verify all the pointers + */ + if (!count) + return 0; + if(verify_area(VERIFY_READ, vector, sizeof(struct iovec32)*count)) + return -EFAULT; + if (count > UIO_MAXIOV) + return -EINVAL; + if (count > UIO_FASTIOV) { + iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL); + if (!iov) + return -ENOMEM; + } + + tot_len = 0; + i = count; + ivp = iov; + while(i > 0) { + u32 len; + u32 buf; + + __get_user(len, &vector->iov_len); + __get_user(buf, &vector->iov_base); + tot_len += len; + ivp->iov_base = (void *)A(buf); + ivp->iov_len = (__kernel_size_t) len; + vector++; + ivp++; + i--; + } + + inode = file->f_dentry->d_inode; + /* VERIFY_WRITE actually means a read, as we write to user space */ + retval = locks_verify_area((type == VERIFY_WRITE + ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE), + inode, file, file->f_pos, tot_len); + if (retval) { + if (iov != iovstack) + kfree(iov); + return retval; + } + + /* Then do the actual IO. Note that sockets need to be handled + * specially as they have atomicity guarantees and can handle + * iovec's natively + */ + if (inode->i_sock) { + int err; + err = sock_readv_writev(type, inode, file, iov, count, tot_len); + if (iov != iovstack) + kfree(iov); + return err; + } + + if (!file->f_op) { + if (iov != iovstack) + kfree(iov); + return -EINVAL; + } + /* VERIFY_WRITE actually means a read, as we write to user space */ + fn = file->f_op->read; + if (type == VERIFY_READ) + fn = (IO_fn_t) file->f_op->write; + ivp = iov; + while (count > 0) { + void * base; + int len, nr; + + base = ivp->iov_base; + len = ivp->iov_len; + ivp++; + count--; + nr = fn(file, base, len, &file->f_pos); + if (nr < 0) { + if (retval) + break; + retval = nr; + break; + } + retval += nr; + if (nr != len) + break; + } + if (iov != iovstack) + kfree(iov); + return retval; +} + +asmlinkage long sys32_readv(u32 fd, struct iovec32 *vector, u32 count) +{ + struct file *file; + long ret = -EBADF; + + PPCDBG(PPCDBG_SYS32, "sys32_readv - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + file = fget(fd); + if(!file) + goto bad_file; + + if (file->f_op && (file->f_mode & FMODE_READ) && + (file->f_op->readv || file->f_op->read)) + ret = do_readv_writev32(VERIFY_WRITE, file, vector, count); + fput(file); + +bad_file: + PPCDBG(PPCDBG_SYS32, "sys32_readv - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return ret; +} + +asmlinkage long sys32_writev(u32 fd, struct iovec32 *vector, u32 count) +{ + struct file *file; + int ret = -EBADF; + + PPCDBG(PPCDBG_SYS32, "sys32_writev - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + file = fget(fd); + if(!file) + goto bad_file; + if (file->f_op && (file->f_mode & FMODE_WRITE) && + (file->f_op->writev || file->f_op->write)) + ret = do_readv_writev32(VERIFY_READ, file, vector, count); + fput(file); + +bad_file: + PPCDBG(PPCDBG_SYS32, "sys32_writev - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return ret; +} + + + +static inline int get_flock(struct flock *kfl, struct flock32 *ufl) +{ + int err; + + err = get_user(kfl->l_type, &ufl->l_type); + err |= __get_user(kfl->l_whence, &ufl->l_whence); + err |= __get_user(kfl->l_start, &ufl->l_start); + err |= __get_user(kfl->l_len, &ufl->l_len); + err |= __get_user(kfl->l_pid, &ufl->l_pid); + return err; +} + +static inline int put_flock(struct flock *kfl, struct flock32 *ufl) +{ + int err; + + err = __put_user(kfl->l_type, &ufl->l_type); + err |= __put_user(kfl->l_whence, &ufl->l_whence); + err |= __put_user(kfl->l_start, &ufl->l_start); + err |= __put_user(kfl->l_len, &ufl->l_len); + err |= __put_user(kfl->l_pid, &ufl->l_pid); + return err; +} + +extern asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg); +asmlinkage long sys32_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + + PPCDBG(PPCDBG_SYS32NI, "sys32_fcntl - running - pid=%ld, comm=%s \n", current->pid, current->comm); + + switch (cmd) { + case F_GETLK: + case F_SETLK: + case F_SETLKW: + { + struct flock f; + mm_segment_t old_fs; + long ret; + + if(get_flock(&f, (struct flock32 *)arg)) + return -EFAULT; + old_fs = get_fs(); set_fs (KERNEL_DS); + ret = sys_fcntl(fd, cmd, (unsigned long)&f); + set_fs (old_fs); + if(put_flock(&f, (struct flock32 *)arg)) + return -EFAULT; + return ret; + } + default: + return sys_fcntl(fd, cmd, (unsigned long)arg); + } +} + + + + + + +struct ncp_mount_data32 { + int version; + unsigned int ncp_fd; + __kernel_uid_t32 mounted_uid; + __kernel_pid_t32 wdog_pid; + unsigned char mounted_vol[NCP_VOLNAME_LEN + 1]; + unsigned int time_out; + unsigned int retry_count; + unsigned int flags; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_mode_t32 file_mode; + __kernel_mode_t32 dir_mode; +}; + +static void *do_ncp_super_data_conv(void *raw_data) +{ + struct ncp_mount_data *n = (struct ncp_mount_data *)raw_data; + struct ncp_mount_data32 *n32 = (struct ncp_mount_data32 *)raw_data; + + n->dir_mode = n32->dir_mode; + n->file_mode = n32->file_mode; + n->gid = low2highgid(n32->gid); + n->uid = low2highuid(n32->uid); + memmove (n->mounted_vol, n32->mounted_vol, (sizeof (n32->mounted_vol) + 3 * sizeof (unsigned int))); + n->wdog_pid = n32->wdog_pid; + n->mounted_uid = low2highuid(n32->mounted_uid); + return raw_data; +} + +struct smb_mount_data32 { + int version; + __kernel_uid_t32 mounted_uid; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_mode_t32 file_mode; + __kernel_mode_t32 dir_mode; +}; + +static void *do_smb_super_data_conv(void *raw_data) +{ + struct smb_mount_data *s = (struct smb_mount_data *)raw_data; + struct smb_mount_data32 *s32 = (struct smb_mount_data32 *)raw_data; + + s->version = s32->version; + s->mounted_uid = low2highuid(s32->mounted_uid); + s->uid = low2highuid(s32->uid); + s->gid = low2highgid(s32->gid); + s->file_mode = s32->file_mode; + s->dir_mode = s32->dir_mode; + return raw_data; +} + +static int copy_mount_stuff_to_kernel(const void *user, unsigned long *kernel) +{ + int i; + unsigned long page; + struct vm_area_struct *vma; + + *kernel = 0; + if(!user) + return 0; + vma = find_vma(current->mm, (unsigned long)user); + if(!vma || (unsigned long)user < vma->vm_start) + return -EFAULT; + if(!(vma->vm_flags & VM_READ)) + return -EFAULT; + i = vma->vm_end - (unsigned long) user; + if(PAGE_SIZE <= (unsigned long) i) + i = PAGE_SIZE - 1; + if(!(page = __get_free_page(GFP_KERNEL))) + return -ENOMEM; + if(copy_from_user((void *) page, user, i)) { + free_page(page); + return -EFAULT; + } + *kernel = page; + return 0; +} + +#define SMBFS_NAME "smbfs" +#define NCPFS_NAME "ncpfs" + +asmlinkage long sys32_mount(char *dev_name, char *dir_name, char *type, unsigned long new_flags, u32 data) +{ + unsigned long type_page = 0; + unsigned long data_page = 0; + unsigned long dev_page = 0; + unsigned long dir_page = 0; + int err, is_smb, is_ncp; + + PPCDBG(PPCDBG_SYS32, "sys32_mount - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + is_smb = is_ncp = 0; + + err = copy_mount_stuff_to_kernel((const void *)type, &type_page); + if (err) + goto out; + + if (!type_page) { + err = -EINVAL; + goto out; + } + + is_smb = !strcmp((char *)type_page, SMBFS_NAME); + is_ncp = !strcmp((char *)type_page, NCPFS_NAME); + + err = copy_mount_stuff_to_kernel((const void *)AA(data), &data_page); + if (err) + goto type_out; + + err = copy_mount_stuff_to_kernel(dev_name, &dev_page); + if (err) + goto data_out; + + err = copy_mount_stuff_to_kernel(dir_name, &dir_page); + if (err) + goto dev_out; + + if (!is_smb && !is_ncp) { + lock_kernel(); + err = do_mount((char*)dev_page, (char*)dir_page, + (char*)type_page, new_flags, (char*)data_page); + unlock_kernel(); + } else { + if (is_ncp) + do_ncp_super_data_conv((void *)data_page); + else + do_smb_super_data_conv((void *)data_page); + + lock_kernel(); + err = do_mount((char*)dev_page, (char*)dir_page, + (char*)type_page, new_flags, (char*)data_page); + unlock_kernel(); + } + free_page(dir_page); + +dev_out: + free_page(dev_page); + +data_out: + free_page(data_page); + +type_out: + free_page(type_page); + +out: + + PPCDBG(PPCDBG_SYS32, "sys32_mount - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return err; +} + + + +struct dqblk32 { + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u32 dqb_curblocks; + __u32 dqb_ihardlimit; + __u32 dqb_isoftlimit; + __u32 dqb_curinodes; + __kernel_time_t32 dqb_btime; + __kernel_time_t32 dqb_itime; +}; + + +extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr); + +/* Note: it is necessary to treat cmd and id as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_quotactl(u32 cmd_parm, const char *special, u32 id_parm, unsigned long addr) +{ + int cmd = (int)cmd_parm; + int id = (int)id_parm; + int cmds = cmd >> SUBCMDSHIFT; + int err; + struct mem_dqblk d; + mm_segment_t old_fs; + char *spec; + + PPCDBG(PPCDBG_SYS32, "sys32_quotactl - entered - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + switch (cmds) { + case Q_GETQUOTA: + break; + case Q_SETQUOTA: + case Q_SETUSE: + case Q_SETQLIM: + if (copy_from_user (&d, (struct dqblk32 *)addr, + sizeof (struct dqblk32))) + return -EFAULT; + d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; + break; + default: + return sys_quotactl(cmd, special, + id, (caddr_t)addr); + } + spec = getname32 (special); + err = PTR_ERR(spec); + if (IS_ERR(spec)) return err; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + set_fs (old_fs); + putname (spec); + if (cmds == Q_GETQUOTA) { + __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; + ((struct dqblk32 *)&d)->dqb_itime = i; + ((struct dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct dqblk32 *)addr, &d, + sizeof (struct dqblk32))) + return -EFAULT; + } + + PPCDBG(PPCDBG_SYS32, "sys32_quotactl - exited - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + return err; +} + + + +/* readdir & getdents */ +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+sizeof(u32)-1) & ~(sizeof(u32)-1)) + +struct old_linux_dirent32 { + u32 d_ino; + u32 d_offset; + unsigned short d_namlen; + /* unsigned char d_type; */ + char d_name[1]; +}; + +struct readdir_callback32 { + struct old_linux_dirent32 * dirent; + int count; +}; + +static int fillonedir(void * __buf, const char * name, int namlen, + off_t offset, ino_t ino, unsigned int d_type) +{ + struct readdir_callback32 * buf = (struct readdir_callback32 *) __buf; + struct old_linux_dirent32 * dirent; + + if (buf->count) + return -EINVAL; + buf->count++; + dirent = buf->dirent; + put_user(ino, &dirent->d_ino); + put_user(offset, &dirent->d_offset); + put_user(namlen, &dirent->d_namlen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + return 0; +} + +asmlinkage int old32_readdir(unsigned int fd, struct old_linux_dirent32 *dirent, unsigned int count) +{ + int error = -EBADF; + struct file * file; + struct readdir_callback32 buf; + + file = fget(fd); + if (!file) + goto out; + + buf.count = 0; + buf.dirent = dirent; + + error = vfs_readdir(file, fillonedir, &buf); + if (error < 0) + goto out_putf; + error = buf.count; + +out_putf: + fput(file); +out: + return error; +} + +#if 0 +struct linux_dirent32 { + u32 d_ino; + u32 d_off; + unsigned short d_reclen; + char d_name[1]; +}; +#else +struct linux_dirent32 { + u32 d_ino; + u32 d_off; + unsigned short d_reclen; + /* unsigned char d_type; */ + char d_name[256]; +}; +#endif + +struct getdents_callback32 { + struct linux_dirent32 * current_dir; + struct linux_dirent32 * previous; + int count; + int error; +}; + +static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino, + unsigned int d_type) +{ + struct linux_dirent32 * dirent; + struct getdents_callback32 * buf = (struct getdents_callback32 *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) + put_user(offset, &dirent->d_off); + dirent = buf->current_dir; + buf->previous = dirent; + put_user(ino, &dirent->d_ino); + put_user(reclen, &dirent->d_reclen); + /* put_user(d_type, &dirent->d_type); */ + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->current_dir = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage long sys32_getdents(unsigned int fd, struct linux_dirent32 *dirent, unsigned int count) +{ + struct file * file; + struct linux_dirent32 * lastdirent; + struct getdents_callback32 buf; + int error = -EBADF; + + PPCDBG(PPCDBG_SYS32NI, "sys32_getdents - running - fd=%x, pid=%ld, comm=%s \n", fd, current->pid, current->comm); + + file = fget(fd); + if (!file) + goto out; + + buf.current_dir = dirent; + buf.previous = NULL; + buf.count = count; + buf.error = 0; + + error = vfs_readdir(file, filldir, &buf); + if (error < 0) + goto out_putf; + lastdirent = buf.previous; + error = buf.error; + if(lastdirent) { + put_user(file->f_pos, &lastdirent->d_off); + error = count - buf.count; + } +out_putf: + fput(file); + + out: + return error; +} +/* end of readdir & getdents */ + + + +/* 32-bit timeval and related flotsam. */ + +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +struct itimerval32 +{ + struct timeval32 it_interval; + struct timeval32 it_value; +}; + + + + +/* + * Ooo, nasty. We need here to frob 32-bit unsigned longs to + * 64-bit unsigned longs. + */ +static inline int +get_fd_set32(unsigned long n, unsigned long *fdset, u32 *ufdset) +{ + if (ufdset) { + unsigned long odd; + + if (verify_area(VERIFY_WRITE, ufdset, n*sizeof(u32))) + return -EFAULT; + + odd = n & 1UL; + n &= ~1UL; + while (n) { + unsigned long h, l; + __get_user(l, ufdset); + __get_user(h, ufdset+1); + ufdset += 2; + *fdset++ = h << 32 | l; + n -= 2; + } + if (odd) + __get_user(*fdset, ufdset); + } else { + /* Tricky, must clear full unsigned long in the + * kernel fdset at the end, this makes sure that + * actually happens. + */ + memset(fdset, 0, ((n + 1) & ~1)*sizeof(u32)); + } + return 0; +} + +static inline void +set_fd_set32(unsigned long n, u32 *ufdset, unsigned long *fdset) +{ + unsigned long odd; + + if (!ufdset) + return; + + odd = n & 1UL; + n &= ~1UL; + while (n) { + unsigned long h, l; + l = *fdset++; + h = l >> 32; + __put_user(l, ufdset); + __put_user(h, ufdset+1); + ufdset += 2; + n -= 2; + } + if (odd) + __put_user(*fdset, ufdset); +} + + + +#define MAX_SELECT_SECONDS ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) + +asmlinkage long sys32_select(int n, u32 *inp, u32 *outp, u32 *exp, u32 tvp_x) +{ + fd_set_bits fds; + struct timeval32 *tvp = (struct timeval32 *)AA(tvp_x); + char *bits; + unsigned long nn; + long timeout; + int ret, size; + + PPCDBG(PPCDBG_SYS32X, "sys32_select - entered - n=%x, inp=%p, outp=%p - pid=%ld comm=%s \n", n, inp, outp, current->pid, current->comm); + + timeout = MAX_SCHEDULE_TIMEOUT; + if (tvp) { + time_t sec, usec; + if ((ret = verify_area(VERIFY_READ, tvp, sizeof(*tvp))) + || (ret = __get_user(sec, &tvp->tv_sec)) + || (ret = __get_user(usec, &tvp->tv_usec))) + goto out_nofds; + + ret = -EINVAL; + if(sec < 0 || usec < 0) + goto out_nofds; + + if ((unsigned long) sec < MAX_SELECT_SECONDS) { + timeout = (usec + 1000000/HZ - 1) / (1000000/HZ); + timeout += sec * (unsigned long) HZ; + } + } + + ret = -EINVAL; + if (n < 0) + goto out_nofds; + if (n > current->files->max_fdset) + n = current->files->max_fdset; + + /* + * We need 6 bitmaps (in/out/ex for both incoming and outgoing), + * since we used fdset we need to allocate memory in units of + * long-words. + */ + ret = -ENOMEM; + size = FDS_BYTES(n); + bits = kmalloc(6 * size, GFP_KERNEL); + if (!bits) + goto out_nofds; + fds.in = (unsigned long *) bits; + fds.out = (unsigned long *) (bits + size); + fds.ex = (unsigned long *) (bits + 2*size); + fds.res_in = (unsigned long *) (bits + 3*size); + fds.res_out = (unsigned long *) (bits + 4*size); + fds.res_ex = (unsigned long *) (bits + 5*size); + + nn = (n + 8*sizeof(u32) - 1) / (8*sizeof(u32)); + if ((ret = get_fd_set32(nn, fds.in, inp)) || + (ret = get_fd_set32(nn, fds.out, outp)) || + (ret = get_fd_set32(nn, fds.ex, exp))) + goto out; + zero_fd_set(n, fds.res_in); + zero_fd_set(n, fds.res_out); + zero_fd_set(n, fds.res_ex); + + ret = do_select(n, &fds, &timeout); + + if (tvp && !(current->personality & STICKY_TIMEOUTS)) { + time_t sec = 0, usec = 0; + if (timeout) { + sec = timeout / HZ; + usec = timeout % HZ; + usec *= (1000000/HZ); + } + put_user(sec, &tvp->tv_sec); + put_user(usec, &tvp->tv_usec); + } + + if (ret < 0) + goto out; + if (!ret) { + ret = -ERESTARTNOHAND; + if (signal_pending(current)) + goto out; + ret = 0; + } + + set_fd_set32(nn, inp, fds.res_in); + set_fd_set32(nn, outp, fds.res_out); + set_fd_set32(nn, exp, fds.res_ex); + +out: + kfree(bits); + +out_nofds: + PPCDBG(PPCDBG_SYS32X, "sys32_select - exited - pid=%ld, comm=%s \n", current->pid, current->comm); + return ret; +} + + + + +/* + * Due to some executables calling the wrong select we sometimes + * get wrong args. This determines how the args are being passed + * (a single ptr to them all args passed) then calls + * sys_select() with the appropriate args. -- Cort + */ +/* Note: it is necessary to treat n as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int ppc32_select(u32 n, u32* inp, u32* outp, u32* exp, u32 tvp_x) +{ + if ((unsigned int)n >= 4096) + panic("ppc32_select - wrong arguments were passed in \n"); + + return sys32_select((int)n, inp, outp, exp, tvp_x); +} + + + +static int cp_new_stat32(struct inode *inode, struct stat32 *statbuf) +{ + unsigned long ino, blksize, blocks; + kdev_t dev, rdev; + umode_t mode; + nlink_t nlink; + uid_t uid; + gid_t gid; + off_t size; + time_t atime, mtime, ctime; + int err; + + /* Stream the loads of inode data into the load buffer, + * then we push it all into the store buffer below. This + * should give optimal cache performance. + */ + ino = inode->i_ino; + dev = inode->i_dev; + mode = inode->i_mode; + nlink = inode->i_nlink; + uid = inode->i_uid; + gid = inode->i_gid; + rdev = inode->i_rdev; + size = inode->i_size; + atime = inode->i_atime; + mtime = inode->i_mtime; + ctime = inode->i_ctime; + blksize = inode->i_blksize; + blocks = inode->i_blocks; + + err = put_user(kdev_t_to_nr(dev), &statbuf->st_dev); + err |= put_user(ino, &statbuf->st_ino); + err |= put_user(mode, &statbuf->st_mode); + err |= put_user(nlink, &statbuf->st_nlink); + err |= put_user(high2lowuid(uid), &statbuf->st_uid); + err |= put_user(high2lowgid(gid), &statbuf->st_gid); + err |= put_user(kdev_t_to_nr(rdev), &statbuf->st_rdev); + err |= put_user(size, &statbuf->st_size); + err |= put_user(atime, &statbuf->st_atime); + err |= put_user(0, &statbuf->__unused1); + err |= put_user(mtime, &statbuf->st_mtime); + err |= put_user(0, &statbuf->__unused2); + err |= put_user(ctime, &statbuf->st_ctime); + err |= put_user(0, &statbuf->__unused3); + if (blksize) { + err |= put_user(blksize, &statbuf->st_blksize); + err |= put_user(blocks, &statbuf->st_blocks); + } else { + unsigned int tmp_blocks; + +#define D_B 7 +#define I_B (BLOCK_SIZE / sizeof(unsigned short)) + tmp_blocks = (size + BLOCK_SIZE - 1) / BLOCK_SIZE; + if (tmp_blocks > D_B) { + unsigned int indirect; + + indirect = (tmp_blocks - D_B + I_B - 1) / I_B; + tmp_blocks += indirect; + if (indirect > 1) { + indirect = (indirect - 1 + I_B - 1) / I_B; + tmp_blocks += indirect; + if (indirect > 1) + tmp_blocks++; + } + } + err |= put_user(BLOCK_SIZE, &statbuf->st_blksize); + err |= put_user((BLOCK_SIZE / 512) * tmp_blocks, &statbuf->st_blocks); +#undef D_B +#undef I_B + } + err |= put_user(0, &statbuf->__unused4[0]); + err |= put_user(0, &statbuf->__unused4[1]); + + return err; +} + +static __inline__ int +do_revalidate(struct dentry *dentry) +{ + struct inode * inode = dentry->d_inode; + if (inode->i_op && inode->i_op->revalidate) + return inode->i_op->revalidate(dentry); + return 0; +} + +asmlinkage long sys32_newstat(char* filename, struct stat32* statbuf) +{ + struct nameidata nd; + int error; + + PPCDBG(PPCDBG_SYS32X, "sys32_newstat - running - filename=%s, statbuf=%p, pid=%ld, comm=%s\n", filename, statbuf, current->pid, current->comm); + + error = user_path_walk(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_new_stat32(nd.dentry->d_inode, statbuf); + path_release(&nd); + } + return error; +} + +asmlinkage long sys32_newlstat(char * filename, struct stat32 *statbuf) +{ + struct nameidata nd; + int error; + + PPCDBG(PPCDBG_SYS32X, "sys32_newlstat - running - fn=%s, pid=%ld, comm=%s\n", filename, current->pid, current->comm); + + error = user_path_walk_link(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_new_stat32(nd.dentry->d_inode, statbuf); + + path_release(&nd); + } + return error; +} + +asmlinkage long sys32_newfstat(unsigned int fd, struct stat32 *statbuf) +{ + struct file *f; + int err = -EBADF; + + PPCDBG(PPCDBG_SYS32X, "sys32_newfstat - running - fd=%x, pid=%ld, comm=%s\n", fd, current->pid, current->comm); + + f = fget(fd); + if (f) { + struct dentry * dentry = f->f_dentry; + + err = do_revalidate(dentry); + if (!err) + err = cp_new_stat32(dentry->d_inode, statbuf); + fput(f); + } + return err; +} + +static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) +{ + int err; + + err = put_user (kbuf->f_type, &ubuf->f_type); + err |= __put_user (kbuf->f_bsize, &ubuf->f_bsize); + err |= __put_user (kbuf->f_blocks, &ubuf->f_blocks); + err |= __put_user (kbuf->f_bfree, &ubuf->f_bfree); + err |= __put_user (kbuf->f_bavail, &ubuf->f_bavail); + err |= __put_user (kbuf->f_files, &ubuf->f_files); + err |= __put_user (kbuf->f_ffree, &ubuf->f_ffree); + err |= __put_user (kbuf->f_namelen, &ubuf->f_namelen); + err |= __put_user (kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]); + err |= __put_user (kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]); + return err; +} + +extern asmlinkage int sys_statfs(const char * path, struct statfs * buf); + +asmlinkage long sys32_statfs(const char * path, struct statfs32 *buf) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + char *pth; + + PPCDBG(PPCDBG_SYS32X, "sys32_statfs - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + pth = getname32 (path); + ret = PTR_ERR(pth); + if (!IS_ERR(pth)) { + set_fs (KERNEL_DS); + ret = sys_statfs((const char *)pth, &s); + set_fs (old_fs); + putname (pth); + if (put_statfs(buf, &s)) + return -EFAULT; + } + + PPCDBG(PPCDBG_SYS32X, "sys32_statfs - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return ret; +} + +extern asmlinkage long sys_fstatfs(unsigned int fd, struct statfs * buf); + +asmlinkage long sys32_fstatfs(unsigned int fd, struct statfs32 *buf) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SYS32X, "sys32_fstatfs - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_fstatfs(fd, &s); + set_fs (old_fs); + if (put_statfs(buf, &s)) + return -EFAULT; + + PPCDBG(PPCDBG_SYS32X, "sys32_fstatfs - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return ret; +} + + + +extern asmlinkage long sys_sysfs(int option, unsigned long arg1, unsigned long arg2); + +/* Note: it is necessary to treat option as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sysfs(u32 option, u32 arg1, u32 arg2) +{ + PPCDBG(PPCDBG_SYS32, "sys32_sysfs - running - pid=%ld, comm=%s\n", current->pid, current->comm); + return sys_sysfs((int)option, arg1, arg2); +} + + + + +extern unsigned long do_mremap(unsigned long addr, + unsigned long old_len, unsigned long new_len, + unsigned long flags, unsigned long new_addr); + +asmlinkage unsigned long sys32_mremap(unsigned long addr, unsigned long old_len, unsigned long new_len, + unsigned long flags, u32 __new_addr) +{ + unsigned long ret = -EINVAL; + unsigned long new_addr = AA(__new_addr); + + PPCDBG(PPCDBG_SYS32, "sys32_mremap - entered - pid=%ld current=%lx comm=%s\n", + current->pid, current, current->comm); + + + if (old_len > 0xf0000000UL || new_len > 0xf0000000UL) + goto out; + if (addr > 0xf0000000UL - old_len) + goto out; + down_write(¤t->mm->mmap_sem); + if (flags & MREMAP_FIXED) { + if (new_addr > 0xf0000000UL - new_len) + goto out_sem; + } else if (addr > 0xf0000000UL - new_len) { + ret = -ENOMEM; + if (!(flags & MREMAP_MAYMOVE)) + goto out_sem; + new_addr = get_unmapped_area (NULL, addr, new_len, 0, 0); + if (!new_addr) + goto out_sem; + flags |= MREMAP_FIXED; + } + ret = do_mremap(addr, old_len, new_len, flags, new_addr); +out_sem: + up_write(¤t->mm->mmap_sem); +out: + + PPCDBG(PPCDBG_SYS32, "sys32_mremap - exited - pid=%ld current=%lx comm=%s\n", + current->pid, current, current->comm); + + return ret; +} + + + +/* Handle adjtimex compatability. */ +struct timex32 { + u32 modes; + s32 offset, freq, maxerror, esterror; + s32 status, constant, precision, tolerance; + struct timeval32 time; + s32 tick; + s32 ppsfreq, jitter, shift, stabil; + s32 jitcnt, calcnt, errcnt, stbcnt; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; +}; + +extern int do_adjtimex(struct timex *); + +asmlinkage long sys32_adjtimex(struct timex32 *utp) +{ + struct timex txc; + int ret; + + PPCDBG(PPCDBG_SYS32, "sys32_adjtimex - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + memset(&txc, 0, sizeof(struct timex)); + + if(get_user(txc.modes, &utp->modes) || + __get_user(txc.offset, &utp->offset) || + __get_user(txc.freq, &utp->freq) || + __get_user(txc.maxerror, &utp->maxerror) || + __get_user(txc.esterror, &utp->esterror) || + __get_user(txc.status, &utp->status) || + __get_user(txc.constant, &utp->constant) || + __get_user(txc.precision, &utp->precision) || + __get_user(txc.tolerance, &utp->tolerance) || + __get_user(txc.time.tv_sec, &utp->time.tv_sec) || + __get_user(txc.time.tv_usec, &utp->time.tv_usec) || + __get_user(txc.tick, &utp->tick) || + __get_user(txc.ppsfreq, &utp->ppsfreq) || + __get_user(txc.jitter, &utp->jitter) || + __get_user(txc.shift, &utp->shift) || + __get_user(txc.stabil, &utp->stabil) || + __get_user(txc.jitcnt, &utp->jitcnt) || + __get_user(txc.calcnt, &utp->calcnt) || + __get_user(txc.errcnt, &utp->errcnt) || + __get_user(txc.stbcnt, &utp->stbcnt)) + return -EFAULT; + + ret = do_adjtimex(&txc); + + if(put_user(txc.modes, &utp->modes) || + __put_user(txc.offset, &utp->offset) || + __put_user(txc.freq, &utp->freq) || + __put_user(txc.maxerror, &utp->maxerror) || + __put_user(txc.esterror, &utp->esterror) || + __put_user(txc.status, &utp->status) || + __put_user(txc.constant, &utp->constant) || + __put_user(txc.precision, &utp->precision) || + __put_user(txc.tolerance, &utp->tolerance) || + __put_user(txc.time.tv_sec, &utp->time.tv_sec) || + __put_user(txc.time.tv_usec, &utp->time.tv_usec) || + __put_user(txc.tick, &utp->tick) || + __put_user(txc.ppsfreq, &utp->ppsfreq) || + __put_user(txc.jitter, &utp->jitter) || + __put_user(txc.shift, &utp->shift) || + __put_user(txc.stabil, &utp->stabil) || + __put_user(txc.jitcnt, &utp->jitcnt) || + __put_user(txc.calcnt, &utp->calcnt) || + __put_user(txc.errcnt, &utp->errcnt) || + __put_user(txc.stbcnt, &utp->stbcnt)) + ret = -EFAULT; + + return ret; +} + + + +#ifdef CONFIG_MODULES + +extern asmlinkage unsigned long sys_create_module(const char *name_user, size_t size); + +asmlinkage unsigned long sys32_create_module(const char *name_user, __kernel_size_t32 size) +{ + + PPCDBG(PPCDBG_SYS32M, "sys32_create_module - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_create_module(name_user, (size_t)size); +} + + + +extern asmlinkage long sys_init_module(const char *name_user, struct module *mod_user); + +asmlinkage long sys32_init_module(const char *name_user, struct module *mod_user) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_init_module - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_init_module(name_user, mod_user); +} + + + +extern asmlinkage long sys_delete_module(const char *name_user); + +asmlinkage long sys32_delete_module(const char *name_user) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_delete_module - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_delete_module(name_user); +} + + + +struct module_info32 { + u32 addr; + u32 size; + u32 flags; + s32 usecount; +}; + +/* Query various bits about modules. */ + +static inline long +get_mod_name(const char *user_name, char **buf) +{ + unsigned long page; + long retval; + + if ((unsigned long)user_name >= TASK_SIZE + && !segment_eq(get_fs (), KERNEL_DS)) + return -EFAULT; + + page = __get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + retval = strncpy_from_user((char *)page, user_name, PAGE_SIZE); + if (retval > 0) { + if (retval < PAGE_SIZE) { + *buf = (char *)page; + return retval; + } + retval = -ENAMETOOLONG; + } else if (!retval) + retval = -EINVAL; + + free_page(page); + return retval; +} + +static inline void +put_mod_name(char *buf) +{ + free_page((unsigned long)buf); +} + +static __inline__ struct module *find_module(const char *name) +{ + struct module *mod; + + for (mod = module_list; mod ; mod = mod->next) { + if (mod->flags & MOD_DELETED) + continue; + if (!strcmp(mod->name, name)) + break; + } + + return mod; +} + +static int +qm_modules(char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + struct module *mod; + size_t nmod, space, len; + + nmod = space = 0; + + for (mod = module_list; mod->next != NULL; mod = mod->next, ++nmod) { + len = strlen(mod->name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, mod->name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + if (put_user(nmod, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + space += len; + while ((mod = mod->next)->next != NULL) + space += strlen(mod->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static int +qm_deps(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + size_t i, space, len; + + if (mod->next == NULL) + return -EINVAL; + if (!MOD_CAN_QUERY(mod)) + return put_user(0, ret); + + space = 0; + for (i = 0; i < mod->ndeps; ++i) { + const char *dep_name = mod->deps[i].dep->name; + + len = strlen(dep_name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, dep_name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + return put_user(i, ret); + +calc_space_needed: + space += len; + while (++i < mod->ndeps) + space += strlen(mod->deps[i].dep->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static int +qm_refs(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + size_t nrefs, space, len; + struct module_ref *ref; + + if (mod->next == NULL) + return -EINVAL; + if (!MOD_CAN_QUERY(mod)) + if (put_user(0, ret)) + return -EFAULT; + else + return 0; + + space = 0; + for (nrefs = 0, ref = mod->refs; ref ; ++nrefs, ref = ref->next_ref) { + const char *ref_name = ref->ref->name; + + len = strlen(ref_name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, ref_name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + if (put_user(nrefs, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + space += len; + while ((ref = ref->next_ref) != NULL) + space += strlen(ref->ref->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static inline int +qm_symbols(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + size_t i, space, len; + struct module_symbol *s; + char *strings; + unsigned *vals; + + if (!MOD_CAN_QUERY(mod)) + if (put_user(0, ret)) + return -EFAULT; + else + return 0; + + space = mod->nsyms * 2*sizeof(u32); + + i = len = 0; + s = mod->syms; + + if (space > bufsize) + goto calc_space_needed; + + if (!access_ok(VERIFY_WRITE, buf, space)) + return -EFAULT; + + bufsize -= space; + vals = (unsigned *)buf; + strings = buf+space; + + for (; i < mod->nsyms ; ++i, ++s, vals += 2) { + len = strlen(s->name)+1; + if (len > bufsize) + goto calc_space_needed; + + if (copy_to_user(strings, s->name, len) + || __put_user(s->value, vals+0) + || __put_user(space, vals+1)) + return -EFAULT; + + strings += len; + bufsize -= len; + space += len; + } + + if (put_user(i, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + for (; i < mod->nsyms; ++i, ++s) + space += strlen(s->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static inline int +qm_info(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + int error = 0; + + if (mod->next == NULL) + return -EINVAL; + + if (sizeof(struct module_info32) <= bufsize) { + struct module_info32 info; + info.addr = (unsigned long)mod; + info.size = mod->size; + info.flags = mod->flags; + info.usecount = + ((mod_member_present(mod, can_unload) + && mod->can_unload) + ? -1 : atomic_read(&mod->uc.usecount)); + + if (copy_to_user(buf, &info, sizeof(struct module_info32))) + return -EFAULT; + } else + error = -ENOSPC; + + if (put_user(sizeof(struct module_info32), ret)) + return -EFAULT; + + return error; +} + +/* Note: it is necessary to treat which as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_query_module(char *name_user, u32 which, char *buf, __kernel_size_t32 bufsize, u32 ret) +{ + struct module *mod; + int err; + + PPCDBG(PPCDBG_SYS32M, "sys32_query_module - entered - pid=%ld current=%lx comm=%s\n", + current->pid, current, current->comm); + + lock_kernel(); + if (name_user == 0) { + /* This finds "kernel_module" which is not exported. */ + for(mod = module_list; mod->next != NULL; mod = mod->next) + ; + } else { + long namelen; + char *name; + + if ((namelen = get_mod_name(name_user, &name)) < 0) { + err = namelen; + goto out; + } + err = -ENOENT; + if (namelen == 0) { + /* This finds "kernel_module" which is not exported. */ + for(mod = module_list; mod->next != NULL; mod = mod->next) + ; + } else if ((mod = find_module(name)) == NULL) { + put_mod_name(name); + goto out; + } + put_mod_name(name); + } + + switch ((int)which) + { + case 0: + err = 0; + break; + case QM_MODULES: + err = qm_modules(buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_DEPS: + err = qm_deps(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_REFS: + err = qm_refs(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_SYMBOLS: + err = qm_symbols(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_INFO: + err = qm_info(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + default: + err = -EINVAL; + break; + } +out: + unlock_kernel(); + + PPCDBG(PPCDBG_SYS32, "sys32_query_module - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return err; +} + + + +struct kernel_sym32 { + u32 value; + char name[60]; +}; + +extern asmlinkage long sys_get_kernel_syms(struct kernel_sym *table); + +asmlinkage long sys32_get_kernel_syms(struct kernel_sym32 *table) +{ + int len, i; + struct kernel_sym *tbl; + mm_segment_t old_fs; + + PPCDBG(PPCDBG_SYS32, "sys32_get_kernel_syms - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + + len = sys_get_kernel_syms(NULL); + if (!table) return len; + tbl = kmalloc (len * sizeof (struct kernel_sym), GFP_KERNEL); + if (!tbl) return -ENOMEM; + old_fs = get_fs(); + set_fs (KERNEL_DS); + sys_get_kernel_syms(tbl); + set_fs (old_fs); + for (i = 0; i < len; i++, table += sizeof (struct kernel_sym32)) { + if (put_user (tbl[i].value, &table->value) || + copy_to_user (table->name, tbl[i].name, 60)) + break; + } + kfree (tbl); + + PPCDBG(PPCDBG_SYS32, "sys32_get_kernel_syms - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return i; +} + +#else /* CONFIG_MODULES */ + +asmlinkage unsigned long sys32_create_module(const char *name_user, size_t size) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_create_module - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + return -ENOSYS; +} + +asmlinkage long sys32_init_module(const char *name_user, struct module *mod_user) +{ + PPCDBG(PPCDBG_SYS32, "sys32_init_module - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + return -ENOSYS; +} + +asmlinkage long sys32_delete_module(const char *name_user) +{ + PPCDBG(PPCDBG_SYS32, "sys32_delete_module - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + return -ENOSYS; +} + +/* Note: it is necessary to treat which as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_query_module(const char *name_user, u32 which, char *buf, size_t bufsize, size_t *ret) +{ + PPCDBG(PPCDBG_SYS32, "sys32_query_module - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + /* Let the program know about the new interface. Not that it'll do them much good. */ + if ((int)which == 0) + return 0; + + PPCDBG(PPCDBG_SYS32, "sys32_query_module - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + return -ENOSYS; +} + +asmlinkage long sys32_get_kernel_syms(struct kernel_sym *table) +{ + PPCDBG(PPCDBG_SYS32, "sys32_get_kernel_syms - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + return -ENOSYS; +} + +#endif /* CONFIG_MODULES */ + + + +/* Stuff for NFS server syscalls... */ +struct nfsctl_svc32 { + u16 svc32_port; + s32 svc32_nthreads; +}; + +struct nfsctl_client32 { + s8 cl32_ident[NFSCLNT_IDMAX+1]; + s32 cl32_naddr; + struct in_addr cl32_addrlist[NFSCLNT_ADDRMAX]; + s32 cl32_fhkeytype; + s32 cl32_fhkeylen; + u8 cl32_fhkey[NFSCLNT_KEYMAX]; +}; + +struct nfsctl_export32 { + s8 ex32_client[NFSCLNT_IDMAX+1]; + s8 ex32_path[NFS_MAXPATHLEN+1]; + __kernel_dev_t32 ex32_dev; + __kernel_ino_t32 ex32_ino; + s32 ex32_flags; + __kernel_uid_t32 ex32_anon_uid; + __kernel_gid_t32 ex32_anon_gid; +}; + +struct nfsctl_uidmap32 { + u32 ug32_ident; /* char * */ + __kernel_uid_t32 ug32_uidbase; + s32 ug32_uidlen; + u32 ug32_udimap; /* uid_t * */ + __kernel_uid_t32 ug32_gidbase; + s32 ug32_gidlen; + u32 ug32_gdimap; /* gid_t * */ +}; + +struct nfsctl_fhparm32 { + struct sockaddr gf32_addr; + __kernel_dev_t32 gf32_dev; + __kernel_ino_t32 gf32_ino; + s32 gf32_version; +}; + +struct nfsctl_fdparm32 { + struct sockaddr gd32_addr; + s8 gd32_path[NFS_MAXPATHLEN+1]; + s32 gd32_version; +}; + +struct nfsctl_fsparm32 { + struct sockaddr gd32_addr; + s8 gd32_path[NFS_MAXPATHLEN+1]; + s32 gd32_maxlen; +}; + +struct nfsctl_arg32 { + s32 ca32_version; /* safeguard */ + union { + struct nfsctl_svc32 u32_svc; + struct nfsctl_client32 u32_client; + struct nfsctl_export32 u32_export; + struct nfsctl_uidmap32 u32_umap; + struct nfsctl_fhparm32 u32_getfh; + struct nfsctl_fdparm32 u32_getfd; + struct nfsctl_fsparm32 u32_getfs; + } u; +#define ca32_svc u.u32_svc +#define ca32_client u.u32_client +#define ca32_export u.u32_export +#define ca32_umap u.u32_umap +#define ca32_getfh u.u32_getfh +#define ca32_getfd u.u32_getfd +#define ca32_getfs u.u32_getfs +#define ca32_authd u.u32_authd +}; + +union nfsctl_res32 { + __u8 cr32_getfh[NFS_FHSIZE]; + struct knfsd_fh cr32_getfs; +}; + +static int nfs_svc32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= __get_user(karg->ca_svc.svc_port, &arg32->ca32_svc.svc32_port); + err |= __get_user(karg->ca_svc.svc_nthreads, &arg32->ca32_svc.svc32_nthreads); + return err; +} + +static int nfs_clnt32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_client.cl_ident[0], + &arg32->ca32_client.cl32_ident[0], + NFSCLNT_IDMAX); + err |= __get_user(karg->ca_client.cl_naddr, &arg32->ca32_client.cl32_naddr); + err |= copy_from_user(&karg->ca_client.cl_addrlist[0], + &arg32->ca32_client.cl32_addrlist[0], + (sizeof(struct in_addr) * NFSCLNT_ADDRMAX)); + err |= __get_user(karg->ca_client.cl_fhkeytype, + &arg32->ca32_client.cl32_fhkeytype); + err |= __get_user(karg->ca_client.cl_fhkeylen, + &arg32->ca32_client.cl32_fhkeylen); + err |= copy_from_user(&karg->ca_client.cl_fhkey[0], + &arg32->ca32_client.cl32_fhkey[0], + NFSCLNT_KEYMAX); + return err; +} + +static int nfs_exp32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_export.ex_client[0], + &arg32->ca32_export.ex32_client[0], + NFSCLNT_IDMAX); + err |= copy_from_user(&karg->ca_export.ex_path[0], + &arg32->ca32_export.ex32_path[0], + NFS_MAXPATHLEN); + err |= __get_user(karg->ca_export.ex_dev, + &arg32->ca32_export.ex32_dev); + err |= __get_user(karg->ca_export.ex_ino, + &arg32->ca32_export.ex32_ino); + err |= __get_user(karg->ca_export.ex_flags, + &arg32->ca32_export.ex32_flags); + err |= __get_user(karg->ca_export.ex_anon_uid, + &arg32->ca32_export.ex32_anon_uid); + err |= __get_user(karg->ca_export.ex_anon_gid, + &arg32->ca32_export.ex32_anon_gid); + karg->ca_export.ex_anon_uid = high2lowuid(karg->ca_export.ex_anon_uid); + karg->ca_export.ex_anon_gid = high2lowgid(karg->ca_export.ex_anon_gid); + return err; +} + +static int nfs_uud32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + u32 uaddr; + int i; + int err; + + memset(karg, 0, sizeof(*karg)); + if(__get_user(karg->ca_version, &arg32->ca32_version)) + return -EFAULT; + karg->ca_umap.ug_ident = (char *)get_free_page(GFP_USER); + if(!karg->ca_umap.ug_ident) + return -ENOMEM; + err = __get_user(uaddr, &arg32->ca32_umap.ug32_ident); + if(strncpy_from_user(karg->ca_umap.ug_ident, + (char *)A(uaddr), PAGE_SIZE) <= 0) + return -EFAULT; + err |= __get_user(karg->ca_umap.ug_uidbase, + &arg32->ca32_umap.ug32_uidbase); + err |= __get_user(karg->ca_umap.ug_uidlen, + &arg32->ca32_umap.ug32_uidlen); + err |= __get_user(uaddr, &arg32->ca32_umap.ug32_udimap); + if (err) + return -EFAULT; + karg->ca_umap.ug_udimap = kmalloc((sizeof(uid_t) * karg->ca_umap.ug_uidlen), + GFP_USER); + if(!karg->ca_umap.ug_udimap) + return -ENOMEM; + for(i = 0; i < karg->ca_umap.ug_uidlen; i++) + err |= __get_user(karg->ca_umap.ug_udimap[i], + &(((__kernel_uid_t32 *)A(uaddr))[i])); + err |= __get_user(karg->ca_umap.ug_gidbase, + &arg32->ca32_umap.ug32_gidbase); + err |= __get_user(karg->ca_umap.ug_uidlen, + &arg32->ca32_umap.ug32_gidlen); + err |= __get_user(uaddr, &arg32->ca32_umap.ug32_gdimap); + if (err) + return -EFAULT; + karg->ca_umap.ug_gdimap = kmalloc((sizeof(gid_t) * karg->ca_umap.ug_uidlen), + GFP_USER); + if(!karg->ca_umap.ug_gdimap) + return -ENOMEM; + for(i = 0; i < karg->ca_umap.ug_gidlen; i++) + err |= __get_user(karg->ca_umap.ug_gdimap[i], + &(((__kernel_gid_t32 *)A(uaddr))[i])); + + return err; +} + +static int nfs_getfh32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfh.gf_addr, + &arg32->ca32_getfh.gf32_addr, + (sizeof(struct sockaddr))); + err |= __get_user(karg->ca_getfh.gf_dev, + &arg32->ca32_getfh.gf32_dev); + err |= __get_user(karg->ca_getfh.gf_ino, + &arg32->ca32_getfh.gf32_ino); + err |= __get_user(karg->ca_getfh.gf_version, + &arg32->ca32_getfh.gf32_version); + return err; +} + +static int nfs_getfd32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfd.gd_addr, + &arg32->ca32_getfd.gd32_addr, + (sizeof(struct sockaddr))); + err |= copy_from_user(&karg->ca_getfd.gd_path, + &arg32->ca32_getfd.gd32_path, + (NFS_MAXPATHLEN+1)); + err |= __get_user(karg->ca_getfd.gd_version, + &arg32->ca32_getfd.gd32_version); + return err; +} + +static int nfs_getfs32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfs.gd_addr, + &arg32->ca32_getfs.gd32_addr, + (sizeof(struct sockaddr))); + err |= copy_from_user(&karg->ca_getfs.gd_path, + &arg32->ca32_getfs.gd32_path, + (NFS_MAXPATHLEN+1)); + err |= __get_user(karg->ca_getfs.gd_maxlen, + &arg32->ca32_getfs.gd32_maxlen); + return err; +} + +/* This really doesn't need translations, we are only passing + * back a union which contains opaque nfs file handle data. + */ +static int nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res32) +{ + return copy_to_user(res32, kres, sizeof(*res32)); +} + +/* Note: it is necessary to treat cmd_parm as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +int asmlinkage sys32_nfsservctl(u32 cmd_parm, struct nfsctl_arg32 *arg32, union nfsctl_res32 *res32) +{ + int cmd = (int)cmd_parm; + struct nfsctl_arg *karg = NULL; + union nfsctl_res *kres = NULL; + mm_segment_t oldfs; + int err; + + karg = kmalloc(sizeof(*karg), GFP_USER); + if(!karg) + return -ENOMEM; + if(res32) { + kres = kmalloc(sizeof(*kres), GFP_USER); + if(!kres) { + kfree(karg); + return -ENOMEM; + } + } + switch(cmd) { + case NFSCTL_SVC: + err = nfs_svc32_trans(karg, arg32); + break; + case NFSCTL_ADDCLIENT: + err = nfs_clnt32_trans(karg, arg32); + break; + case NFSCTL_DELCLIENT: + err = nfs_clnt32_trans(karg, arg32); + break; + case NFSCTL_EXPORT: + case NFSCTL_UNEXPORT: + err = nfs_exp32_trans(karg, arg32); + break; + /* This one is unimplemented, be we're ready for it. */ + case NFSCTL_UGIDUPDATE: + err = nfs_uud32_trans(karg, arg32); + break; + case NFSCTL_GETFH: + err = nfs_getfh32_trans(karg, arg32); + break; + case NFSCTL_GETFD: + err = nfs_getfd32_trans(karg, arg32); + break; + case NFSCTL_GETFS: + err = nfs_getfs32_trans(karg, arg32); + break; + default: + err = -EINVAL; + break; + } + if(err) + goto done; + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sys_nfsservctl(cmd, karg, kres); + set_fs(oldfs); + + if (err) + goto done; + + if((cmd == NFSCTL_GETFH) || + (cmd == NFSCTL_GETFD) || + (cmd == NFSCTL_GETFS)) + err = nfs_getfh32_res_trans(kres, res32); + +done: + if(karg) { + if(cmd == NFSCTL_UGIDUPDATE) { + if(karg->ca_umap.ug_ident) + kfree(karg->ca_umap.ug_ident); + if(karg->ca_umap.ug_udimap) + kfree(karg->ca_umap.ug_udimap); + if(karg->ca_umap.ug_gdimap) + kfree(karg->ca_umap.ug_gdimap); + } + kfree(karg); + } + if(kres) + kfree(kres); + return err; +} + + + +struct timespec32 { + s32 tv_sec; + s32 tv_nsec; +}; + +extern asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp); + +asmlinkage long sys32_nanosleep(struct timespec32 *rqtp, struct timespec32 *rmtp) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + PPCDBG(PPCDBG_SYS32NI, "sys32_nanosleep - running - pid=%ld, comm=%s \n", current->pid, current->comm); + + if (get_user (t.tv_sec, &rqtp->tv_sec) || + __get_user (t.tv_nsec, &rqtp->tv_nsec)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_nanosleep(&t, rmtp ? &t : NULL); + set_fs (old_fs); + if (rmtp && ret == -EINTR) { + if (__put_user (t.tv_sec, &rmtp->tv_sec) || + __put_user (t.tv_nsec, &rmtp->tv_nsec)) + return -EFAULT; + } + + return ret; +} + + + + +/* These are here just in case some old sparc32 binary calls it. */ +asmlinkage long sys32_pause(void) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_pause - running - pid=%ld, comm=%s \n", current->pid, current->comm); + + current->state = TASK_INTERRUPTIBLE; + schedule(); + + return -ERESTARTNOHAND; +} + + + +static inline long get_it32(struct itimerval *o, struct itimerval32 *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + (__get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec) | + __get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec) | + __get_user(o->it_value.tv_sec, &i->it_value.tv_sec) | + __get_user(o->it_value.tv_usec, &i->it_value.tv_usec))); +} + +static inline long put_it32(struct itimerval32 *o, struct itimerval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + (__put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) | + __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) | + __put_user(i->it_value.tv_sec, &o->it_value.tv_sec) | + __put_user(i->it_value.tv_usec, &o->it_value.tv_usec))); +} + +static inline long get_tv32(struct timeval *o, struct timeval32 *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + (__get_user(o->tv_sec, &i->tv_sec) | + __get_user(o->tv_usec, &i->tv_usec))); +} + +static inline long put_tv32(struct timeval32 *o, struct timeval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + (__put_user(i->tv_sec, &o->tv_sec) | + __put_user(i->tv_usec, &o->tv_usec))); +} + + + + +extern int do_getitimer(int which, struct itimerval *value); + +/* Note: it is necessary to treat which as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getitimer(u32 which, struct itimerval32 *it) +{ + struct itimerval kit; + int error; + + PPCDBG(PPCDBG_SYS32, "sys32_getitimer - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + error = do_getitimer((int)which, &kit); + if (!error && put_it32(it, &kit)) + error = -EFAULT; + + + PPCDBG(PPCDBG_SYS32, "sys32_getitimer - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + return error; +} + + + +extern int do_setitimer(int which, struct itimerval *, struct itimerval *); + +/* Note: it is necessary to treat which as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setitimer(u32 which, struct itimerval32 *in, struct itimerval32 *out) +{ + struct itimerval kin, kout; + int error; + + PPCDBG(PPCDBG_SYS32, "sys32_setitimer - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + if (in) { + if (get_it32(&kin, in)) + return -EFAULT; + } else + memset(&kin, 0, sizeof(kin)); + + error = do_setitimer((int)which, &kin, out ? &kout : NULL); + if (error || !out) + return error; + if (put_it32(out, &kout)) + return -EFAULT; + + + PPCDBG(PPCDBG_SYS32, "sys32_setitimer - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + return 0; +} + + +#define RLIM_INFINITY32 0x7fffffff +#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) + +struct rlimit32 { + u32 rlim_cur; + u32 rlim_max; +}; + +extern asmlinkage long sys_getrlimit(unsigned int resource, struct rlimit *rlim); +asmlinkage long sys32_getrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs = get_fs (); + + PPCDBG(PPCDBG_SYS32, "sys32_getrlimit - entered - pid=%ld, comm=%s\n", current->pid, current->comm); + + set_fs (KERNEL_DS); + ret = sys_getrlimit(resource, &r); + set_fs (old_fs); + if (!ret) { + ret = put_user (RESOURCE32(r.rlim_cur), &rlim->rlim_cur); + ret |= __put_user (RESOURCE32(r.rlim_max), &rlim->rlim_max); + } + + PPCDBG(PPCDBG_SYS32, "sys32_getrlimit - exited w/ %x - resource=%x, rlim_max=%lx, rlim_cur=%lx - pid=%ld, comm=%s\n", + ret, resource, RESOURCE32(r.rlim_max), RESOURCE32(r.rlim_cur), current->pid, current->comm); + return ret; +} + +/* Back compatibility for getrlimit. Needed for some apps. */ +asmlinkage long sys32_old_getrlimit(unsigned int resource, struct rlimit32* rlim) +{ + struct rlimit x; // 64-bit version of the resource limits. + struct rlimit32 x32; // 32-bit version of the resource limits. + long rc = 0; + + if (resource >= RLIM_NLIMITS) + { + PPCDBG(PPCDBG_SYS32, "sys32_old_getrlimit - specified resource is too large (%x) - pid=%ld, comm=%s\n", resource, current->pid, current->comm); + return -EINVAL; + } + + memcpy(&x, current->rlim+resource, sizeof(struct rlimit)); + + if(x.rlim_cur > RLIM_INFINITY32) + x32.rlim_cur = RLIM_INFINITY32; + else + x32.rlim_cur = x.rlim_cur; + + if(x.rlim_max > RLIM_INFINITY32) + x32.rlim_max = RLIM_INFINITY32; + else + x32.rlim_max = x.rlim_max; + + rc = (copy_to_user(rlim, &x32, sizeof(x32))) ? (-EFAULT) : 0; + if (rc == 0) + { + PPCDBG(PPCDBG_SYS32, "sys32_old_getrlimit - current=%x, maximum=%x - pid=%ld, comm=%s\n", x32.rlim_cur, x32.rlim_max, current->pid, current->comm); + } + else + { + PPCDBG(PPCDBG_SYS32, "sys32_old_getrlimit - unable to copy into user's storage - pid=%ld, comm=%s\n", current->pid, current->comm); + } + return rc; +} + +extern asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit *rlim); +asmlinkage long sys32_setrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + long ret; + mm_segment_t old_fs = get_fs (); + + PPCDBG(PPCDBG_SYS32, "sys32_setrlimit - entered - resource=%x, rlim=%p - pid=%ld, comm=%s\n", resource, rlim, current->pid, current->comm); + + if (resource >= RLIM_NLIMITS) return -EINVAL; + if (get_user (r.rlim_cur, &rlim->rlim_cur) || + __get_user (r.rlim_max, &rlim->rlim_max)) + return -EFAULT; + if (r.rlim_cur >= RLIM_INFINITY32) + r.rlim_cur = RLIM_INFINITY; + if (r.rlim_max >= RLIM_INFINITY32) + r.rlim_max = RLIM_INFINITY; + set_fs (KERNEL_DS); + ret = sys_setrlimit(resource, &r); + set_fs (old_fs); + + PPCDBG(PPCDBG_SYS32, "sys32_setrlimit - exited w/ ret=%x - pid=%ld, comm=%s\n", ret, current->pid, current->comm); + return ret; +} + + +struct rusage32 { + struct timeval32 ru_utime; + struct timeval32 ru_stime; + s32 ru_maxrss; + s32 ru_ixrss; + s32 ru_idrss; + s32 ru_isrss; + s32 ru_minflt; + s32 ru_majflt; + s32 ru_nswap; + s32 ru_inblock; + s32 ru_oublock; + s32 ru_msgsnd; + s32 ru_msgrcv; + s32 ru_nsignals; + s32 ru_nvcsw; + s32 ru_nivcsw; +}; + +static int put_rusage (struct rusage32 *ru, struct rusage *r) +{ + int err; + + err = put_user (r->ru_utime.tv_sec, &ru->ru_utime.tv_sec); + err |= __put_user (r->ru_utime.tv_usec, &ru->ru_utime.tv_usec); + err |= __put_user (r->ru_stime.tv_sec, &ru->ru_stime.tv_sec); + err |= __put_user (r->ru_stime.tv_usec, &ru->ru_stime.tv_usec); + err |= __put_user (r->ru_maxrss, &ru->ru_maxrss); + err |= __put_user (r->ru_ixrss, &ru->ru_ixrss); + err |= __put_user (r->ru_idrss, &ru->ru_idrss); + err |= __put_user (r->ru_isrss, &ru->ru_isrss); + err |= __put_user (r->ru_minflt, &ru->ru_minflt); + err |= __put_user (r->ru_majflt, &ru->ru_majflt); + err |= __put_user (r->ru_nswap, &ru->ru_nswap); + err |= __put_user (r->ru_inblock, &ru->ru_inblock); + err |= __put_user (r->ru_oublock, &ru->ru_oublock); + err |= __put_user (r->ru_msgsnd, &ru->ru_msgsnd); + err |= __put_user (r->ru_msgrcv, &ru->ru_msgrcv); + err |= __put_user (r->ru_nsignals, &ru->ru_nsignals); + err |= __put_user (r->ru_nvcsw, &ru->ru_nvcsw); + err |= __put_user (r->ru_nivcsw, &ru->ru_nivcsw); + return err; +} + + +extern asmlinkage long sys_getrusage(int who, struct rusage *ru); + +/* Note: it is necessary to treat who as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getrusage(u32 who, struct rusage32 *ru) +{ + struct rusage r; + int ret; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SYS32X, "sys32_getrusage - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + set_fs (KERNEL_DS); + ret = sys_getrusage((int)who, &r); + set_fs (old_fs); + if (put_rusage (ru, &r)) + return -EFAULT; + + return ret; +} + + + + +struct sysinfo32 { + s32 uptime; + u32 loads[3]; + u32 totalram; + u32 freeram; + u32 sharedram; + u32 bufferram; + u32 totalswap; + u32 freeswap; + unsigned short procs; + char _f[22]; +}; + +extern asmlinkage long sys_sysinfo(struct sysinfo *info); + +asmlinkage long sys32_sysinfo(struct sysinfo32 *info) +{ + struct sysinfo s; + int ret, err; + mm_segment_t old_fs = get_fs (); + + PPCDBG(PPCDBG_SYS32, "sys32_sysinfo - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_sysinfo(&s); + set_fs (old_fs); + err = put_user (s.uptime, &info->uptime); + err |= __put_user (s.loads[0], &info->loads[0]); + err |= __put_user (s.loads[1], &info->loads[1]); + err |= __put_user (s.loads[2], &info->loads[2]); + err |= __put_user (s.totalram, &info->totalram); + err |= __put_user (s.freeram, &info->freeram); + err |= __put_user (s.sharedram, &info->sharedram); + err |= __put_user (s.bufferram, &info->bufferram); + err |= __put_user (s.totalswap, &info->totalswap); + err |= __put_user (s.freeswap, &info->freeswap); + err |= __put_user (s.procs, &info->procs); + if (err) + return -EFAULT; + + PPCDBG(PPCDBG_SYS32, "sys32_sysinfo - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return ret; +} + + + + +/* Translations due to time_t size differences. Which affects all + sorts of things, like timeval and itimerval. */ +extern struct timezone sys_tz; +extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz); + +asmlinkage long sys32_gettimeofday(struct timeval32 *tv, struct timezone *tz) +{ + + PPCDBG(PPCDBG_SYS32X, "sys32_gettimeofday - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + if (tv) { + struct timeval ktv; + do_gettimeofday(&ktv); + if (put_tv32(tv, &ktv)) + return -EFAULT; + } + if (tz) { + if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) + return -EFAULT; + } + + return 0; +} + + + +asmlinkage long sys32_settimeofday(struct timeval32 *tv, struct timezone *tz) +{ + struct timeval ktv; + struct timezone ktz; + + PPCDBG(PPCDBG_SYS32, "sys32_settimeofday - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + if (tv) { + if (get_tv32(&ktv, tv)) + return -EFAULT; + } + if (tz) { + if (copy_from_user(&ktz, tz, sizeof(ktz))) + return -EFAULT; + } + + return do_sys_settimeofday(tv ? &ktv : NULL, tz ? &ktz : NULL); +} + + + + +struct tms32 { + __kernel_clock_t32 tms_utime; + __kernel_clock_t32 tms_stime; + __kernel_clock_t32 tms_cutime; + __kernel_clock_t32 tms_cstime; +}; + +extern asmlinkage long sys_times(struct tms * tbuf); + +asmlinkage long sys32_times(struct tms32 *tbuf) +{ + struct tms t; + long ret; + mm_segment_t old_fs = get_fs (); + int err; + + PPCDBG(PPCDBG_SYS32, "sys32_times - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_times(tbuf ? &t : NULL); + set_fs (old_fs); + if (tbuf) { + err = put_user (t.tms_utime, &tbuf->tms_utime); + err |= __put_user (t.tms_stime, &tbuf->tms_stime); + err |= __put_user (t.tms_cutime, &tbuf->tms_cutime); + err |= __put_user (t.tms_cstime, &tbuf->tms_cstime); + if (err) + ret = -EFAULT; + } + + PPCDBG(PPCDBG_SYS32, "sys32_times - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return ret; +} + + + + + +struct msgbuf32 { s32 mtype; char mtext[1]; }; + +struct ipc_perm32 +{ + key_t key; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_uid_t32 cuid; + __kernel_gid_t32 cgid; + __kernel_mode_t32 mode; + unsigned short seq; +}; + +struct semid_ds32 { + struct ipc_perm32 sem_perm; /* permissions .. see ipc.h */ + __kernel_time_t32 sem_otime; /* last semop time */ + __kernel_time_t32 sem_ctime; /* last change time */ + u32 sem_base; /* ptr to first semaphore in array */ + u32 sem_pending; /* pending operations to be processed */ + u32 sem_pending_last; /* last pending operation */ + u32 undo; /* undo requests on this array */ + unsigned short sem_nsems; /* no. of semaphores in array */ +}; + +struct semid64_ds32 { + struct ipc64_perm sem_perm; /* this structure is the same on ppc32 and ppc64 */ + unsigned int __pad1; + __kernel_time_t32 sem_otime; + unsigned int __pad2; + __kernel_time_t32 sem_ctime; + u32 sem_nsems; + u32 __unused1; + u32 __unused2; +}; + +struct msqid_ds32 +{ + struct ipc_perm32 msg_perm; + u32 msg_first; + u32 msg_last; + __kernel_time_t32 msg_stime; + __kernel_time_t32 msg_rtime; + __kernel_time_t32 msg_ctime; + u32 wwait; + u32 rwait; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + __kernel_ipc_pid_t32 msg_lspid; + __kernel_ipc_pid_t32 msg_lrpid; +}; + +struct msqid64_ds32 { + struct ipc64_perm msg_perm; + unsigned int __pad1; + __kernel_time_t32 msg_stime; + unsigned int __pad2; + __kernel_time_t32 msg_rtime; + unsigned int __pad3; + __kernel_time_t32 msg_ctime; + unsigned int msg_cbytes; + unsigned int msg_qnum; + unsigned int msg_qbytes; + __kernel_pid_t32 msg_lspid; + __kernel_pid_t32 msg_lrpid; + unsigned int __unused1; + unsigned int __unused2; +}; + + +struct shmid_ds32 { + struct ipc_perm32 shm_perm; + int shm_segsz; + __kernel_time_t32 shm_atime; + __kernel_time_t32 shm_dtime; + __kernel_time_t32 shm_ctime; + __kernel_ipc_pid_t32 shm_cpid; + __kernel_ipc_pid_t32 shm_lpid; + unsigned short shm_nattch; +}; + + +struct shmid64_ds32 { + struct ipc64_perm shm_perm; + unsigned int __pad1; + __kernel_time_t32 shm_atime; + unsigned int __pad2; + __kernel_time_t32 shm_dtime; + unsigned int __pad3; + __kernel_time_t32 shm_ctime; + __kernel_size_t32 shm_segsz; + __kernel_pid_t32 shm_cpid; + __kernel_pid_t32 shm_lpid; + unsigned int shm_nattch; + unsigned int __unused1; + unsigned int __unused2; +}; + + + +/* + * sys32_ipc + () is the de-multiplexer for the SysV IPC calls in 32bit emulation.. + * + * This is really horribly ugly. + */ +static long do_sys32_semctl(int first, int second, int third, void *uptr) +{ + union semun fourth; + u32 pad; + int err, err2; + mm_segment_t old_fs; + + if (!uptr) + return -EINVAL; + err = -EFAULT; + if (get_user (pad, (u32 *)uptr)) + return err; + if(third == SETVAL) + fourth.val = (int)pad; + else + fourth.__pad = (void *)A(pad); + switch (third & (~IPC_64)) { + + case IPC_INFO: + case IPC_RMID: + case SEM_INFO: + case GETVAL: + case GETPID: + case GETNCNT: + case GETZCNT: + case GETALL: + case SETALL: + case SETVAL: + err = sys_semctl (first, second, third, fourth); + break; + + case IPC_STAT: + case SEM_STAT: + if (third & IPC_64) + { + struct semid64_ds s; + struct semid64_ds32 *usp = (struct semid64_ds32 *)A(pad); + + fourth.__pad = &s; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_semctl (first, second, third, fourth); + set_fs (old_fs); + err2 = copy_to_user (&usp->sem_perm, &s.sem_perm, sizeof(struct ipc64_perm) + 2*sizeof(time_t)); + err2 |= __put_user (s.sem_nsems, &usp->sem_nsems); + if (err2) err = -EFAULT; + + } + else + { + struct semid64_ds s; + struct semid_ds32 *usp; + + usp = (struct semid_ds32 *)A(pad); + fourth.__pad = &s; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_semctl (first, second, third, fourth); + set_fs (old_fs); + err2 = put_user(s.sem_perm.key, &usp->sem_perm.key); + err2 |= __put_user(s.sem_perm.uid, &usp->sem_perm.uid); + err2 |= __put_user(s.sem_perm.gid, &usp->sem_perm.gid); + err2 |= __put_user(s.sem_perm.cuid, + &usp->sem_perm.cuid); + err2 |= __put_user (s.sem_perm.cgid, + &usp->sem_perm.cgid); + err2 |= __put_user (s.sem_perm.mode, + &usp->sem_perm.mode); + err2 |= __put_user (s.sem_perm.seq, &usp->sem_perm.seq); + err2 |= __put_user (s.sem_otime, &usp->sem_otime); + err2 |= __put_user (s.sem_ctime, &usp->sem_ctime); + err2 |= __put_user (s.sem_nsems, &usp->sem_nsems); + if (err2) + err = -EFAULT; + } + break; + + case IPC_SET: + if (third & IPC_64) + { + + + struct semid64_ds s; + struct semid64_ds32 *usp = (struct semid64_ds32 *)A(pad); + mm_segment_t old_fs; + + + err = get_user (s.sem_perm.uid, &usp->sem_perm.uid); + err |= __get_user (s.sem_perm.gid, &usp->sem_perm.gid); + err |= __get_user (s.sem_perm.mode, &usp->sem_perm.mode); + if (err) + goto out; + fourth.__pad = &s; + + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_semctl (first, second, third, fourth); + set_fs (old_fs); + + } + else + { + struct semid64_ds s; + struct semid_ds32 *usp; + + usp = (struct semid_ds32 *)A(pad); + err = get_user (s.sem_perm.uid, &usp->sem_perm.uid); + err |= __get_user (s.sem_perm.gid, &usp->sem_perm.gid); + err |= __get_user (s.sem_perm.mode, &usp->sem_perm.mode); + if (err) + goto out; + + fourth.__pad = &s; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_semctl (first, second, third, fourth); + set_fs (old_fs); + } + break; + + } + out: + return err; + + +} + +static int +do_sys32_msgsnd (int first, int second, int third, void *uptr) +{ + struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf) + + 4, GFP_USER); + struct msgbuf32 *up = (struct msgbuf32 *)uptr; + mm_segment_t old_fs; + int err; + + if (!p) + return -ENOMEM; + err = get_user (p->mtype, &up->mtype); + err |= __copy_from_user (p->mtext, &up->mtext, second); + if (err) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgsnd (first, p, second, third); + set_fs (old_fs); +out: + kfree (p); + return err; +} + +static int +do_sys32_msgrcv (int first, int second, int msgtyp, int third, + int version, void *uptr) +{ + struct msgbuf32 *up; + struct msgbuf *p; + mm_segment_t old_fs; + int err; + + if (!version) { + struct ipc_kludge *uipck = (struct ipc_kludge *)uptr; + struct ipc_kludge ipck; + + err = -EINVAL; + if (!uptr) + goto out; + err = -EFAULT; + if (copy_from_user (&ipck, uipck, sizeof (struct ipc_kludge))) + goto out; + uptr = (void *)A(ipck.msgp); + msgtyp = ipck.msgtyp; + } + err = -ENOMEM; + p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER); + if (!p) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgrcv (first, p, second + 4, msgtyp, third); + set_fs (old_fs); + if (err < 0) + goto free_then_out; + up = (struct msgbuf32 *)uptr; + if (put_user (p->mtype, &up->mtype) || + __copy_to_user (&up->mtext, p->mtext, err)) + err = -EFAULT; +free_then_out: + kfree (p); +out: + return err; +} + +static int +do_sys32_msgctl (int first, int second, void *uptr) +{ + int err = -EINVAL, err2; + mm_segment_t old_fs; + + switch (second & (~IPC_64)) { + + case IPC_INFO: + case IPC_RMID: + case MSG_INFO: + err = sys_msgctl (first, second, (struct msqid_ds *)uptr); + break; + + case IPC_SET: + if (second & IPC_64) + { + struct msqid64_ds m; + struct msqid64_ds32 *up = (struct msqid64_ds32 *)uptr; + + + err = get_user (m.msg_perm.uid, &up->msg_perm.uid); + err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid); + err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode); + err |= __get_user (m.msg_qbytes, &up->msg_qbytes); + if (err) + goto out; + + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, (struct msqid_ds *)&m); + set_fs (old_fs); + + + } + else + { + struct msqid_ds m; + struct msqid_ds32 *up = (struct msqid_ds32 *)uptr; + + + err = get_user (m.msg_perm.uid, &up->msg_perm.uid); + err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid); + err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode); + err |= __get_user (m.msg_qbytes, &up->msg_qbytes); + if (err) + break; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, &m); + set_fs (old_fs); + } + break; + + case IPC_STAT: + case MSG_STAT: + if (second & IPC_64) + { + struct msqid64_ds m; + struct msqid64_ds32 *up = (struct msqid64_ds32 *)uptr; + + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, (struct msqid_ds *)&m); + set_fs (old_fs); + err2 = copy_to_user(&up->msg_perm, &m.msg_perm, sizeof(struct ipc64_perm) + 3*sizeof(time_t)); + err2 |= __put_user (m.msg_cbytes, &up->msg_cbytes); + err2 |= __put_user (m.msg_qnum, &up->msg_qnum); + err2 |= __put_user (m.msg_qbytes, &up->msg_qbytes); + err2 |= __put_user (m.msg_lspid, &up->msg_lspid); + err2 |= __put_user (m.msg_lrpid, &up->msg_lrpid); + if (err2) + err = -EFAULT; + + } + else + { + struct msqid64_ds m64; + struct msqid_ds32 *up = (struct msqid_ds32 *)uptr; + + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, (void *) &m64); + set_fs (old_fs); + err2 = put_user (m64.msg_perm.key, &up->msg_perm.key); + err2 |= __put_user(m64.msg_perm.uid, &up->msg_perm.uid); + err2 |= __put_user(m64.msg_perm.gid, &up->msg_perm.gid); + err2 |= __put_user(m64.msg_perm.cuid, &up->msg_perm.cuid); + err2 |= __put_user(m64.msg_perm.cgid, &up->msg_perm.cgid); + err2 |= __put_user(m64.msg_perm.mode, &up->msg_perm.mode); + err2 |= __put_user(m64.msg_perm.seq, &up->msg_perm.seq); + err2 |= __put_user(m64.msg_stime, &up->msg_stime); + err2 |= __put_user(m64.msg_rtime, &up->msg_rtime); + err2 |= __put_user(m64.msg_ctime, &up->msg_ctime); + err2 |= __put_user(m64.msg_cbytes, &up->msg_cbytes); + err2 |= __put_user(m64.msg_qnum, &up->msg_qnum); + err2 |= __put_user(m64.msg_qbytes, &up->msg_qbytes); + err2 |= __put_user(m64.msg_lspid, &up->msg_lspid); + err2 |= __put_user(m64.msg_lrpid, &up->msg_lrpid); + if (err2) + err = -EFAULT; + } + break; + + } +out: + return err; +} + + + +static int +do_sys32_shmat (int first, int second, int third, int version, void *uptr) +{ + unsigned long raddr; + u32 *uaddr = (u32 *)A((u32)third); + int err = -EINVAL; + + if (version == 1) + return err; + err = sys_shmat (first, uptr, second, &raddr); + if (err) + return err; + err = put_user (raddr, uaddr); + return err; +} + +static int +do_sys32_shmctl (int first, int second, void *uptr) +{ + int err = -EFAULT, err2; + struct shmid_ds s; + struct shmid64_ds s64; + mm_segment_t old_fs; + struct shm_info32 { + int used_ids; + u32 shm_tot, shm_rss, shm_swp; + u32 swap_attempts, swap_successes; + } *uip = (struct shm_info32 *)uptr; + struct shm_info si; + + switch (second & (~IPC_64)) { + + case IPC_INFO: + case IPC_RMID: + case SHM_LOCK: + case SHM_UNLOCK: + err = sys_shmctl (first, second, (struct shmid_ds *)uptr); + break; + case IPC_SET: + if (second & IPC_64) + { + + struct shmid64_ds32 *up = (struct shmid64_ds32 *)uptr; + + err = get_user (s.shm_perm.uid, &up->shm_perm.uid); + err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid); + err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode); + if (err) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, (struct shmid_ds *)&s); + set_fs (old_fs); + + } + else + { + struct shmid_ds32 *up = (struct shmid_ds32 *)uptr; + + err = get_user (s.shm_perm.uid, &up->shm_perm.uid); + err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid); + err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode); + if (err) + break; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, &s); + set_fs (old_fs); + } + break; + + case IPC_STAT: + case SHM_STAT: + if (second & IPC_64) + { + + struct shmid64_ds32 *up = (struct shmid64_ds32 *)uptr; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, (struct shmid_ds *)&s); + set_fs (old_fs); + if (err < 0) + goto out; + + err2 = copy_to_user (&up->shm_perm, &s.shm_perm, sizeof(struct ipc64_perm) + 3*sizeof(time_t)); + err2 |= __put_user (s.shm_segsz, &up->shm_segsz); + err2 |= __put_user (s.shm_nattch, &up->shm_nattch); + err2 |= __put_user (s.shm_cpid, &up->shm_cpid); + err2 |= __put_user (s.shm_lpid, &up->shm_lpid); + if (err2) + err = -EFAULT; + + } + else + { + struct shmid_ds32 *up = (struct shmid_ds32 *)uptr; + + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, (void *) &s64); + set_fs (old_fs); + if (err < 0) + break; + err2 = put_user (s64.shm_perm.key, &up->shm_perm.key); + err2 |= __put_user (s64.shm_perm.uid, &up->shm_perm.uid); + err2 |= __put_user (s64.shm_perm.gid, &up->shm_perm.gid); + err2 |= __put_user (s64.shm_perm.cuid, + &up->shm_perm.cuid); + err2 |= __put_user (s64.shm_perm.cgid, + &up->shm_perm.cgid); + err2 |= __put_user (s64.shm_perm.mode, + &up->shm_perm.mode); + err2 |= __put_user (s64.shm_perm.seq, &up->shm_perm.seq); + err2 |= __put_user (s64.shm_atime, &up->shm_atime); + err2 |= __put_user (s64.shm_dtime, &up->shm_dtime); + err2 |= __put_user (s64.shm_ctime, &up->shm_ctime); + err2 |= __put_user (s64.shm_segsz, &up->shm_segsz); + err2 |= __put_user (s64.shm_nattch, &up->shm_nattch); + err2 |= __put_user (s64.shm_cpid, &up->shm_cpid); + err2 |= __put_user (s64.shm_lpid, &up->shm_lpid); + if (err2) + err = -EFAULT; + } + break; + + case SHM_INFO: + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, (void *)&si); + set_fs (old_fs); + if (err < 0) + break; + err2 = put_user (si.used_ids, &uip->used_ids); + err2 |= __put_user (si.shm_tot, &uip->shm_tot); + err2 |= __put_user (si.shm_rss, &uip->shm_rss); + err2 |= __put_user (si.shm_swp, &uip->shm_swp); + err2 |= __put_user (si.swap_attempts, + &uip->swap_attempts); + err2 |= __put_user (si.swap_successes, + &uip->swap_successes); + if (err2) + err = -EFAULT; + break; + + } + out: + return err; +} + + + +/* Note: it is necessary to treat first_parm, second_parm, and third_parm as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_ipc(u32 call, u32 first_parm, u32 second_parm, u32 third_parm, u32 ptr, u32 fifth) +{ + int first = (int)first_parm; + int second = (int)second_parm; + int third = (int)third_parm; + int version, err; + + PPCDBG(PPCDBG_SYS32, "sys32_ipc - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + + case SEMOP: + /* struct sembuf is the same on 32 and 64bit :)) */ + err = sys_semop (first, (struct sembuf *)AA(ptr), + second); + break; + case SEMGET: + err = sys_semget (first, second, third); + break; + case SEMCTL: + err = do_sys32_semctl (first, second, third, + (void *)AA(ptr)); + break; + + case MSGSND: + err = do_sys32_msgsnd (first, second, third, + (void *)AA(ptr)); + break; + case MSGRCV: + err = do_sys32_msgrcv (first, second, fifth, third, + version, (void *)AA(ptr)); + break; + case MSGGET: + err = sys_msgget ((key_t) first, second); + break; + case MSGCTL: + err = do_sys32_msgctl (first, second, (void *)AA(ptr)); + break; + + case SHMAT: + err = do_sys32_shmat (first, second, third, + version, (void *)AA(ptr)); + break; + case SHMDT: + err = sys_shmdt ((char *)AA(ptr)); + break; + case SHMGET: + err = sys_shmget (first, second, third); + break; + case SHMCTL: + err = do_sys32_shmctl (first, second, (void *)AA(ptr)); + break; + default: + err = -EINVAL; + break; + } + + + PPCDBG(PPCDBG_SYS32, "sys32_ipc - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return err; +} + + + +/* stat syscall methods. */ +extern asmlinkage int sys_stat(char* filename, struct __old_kernel_stat* statbuf); + +static int cp_old_stat32(struct inode* inode, struct __old_kernel_stat32* statbuf) +{ + static int warncount = 5; + struct __old_kernel_stat32 tmp; + + if (warncount) { + warncount--; + printk("VFS: Warning: %s using old stat() call. Recompile your binary.\n", + current->comm); + } + + tmp.st_dev = kdev_t_to_nr(inode->i_dev); + tmp.st_ino = inode->i_ino; + tmp.st_mode = inode->i_mode; + tmp.st_nlink = inode->i_nlink; + SET_OLDSTAT_UID(tmp, inode->i_uid); + SET_OLDSTAT_GID(tmp, inode->i_gid); + tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); + tmp.st_size = inode->i_size; + tmp.st_atime = inode->i_atime; + tmp.st_mtime = inode->i_mtime; + tmp.st_ctime = inode->i_ctime; + return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; +} + +asmlinkage long sys32_stat(char* filename, struct __old_kernel_stat32* statbuf) +{ + struct nameidata nd; + int error; + + PPCDBG(PPCDBG_SYS32X, "sys32_stat - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + error = user_path_walk(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_old_stat32(nd.dentry->d_inode, statbuf); + path_release(&nd); + } + + PPCDBG(PPCDBG_SYS32X, "sys32_stat - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return error; +} + +asmlinkage long sys32_fstat(unsigned int fd, struct __old_kernel_stat32* statbuf) +{ + struct file *f; + int err = -EBADF; + + PPCDBG(PPCDBG_SYS32X, "sys32_fstat - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + f = fget(fd); + if (f) { + struct dentry * dentry = f->f_dentry; + + err = do_revalidate(dentry); + if (!err) + err = cp_old_stat32(dentry->d_inode, statbuf); + fput(f); + } + + PPCDBG(PPCDBG_SYS32X, "sys32_fstat - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return err; +} + +asmlinkage long sys32_lstat(char* filename, struct __old_kernel_stat32* statbuf) +{ + struct nameidata nd; + int error; + + PPCDBG(PPCDBG_SYS32X, "sys32_lstat - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + error = user_path_walk_link(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_old_stat32(nd.dentry->d_inode, statbuf); + + path_release(&nd); + } + + PPCDBG(PPCDBG_SYS32X, "sys32_lstat - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return error; +} + +extern asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t* offset, size_t count); + +/* Note: it is necessary to treat out_fd and in_fd as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sendfile(u32 out_fd, u32 in_fd, __kernel_off_t32* offset, u32 count) +{ + mm_segment_t old_fs = get_fs(); + int ret; + off_t of; + + PPCDBG(PPCDBG_SYS32, "sys32_sendfile - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + if (offset && get_user(of, offset)) + return -EFAULT; + + set_fs(KERNEL_DS); + ret = sys_sendfile((int)out_fd, (int)in_fd, offset ? &of : NULL, count); + set_fs(old_fs); + + if (!ret && offset && put_user(of, offset)) + return -EFAULT; + + + PPCDBG(PPCDBG_SYS32, "sys32_sendfile - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + return ret; +} + + + + +extern asmlinkage int sys_setsockopt(int fd, int level, int optname, char *optval, int optlen); + +asmlinkage long sys32_setsockopt(int fd, int level, int optname, char* optval, int optlen) +{ + + PPCDBG(PPCDBG_SYS32,"sys32_setsockopt - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + if (optname == SO_ATTACH_FILTER) { + struct sock_fprog32 { + __u16 len; + __u32 filter; + } *fprog32 = (struct sock_fprog32 *)optval; + struct sock_fprog kfprog; + struct sock_filter *kfilter; + unsigned int fsize; + mm_segment_t old_fs; + __u32 uptr; + int ret; + + if (get_user(kfprog.len, &fprog32->len) || + __get_user(uptr, &fprog32->filter)) + return -EFAULT; + kfprog.filter = (struct sock_filter *)A(uptr); + fsize = kfprog.len * sizeof(struct sock_filter); + kfilter = (struct sock_filter *)kmalloc(fsize, GFP_KERNEL); + if (kfilter == NULL) + return -ENOMEM; + if (copy_from_user(kfilter, kfprog.filter, fsize)) { + kfree(kfilter); + return -EFAULT; + } + kfprog.filter = kfilter; + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_setsockopt(fd, level, optname, + (char *)&kfprog, sizeof(kfprog)); + set_fs(old_fs); + kfree(kfilter); + return ret; + } + return sys_setsockopt(fd, level, optname, optval, optlen); +} + + + + +#define MAX_SOCK_ADDR 128 /* 108 for Unix domain - 16 for IP, 16 for IPX, 24 for IPv6, about 80 for AX.25 */ +#define __CMSG32_NXTHDR(ctl, len, cmsg, cmsglen) __cmsg32_nxthdr((ctl),(len),(cmsg),(cmsglen)) +#define CMSG32_NXTHDR(mhdr, cmsg, cmsglen) cmsg32_nxthdr((mhdr), (cmsg), (cmsglen)) + +#define CMSG32_ALIGN(len) ( ((len)+sizeof(int)-1) & ~(sizeof(int)-1) ) + +#define CMSG32_DATA(cmsg) ((void *)((char *)(cmsg) + CMSG32_ALIGN(sizeof(struct cmsghdr32)))) +#define CMSG32_SPACE(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + CMSG32_ALIGN(len)) +#define CMSG32_LEN(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + (len)) +#define __CMSG32_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr32) ? \ + (struct cmsghdr32 *)(ctl) : \ + (struct cmsghdr32 *)NULL) +#define CMSG32_FIRSTHDR(msg) __CMSG32_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen) + +struct msghdr32 +{ + u32 msg_name; + int msg_namelen; + u32 msg_iov; + __kernel_size_t32 msg_iovlen; + u32 msg_control; + __kernel_size_t32 msg_controllen; + unsigned msg_flags; +}; + +struct cmsghdr32 +{ + __kernel_size_t32 cmsg_len; + int cmsg_level; + int cmsg_type; +}; + +__inline__ struct cmsghdr32 *__cmsg32_nxthdr(void *__ctl, __kernel_size_t __size, + struct cmsghdr32 *__cmsg, int __cmsg_len) +{ + struct cmsghdr32 * __ptr; + + __ptr = (struct cmsghdr32 *)(((unsigned char *) __cmsg) + + CMSG32_ALIGN(__cmsg_len)); + if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size) + return NULL; + + return __ptr; +} + +__inline__ struct cmsghdr32 *cmsg32_nxthdr (struct msghdr *__msg, + struct cmsghdr32 *__cmsg, + int __cmsg_len) +{ + return __cmsg32_nxthdr(__msg->msg_control, __msg->msg_controllen, + __cmsg, __cmsg_len); +} + +extern __inline__ struct socket *socki_lookup(struct inode *inode) +{ + return &inode->u.socket_i; +} + +extern __inline__ struct socket *sockfd_lookup(int fd, int *err) +{ + struct file *file; + struct inode *inode; + + if (!(file = fget(fd))) + { + *err = -EBADF; + return NULL; + } + + inode = file->f_dentry->d_inode; + if (!inode || !inode->i_sock || !socki_lookup(inode)) + { + *err = -ENOTSOCK; + fput(file); + return NULL; + } + + return socki_lookup(inode); +} + +extern __inline__ void sockfd_put(struct socket *sock) +{ + fput(sock->file); +} + +static inline int msghdr_from_user32_to_kern(struct msghdr *kmsg, struct msghdr32 *umsg) +{ + u32 tmp1, tmp2, tmp3; + int err; + + err = get_user(tmp1, &umsg->msg_name); + err |= __get_user(tmp2, &umsg->msg_iov); + err |= __get_user(tmp3, &umsg->msg_control); + if (err) + return -EFAULT; + + kmsg->msg_name = (void *)A(tmp1); + kmsg->msg_iov = (struct iovec *)A(tmp2); + kmsg->msg_control = (void *)A(tmp3); + + err = get_user(kmsg->msg_namelen, &umsg->msg_namelen); + err |= get_user(kmsg->msg_iovlen, &umsg->msg_iovlen); + err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen); + err |= get_user(kmsg->msg_flags, &umsg->msg_flags); + + return err; +} + +static inline int iov_from_user32_to_kern(struct iovec *kiov, + struct iovec32 *uiov32, + int niov) +{ + int tot_len = 0; + + while(niov > 0) { + u32 len, buf; + + if(get_user(len, &uiov32->iov_len) || + get_user(buf, &uiov32->iov_base)) { + tot_len = -EFAULT; + break; + } + tot_len += len; + kiov->iov_base = (void *)A(buf); + kiov->iov_len = (__kernel_size_t) len; + uiov32++; + kiov++; + niov--; + } + return tot_len; +} + +/* I've named the args so it is easy to tell whose space the pointers are in. */ +static int verify_iovec32(struct msghdr *kern_msg, struct iovec *kern_iov, + char *kern_address, int mode) +{ + int tot_len; + + if(kern_msg->msg_namelen) { + if(mode==VERIFY_READ) { + int err = move_addr_to_kernel(kern_msg->msg_name, + kern_msg->msg_namelen, + kern_address); + if(err < 0) + return err; + } + kern_msg->msg_name = kern_address; + } else + kern_msg->msg_name = NULL; + + if(kern_msg->msg_iovlen > UIO_FASTIOV) { + kern_iov = kmalloc(kern_msg->msg_iovlen * sizeof(struct iovec), + GFP_KERNEL); + if(!kern_iov) + return -ENOMEM; + } + + tot_len = iov_from_user32_to_kern(kern_iov, + (struct iovec32 *)kern_msg->msg_iov, + kern_msg->msg_iovlen); + if(tot_len >= 0) + kern_msg->msg_iov = kern_iov; + else if(kern_msg->msg_iovlen > UIO_FASTIOV) + kfree(kern_iov); + + return tot_len; +} + +/* There is a lot of hair here because the alignment rules (and + * thus placement) of cmsg headers and length are different for + * 32-bit apps. -DaveM + */ +static int cmsghdr_from_user32_to_kern(struct msghdr *kmsg, + unsigned char *stackbuf, int stackbuf_size) +{ + struct cmsghdr32 *ucmsg; + struct cmsghdr *kcmsg, *kcmsg_base; + __kernel_size_t32 ucmlen; + __kernel_size_t kcmlen, tmp; + + kcmlen = 0; + kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + if(get_user(ucmlen, &ucmsg->cmsg_len)) + return -EFAULT; + + /* Catch bogons. */ + if(CMSG32_ALIGN(ucmlen) < + CMSG32_ALIGN(sizeof(struct cmsghdr32))) + return -EINVAL; + if((unsigned long)(((char *)ucmsg - (char *)kmsg->msg_control) + + ucmlen) > kmsg->msg_controllen) + return -EINVAL; + + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmlen += tmp; + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + if(kcmlen == 0) + return -EINVAL; + + /* The kcmlen holds the 64-bit version of the control length. + * It may not be modified as we do not stick it into the kmsg + * until we have successfully copied over all of the data + * from the user. + */ + if(kcmlen > stackbuf_size) + kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); + if(kcmsg == NULL) + return -ENOBUFS; + + /* Now copy them over neatly. */ + memset(kcmsg, 0, kcmlen); + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + __get_user(ucmlen, &ucmsg->cmsg_len); + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmsg->cmsg_len = tmp; + __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); + + /* Copy over the data. */ + if(copy_from_user(CMSG_DATA(kcmsg), + CMSG32_DATA(ucmsg), + (ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))))) + goto out_free_efault; + + /* Advance. */ + kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + + /* Ok, looks like we made it. Hook it up and return success. */ + kmsg->msg_control = kcmsg_base; + kmsg->msg_controllen = kcmlen; + return 0; + +out_free_efault: + if(kcmsg_base != (struct cmsghdr *)stackbuf) + kfree(kcmsg_base); + return -EFAULT; +} + +asmlinkage long sys32_sendmsg(int fd, struct msghdr32* user_msg, unsigned int user_flags) +{ + struct socket *sock; + char address[MAX_SOCK_ADDR]; + struct iovec iov[UIO_FASTIOV]; + unsigned char ctl[sizeof(struct cmsghdr) + 20]; + unsigned char *ctl_buf = ctl; + struct msghdr kern_msg; + int err, total_len; + + PPCDBG(PPCDBG_SYS32, "sys32_sendmsg - entered - pid=%ld, comm=%s \n", current->pid, current->comm); + + if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ); + if (err < 0) + goto out; + total_len = err; + + if(kern_msg.msg_controllen) { + err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl)); + if(err) + goto out_freeiov; + ctl_buf = kern_msg.msg_control; + } + kern_msg.msg_flags = user_flags; + + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + if (sock->file->f_flags & O_NONBLOCK) + kern_msg.msg_flags |= MSG_DONTWAIT; + err = sock_sendmsg(sock, &kern_msg, total_len); + sockfd_put(sock); + } + + /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */ + if(ctl_buf != ctl) + kfree(ctl_buf); +out_freeiov: + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + + PPCDBG(PPCDBG_SYS32, "sys32_sendmsg - exited - pid=%ld, comm=%s \n", current->pid, current->comm); + return err; +} + +static void put_cmsg32(struct msghdr *kmsg, int level, int type, + int len, void *data) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + struct cmsghdr32 cmhdr; + int cmlen = CMSG32_LEN(len); + + if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { + kmsg->msg_flags |= MSG_CTRUNC; + return; + } + + if(kmsg->msg_controllen < cmlen) { + kmsg->msg_flags |= MSG_CTRUNC; + cmlen = kmsg->msg_controllen; + } + cmhdr.cmsg_level = level; + cmhdr.cmsg_type = type; + cmhdr.cmsg_len = cmlen; + + if(copy_to_user(cm, &cmhdr, sizeof cmhdr)) + return; + if(copy_to_user(CMSG32_DATA(cm), data, cmlen - sizeof(struct cmsghdr32))) + return; + cmlen = CMSG32_SPACE(len); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; +} + + +static void scm_detach_fds32(struct msghdr *kmsg, struct scm_cookie *scm) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + int fdmax = (kmsg->msg_controllen - sizeof(struct cmsghdr32)) / sizeof(int); + int fdnum = scm->fp->count; + struct file **fp = scm->fp->fp; + int *cmfptr; + int err = 0, i; + + if (fdnum < fdmax) + fdmax = fdnum; + + for (i = 0, cmfptr = (int *) CMSG32_DATA(cm); i < fdmax; i++, cmfptr++) { + int new_fd; + err = get_unused_fd(); + if (err < 0) + break; + new_fd = err; + err = put_user(new_fd, cmfptr); + if (err) { + put_unused_fd(new_fd); + break; + } + /* Bump the usage count and install the file. */ + get_file(fp[i]); + fd_install(new_fd, fp[i]); + } + + if (i > 0) { + int cmlen = CMSG32_LEN(i * sizeof(int)); + if (!err) + err = put_user(SOL_SOCKET, &cm->cmsg_level); + if (!err) + err = put_user(SCM_RIGHTS, &cm->cmsg_type); + if (!err) + err = put_user(cmlen, &cm->cmsg_len); + if (!err) { + cmlen = CMSG32_SPACE(i * sizeof(int)); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; + } + } + if (i < fdnum) + kmsg->msg_flags |= MSG_CTRUNC; + + /* + * All of the files that fit in the message have had their + * usage counts incremented, so we just free the list. + */ + __scm_destroy(scm); +} + +/* In these cases we (currently) can just copy to data over verbatim + * because all CMSGs created by the kernel have well defined types which + * have the same layout in both the 32-bit and 64-bit API. One must add + * some special cased conversions here if we start sending control messages + * with incompatible types. + * + * SCM_RIGHTS and SCM_CREDENTIALS are done by hand in recvmsg32 right after + * we do our work. The remaining cases are: + * + * SOL_IP IP_PKTINFO struct in_pktinfo 32-bit clean + * IP_TTL int 32-bit clean + * IP_TOS __u8 32-bit clean + * IP_RECVOPTS variable length 32-bit clean + * IP_RETOPTS variable length 32-bit clean + * (these last two are clean because the types are defined + * by the IPv4 protocol) + * IP_RECVERR struct sock_extended_err + + * struct sockaddr_in 32-bit clean + * SOL_IPV6 IPV6_RECVERR struct sock_extended_err + + * struct sockaddr_in6 32-bit clean + * IPV6_PKTINFO struct in6_pktinfo 32-bit clean + * IPV6_HOPLIMIT int 32-bit clean + * IPV6_FLOWINFO u32 32-bit clean + * IPV6_HOPOPTS ipv6 hop exthdr 32-bit clean + * IPV6_DSTOPTS ipv6 dst exthdr(s) 32-bit clean + * IPV6_RTHDR ipv6 routing exthdr 32-bit clean + * IPV6_AUTHHDR ipv6 auth exthdr 32-bit clean + */ +static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_uptr) +{ + unsigned char *workbuf, *wp; + unsigned long bufsz, space_avail; + struct cmsghdr *ucmsg; + + bufsz = ((unsigned long)kmsg->msg_control) - orig_cmsg_uptr; + space_avail = kmsg->msg_controllen + bufsz; + wp = workbuf = kmalloc(bufsz, GFP_KERNEL); + if(workbuf == NULL) + goto fail; + + /* To make this more sane we assume the kernel sends back properly + * formatted control messages. Because of how the kernel will truncate + * the cmsg_len for MSG_TRUNC cases, we need not check that case either. + */ + ucmsg = (struct cmsghdr *) orig_cmsg_uptr; + while(((unsigned long)ucmsg) <= + (((unsigned long)kmsg->msg_control) - sizeof(struct cmsghdr))) { + struct cmsghdr32 *kcmsg32 = (struct cmsghdr32 *) wp; + int clen64, clen32; + + /* UCMSG is the 64-bit format CMSG entry in user-space. + * KCMSG32 is within the kernel space temporary buffer + * we use to convert into a 32-bit style CMSG. + */ + __get_user(kcmsg32->cmsg_len, &ucmsg->cmsg_len); + __get_user(kcmsg32->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg32->cmsg_type, &ucmsg->cmsg_type); + + clen64 = kcmsg32->cmsg_len; + copy_from_user(CMSG32_DATA(kcmsg32), CMSG_DATA(ucmsg), + clen64 - CMSG_ALIGN(sizeof(*ucmsg))); + clen32 = ((clen64 - CMSG_ALIGN(sizeof(*ucmsg))) + + CMSG32_ALIGN(sizeof(struct cmsghdr32))); + kcmsg32->cmsg_len = clen32; + + ucmsg = (struct cmsghdr *) (((char *)ucmsg) + CMSG_ALIGN(clen64)); + wp = (((char *)kcmsg32) + CMSG32_ALIGN(clen32)); + } + + /* Copy back fixed up data, and adjust pointers. */ + bufsz = (wp - workbuf); + copy_to_user((void *)orig_cmsg_uptr, workbuf, bufsz); + + kmsg->msg_control = (struct cmsghdr *) + (((char *)orig_cmsg_uptr) + bufsz); + kmsg->msg_controllen = space_avail - bufsz; + + kfree(workbuf); + return; + +fail: + /* If we leave the 64-bit format CMSG chunks in there, + * the application could get confused and crash. So to + * ensure greater recovery, we report no CMSGs. + */ + kmsg->msg_controllen += bufsz; + kmsg->msg_control = (void *) orig_cmsg_uptr; +} + +asmlinkage long sys32_recvmsg(int fd, struct msghdr32* user_msg, unsigned int user_flags) +{ + struct iovec iovstack[UIO_FASTIOV]; + struct msghdr kern_msg; + char addr[MAX_SOCK_ADDR]; + struct socket *sock; + struct iovec *iov = iovstack; + struct sockaddr *uaddr; + int *uaddr_len; + unsigned long cmsg_ptr; + int err, total_len, len = 0; + + PPCDBG(PPCDBG_SYS32, "sys32_recvmsg - entered - pid=%ld, comm=%s\n", current->pid, current->comm); + + if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + + uaddr = kern_msg.msg_name; + uaddr_len = &user_msg->msg_namelen; + err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE); + if (err < 0) + goto out; + total_len = err; + + cmsg_ptr = (unsigned long) kern_msg.msg_control; + kern_msg.msg_flags = 0; + + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + struct scm_cookie scm; + + if (sock->file->f_flags & O_NONBLOCK) + user_flags |= MSG_DONTWAIT; + memset(&scm, 0, sizeof(scm)); + err = sock->ops->recvmsg(sock, &kern_msg, total_len, + user_flags, &scm); + if(err >= 0) { + len = err; + if(!kern_msg.msg_control) { + if(sock->passcred || scm.fp) + kern_msg.msg_flags |= MSG_CTRUNC; + if(scm.fp) + __scm_destroy(&scm); + } else { + /* If recvmsg processing itself placed some + * control messages into user space, it's is + * using 64-bit CMSG processing, so we need + * to fix it up before we tack on more stuff. + */ + if((unsigned long) kern_msg.msg_control != cmsg_ptr) + cmsg32_recvmsg_fixup(&kern_msg, cmsg_ptr); + + /* Wheee... */ + if(sock->passcred) + put_cmsg32(&kern_msg, + SOL_SOCKET, SCM_CREDENTIALS, + sizeof(scm.creds), &scm.creds); + if(scm.fp != NULL) + scm_detach_fds32(&kern_msg, &scm); + } + } + sockfd_put(sock); + } + + if(uaddr != NULL && err >= 0) + err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); + if(cmsg_ptr != 0 && err >= 0) { + unsigned long ucmsg_ptr = ((unsigned long)kern_msg.msg_control); + __kernel_size_t32 uclen = (__kernel_size_t32) (ucmsg_ptr - cmsg_ptr); + err |= __put_user(uclen, &user_msg->msg_controllen); + } + if(err >= 0) + err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags); + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + if(err < 0) + return err; + + PPCDBG(PPCDBG_SYS32, "sys32_recvmsg - exited - pid=%ld, comm=%s\n", current->pid, current->comm); + return len; +} + +/* + * count32() counts the number of arguments/envelopes + */ +static int count32(u32 * argv, int max) +{ + int i = 0; + + if (argv != NULL) { + for (;;) { + u32 p; int error; + + error = get_user(p,argv); + if (error) + return error; + if (!p) + break; + argv++; + if (++i > max) + return -E2BIG; + } + } + return i; +} + +/* + * 'copy_string32()' copies argument/envelope strings from user + * memory to free pages in kernel mem. These are in a format ready + * to be put directly into the top of new user memory. + */ +static int copy_strings32(int argc, u32 * argv, struct linux_binprm *bprm) +{ + while (argc-- > 0) + { + u32 str; + int len; + unsigned long pos; + + if (get_user(str, argv + argc) || + !str || + !(len = strnlen_user((char *)A(str), bprm->p))) + return -EFAULT; + + if (bprm->p < len) + return -E2BIG; + + bprm->p -= len; + + pos = bprm->p; + while (len) { + char *kaddr; + struct page *page; + int offset, bytes_to_copy, new, err; + + offset = pos % PAGE_SIZE; + page = bprm->page[pos / PAGE_SIZE]; + new = 0; + if (!page) { + page = alloc_page(GFP_USER); + bprm->page[pos / PAGE_SIZE] = page; + if (!page) + return -ENOMEM; + new = 1; + } + kaddr = (char *)kmap(page); + + if (new && offset) + memset(kaddr, 0, offset); + bytes_to_copy = PAGE_SIZE - offset; + if (bytes_to_copy > len) { + bytes_to_copy = len; + if (new) + memset(kaddr+offset+len, 0, + PAGE_SIZE-offset-len); + } + + err = copy_from_user(kaddr + offset, (char *)A(str), + bytes_to_copy); + flush_page_to_ram(page); + kunmap((unsigned long)kaddr); + + if (err) + return -EFAULT; + + pos += bytes_to_copy; + str += bytes_to_copy; + len -= bytes_to_copy; + } + } + return 0; +} + +/* + * sys32_execve() executes a new program. + */ +static int do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs) +{ + struct linux_binprm bprm; + struct file * file; + int retval; + int i; + + bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); + memset(bprm.page, 0, MAX_ARG_PAGES * sizeof(bprm.page[0])); + + file = open_exec(filename); + + retval = PTR_ERR(file); + if (IS_ERR(file)) + return retval; + + bprm.file = file; + bprm.filename = filename; + bprm.sh_bang = 0; + bprm.loader = 0; + bprm.exec = 0; + if ((bprm.argc = count32(argv, bprm.p / sizeof(u32))) < 0) { + allow_write_access(file); + fput(file); + return bprm.argc; + } + if ((bprm.envc = count32(envp, bprm.p / sizeof(u32))) < 0) { + allow_write_access(file); + fput(file); + return bprm.argc; + } + + retval = prepare_binprm(&bprm); + if (retval < 0) + goto out; + + retval = copy_strings_kernel(1, &bprm.filename, &bprm); + if (retval < 0) + goto out; + + bprm.exec = bprm.p; + retval = copy_strings32(bprm.envc, envp, &bprm); + if (retval < 0) + goto out; + + retval = copy_strings32(bprm.argc, argv, &bprm); + if (retval < 0) + goto out; + + retval = search_binary_handler(&bprm, regs); + if (retval >= 0) + /* execve success */ + return retval; + +out: + /* Something went wrong, return the inode and free the argument pages*/ + allow_write_access(bprm.file); + if (bprm.file) + fput(bprm.file); + + for (i=0 ; ipid, current->comm); + //PPCDBG(PPCDBG_SYS32NI, " a0=%lx, a1=%lx, a2=%lx, a3=%lx, a4=%lx, a5=%lx, regs=%p \n", a0, a1, a2, a3, a4, a5, regs); + } + + filename = getname((char *) a0); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + if (regs->msr & MSR_FP) + giveup_fpu(current); + + error = do_execve32(filename, (u32*) a1, (u32*) a2, regs); + + if (error == 0) + current->ptrace &= ~PT_DTRACE; + putname(filename); + +out: + ifppcdebug(PPCDBG_SYS32) + { + udbg_printf("sys32_execve - exited - returning %x - pid=%ld \n", error, current->pid); + //udbg_printf("sys32_execve - at exit - regs->gpr[1]=%lx, gpr[3]=%lx, gpr[4]=%lx, gpr[5]=%lx, gpr[6]=%lx \n", regs->gpr[1], regs->gpr[3], regs->gpr[4], regs->gpr[5], regs->gpr[6]); + } + return error; +} + + + + +/* ld.so expects the auxiliary table to start on a 16-byte boundary, + * so this routine determines if the aux table in the stack meets this + * criteria and if not rearranges it on the stack so it does start on a + * 16-byte boundary. + */ +static inline void shove_aux_table32(unsigned long sp) +{ + int argc; + u_int p = 0; + + unsigned long e; + unsigned long aux_start, offset; + + /* Get the argc value. */ + if (__get_user(argc, (u_int*)sp)) + { + return; + } + + /* Skip over the arguments. */ + sp += sizeof(int) + (argc + 1) * sizeof(u_int); + + /* skip over the environment pointers */ + do + { + /* Get the value of the next 32-bit environment pointer. */ + if (__get_user(p, (u_int*)sp)) + { + return; + } + /* Advance the stack pointer to the address of the next 32-bit pointer. */ + sp += sizeof(u_int); + } while (p != 0); + + aux_start = sp; + /* skip to the end of the auxiliary table */ + do + { + /* Get the value of the next aux table entry. */ + if (__get_user(e, (u_int*)sp)) + { + return; + } + /* Advance the stack pointer to the address of the next aux table entry (from 32-bit pgm). */ + sp += 2 * sizeof(u_int); + } while (e != AT_NULL); + + /* Determine how far we have to move the aux table to get it onto a 16-byte boundary. */ + offset = ((aux_start + 15) & ~15) - aux_start; + + /* Move the aux table to the new 16-byte boundary area (if necessary). */ + if (offset != 0) + { + do + { + sp -= sizeof(u_int); + /* Read the entry into kernel stg then Write the entry back into the stack at the new location. */ + if (__get_user(e, (u_int*)sp) || __put_user(e, (u_int*)(sp + offset))) + { /* either the get or put failed. */ + return; + } + } while (sp > aux_start); + } +} + +/* Set up a thread for executing a new program. */ +void start_thread32(struct pt_regs* regs, unsigned long nip, unsigned long sp) +{ + set_fs(USER_DS); + regs->nip = nip; + regs->gpr[1] = sp; + regs->msr = MSR_USER32; + + shove_aux_table32(sp); + + if (last_task_used_math == current) + last_task_used_math = 0; + current->thread.fpscr = 0; +} + + + +extern asmlinkage int sys_prctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5); + +/* Note: it is necessary to treat option as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_prctl(u32 option, u32 arg2, u32 arg3, u32 arg4, u32 arg5) +{ + PPCDBG(PPCDBG_SYS32, "sys32_prctl - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_prctl((int)option, + (unsigned long) arg2, + (unsigned long) arg3, + (unsigned long) arg4, + (unsigned long) arg5); +} + + + + +extern asmlinkage int sys_sched_rr_get_interval(pid_t pid, struct timespec *interval); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_sched_rr_get_interval(u32 pid, struct timespec32 *interval) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + PPCDBG(PPCDBG_SYS32, "sys32_sched_rr_get_interval - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_sched_rr_get_interval((int)pid, &t); + set_fs (old_fs); + if (put_user (t.tv_sec, &interval->tv_sec) || + __put_user (t.tv_nsec, &interval->tv_nsec)) + return -EFAULT; + + PPCDBG(PPCDBG_SYS32, "sys32_sched_rr_get_interval - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return ret; +} + + + + +extern asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn, unsigned long off, + unsigned long len, unsigned char *buf); + +asmlinkage int sys32_pciconfig_read(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_pciconfig_read - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_pciconfig_read((unsigned long) bus, + (unsigned long) dfn, + (unsigned long) off, + (unsigned long) len, + (unsigned char *)AA(ubuf)); +} + + + + +extern asmlinkage int sys_pciconfig_write(unsigned long bus, unsigned long dfn, unsigned long off, + unsigned long len, unsigned char *buf); + +asmlinkage int sys32_pciconfig_write(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_pciconfig_write - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return sys_pciconfig_write((unsigned long) bus, + (unsigned long) dfn, + (unsigned long) off, + (unsigned long) len, + (unsigned char *)AA(ubuf)); +} + +extern asmlinkage int sys_newuname(struct new_utsname * name); + +asmlinkage int ppc64_newuname(struct new_utsname * name) +{ + int ret = sys_newuname(name); + + if (current->personality == PER_LINUX32 && !ret) { + ret = copy_to_user(name->machine, "ppc\0\0", 8); + } + return ret; +} + +extern asmlinkage long sys_personality(unsigned long); + +asmlinkage int sys32_personality(unsigned long personality) +{ + int ret; + if (current->personality == PER_LINUX32 && personality == PER_LINUX) + personality = PER_LINUX32; + ret = sys_personality(personality); + if (ret == PER_LINUX32) + ret = PER_LINUX; + return ret; +} + + + +extern asmlinkage long sys_access(const char * filename, int mode); + +/* Note: it is necessary to treat mode as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_access(const char * filename, u32 mode) +{ + return sys_access(filename, (int)mode); +} + + +extern asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs); + +/* Note: it is necessary to treat p1, p2, p3, p4, p5, p7, and regs as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_clone(u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, struct pt_regs *regs) +{ + return sys_clone((int)p1, (int)p2, (int)p3, (int)p4, (int)p5, (int)p6, regs); +} + + +extern asmlinkage long sys_creat(const char * pathname, int mode); + +/* Note: it is necessary to treat mode as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_creat(const char * pathname, u32 mode) +{ + return sys_creat(pathname, (int)mode); +} + + +extern asmlinkage long sys_exit(int error_code); + +/* Note: it is necessary to treat error_code as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_exit(u32 error_code) +{ + return sys_exit((int)error_code); +} + + +extern asmlinkage long sys_wait4(pid_t pid, unsigned int * stat_addr, int options, struct rusage * ru); + +/* Note: it is necessary to treat pid and options as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_wait4(u32 pid, unsigned int * stat_addr, u32 options, struct rusage * ru) +{ + PPCDBG(PPCDBG_SYS32, "sys32_wait4 - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + if (!ru) + return sys_wait4((int)pid, stat_addr, options, NULL); + else { + struct rusage r; + int ret; + unsigned int status; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_wait4((int)pid, stat_addr ? &status : NULL, options, &r); + set_fs (old_fs); + if (put_rusage (ru, &r)) return -EFAULT; + if (stat_addr && put_user (status, stat_addr)) + return -EFAULT; + return ret; + } + +} + + +extern asmlinkage long sys_waitpid(pid_t pid, unsigned int * stat_addr, int options); + +/* Note: it is necessary to treat pid and options as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_waitpid(u32 pid, unsigned int * stat_addr, u32 options) +{ + return sys_waitpid((int)pid, stat_addr, (int)options); +} + + +extern asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs); + +/* Note: it is necessary to treat p1, p2, p3, p4, p5, and p6 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_fork(u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, struct pt_regs *regs) +{ + return sys_fork((int)p1, (int)p2, (int)p3, (int)p4, (int)p5, (int)p6, regs); +} + + +extern asmlinkage long sys_getgroups(int gidsetsize, gid_t *grouplist); + +/* Note: it is necessary to treat gidsetsize as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getgroups(u32 gidsetsize, gid_t *grouplist) +{ + return sys_getgroups((int)gidsetsize, grouplist); +} + + +extern asmlinkage long sys_getpgid(pid_t pid); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getpgid(u32 pid) +{ + return sys_getpgid((int)pid); +} + + +extern asmlinkage long sys_getpriority(int which, int who); + +/* Note: it is necessary to treat which and who as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getpriority(u32 which, u32 who) +{ + return sys_getpriority((int)which, (int)who); +} + + +extern asmlinkage long sys_getsid(pid_t pid); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getsid(u32 pid) +{ + return sys_getsid((int)pid); +} + + +extern asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); + +/* Note: it is necessary to treat on as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_ioperm(unsigned long from, unsigned long num, u32 on) +{ + return sys_ioperm(from, num, (int)on); +} + + +extern asmlinkage int sys_iopl(int a1, int a2, int a3, int a4); + +/* Note: it is necessary to treat a1, a2, a3, and a4 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_iopl(u32 a1, u32 a2, u32 a3, u32 a4) +{ + return sys_iopl((int)a1, (int)a2, (int)a3, (int)a4); +} + + +extern asmlinkage long sys_kill(int pid, int sig); + +/* Note: it is necessary to treat pid and sig as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_kill(u32 pid, u32 sig) +{ + return sys_kill((int)pid, (int)sig); +} + + +extern asmlinkage long sys_mkdir(const char * pathname, int mode); + +/* Note: it is necessary to treat mode as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_mkdir(const char * pathname, u32 mode) +{ + return sys_mkdir(pathname, (int)mode); +} + + +extern asmlinkage long sys_mlockall(int flags); + +/* Note: it is necessary to treat flags as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_mlockall(u32 flags) +{ + return sys_mlockall((int)flags); +} + + +extern asmlinkage int sys_modify_ldt(int a1, int a2, int a3, int a4); + +/* Note: it is necessary to treat a1, a2, a3, and a4 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_modify_ldt(u32 a1, u32 a2, u32 a3, u32 a4) +{ + return sys_modify_ldt((int)a1, (int)a2, (int)a3, (int)a4); +} + + +extern asmlinkage long sys_msync(unsigned long start, size_t len, int flags); + +/* Note: it is necessary to treat flags as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_msync(unsigned long start, size_t len, u32 flags) +{ + return sys_msync(start, len, (int)flags); +} + + +extern asmlinkage long sys_nice(int increment); + +/* Note: it is necessary to treat increment as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_nice(u32 increment) +{ + return sys_nice((int)increment); +} + + +extern asmlinkage long sys_open(const char * filename, int flags, int mode); + +/* Note: it is necessary to treat flags and mode as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_open(const char * filename, int flags, int mode) +{ + return sys_open(filename, (int)flags, (int)mode); +} + + +extern asmlinkage long sys_readlink(const char * path, char * buf, int bufsiz); + +/* Note: it is necessary to treat bufsiz as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_readlink(const char * path, char * buf, u32 bufsiz) +{ + return sys_readlink(path, buf, (int)bufsiz); +} + + +extern asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void * arg); + +/* Note: it is necessary to treat magic1 and magic2 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_reboot(u32 magic1, u32 magic2, unsigned int cmd, void * arg) +{ + return sys_reboot((int)magic1, (int)magic2, cmd, arg); +} + + +extern asmlinkage long sys_sched_get_priority_max(int policy); + +/* Note: it is necessary to treat option as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_get_priority_max(u32 policy) +{ + return sys_sched_get_priority_max((int)policy); +} + + +extern asmlinkage long sys_sched_get_priority_min(int policy); + +/* Note: it is necessary to treat policy as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_get_priority_min(u32 policy) +{ + return sys_sched_get_priority_min((int)policy); +} + + +extern asmlinkage long sys_sched_getparam(pid_t pid, struct sched_param *param); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_getparam(u32 pid, struct sched_param *param) +{ + return sys_sched_getparam((int)pid, param); +} + + +extern asmlinkage long sys_sched_getscheduler(pid_t pid); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_getscheduler(u32 pid) +{ + return sys_sched_getscheduler((int)pid); +} + + +extern asmlinkage long sys_sched_setparam(pid_t pid, struct sched_param *param); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_setparam(u32 pid, struct sched_param *param) +{ + return sys_sched_setparam((int)pid, param); +} + + +extern asmlinkage long sys_sched_setscheduler(pid_t pid, int policy, struct sched_param *param); + +/* Note: it is necessary to treat pid and policy as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_setscheduler(u32 pid, u32 policy, struct sched_param *param) +{ + return sys_sched_setscheduler((int)pid, (int)policy, param); +} + + +extern asmlinkage long sys_setdomainname(char *name, int len); + +/* Note: it is necessary to treat len as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setdomainname(char *name, u32 len) +{ + return sys_setdomainname(name, (int)len); +} + + +extern asmlinkage long sys_setgroups(int gidsetsize, gid_t *grouplist); + +/* Note: it is necessary to treat gidsetsize as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setgroups(u32 gidsetsize, gid_t *grouplist) +{ + return sys_setgroups((int)gidsetsize, grouplist); +} + + +extern asmlinkage long sys_sethostname(char *name, int len); + +/* Note: it is necessary to treat len as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sethostname(char *name, u32 len) +{ + return sys_sethostname(name, (int)len); +} + + +extern asmlinkage long sys_setpgid(pid_t pid, pid_t pgid); + +/* Note: it is necessary to treat pid and pgid as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setpgid(u32 pid, u32 pgid) +{ + return sys_setpgid((int)pid, (int)pgid); +} + + +extern asmlinkage long sys_setpriority(int which, int who, int niceval); + +/* Note: it is necessary to treat which, who, and niceval as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setpriority(u32 which, u32 who, u32 niceval) +{ + return sys_setpriority((int)which, (int)who, (int)niceval); +} + + +extern asmlinkage long sys_ssetmask(int newmask); + +/* Note: it is necessary to treat newmask as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_ssetmask(u32 newmask) +{ + return sys_ssetmask((int) newmask); +} + + +extern asmlinkage long sys_swapon(const char * specialfile, int swap_flags); + +/* Note: it is necessary to treat swap_flags as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_swapon(const char * specialfile, u32 swap_flags) +{ + return sys_swapon(specialfile, (int)swap_flags); +} + + +extern asmlinkage long sys_syslog(int type, char * buf, int len); + +/* Note: it is necessary to treat type and len as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_syslog(u32 type, char * buf, u32 len) +{ + return sys_syslog((int)type, buf, (int)len); +} + + +extern asmlinkage long sys_umask(int mask); + +/* Note: it is necessary to treat mask as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_umask(u32 mask) +{ + return sys_umask((int)mask); +} + + +extern asmlinkage long sys_umount(char * name, int flags); + +/* Note: it is necessary to treat flags as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_umount(char * name, u32 flags) +{ + return sys_umount(name, (int)flags); +} + + +extern asmlinkage int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs); + +/* Note: it is necessary to treat p1, p2, p3, p4, p5, and p6 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_vfork(u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, struct pt_regs *regs) +{ + return sys_vfork((int)p1, (int)p2, (int)p3, (int)p4, (int)p5, (int)p6, regs); +} + + +extern asmlinkage int sys_vm86(int a1, int a2, int a3, int a4); + +/* Note: it is necessary to treat a1, a2, a3, and a4 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_vm86(u32 a1, u32 a2, u32 a3, u32 a4) +{ + return sys_vm86((int)a1, (int)a2, (int)a3, (int)a4); +} + + + + + +extern asmlinkage ssize_t sys_pread(unsigned int fd, char * buf, + size_t count, loff_t pos); + +extern asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, + size_t count, loff_t pos); + +typedef __kernel_ssize_t32 ssize_t32; + +asmlinkage ssize_t32 sys32_pread(unsigned int fd, char *ubuf, + __kernel_size_t32 count, u32 reg6, u32 poshi, u32 poslo) +{ + return sys_pread(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + +asmlinkage ssize_t32 sys32_pwrite(unsigned int fd, char *ubuf, + __kernel_size_t32 count, u32 reg6 ,u32 poshi, u32 poslo) +{ + return sys_pwrite(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + + +extern asmlinkage long sys_truncate(const char * path, unsigned long length); +extern asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length); + +asmlinkage int sys32_truncate64(const char * path, u32 reg4, unsigned long high, unsigned long low) +{ + if ((int)high < 0) + return -EINVAL; + else + return sys_truncate(path, (high << 32) | low); +} + +asmlinkage int sys32_ftruncate64(unsigned int fd, u32 reg4, unsigned long high, unsigned long low) +{ + if ((int)high < 0) + return -EINVAL; + else + return sys_ftruncate(fd, (high << 32) | low); +} + + + +asmlinkage long sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + if (cmd >= F_GETLK64 && cmd <= F_SETLKW64) + return sys_fcntl(fd, cmd + F_GETLK - F_GETLK64, arg); + return sys32_fcntl(fd, cmd, arg); +} + + + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/syscalls.c linux.ac/arch/ppc64/kernel/syscalls.c --- linux.vanilla/arch/ppc64/kernel/syscalls.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/syscalls.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,288 @@ +/* + * linux/arch/ppc/kernel/sys_ppc.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/kernel/sys_i386.c" + * Adapted from the i386 version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au). + * + * This file contains various random system calls that + * have a non-standard calling sequence on the Linux/PPC + * platform. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +void +check_bugs(void) +{ +} + +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) +{ + printk(KERN_ERR "sys_ioperm()\n"); + return -EIO; +} + +int sys_iopl(int a1, int a2, int a3, int a4) +{ + printk(KERN_ERR "sys_iopl(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + return (-ENOSYS); +} + +int sys_vm86(int a1, int a2, int a3, int a4) +{ + printk(KERN_ERR "sys_vm86(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + return (-ENOSYS); +} + +int sys_modify_ldt(int a1, int a2, int a3, int a4) +{ + printk(KERN_ERR "sys_modify_ldt(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + return (-ENOSYS); +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +asmlinkage int +sys_ipc (uint call, int first, int second, long third, void *ptr, long fifth) +{ + int version, ret; + + PPCDBG(PPCDBG_SYS64X, "sys_ipc - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + ret = -EINVAL; + switch (call) { + case SEMOP: + ret = sys_semop (first, (struct sembuf *)ptr, second); + break; + case SEMGET: + ret = sys_semget (first, second, third); + break; + case SEMCTL: { + union semun fourth; + + if (!ptr) + break; + if ((ret = verify_area (VERIFY_READ, ptr, sizeof(long))) + || (ret = get_user(fourth.__pad, (void **)ptr))) + break; + ret = sys_semctl (first, second, third, fourth); + break; + } + case MSGSND: + ret = sys_msgsnd (first, (struct msgbuf *) ptr, second, third); + break; + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + + if (!ptr) + break; + if ((ret = verify_area (VERIFY_READ, ptr, sizeof(tmp))) + || (ret = copy_from_user(&tmp, + (struct ipc_kludge *) ptr, + sizeof (tmp)))) + break; + ret = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, + third); + break; + } + default: + ret = sys_msgrcv (first, (struct msgbuf *) ptr, + second, fifth, third); + break; + } + break; + case MSGGET: + ret = sys_msgget ((key_t) first, second); + break; + case MSGCTL: + ret = sys_msgctl (first, second, (struct msqid_ds *) ptr); + break; + case SHMAT: + switch (version) { + default: { + ulong raddr; + + if ((ret = verify_area(VERIFY_WRITE, (ulong*) third, + sizeof(ulong)))) + break; + ret = sys_shmat (first, (char *) ptr, second, &raddr); + if (ret) + break; + ret = put_user (raddr, (ulong *) third); + break; + } + case 1: /* iBCS2 emulator entry point */ + if (!segment_eq(get_fs(), get_ds())) + break; + ret = sys_shmat (first, (char *) ptr, second, + (ulong *) third); + break; + } + break; + case SHMDT: + ret = sys_shmdt ((char *)ptr); + break; + case SHMGET: + ret = sys_shmget (first, second, third); + break; + case SHMCTL: + ret = sys_shmctl (first, second, (struct shmid_ds *) ptr); + break; + } + + + PPCDBG(PPCDBG_SYS64X, "sys_ipc - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return ret; +} + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way unix traditionally does this, though. + */ +asmlinkage int sys_pipe(int *fildes) +{ + int fd[2]; + int error; + + PPCDBG(PPCDBG_SYS64X, "sys_pipe - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + error = do_pipe(fd); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + + PPCDBG(PPCDBG_SYS64X, "sys_pipe - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return error; +} + +asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, + unsigned long prot, unsigned long flags, + unsigned long fd, off_t offset) +{ + struct file * file = NULL; + unsigned long ret = -EBADF; + + PPCDBG(PPCDBG_SYS64X, "sys_mmap - entered - addr=%lx, len=%lx - pid=%ld, comm=%s \n", addr, len, current->pid, current->comm); + + if (!(flags & MAP_ANONYMOUS)) { + if (!(file = fget(fd))) + goto out; + } + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + down_write(¤t->mm->mmap_sem); + ret = do_mmap(file, addr, len, prot, flags, offset); + up_write(¤t->mm->mmap_sem); + if (file) + fput(file); + +out: + + PPCDBG(PPCDBG_SYS64X, "sys_mmap - exited - ret=%x \n", ret); + + return ret; +} + +asmlinkage int sys_pause(void) +{ + + PPCDBG(PPCDBG_SYS64X, "sys_pause - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + current->state = TASK_INTERRUPTIBLE; + schedule(); + + PPCDBG(PPCDBG_SYS64X, "sys_pause - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return -ERESTARTNOHAND; +} + +static int __init set_fakeppc(char *str) +{ + if (*str) + return 0; + init_task.personality = PER_LINUX32; + return 1; +} +__setup("fakeppc", set_fakeppc); + +asmlinkage int sys_uname(struct old_utsname * name) +{ + int err = -EFAULT; + + PPCDBG(PPCDBG_SYS64X, "sys_uname - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + down_read(&uts_sem); + if (name && !copy_to_user(name, &system_utsname, sizeof (*name))) + err = 0; + up_read(&uts_sem); + + PPCDBG(PPCDBG_SYS64X, "sys_uname - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return err; +} + +asmlinkage int sys_olduname(struct oldold_utsname * name) +{ + int error; + + PPCDBG(PPCDBG_SYS64X, "sys_olduname - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + down_read(&uts_sem); + error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); + error -= __put_user(0,name->sysname+__OLD_UTS_LEN); + error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); + error -= __put_user(0,name->nodename+__OLD_UTS_LEN); + error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); + error -= __put_user(0,name->release+__OLD_UTS_LEN); + error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); + error -= __put_user(0,name->version+__OLD_UTS_LEN); + error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); + error = __put_user(0,name->machine+__OLD_UTS_LEN); + up_read(&uts_sem); + + error = error ? -EFAULT : 0; + + PPCDBG(PPCDBG_SYS64X, "sys_olduname - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return error; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/time.c linux.ac/arch/ppc64/kernel/time.c --- linux.vanilla/arch/ppc64/kernel/time.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/time.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,478 @@ +/* + * + * Common time routines among all ppc machines. + * + * Written by Cort Dougan (cort@cs.nmt.edu) to merge + * Paul Mackerras' version and mine for PReP and Pmac. + * MPC8xx/MBX changes by Dan Malek (dmalek@jlc.net). + * Converted for 64-bit by Mike Corrigan + * + * First round of bugfixes by Gabriel Paubert (paubert@iram.es) + * to make clock more stable (2.4.0-test5). The only thing + * that this code assumes is that the timebases have been synchronized + * by firmware on SMP and are never stopped (never do sleep + * on SMP then, nap and doze are OK). + * + * TODO (not necessarily in this file): + * - improve precision and reproducibility of timebase frequency + * measurement at boot time. + * - get rid of xtime_lock for gettimeofday (generic kernel problem + * to be implemented on all architectures for SMP scalability and + * eventually implementing gettimeofday without entering the kernel). + * - put all time/clock related variables in a single structure + * to minimize number of cache lines touched by gettimeofday() + * - for astronomical applications: add a new function to get + * non ambiguous timestamps even around leap seconds. This needs + * a new timestamp format and a good name. + * + * + * The following comment is partially obsolete (at least the long wait + * is no more a valid reason): + * Since the MPC8xx has a programmable interrupt timer, I decided to + * use that rather than the decrementer. Two reasons: 1.) the clock + * frequency is low, causing 2.) a long wait in the timer interrupt + * while ((d = get_dec()) == dval) + * loop. The MPC8xx can be driven from a variety of input clocks, + * so a number of assumptions have been made here because the kernel + * parameter HZ is a constant. We assume (correctly, today :-) that + * the MPC8xx on the MBX board is driven from a 32.768 kHz crystal. + * This is then divided by 4, providing a 8192 Hz clock into the PIT. + * Since it is not possible to get a nice 100 Hz clock out of this, without + * creating a software PLL, I have set HZ to 128. -- Dan + * + * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +void smp_local_timer_interrupt(struct pt_regs *); + +/* keep track of when we need to update the rtc */ +time_t last_rtc_update; +extern rwlock_t xtime_lock; +extern int piranha_simulator; + +unsigned long tb_ticks_per_jiffy = 5000000; /* initial sane values */ +unsigned long tb_ticks_per_usec = 500; +unsigned long tb_to_us; +spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; + +extern unsigned long wall_jiffies; +extern unsigned long lpEvent_count; +extern int smp_tb_synchronized; + +static long time_offset = 0; + +extern unsigned long prof_cpu_mask; +extern unsigned int * prof_buffer; +extern unsigned long prof_len; +extern unsigned long prof_shift; +extern char _stext; + +static inline void ppc_do_profile (unsigned long nip) +{ + if (!prof_buffer) + return; + + /* + * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. + * (default is all CPUs.) + */ + if (!((1<>= prof_shift; + /* + * Don't ignore out-of-bounds EIP values silently, + * put them into the last histogram slot, so if + * present, they will show up as a sharp peak. + */ + if (nip > prof_len-1) + nip = prof_len-1; + atomic_inc((atomic_t *)&prof_buffer[nip]); +} + +static __inline__ void timer_check_rtc(void) +{ + /* + * update the rtc when needed, this should be performed on the + * right fraction of a second. Half or full second ? + * Full second works on mk48t59 clocks, others need testing. + * Note that this update is basically only used through + * the adjtimex system calls. Setting the HW clock in + * any other way is a /dev/rtc and userland business. + * This is still wrong by -0.5/+1.5 jiffies because of the + * timer interrupt resolution and possible delay, but here we + * hit a quantization limit which can only be solved by higher + * resolution timers and decoupling time management from timer + * interrupts. This is also wrong on the clocks + * which require being written at the half second boundary. + * We should have an rtc call that only sets the minutes and + * seconds like on Intel to avoid problems with non UTC clocks. + */ + if ( (time_status & STA_UNSYNC) == 0 && + xtime.tv_sec - last_rtc_update >= 659 && + abs(xtime.tv_usec - (1000000-1000000/HZ)) < 500000/HZ && + jiffies - wall_jiffies == 1) { + if (ppc_md.set_rtc_time(xtime.tv_sec+1 + time_offset) == 0) + last_rtc_update = xtime.tv_sec+1; + else + /* Try again one minute later */ + last_rtc_update += 60; + } + +} + +/* + * For iSeries shared processors, we have to let the hypervisor + * set the hardware decrementer. We set a virtual decrementer + * in the ItLpPaca and call the hypervisor if the virtual + * decrementer is less than the current value in the hardware + * decrementer. (almost always the new decrementer value will + * be greater than the current hardware decementer so the hypervisor + * call will not be needed) + */ + +/* + * timer_interrupt - gets called when the decrementer overflows, + * with interrupts disabled. + * We set it up to overflow again in 1/HZ seconds. + */ +int timer_interrupt(struct pt_regs * regs) +{ + int next_dec; + unsigned long cur_tb; + + struct Paca * paca = (struct Paca *)mfspr(SPRG3); + unsigned long cpu = paca->xPacaIndex; + struct ItLpQueue * lpq; + + irq_enter(cpu); + + paca = (struct Paca *)mfspr(SPRG3); + + if (!user_mode(regs)) + ppc_do_profile(instruction_pointer(regs)); + + while ( paca->next_jiffy_update_tb <= (cur_tb = get_tb()) ) { + +#ifdef CONFIG_SMP + smp_local_timer_interrupt(regs); +#endif + if ( cpu == 0 ) { + write_lock(&xtime_lock); + do_timer(regs); + timer_check_rtc(); + write_unlock(&xtime_lock); + } + paca->next_jiffy_update_tb += tb_ticks_per_jiffy; + } + next_dec = paca->next_jiffy_update_tb - cur_tb; + if ( next_dec > paca->default_decr ) + next_dec = paca->default_decr; + set_dec(next_dec); + + if (ppc_md.heartbeat && !ppc_md.heartbeat_count--) + ppc_md.heartbeat(); + + paca->xLpPaca.xIntDword.xFields.xDecrInt = 0; + lpq = paca->lpQueuePtr; + if ( lpq ) + lpEvent_count += ItLpQueue_process( lpq, regs ); + + irq_exit(cpu); + + if (softirq_pending(cpu)) + do_softirq(); + + return 1; /* lets ret_from_int know we can do checks */ +} + +/* + * This version of gettimeofday has microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + unsigned long delta, lost_ticks, usec, sec; + + read_lock_irqsave(&xtime_lock, flags); + sec = xtime.tv_sec; + usec = xtime.tv_usec; + + delta = tb_ticks_per_jiffy - ( xPaca[0].next_jiffy_update_tb - get_tb() ); + +#ifdef CONFIG_SMP + /* As long as timebases are not in sync, gettimeofday can only + * have jiffy resolution on SMP. + */ + if (!smp_tb_synchronized) + delta = 0; +#endif /* CONFIG_SMP */ + lost_ticks = jiffies - wall_jiffies; + read_unlock_irqrestore(&xtime_lock, flags); + + /* Convert the lost ticks into micro-seconds. */ + usec += ( ( tb_ticks_per_jiffy * lost_ticks ) + delta ) / tb_ticks_per_usec; + while (usec > 1000000) { + sec++; + usec -= 1000000; + } + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +void do_settimeofday(struct timeval *tv) +{ + unsigned long flags; + long int tb_delta, new_usec, new_sec; + + write_lock_irqsave(&xtime_lock, flags); + /* Updating the RTC is not the job of this code. If the time is + * stepped under NTP, the RTC will be update after STA_UNSYNC + * is cleared. Tool like clock/hwclock either copy the RTC + * to the system time, in which case there is no point in writing + * to the RTC again, or write to the RTC but then they don't call + * settimeofday to perform this operation. Note also that + * we don't touch the decrementer since: + * a) it would lose timer interrupt synchronization on SMP + * (if it is working one day) + * b) it could make one jiffy spuriously shorter or longer + * which would introduce another source of uncertainty potentially + * harmful to relatively short timers. + */ + + /* This works perfectly on SMP only if the tb are in sync but + * guarantees an error < 1 jiffy even if they are off by eons, + * still reasonable when gettimeofday resolution is 1 jiffy. + */ + + tb_delta = tb_ticks_per_jiffy - ( xPaca[0].next_jiffy_update_tb - get_tb() ); + tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy; + + new_sec = tv->tv_sec; + new_usec = tv->tv_usec - tb_delta / tb_ticks_per_usec; + while (new_usec <0) { + new_sec--; + new_usec += 1000000; + } + xtime.tv_usec = new_usec; + xtime.tv_sec = new_sec; + + /* In case of a large backwards jump in time with NTP, we want the + * clock to be updated as soon as the PLL is again in lock. + */ + last_rtc_update = new_sec - 658; + + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_state = TIME_ERROR; /* p. 24, (a) */ + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_unlock_irqrestore(&xtime_lock, flags); +} + +extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz); + +void __init time_init(void) +{ + time_t sec, old_sec; + unsigned long old_stamp, stamp, elapsed; + /* This function is only called on the boot processor */ + unsigned long flags; + + if (ppc_md.time_init != NULL) + time_offset = ppc_md.time_init(); + + ppc_md.calibrate_decr(); + + /* Now that the decrementer is calibrated, it can be used in case the + * clock is stuck, but the fact that we have to handle the 601 + * makes things more complex. Repeatedly read the RTC until the + * next second boundary to try to achieve some precision... + */ + stamp = get_tb(); + sec = ppc_md.get_rtc_time(); + elapsed = 0; + if ( ! piranha_simulator ) { + do { + old_stamp = stamp; + old_sec = sec; + stamp = get_tb(); + elapsed += stamp - old_stamp; + sec = ppc_md.get_rtc_time(); + } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy); + if (sec==old_sec) { + printk("Warning: real time clock seems stuck!\n"); + } + } + write_lock_irqsave(&xtime_lock, flags); + xtime.tv_sec = sec; + last_jiffy_stamp(0) = stamp; + xtime.tv_usec = 0; + /* No update now, we just read the time from the RTC ! */ + last_rtc_update = xtime.tv_sec; + write_unlock_irqrestore(&xtime_lock, flags); + /* Not exact, but the timer interrupt takes care of this */ + set_dec(tb_ticks_per_jiffy); + + /* If platform provided a timezone (pmac), we correct the time + * using do_sys_settimeofday() which in turn calls warp_clock() + */ + if (time_offset) { + struct timezone tz; + tz.tz_minuteswest = -time_offset / 60; + tz.tz_dsttime = 0; + do_sys_settimeofday(NULL, &tz); + } +} + +#define TICK_SIZE tick +#define FEBRUARY 2 +#define STARTOFTIME 1970 +#define SECDAY 86400L +#define SECYR (SECDAY * 365) +#define leapyear(year) ((year) % 4 == 0) +#define days_in_year(a) (leapyear(a) ? 366 : 365) +#define days_in_month(a) (month_days[(a) - 1]) + +static int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) + */ +void GregorianDay(struct rtc_time * tm) +{ + int leapsToDate; + int lastYear; + int day; + int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + lastYear=tm->tm_year-1; + + /* + * Number of leap corrections to apply up to end of last year + */ + leapsToDate = lastYear/4 - lastYear/100 + lastYear/400; + + /* + * This year is a leap year if it is divisible by 4 except when it is + * divisible by 100 unless it is divisible by 400 + * + * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be + */ + if((tm->tm_year%4==0) && + ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && + (tm->tm_mon>2)) + { + /* + * We are past Feb. 29 in a leap year + */ + day=1; + } + else + { + day=0; + } + + day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + + tm->tm_mday; + + tm->tm_wday=day%7; +} + +void to_tm(int tim, struct rtc_time * tm) +{ + register int i; + register long hms, day; + + day = tim / SECDAY; + hms = tim % SECDAY; + + /* Hours, minutes, seconds are easy */ + tm->tm_hour = hms / 3600; + tm->tm_min = (hms % 3600) / 60; + tm->tm_sec = (hms % 3600) % 60; + + /* Number of years in days */ + for (i = STARTOFTIME; day >= days_in_year(i); i++) + day -= days_in_year(i); + tm->tm_year = i; + + /* Number of months in days left */ + if (leapyear(tm->tm_year)) + days_in_month(FEBRUARY) = 29; + for (i = 1; day >= days_in_month(i); i++) + day -= days_in_month(i); + days_in_month(FEBRUARY) = 28; + tm->tm_mon = i; + + /* Days are what is left over (+1) from all that. */ + tm->tm_mday = day + 1; + + /* + * Determine the day of week + */ + GregorianDay(tm); +} + +/* Auxiliary function to compute scaling factors */ +/* Actually the choice of a timebase running at 1/4 the of the bus + * frequency giving resolution of a few tens of nanoseconds is quite nice. + * It makes this computation very precise (27-28 bits typically) which + * is optimistic considering the stability of most processor clock + * oscillators and the precision with which the timebase frequency + * is measured but does not harm. + */ +/*unsigned mulhwu_scale_factor(unsigned inscale, unsigned outscale) { */ +/* unsigned mlt=0, tmp, err; */ + /* No concern for performance, it's done once: use a stupid + * but safe and compact method to find the multiplier. + */ +/* + for (tmp = 1U<<31; tmp != 0; tmp >>= 1) { + if (mulhwu(inscale, mlt|tmp) < outscale) mlt|=tmp; + } +*/ + /* We might still be off by 1 for the best approximation. + * A side effect of this is that if outscale is too large + * the returned value will be zero. + * Many corner cases have been checked and seem to work, + * some might have been forgotten in the test however. + */ +/* + err = inscale*(mlt+1); + if (err <= inscale/2) mlt++; + return mlt; + } +*/ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/time.h linux.ac/arch/ppc64/kernel/time.h --- linux.vanilla/arch/ppc64/kernel/time.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/time.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1 @@ + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/traps.c linux.ac/arch/ppc64/kernel/traps.c --- linux.vanilla/arch/ppc64/kernel/traps.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/traps.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,368 @@ +/* + * linux/arch/ppc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_KDB +#include +#endif + +#include +#include +#include +#include +#include +#include + +extern int fix_alignment(struct pt_regs *); +extern void bad_page_fault(struct pt_regs *, unsigned long); + +#ifdef CONFIG_XMON +extern void xmon(struct pt_regs *regs); +extern int xmon_bpt(struct pt_regs *regs); +extern int xmon_sstep(struct pt_regs *regs); +extern int xmon_iabr_match(struct pt_regs *regs); +extern int xmon_dabr_match(struct pt_regs *regs); +extern void (*xmon_fault_handler)(struct pt_regs *regs); +#endif + +#ifdef CONFIG_XMON +void (*debugger)(struct pt_regs *regs) = xmon; +int (*debugger_bpt)(struct pt_regs *regs) = xmon_bpt; +int (*debugger_sstep)(struct pt_regs *regs) = xmon_sstep; +int (*debugger_iabr_match)(struct pt_regs *regs) = xmon_iabr_match; +int (*debugger_dabr_match)(struct pt_regs *regs) = xmon_dabr_match; +void (*debugger_fault_handler)(struct pt_regs *regs); +#else +#ifdef CONFIG_KGDB +void (*debugger)(struct pt_regs *regs); +int (*debugger_bpt)(struct pt_regs *regs); +int (*debugger_sstep)(struct pt_regs *regs); +int (*debugger_iabr_match)(struct pt_regs *regs); +int (*debugger_dabr_match)(struct pt_regs *regs); +void (*debugger_fault_handler)(struct pt_regs *regs); +#endif +#endif +/* + * Trap & Exception support + */ + +void +_exception(int signr, struct pt_regs *regs) +{ + if (!user_mode(regs)) + { + show_regs(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); +#endif +#if defined(CONFIG_KDB) + kdb(KDB_REASON_OOPS, 0, (kdb_eframe_t) regs); +#endif + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +#if defined(CONFIG_PPCDBG) && (defined(CONFIG_XMON) || defined(CONFIG_KGDB)) + /* Allow us to catch SIGILLs for 64-bit app/glibc debugging. -Peter */ + } else if (signr == SIGILL) { + ifppcdebug(PPCDBG_SIGNALXMON) + debugger(regs); +#endif + } + force_sig(signr, current); +} + +void +SystemResetException(struct pt_regs *regs) +{ + udbg_printf("System Reset in kernel mode.\n"); + printk("System Reset in kernel mode.\n"); +#if defined(CONFIG_XMON) + xmon(regs); +#endif + for(;;); +} + + +void +MachineCheckException(struct pt_regs *regs) +{ + if ( !user_mode(regs) ) + { +#if defined(CONFIG_8xx) && defined(CONFIG_PCI) + /* the qspan pci read routines can cause machine checks -- Cort */ + bad_page_fault(regs, regs->dar); + return; +#endif +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_fault_handler) { + debugger_fault_handler(regs); + return; + } +#endif +#ifdef CONFIG_KDB + if (kdb(KDB_REASON_FAULT, 0, regs)) + return; +#endif + printk("Machine check in kernel mode.\n"); + printk("Caused by (from SRR1=%lx): ", regs->msr); + switch (regs->msr & 0xF0000) { + case 0x80000: + printk("Machine check signal\n"); + break; + case 0x40000: + printk("Transfer error ack signal\n"); + break; + case 0x20000: + printk("Data parity error signal\n"); + break; + case 0x10000: + printk("Address parity error signal\n"); + break; + default: + printk("Unknown values in msr\n"); + } + show_regs(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); +#endif +#ifdef CONFIG_KDB + if (kdb(KDB_REASON_FAULT, 0, regs)) + return ; +#endif + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); + } + _exception(SIGSEGV, regs); +} + +void +SMIException(struct pt_regs *regs) +{ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + { + debugger(regs); + return; + } +#endif +#ifdef CONFIG_KDB + { + kdb(KDB_REASON_OOPS, 0, regs); + return; + } +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("System Management Interrupt"); +} + +void +UnknownException(struct pt_regs *regs) +{ + printk("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(SIGTRAP, regs); +} + +void +InstructionBreakpoint(struct pt_regs *regs) +{ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_iabr_match(regs)) + return; +#endif +#ifdef CONFIG_KDB + if (kdb(KDB_REASON_BREAK, 0, regs)) + return ; +#endif + _exception(SIGTRAP, regs); +} + +void +RunModeException(struct pt_regs *regs) +{ + _exception(SIGTRAP, regs); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ +#if defined(CONFIG_4xx) + unsigned int esr = mfspr(SPRN_ESR); + + if (esr & ESR_PTR) { +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_bpt(regs)) + return; +#endif +#ifdef CONFIG_KDB + if (kdb(KDB_REASON_BREAK, 0, regs)) + return; +#endif + _exception(SIGTRAP, regs); + } else { + _exception(SIGILL, regs); + } +#else + if (regs->msr & 0x100000) { + /* IEEE FP exception */ + _exception(SIGFPE, regs); + } else if (regs->msr & 0x20000) { + /* trap exception */ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_bpt(regs)) + return; +#endif +#ifdef CONFIG_KDB + if (kdb(KDB_REASON_BREAK, 0, regs)) + return; +#endif + _exception(SIGTRAP, regs); + } else { + _exception(SIGILL, regs); + } +#endif +} + +void +SingleStepException(struct pt_regs *regs) +{ + regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_sstep(regs)) + return; +#endif +#ifdef CONFIG_KDB + if (kdb(KDB_REASON_DEBUG, 0, regs)) + return; +#endif + _exception(SIGTRAP, regs); +} + +/* Dummy handler for Performance Monitor */ + +void +PerformanceMonitorException(struct pt_regs *regs) +{ + _exception(SIGTRAP, regs); +} + +void +AlignmentException(struct pt_regs *regs) +{ + int fixed; + + fixed = fix_alignment(regs); + if (fixed == 1) { + ifppcdebug(PPCDBG_ALIGNFIXUP) + if (!user_mode(regs)) + PPCDBG(PPCDBG_ALIGNFIXUP, "fix alignment at %lx\n", regs->nip); + regs->nip += 4; /* skip over emulated instruction */ + return; + } + if (fixed == -EFAULT) { + /* fixed == -EFAULT means the operand address was bad */ + if (user_mode(regs)) + force_sig(SIGSEGV, current); + else + bad_page_fault(regs, regs->dar); + return; + } + _exception(SIGBUS, regs); +} + +void +StackOverflow(struct pt_regs *regs) +{ + printk(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n", + current, regs->gpr[1]); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); +#endif +#if defined(CONFIG_KDB) + kdb(KDB_REASON_OOPS, 0, regs); +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("kernel stack overflow"); +} + +void +trace_syscall(struct pt_regs *regs) +{ + printk("Task: %p(%d), PC: %08lX/%08lX, Syscall: %3ld, Result: %s%ld\n", + current, current->pid, regs->nip, regs->link, regs->gpr[0], + regs->ccr&0x10000000?"Error=":"", regs->gpr[3]); +} + +#ifdef CONFIG_8xx +void +SoftwareEmulation(struct pt_regs *regs) +{ + extern int do_mathemu(struct pt_regs *); + int errcode; + + if (!user_mode(regs)) { + show_regs(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); +#endif +#ifdef CONFIG_KDB + if (kdb(KDB_REASON_FAULT, 0, regs)) + return ; +#endif + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Kernel Mode Software FPU Emulation"); + } + +#ifdef CONFIG_MATH_EMULATION + if ((errcode = do_mathemu(regs))) { +#else + if ((errcode = Soft_emulate_8xx(regs))) { +#endif + if (errcode > 0) + _exception(SIGFPE, regs); + else if (errcode == -EFAULT) + _exception(SIGSEGV, regs); + else + _exception(SIGILL, regs); + } +} +#endif + +void +TAUException(struct pt_regs *regs) +{ + printk("TAU trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); +} + +void __init trap_init(void) +{ +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/udbg.c linux.ac/arch/ppc64/kernel/udbg.c --- linux.vanilla/arch/ppc64/kernel/udbg.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/udbg.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,265 @@ +/* + * NS16550 Serial Port (uart) debugging stuff. + * + * c 2001 PPC 64 Team, IBM Corp + * + * NOTE: I am trying to make this code avoid any static data references to + * simplify debugging early boot. We'll see how that goes... + * + * To use this call udbg_init() first. It will init the uart to 9600 8N1. + * You may need to update the COM1 define if your uart is at a different addr. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#define WANT_PPCDBG_TAB /* Only defined here */ +#include +#include + +extern struct Naca *naca; +extern int _machine; + +struct NS16550 { + /* this struct must be packed */ + unsigned char rbr; /* 0 */ + unsigned char ier; /* 1 */ + unsigned char fcr; /* 2 */ + unsigned char lcr; /* 3 */ + unsigned char mcr; /* 4 */ + unsigned char lsr; /* 5 */ + unsigned char msr; /* 6 */ + unsigned char scr; /* 7 */ +}; + +#define thr rbr +#define iir fcr +#define dll rbr +#define dlm ier +#define dlab lcr + +#define LSR_DR 0x01 /* Data ready */ +#define LSR_OE 0x02 /* Overrun */ +#define LSR_PE 0x04 /* Parity error */ +#define LSR_FE 0x08 /* Framing error */ +#define LSR_BI 0x10 /* Break */ +#define LSR_THRE 0x20 /* Xmit holding register empty */ +#define LSR_TEMT 0x40 /* Xmitter empty */ +#define LSR_ERR 0x80 /* Error */ + +void *comport1; + +static inline void eieio(void) { asm volatile ("eieio" : :); } + +void +udbg_init(void) +{ + if ( _machine != _MACH_iSeries ) { + volatile struct NS16550 *port = (struct NS16550 *)comport1; + port->lcr = 0x00; eieio(); + port->ier = 0xFF; eieio(); + port->ier = 0x00; eieio(); + port->lcr = 0x80; eieio(); /* Access baud rate */ + port->dll = 12; eieio(); /* 1 = 115200, 2 = 57600, 3 = 38400, 12 = 9600 baud */ + port->dlm = 0; eieio(); /* dll >> 8 which should be zero for fast rates; */ + port->lcr = 0x03; eieio(); /* 8 data, 1 stop, no parity */ + port->mcr = 0x03; eieio(); /* RTS/DTR */ + port->fcr = 0x07; eieio(); /* Clear & enable FIFOs */ + } +} + + +void +udbg_putc(unsigned char c) +{ + if ( _machine != _MACH_iSeries ) { + volatile struct NS16550 *port = (struct NS16550 *)comport1; + while ((port->lsr & LSR_THRE) == 0) + /* wait for idle */; + port->thr = c; eieio(); + if (c == '\n') { + /* Also put a CR. This is for convenience. */ + while ((port->lsr & LSR_THRE) == 0) + /* wait for idle */; + port->thr = '\r'; eieio(); + } + } + else { + printk("%c", c); + } + +} + +unsigned char +udbg_getc(void) +{ + if ( _machine != _MACH_iSeries ) { + volatile struct NS16550 *port = (struct NS16550 *)comport1; + while ((port->lsr & LSR_DR) == 0) + /* wait for char */; + return port->rbr; + } + return 0; +} + +void +udbg_puts(const char *s) +{ + if ( _machine != _MACH_iSeries ) { + char c; + if ( s && *s != '\0' ) + while ( ( c = *s++ ) != '\0' ) + udbg_putc(c); + else + udbg_puts("NULL"); + } + else + printk("%s", s); +} + +void +udbg_puthex(unsigned long val) +{ + int i, nibbles = sizeof(val)*2; + unsigned char buf[sizeof(val)*2+1]; + for (i = nibbles-1; i >= 0; i--) { + buf[i] = (val & 0xf) + '0'; + if (buf[i] > '9') + buf[i] += ('a'-'0'-10); + val >>= 4; + } + buf[nibbles] = '\0'; + udbg_puts(buf); +} + +void +udbg_printSP(const char *s) +{ + if ( _machine != _MACH_iSeries ) { + unsigned long sp; + asm("mr %0,1" : "=r" (sp) :); + if (s) + udbg_puts(s); + udbg_puthex(sp); + } +} + +void +udbg_printf(const char *fmt, ...) +{ + unsigned char buf[256]; + + va_list args; + va_start(args, fmt); + + vsprintf(buf, fmt, args); + udbg_puts(buf); + + va_end(args); +} + +/* Special print used by PPCDBG() macro */ +void +udbg_ppcdbg(unsigned long flags, const char *fmt, ...) +{ + unsigned long active_debugs = flags & naca->debug_switch; + if ( active_debugs ) { + va_list ap; + unsigned char buf[256]; + unsigned long i, len = 0; + + for(i=0; i < PPCDBG_NUM_FLAGS ;i++) { + if (((1U << i) & active_debugs) && + trace_names[i]) { + len += strlen(trace_names[i]); + udbg_puts(trace_names[i]); + break; + } + } + sprintf(buf, " [%s]: ", current->comm); + len += strlen(buf); + udbg_puts(buf); + + while(len < 18) { + udbg_putc(' '); + len++; + } + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + udbg_puts(buf); + va_end(ap); + } +} + +unsigned long +udbg_ifdebug(unsigned long flags) +{ + return (flags & naca->debug_switch); +} + +/* Print out an area of memory. + * The msg will be printed, followed by a newline, followed by the + * memory dump. + */ +void +udbg_dump(char *msg, void *addr, unsigned len) +{ + if ( _machine != _MACH_iSeries ) { + int i; + int j; + int numrows; + unsigned char c; + unsigned char tc; + char *buff = addr; + + if (msg) { + udbg_puts(msg); + udbg_puts("\n"); + } + + if (!buff) { + udbg_puts(" Danger, Will Robinson, Null Pointer\n"); + return; + } + numrows = (len + 15) / 16; + + for (i = 0; i < numrows; i++) { + udbg_printf("%016lx ", &buff[i*16]); + for (j = 0; j < 16; j++) { + if (j + i * 16 >= len) { + udbg_puts(" "); + } else { + udbg_printf("%02x", (unsigned char)buff[j + i * 16]); + } /* endif */ + if (j % 8 == 7) { + udbg_puts(" "); + } /* endif */ + } /* endfor */ + + udbg_puts(" a|"); + + for (j = 0; j < 16; j++) { + if (j + i * 16 >= len) { + udbg_putc(' '); + } else { + c = (unsigned char)buff[j + i * 16]; + tc = c; + if (tc >= 32 && tc < 127 && tc != ' ') { + udbg_putc(tc); + } else { + udbg_putc('.'); + } /* endif */ + } /* endif */ + } /* endfor */ + udbg_putc('|'); + udbg_putc('\n'); + } /* endfor */ + } +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/viopath.c linux.ac/arch/ppc64/kernel/viopath.c --- linux.vanilla/arch/ppc64/kernel/viopath.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/viopath.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,711 @@ +/* + * arch/ppc64/viopath.c + * + * iSeries Virtual I/O Message Path code + * + * 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 + * + */ +#ifndef _HVLPEVENT_H +#include +#endif +#ifndef _HVLPConfig_H +#include +#endif +#ifndef _HVCallCfg_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _VIO_H +#include +#endif +#ifndef _MF_H +#include +#endif +#ifndef _ISERIES_PROC_H +#include +#endif + +/* Status of the path to each other partition in the system. + * This is overkill, since we will only ever establish connections + * to our hosting partition and the primary partition on the system. + * But this allows for other support in the future. + */ +static struct viopathStatus { + int isOpen:1; /* Did we open the path? */ + int isActive:1; /* Do we have a mon msg outstanding */ +#if defined(CONFIG_VIOCONS) + int xCharUsers; +#endif +#if defined(CONFIG_VIODASD) + int xBlockUsers; +#endif +#if defined(CONFIG_VIOCD) + int xCDUsers; +#endif +#if defined(CONFIG_VIOTAPE) + int xTapeUsers; +#endif + int xConfigUsers; + HvLpInstanceId mSourceInst; + HvLpInstanceId mTargetInst; + int numberAllocated; + spinlock_t statuslock; + unsigned long statuslockFlags; +} viopathStatus[HVMAXARCHITECTEDLPS]; + +/* We use this structure to handle asynchronous responses. The caller + * blocks on the semaphore and the handler posts the semaphore. + */ +struct doneAllocParms_t { + struct semaphore *sem; + int number; +}; + +/* Put a sequence number in each mon msg. The value is not + * important. Start at something other than 0 just for + * readability. wrapping this is ok. + */ +static u8 viomonseq = 22; + +/* Our hosting logical partition. We get this at startup + * time, and different modules access this variable directly. + */ +HvLpIndex viopath_hostLp; + +/* For each kind of incoming event we set a pointer to a + * routine to call. + */ +static vio_event_handler_t *vio_handleCharEvent; +static vio_event_handler_t *vio_handleBlockEvent; +static vio_event_handler_t *vio_handleCDEvent; +static vio_event_handler_t *vio_handleTapeEvent; + +/* + * For each kind of event we allocate a buffer that is + * guaranteed not to cross a page boundary + */ +void * vio_char_event_buffer; +void * vio_block_event_buffer; +void * vio_cd_event_buffer; +void * vio_tape_event_buffer; + +/*************************************************************************** + * A page to build an lp event in + ***************************************************************************/ +unsigned long VIOReqPage; + +/*************************************************************************** + * Handle reads from the proc file system + ***************************************************************************/ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + HvLpEvent_Rc hvrc; + DECLARE_MUTEX_LOCKED(Semaphore); + dma_addr_t dmaa = pci_map_single( NULL, buf, PAGE_SIZE, PCI_DMA_FROMDEVICE); + int len = PAGE_SIZE; + + if (len > blen) + len = blen; + + memset(buf,0x00,len); + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_config | vioconfigget, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(unsigned long)&Semaphore, + VIOVERSION << 16, + ((u64)dmaa) << 32, + len, + 0, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viopath hv error on op %d\n",(int)hvrc); + } + + down(&Semaphore); + + pci_unmap_single( NULL, dmaa, PAGE_SIZE, PCI_DMA_FROMDEVICE); + + *eof = 1; + return strlen(buf); +} + +/*************************************************************************** + * Handle writes to our proc file system + ***************************************************************************/ +static int proc_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + printk("viopath: in proc_write, got %ld bytes starting with %c\n", + count, buffer[0]); + return count; +} + +/*************************************************************************** + * setup our proc file system entries + ***************************************************************************/ +void vio_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent; + ent = create_proc_entry("config", 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; +} + +/* Boot time initialization of the vio path code. Needs + * to be called before any VIO components start + */ +void viopath_init(void) +{ + int i; + memset(viopathStatus,0x00,sizeof(viopathStatus)); + for (i=0; ixFlags.xFunction == HvLpEvent_Function_Int) + { + printk("viopath: got monitor INT event\n"); + remoteLp = event->xSourceLp; + if (!viopathStatus[remoteLp].isActive) + sendMonMsg(remoteLp); + } + else + { + remoteLp = event->xTargetLp; + printk("viopath: got monitor ACK event %d, sinst %d, tinst %d\n", + (int)event->xCorrelationToken, + event->xSourceInstanceId, + event->xTargetInstanceId); + /* Other partition went away! */ + if ((event->xSourceInstanceId != viopathStatus[remoteLp].mSourceInst) || + (event->xTargetInstanceId != viopathStatus[remoteLp].mTargetInst)) + { + printk("viopath: ignoring ack....mismatched instances\n"); + } + else + { + printk("viopath: closing path %d\n",remoteLp); + + viopathStatus[remoteLp].isActive = 0; + + if (vio_handleBlockEvent!= NULL) + (*vio_handleBlockEvent)(NULL); + + if (vio_handleCharEvent != NULL) + (*vio_handleCharEvent)(NULL); + + if (vio_handleCDEvent != NULL) + (*vio_handleCDEvent)(NULL); + + if (vio_handleTapeEvent != NULL) + (*vio_handleTapeEvent)(NULL); + } + } +} + +void vio_setBlockHandler(vio_event_handler_t *beh) +{ + vio_handleBlockEvent = *beh; +} + +void vio_clearBlockHandler(void) +{ + vio_handleBlockEvent = NULL; +} + +void vio_setCharHandler(vio_event_handler_t *ceh) +{ + vio_handleCharEvent = *ceh; +} + +void vio_clearCharHandler(void) +{ + vio_handleCharEvent = NULL; +} + +void vio_setCDHandler(vio_event_handler_t *ceh) +{ + vio_handleCDEvent = *ceh; +} + +void vio_clearCDHandler(void) +{ + vio_handleCDEvent = NULL; +} + +void vio_setTapeHandler(vio_event_handler_t *ceh) +{ + vio_handleTapeEvent = *ceh; +} + +void vio_clearTapeHandler(void) +{ + vio_handleTapeEvent = NULL; +} + +static void vio_handleEvent(struct HvLpEvent *event, struct pt_regs *regs) +{ + HvLpIndex remoteLp; + if (event->xFlags.xFunction == HvLpEvent_Function_Int) + { + remoteLp = event->xSourceLp; + if (event->xSourceInstanceId != viopathStatus[remoteLp].mTargetInst) + { + printk("viopath: int msg rcvd, source inst (%d) doesnt match (%d)\n", + viopathStatus[remoteLp].mTargetInst, + event->xSourceInstanceId); + return; + } + + if (event->xTargetInstanceId != viopathStatus[remoteLp].mSourceInst) + { + printk("viopath: int msg rcvd, target inst (%d) doesnt match (%d)\n", + viopathStatus[remoteLp].mSourceInst, + event->xTargetInstanceId); + return; + } + } + else + { + remoteLp = event->xTargetLp; + if (event->xSourceInstanceId != viopathStatus[remoteLp].mSourceInst) + { + printk("viopath: ack msg rcvd, source inst (%d) doesnt match (%d)\n", + viopathStatus[remoteLp].mSourceInst, + event->xSourceInstanceId); + return; + } + + if (event->xTargetInstanceId != viopathStatus[remoteLp].mTargetInst) + { + printk("viopath: ack msg rcvd, target inst (%d) doesnt match (%d)\n", + viopathStatus[remoteLp].mTargetInst, + event->xTargetInstanceId); + return; + } + } + + + switch (event->xSubtype & VIOMAJOR_SUBTYPE_MASK) { + case viomajorsubtype_config: + up((struct semaphore *)event->xCorrelationToken); + break; + + case viomajorsubtype_monitor: + handleMonitorEvent(event); + break; + case viomajorsubtype_blockio: + if (vio_handleBlockEvent) + { + (*vio_handleBlockEvent)(event); + } + else + { + printk("vio: unexpected virtual blockio event\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + break; + case viomajorsubtype_chario: + if (vio_handleCharEvent) + { + (*vio_handleCharEvent)(event); + } + else + { + printk("vio: unexpected virtual chario event\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + break; + case viomajorsubtype_cdio: + if (vio_handleCDEvent) + { + (*vio_handleCDEvent)(event); + } + else + { + printk("vio: unexpected virtual cd event\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + break; + case viomajorsubtype_tape: + if (vio_handleTapeEvent) + { + (*vio_handleTapeEvent)(event); + } + else + { + printk("vio: unexpected virtual tape event\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + break; + default: + printk("vio: unexpected virtual io event subtype %d\n",event->xSubtype); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } +} + + +static void viopath_donealloc(void *parm, int number) +{ + struct doneAllocParms_t *doneAllocParmsp = (struct doneAllocParms_t *)parm; + doneAllocParmsp->number = number; + up(doneAllocParmsp->sem); +} + +int viopath_open(HvLpIndex remoteLp, int subtype) +{ + HvLpEvent_Rc hvrc; + struct doneAllocParms_t doneAllocParms; + DECLARE_MUTEX_LOCKED(Semaphore); + + if ((remoteLp >= HvMaxArchitectedLps) || (remoteLp == HvLpIndexInvalid)) + return -EINVAL; + + if (VIOReqPage == 0) + { + /* Get a page to build read/write LP events in */ + VIOReqPage = get_free_page(GFP_KERNEL); + if (!VIOReqPage) + { + printk("viopath: error allocating I/O memory\n"); + return -ENOMEM; + } + + vio_char_event_buffer = (void *)VIOReqPage; + vio_block_event_buffer = (void *)(VIOReqPage+256); + vio_cd_event_buffer = (void *)(VIOReqPage+512); + vio_tape_event_buffer = (void *)(VIOReqPage+768); + } + + if ( (0) +#if defined(CONFIG_VIODASD) + || (subtype == viomajorsubtype_blockio) +#endif +#if defined(CONFIG_VIOCONS) + || (subtype == viomajorsubtype_chario) +#endif +#if defined(CONFIG_VIOCD) + || (subtype == viomajorsubtype_cdio) +#endif +#if defined(CONFIG_VIOTAPE) + || (subtype == viomajorsubtype_tape) +#endif + ) + { + viopath_statuslock(remoteLp); + switch (subtype) { +#if defined(CONFIG_VIODASD) + case viomajorsubtype_blockio: + viopathStatus[remoteLp].xBlockUsers++; + break; +#endif +#if defined(CONFIG_VIOCONS) + case viomajorsubtype_chario: + viopathStatus[remoteLp].xCharUsers++; + break; +#endif +#if defined(CONFIG_VIOCD) + case viomajorsubtype_cdio: + viopathStatus[remoteLp].xCDUsers++; + break; +#endif +#if defined(CONFIG_VIOTAPE) + case viomajorsubtype_tape: + viopathStatus[remoteLp].xTapeUsers++; + break; +#endif + default: + } + if (!viopathStatus[remoteLp].isOpen) + { + HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualIo); + + viopathStatus[remoteLp].mSourceInst = HvCallEvent_getSourceLpInstanceId(remoteLp, HvLpEvent_Type_VirtualIo); + viopathStatus[remoteLp].mTargetInst = HvCallEvent_getTargetLpInstanceId(remoteLp, HvLpEvent_Type_VirtualIo); + + printk("viopath: open, setting sinst %d, tinst %d\n", + viopathStatus[remoteLp].mSourceInst, + viopathStatus[remoteLp].mTargetInst); + + doneAllocParms.sem = &Semaphore; + + mf_allocateLpEvents(remoteLp, + HvLpEvent_Type_VirtualIo, + 250, /* TODO: Put a sizeof VIOLpEvent in here! */ + 25, /* TODO: Work out the real number */ + &viopath_donealloc, + &doneAllocParms); + + down(&Semaphore); + + viopathStatus[remoteLp].numberAllocated = doneAllocParms.number; + + HvLpEvent_registerHandler(HvLpEvent_Type_VirtualIo, &vio_handleEvent); + + viopathStatus[remoteLp].isOpen = 1; + + hvrc = HvCallEvent_signalLpEventFast(remoteLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_monitor, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_DeferredAck, + viopathStatus[remoteLp].mSourceInst, + viopathStatus[remoteLp].mTargetInst, + 0, 0, 0, 0, 0, 0); + + if (hvrc == HvLpEvent_Rc_Good) + { + viopathStatus[remoteLp].isActive = 1; + } + else + { + viopathStatus[remoteLp].isActive = 0; + } + } + viopath_statusunlock(remoteLp); + + return 0; + } + else /* invalid subtype */ + { + printk("viopath: invalid path subtype %d\n",subtype); + return -EINVAL; + } +} + +int viopath_close(HvLpIndex remoteLp, int subtype) +{ + printk("viopath: close(%d,%4.4x)\n",remoteLp,subtype); + + if ((remoteLp >= HvMaxArchitectedLps) || (remoteLp == HvLpIndexInvalid)) + return -EINVAL; + + if ( (0) +#if defined(CONFIG_VIODASD) + || (subtype == viomajorsubtype_blockio) +#endif +#if defined(CONFIG_VIOCONS) + || (subtype == viomajorsubtype_chario) +#endif +#if defined(CONFIG_VIOCD) + || (subtype == viomajorsubtype_cdio) +#endif +#if defined(CONFIG_VIOTAPE) + || (subtype == viomajorsubtype_tape) +#endif + ) + { + viopath_statuslock(remoteLp); + switch (subtype) { +#if defined(CONFIG_VIODASD) + case viomajorsubtype_blockio: + viopathStatus[remoteLp].xBlockUsers--; + break; +#endif +#if defined(CONFIG_VIOCONS) + case viomajorsubtype_chario: + viopathStatus[remoteLp].xCharUsers--; + break; +#endif +#if defined(CONFIG_VIOCD) + case viomajorsubtype_cdio: + viopathStatus[remoteLp].xCDUsers--; + break; +#endif +#if defined(CONFIG_VIOTAPE) + case viomajorsubtype_tape: + viopathStatus[remoteLp].xTapeUsers--; + break; +#endif + default: + } + + if ((viopathStatus[remoteLp].isOpen) +#if defined(CONFIG_VIODASD) + && (viopathStatus[remoteLp].xBlockUsers == 0) +#endif +#if defined(CONFIG_VIOCONS) + && (viopathStatus[remoteLp].xCharUsers == 0) +#endif +#if defined(CONFIG_VIOCD) + && (viopathStatus[remoteLp].xCDUsers == 0) +#endif +#if defined(CONFIG_VIOTAPE) + && (viopathStatus[remoteLp].xTapeUsers == 0) +#endif + ) + { + printk("viopath: closing event path\n"); + HvCallEvent_closeLpEventPath(remoteLp, HvLpEvent_Type_VirtualIo); + viopathStatus[remoteLp].isOpen = 0; + viopathStatus[remoteLp].isActive = 0; + } + viopath_statusunlock(remoteLp); + + return 0; + } + else /* invalid subtype */ + { + printk("viopath: invalid path subtype %d\n",subtype); + return -EINVAL; + } +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/xics.c linux.ac/arch/ppc64/kernel/xics.c --- linux.vanilla/arch/ppc64/kernel/xics.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/xics.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,343 @@ +/* + * arch/ppc/kernel/xics.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) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "i8259.h" +#include "xics.h" +#include + +extern struct Naca *naca; + +void xics_enable_irq(u_int irq); +void xics_disable_irq(u_int irq); +void xics_mask_and_ack_irq(u_int irq); +void xics_end_irq(u_int irq); + +struct hw_interrupt_type xics_pic = { + " XICS ", + NULL, + NULL, + xics_enable_irq, + xics_disable_irq, + xics_mask_and_ack_irq, + xics_end_irq +}; + +struct hw_interrupt_type xics_8259_pic = { + " XICS/8259", + NULL, + NULL, + NULL, + NULL, + xics_mask_and_ack_irq, + NULL +}; + +#define XICS_IPI 2 +#define XICS_IRQ_OFFSET 0x10 + +#define XICS_IRQ_SPURIOUS 0 + +#define DEFAULT_PRIORITY 0 + +struct xics_ipl { + union { + u32 word; + u8 bytes[4]; + } xirr_poll; + union { + u32 word; + u8 bytes[4]; + } xirr; + u32 dummy; + union { + u32 word; + u8 bytes[4]; + } qirr; +}; + +struct xics_info { + volatile struct xics_ipl * per_cpu[NR_CPUS]; +}; + +struct xics_info xics_info; + +#define xirr_info(n_cpu) (xics_info.per_cpu[n_cpu]->xirr.word) +#define cppr_info(n_cpu) (xics_info.per_cpu[n_cpu]->xirr.bytes[0]) +#define poll_info(n_cpu) (xics_info.per_cpu[n_cpu]->xirr_poll.word) +#define qirr_info(n_cpu) (xics_info.per_cpu[n_cpu]->qirr.bytes[0]) + +unsigned long long intr_base = 0; +unsigned int xics_irq_8259_cascade = 0; + +struct xics_interrupt_node { + unsigned long long addr; + unsigned long long size; +} inodes[NR_CPUS*2]; + +void +xics_enable_irq( + u_int irq + ) +{ + unsigned long status; + long call_status; + + irq -= XICS_IRQ_OFFSET; + if (irq == XICS_IPI) + return; + call_status = call_rtas("ibm,set-xive", 3, 1, (unsigned long*)&status, + irq, cpu_hw_index[0], DEFAULT_PRIORITY); + if( call_status != 0 ) { + printk("xics_enable_irq: irq=%x: call_rtas failed; retn=%x, status=%x\n", + irq, call_status, status); + return; + } +} + +void +xics_disable_irq( + u_int irq + ) +{ + unsigned long status; + long call_status; + + irq -= XICS_IRQ_OFFSET; + call_status = call_rtas("ibm,int-off", 1, 1, (unsigned long*)&status, + irq); + if( call_status != 0 ) { + printk("xics_disable_irq: irq=%x: call_rtas failed, retn=%x\n", + irq, call_status); + return; + } +} + +void +xics_end_irq( + u_int irq + ) +{ + int cpu = smp_processor_id(); + + cppr_info(cpu) = 0; /* actually the value overwritten by ack */ + iosync(); + xirr_info(cpu) = (0xff<<24) | (irq-XICS_IRQ_OFFSET); + iosync(); +} + +void +xics_mask_and_ack_irq( + u_int irq + ) +{ + int cpu = smp_processor_id(); + + if( irq < XICS_IRQ_OFFSET ) { + i8259_pic.ack(irq); + iosync(); + xirr_info(cpu) = (0xff<<24) | xics_irq_8259_cascade; + iosync(); + } + else { + cppr_info(cpu) = 0xff; + iosync(); + } +} + +int +xics_get_irq(struct pt_regs *regs) +{ + u_int cpu = smp_processor_id(); + u_int vec; + int irq; + + vec = xirr_info(cpu); + /* (vec >> 24) == old priority */ + vec &= 0x00ffffff; + /* for sanity, this had better be < NR_IRQS - 16 */ + if( vec == xics_irq_8259_cascade ) { + irq = i8259_irq(cpu); + if(irq == -1) { + /* Spurious cascaded interrupt. Still must ack xics */ + xics_end_irq(XICS_IRQ_OFFSET + xics_irq_8259_cascade); + } + } else if( vec == XICS_IRQ_SPURIOUS ) + irq = -1; + else + irq = vec + XICS_IRQ_OFFSET; + return irq; +} + +int +xics_to_irq(int line) +{ + int irq; + + irq = line + XICS_IRQ_OFFSET; + return irq; +} + +#ifdef CONFIG_SMP +void xics_ipi_action(int irq, void *dev_id, struct pt_regs *regs) +{ + extern volatile unsigned long xics_ipi_message[]; + int cpu = smp_processor_id(); + + qirr_info(cpu) = 0xff; + + while (xics_ipi_message[cpu]) { + if (test_and_clear_bit(PPC_MSG_CALL_FUNCTION, &xics_ipi_message[cpu])) { + mb(); + smp_message_recv(PPC_MSG_CALL_FUNCTION, regs); + } + if (test_and_clear_bit(PPC_MSG_RESCHEDULE, &xics_ipi_message[cpu])) { + mb(); + smp_message_recv(PPC_MSG_RESCHEDULE, regs); + } + } + +} + +void xics_cause_IPI(int cpu) +{ + qirr_info(cpu) = 0; +} + +void xics_setup_cpu(void) +{ + int cpu = smp_processor_id(); + + cppr_info(cpu) = 0xff; + iosync(); +} +#endif /* CONFIG_SMP */ + +void +xics_init_IRQ( void ) +{ + int i; + unsigned long intr_size = 0; + struct device_node *np; + uint *ireg, ilen, indx=0; + + np = find_type_devices("PowerPC-External-Interrupt-Presentation"); + if (!np) { + printk(KERN_WARNING "Can't find Interrupt Presentation\n"); + udbg_printf("Can't find Interrupt Presentation\n"); + while (1); + } +nextnode: + ireg = (uint *)get_property(np, "ibm,interrupt-server-ranges", 0); + if (ireg) { + /* + * set node starting index for this node + */ + indx = *ireg; + } + + ireg = (uint *)get_property(np, "reg", &ilen); + if (!ireg) { + printk(KERN_WARNING "Can't find Interrupt Reg Property\n"); + udbg_printf("Can't find Interrupt Reg Property\n"); + while (1); + } + + while (ilen) { + inodes[indx].addr = (unsigned long long)*ireg++ << 32; + ilen -= sizeof(uint); + inodes[indx].addr |= *ireg++; + ilen -= sizeof(uint); + inodes[indx].size = (unsigned long long)*ireg++ << 32; + ilen -= sizeof(uint); + inodes[indx].size |= *ireg++; + ilen -= sizeof(uint); + indx++; + if (indx >= NR_CPUS) break; + } + + np = np->next; + if ((indx < NR_CPUS) && np) goto nextnode; + + /* + * XXX Assume for now that nodes are in order + * We could (and should) get the "interrupt-server-ranges" property + * for each "presentation" node, and for a given CPU, look at + * the CPU's "ibm,ppc-interrupt-server#s" property to see which + * "presentation" node this CPU is routed to. But for now, the + * server numbers appear to match the physical cpu index, and + * are sequentially assigned to the nodes in order. + * XXX Also a gross assumption in the reload handler (hashtable.S) + * that all CPU's XICS nodes addresses have the same upper 16 bits + */ + intr_base = inodes[0].addr; + intr_size = (ulong)inodes[0].size; + + np = find_type_devices("interrupt-controller"); + if (!np) { + printk(KERN_WARNING "Can't find ISA Interrupt Controller\n"); + udbg_printf("Can't find ISA Interrupt Controller\n"); + while (1); + } + ireg = (uint *) get_property(np, "interrupts", 0); + if (!ireg) { + printk(KERN_WARNING "Can't find ISA Interrupts Property\n"); + udbg_printf("Can't find ISA Interrupts Property\n"); + while (1); + } + + xics_irq_8259_cascade = *ireg; + +#ifdef CONFIG_SMP + for (i = 0; i < naca->processorCount; ++i) { + xics_info.per_cpu[i] = + ioremap((ulong)inodes[cpu_hw_index[i]].addr, + (ulong)inodes[cpu_hw_index[i]].size); + } +#else + xics_info.per_cpu[0] = ioremap((ulong)intr_base, intr_size); +#endif /* CONFIG_SMP */ + + xics_8259_pic.enable = i8259_pic.enable; + xics_8259_pic.disable = i8259_pic.disable; + for (i = 0; i < 16; ++i) + irq_desc[i].handler = &xics_8259_pic; + for (; i < NR_IRQS; ++i) + irq_desc[i].handler = &xics_pic; + + cppr_info(0) = 0xff; + iosync(); + if (request_irq(xics_irq_8259_cascade + XICS_IRQ_OFFSET, no_action, + 0, "8259 cascade", 0)) + printk(KERN_ERR "xics_init_IRQ: couldn't get 8259 cascade\n"); + i8259_init(); + +#ifdef CONFIG_SMP + request_irq(XICS_IPI + XICS_IRQ_OFFSET, xics_ipi_action, 0, "IPI", 0); + irq_desc[XICS_IPI+XICS_IRQ_OFFSET].status |= IRQ_PER_CPU; +#endif +} + +void xics_isa_init(void) +{ + return; + if (request_irq(xics_irq_8259_cascade + XICS_IRQ_OFFSET, no_action, + 0, "8259 cascade", 0)) + printk(KERN_ERR "xics_init_IRQ: couldn't get 8259 cascade\n"); + i8259_init(); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/kernel/xics.h linux.ac/arch/ppc64/kernel/xics.h --- linux.vanilla/arch/ppc64/kernel/xics.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/kernel/xics.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,24 @@ +/* + * arch/ppc/kernel/xics.h + * + * 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) any later version. + */ + +#ifndef _PPC_KERNEL_XICS_H +#define _PPC_KERNEL_XICS_H + +#include "local_irq.h" + +extern struct hw_interrupt_type xics_pic; +extern struct hw_interrupt_type xics_8259_pic; + +void xics_init_IRQ(void); +int xics_get_irq(struct pt_regs *); +void xics_isa_init(void); + +#endif /* _PPC_KERNEL_XICS_H */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/lib/Makefile linux.ac/arch/ppc64/lib/Makefile --- linux.vanilla/arch/ppc64/lib/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/lib/Makefile Wed Oct 10 01:47:34 2001 @@ -0,0 +1,11 @@ +# +# Makefile for ppc64-specific library files.. +# + +USE_STANDARD_AS_RULE := true + +O_TARGET = lib.o + +obj-y := checksum.o string.o strcase.o + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/lib/checksum.S linux.ac/arch/ppc64/lib/checksum.S --- linux.vanilla/arch/ppc64/lib/checksum.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/lib/checksum.S Wed Oct 10 01:47:34 2001 @@ -0,0 +1,231 @@ +/* + * This file contains assembly-language implementations + * of IP-style 1's complement checksum routines. + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Severely hacked about by Paul Mackerras (paulus@cs.anu.edu.au). + */ + +#include +#include +#include +#include "../kernel/ppc_asm.tmpl" + + .text + +/* + * ip_fast_csum(r3=buf, r4=len) -- Optimized for IP header + * len is in words and is always >= 5. + * + * In practice len == 5, but this is not guaranteed. So this code does not + * attempt to use doubleword instructions. + */ +_GLOBAL(ip_fast_csum) + lwz r0,0(r3) + lwzu r5,4(r3) + addic. r4,r4,-2 + addc r0,r0,r5 + mtctr r4 + blelr- +1: lwzu r4,4(r3) + adde r0,r0,r4 + bdnz 1b + addze r0,r0 /* add in final carry */ + rldicl r4,r0,32,0 /* fold two 32-bit halves together */ + add r0,r0,r4 + srdi r0,r0,32 + rlwinm r3,r0,16,0,31 /* fold two halves together */ + add r3,r0,r3 + not r3,r3 + srwi r3,r3,16 + blr + +/* + * Compute checksum of TCP or UDP pseudo-header: + * csum_tcpudp_magic(r3=saddr, r4=daddr, r5=len, r6=proto, r7=sum) + * No real gain trying to do this specially for 64 bit, but + * the 32 bit addition may spill into the upper bits of + * the doubleword so we still must fold it down from 64. + */ +_GLOBAL(csum_tcpudp_magic) + rlwimi r5,r6,16,0,15 /* put proto in upper half of len */ + addc r0,r3,r4 /* add 4 32-bit words together */ + adde r0,r0,r5 + adde r0,r0,r7 + rldicl r4,r0,32,0 /* fold 64 bit value */ + add r0,r4,r0 + srdi r0,r0,32 + rlwinm r3,r0,16,0,31 /* fold two halves together */ + add r3,r0,r3 + not r3,r3 + srwi r3,r3,16 + blr + +/* + * Computes the checksum of a memory block at buff, length len, + * and adds in "sum" (32-bit). + * + * This code assumes at least halfword alignment, though the length + * can be any number of bytes. The sum is accumulated in r5. + * + * csum_partial(r3=buff, r4=len, r5=sum) + */ +_GLOBAL(csum_partial) + subi r3,r3,8 /* we'll offset by 8 for the loads */ + srdi. r6,r4,3 /* divide by 8 for doubleword count */ + addic r5,r5,0 /* clear carry */ + beq 3f /* if we're doing < 8 bytes */ + andi. r0,r3,2 /* aligned on a word boundary already? */ + beq+ 1f + lhz r6,8(r3) /* do 2 bytes to get aligned */ + addi r3,r3,2 + subi r4,r4,2 + addc r5,r5,r6 + srdi. r6,r4,3 /* recompute number of doublewords */ + beq 3f /* any left? */ +1: mtctr r6 +2: ldu r6,8(r3) /* main sum loop */ + adde r5,r5,r6 + bdnz 2b + andi. r4,r4,7 /* compute bytes left to sum after doublewords */ +3: cmpi 0,r4,4 /* is at least a full word left? */ + blt 4f + lwz r6,8(r3) /* sum this word */ + addi r3,r3,4 + subi r4,r4,4 + adde r5,r5,r6 +4: cmpi 0,r4,2 /* is at least a halfword left? */ + blt+ 5f + lhz r6,8(r3) /* sum this halfword */ + addi r3,r3,2 + subi r4,r4,2 + adde r5,r5,r6 +5: cmpi 0,r4,1 /* is at least a byte left? */ + bne+ 6f + lbz r6,8(r3) /* sum this byte */ + slwi r6,r6,8 /* this byte is assumed to be the upper byte of a halfword */ + adde r5,r5,r6 +6: addze r5,r5 /* add in final carry */ + rldicl r4,r5,32,0 /* fold two 32-bit halves together */ + add r3,r4,r5 + srdi r3,r3,32 + blr + +/* + * Computes the checksum of a memory block at src, length len, + * and adds in "sum" (32-bit), while copying the block to dst. + * If an access exception occurs on src or dst, it stores -EFAULT + * to *src_err or *dst_err respectively, and (for an error on + * src) zeroes the rest of dst. + * + * This code needs to be reworked to take advantage of 64 bit sum+copy. + * However, due to tokenring halfword alignment problems this will be very + * tricky. For now we'll leave it until we instrument it somehow. + * + * csum_partial_copy_generic(r3=src, r4=dst, r5=len, r6=sum, r7=src_err, r8=dst_err) + */ +_GLOBAL(csum_partial_copy_generic) + addic r0,r6,0 + subi r3,r3,4 + subi r4,r4,4 + srwi. r6,r5,2 + beq 3f /* if we're doing < 4 bytes */ + andi. r9,r4,2 /* Align dst to longword boundary */ + beq+ 1f +81: lhz r6,4(r3) /* do 2 bytes to get aligned */ + addi r3,r3,2 + subi r5,r5,2 +91: sth r6,4(r4) + addi r4,r4,2 + addc r0,r0,r6 + srwi. r6,r5,2 /* # words to do */ + beq 3f +1: mtctr r6 +82: lwzu r6,4(r3) /* the bdnz has zero overhead, so it should */ +92: stwu r6,4(r4) /* be unnecessary to unroll this loop */ + adde r0,r0,r6 + bdnz 82b + andi. r5,r5,3 +3: cmpi 0,r5,2 + blt+ 4f +83: lhz r6,4(r3) + addi r3,r3,2 + subi r5,r5,2 +93: sth r6,4(r4) + addi r4,r4,2 + adde r0,r0,r6 +4: cmpi 0,r5,1 + bne+ 5f +84: lbz r6,4(r3) +94: stb r6,4(r4) + slwi r6,r6,8 /* Upper byte of word */ + adde r0,r0,r6 +5: addze r3,r0 /* add in final carry (unlikely with 64-bit regs) */ + rldicl r4,r3,32,0 /* fold 64 bit value */ + add r3,r4,r3 + srdi r3,r3,32 + blr + +/* These shouldn't go in the fixup section, since that would + cause the ex_table addresses to get out of order. */ + + .globl src_error_1 +src_error_1: + li r6,0 + subi r5,r5,2 +95: sth r6,4(r4) + addi r4,r4,2 + srwi. r6,r5,2 + beq 3f + mtctr r6 + .globl src_error_2 +src_error_2: + li r6,0 +96: stwu r6,4(r4) + bdnz 96b +3: andi. r5,r5,3 + beq src_error + .globl src_error_3 +src_error_3: + li r6,0 + mtctr r5 + addi r4,r4,3 +97: stbu r6,1(r4) + bdnz 97b + .globl src_error +src_error: + cmpi 0,r7,0 + beq 1f + li r6,-EFAULT + stw r6,0(r7) +1: addze r3,r0 + blr + + .globl dst_error +dst_error: + cmpi 0,r8,0 + beq 1f + li r6,-EFAULT + stw r6,0(r8) +1: addze r3,r0 + blr + +.section __ex_table,"a" + .align 3 + .llong 81b,src_error_1 + .llong 91b,dst_error + .llong 82b,src_error_2 + .llong 92b,dst_error + .llong 83b,src_error_3 + .llong 93b,dst_error + .llong 84b,src_error_3 + .llong 94b,dst_error + .llong 95b,dst_error + .llong 96b,dst_error + .llong 97b,dst_error diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/lib/strcase.c linux.ac/arch/ppc64/lib/strcase.c --- linux.vanilla/arch/ppc64/lib/strcase.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/lib/strcase.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,31 @@ +/* + * c 2001 PPC 64 Team, 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. + */ +#include + +int strcasecmp(const char *s1, const char *s2) +{ + int c1, c2; + + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } while (c1 == c2 && c1 != 0); + return c1 - c2; +} + +int strncasecmp(const char *s1, const char *s2, int n) +{ + int c1, c2; + + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } while ((--n > 0) && c1 == c2 && c1 != 0); + return c1 - c2; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/lib/string.S linux.ac/arch/ppc64/lib/string.S --- linux.vanilla/arch/ppc64/lib/string.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/lib/string.S Wed Oct 10 01:47:34 2001 @@ -0,0 +1,661 @@ +/* + * String handling functions for PowerPC. + * + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include "../kernel/ppc_asm.tmpl" +#include +#include +#include + +#define CACHE_LINE_SIZE 128 +#define LG_CACHE_LINE_SIZE 7 +#define MAX_COPY_PREFETCH 1 + +#define COPY_16_BYTES \ + lwz r7,4(r4); \ + lwz r8,8(r4); \ + lwz r9,12(r4); \ + lwzu r10,16(r4); \ + stw r7,4(r6); \ + stw r8,8(r6); \ + stw r9,12(r6); \ + stwu r10,16(r6) + +#define COPY_16_BYTES_WITHEX(n) \ +8 ## n ## 0: \ + lwz r7,4(r4); \ +8 ## n ## 1: \ + lwz r8,8(r4); \ +8 ## n ## 2: \ + lwz r9,12(r4); \ +8 ## n ## 3: \ + lwzu r10,16(r4); \ +8 ## n ## 4: \ + stw r7,4(r6); \ +8 ## n ## 5: \ + stw r8,8(r6); \ +8 ## n ## 6: \ + stw r9,12(r6); \ +8 ## n ## 7: \ + stwu r10,16(r6) + +#define COPY_16_BYTES_EXCODE(n) \ +9 ## n ## 0: \ + addi r5,r5,-(16 * n); \ + b 104f; \ +9 ## n ## 1: \ + addi r5,r5,-(16 * n); \ + b 105f; \ +.section __ex_table,"a"; \ + .align 3; \ + .llong 8 ## n ## 0b,9 ## n ## 0b; \ + .llong 8 ## n ## 1b,9 ## n ## 0b; \ + .llong 8 ## n ## 2b,9 ## n ## 0b; \ + .llong 8 ## n ## 3b,9 ## n ## 0b; \ + .llong 8 ## n ## 4b,9 ## n ## 1b; \ + .llong 8 ## n ## 5b,9 ## n ## 1b; \ + .llong 8 ## n ## 6b,9 ## n ## 1b; \ + .llong 8 ## n ## 7b,9 ## n ## 1b; \ +.text + +CACHELINE_BYTES = CACHE_LINE_SIZE +LG_CACHELINE_BYTES = LG_CACHE_LINE_SIZE +CACHELINE_MASK = (CACHE_LINE_SIZE-1) + +_GLOBAL(strcpy) + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + +_GLOBAL(strncpy) + cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + blr + +_GLOBAL(strcat) + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r5) + cmpwi 0,r0,0 + bne 1b + addi r5,r5,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + +_GLOBAL(strcmp) + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r5) + cmpwi 1,r3,0 + lbzu r0,1(r4) + subf. r3,r0,r3 + beqlr 1 + beq 1b + blr + +_GLOBAL(strlen) + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + blr + +/* + * Use dcbz on the complete cache lines in the destination + * to set them to zero. This requires that the destination + * area is cacheable. -- paulus + */ +_GLOBAL(cacheable_memzero) + mr r5,r4 + li r4,0 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + clrlwi r7,r6,32-LG_CACHELINE_BYTES + add r8,r7,r5 + srwi r9,r8,LG_CACHELINE_BYTES + addic. r9,r9,-1 /* total number of complete cachelines */ + ble 2f + xori r0,r7,CACHELINE_MASK & ~3 + srwi. r0,r0,2 + beq 3f + mtctr r0 +4: stwu r4,4(r6) + bdnz 4b +3: mtctr r9 + li r7,4 +10: dcbz r7,r6 + addi r6,r6,CACHELINE_BYTES + bdnz 10b + clrlwi r5,r8,32-LG_CACHELINE_BYTES + addi r5,r5,4 +2: srwi r0,r5,2 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + +_GLOBAL(memset) + rlwimi r4,r4,8,16,23 + rlwimi r4,r4,16,0,15 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + srwi r0,r5,2 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + +_GLOBAL(bcopy) + mr r6,r3 + mr r3,r4 + mr r4,r6 + b .memcpy + +/* + * This version uses dcbz on the complete cache lines in the + * destination area to reduce memory traffic. This requires that + * the destination area is cacheable. + * We only use this version if the source and dest don't overlap. + * -- paulus. + */ +_GLOBAL(cacheable_memcpy) + add r7,r3,r5 /* test if the src & dst overlap */ + add r8,r4,r5 + cmplw 0,r4,r7 + cmplw 1,r3,r8 + crand 0,0,4 /* cr0.lt &= cr1.lt */ + blt .memcpy /* if regions overlap */ + + addi r4,r4,-4 + addi r6,r3,-4 + neg r0,r3 + andi. r0,r0,CACHELINE_MASK /* # bytes to start of cache line */ + beq 58f + + cmplw 0,r5,r0 /* is this more than total to do? */ + blt 63f /* if not much to do */ + andi. r8,r0,3 /* get it word-aligned first */ + subf r5,r0,r5 + mtctr r8 + beq+ 61f +70: lbz r9,4(r4) /* do some bytes */ + stb r9,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 70b +61: srwi. r0,r0,2 + mtctr r0 + beq 58f +72: lwzu r9,4(r4) /* do some words */ + stwu r9,4(r6) + bdnz 72b + +58: srwi. r0,r5,LG_CACHELINE_BYTES /* # complete cachelines */ + clrlwi r5,r5,32-LG_CACHELINE_BYTES + li r11,4 + mtctr r0 + beq 63f +53: + dcbz r11,r6 + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 32 + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 64 + COPY_16_BYTES + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 128 + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES +#endif +#endif +#endif + bdnz 53b + +63: srwi. r0,r5,2 + mtctr r0 + beq 64f +30: lwzu r0,4(r4) + stwu r0,4(r6) + bdnz 30b + +64: andi. r0,r5,3 + mtctr r0 + beq+ 65f +40: lbz r0,4(r4) + stb r0,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 40b +65: blr + +_GLOBAL(memmove) + cmplw 0,r3,r4 + bgt .backwards_memcpy + /* fall through */ + +_GLOBAL(memcpy) + srwi. r7,r5,3 + addi r6,r3,-4 + addi r4,r4,-4 + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) + lwzu r8,8(r4) + stw r7,4(r6) + stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,4(r4) + addi r5,r5,-4 + stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) + stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 + stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + +_GLOBAL(backwards_memcpy) + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + add r6,r3,r5 + add r4,r4,r5 + beq 2f + andi. r0,r6,3 + mtctr r7 + bne 5f +1: lwz r7,-4(r4) + lwzu r8,-8(r4) + stw r7,-4(r6) + stwu r8,-8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,-4(r4) + subi r5,r5,4 + stwu r0,-4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 +4: lbzu r0,-1(r4) + stbu r0,-1(r6) + bdnz 4b + blr +5: mtctr r0 +6: lbzu r7,-1(r4) + stbu r7,-1(r6) + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + +_GLOBAL(memcmp) + cmpwi 0,r5,0 + ble- 2f + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r6) + lbzu r0,1(r4) + subf. r3,r0,r3 + bdnzt 2,1b + blr +2: li r3,0 + blr + +_GLOBAL(memchr) + cmpwi 0,r5,0 + ble- 2f + mtctr r5 + addi r3,r3,-1 +1: lbzu r0,1(r3) + cmpw 0,r0,r4 + bdnzf 2,1b + beqlr +2: li r3,0 + blr + +_GLOBAL(__copy_tofrom_user) + addi r4,r4,-4 + addi r6,r3,-4 + neg r0,r3 + andi. r0,r0,CACHELINE_MASK /* # bytes to start of cache line */ + beq 58f + + cmplw 0,r5,r0 /* is this more than total to do? */ + blt 63f /* if not much to do */ + andi. r8,r0,3 /* get it word-aligned first */ + mtctr r8 + beq+ 61f +70: lbz r9,4(r4) /* do some bytes */ +71: stb r9,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 70b +61: subf r5,r0,r5 + srwi. r0,r0,2 + mtctr r0 + beq 58f +72: lwzu r9,4(r4) /* do some words */ +73: stwu r9,4(r6) + bdnz 72b + +58: srwi. r0,r5,LG_CACHELINE_BYTES /* # complete cachelines */ + clrlwi r5,r5,32-LG_CACHELINE_BYTES + li r11,4 + beq 63f + + /* Here we decide how far ahead to prefetch the source */ +#if MAX_COPY_PREFETCH > 1 + /* Heuristically, for large transfers we prefetch + MAX_COPY_PREFETCH cachelines ahead. For small transfers + we prefetch 1 cacheline ahead. */ + cmpwi r0,MAX_COPY_PREFETCH + li r7,1 + li r3,4 + ble 111f + li r7,MAX_COPY_PREFETCH +111: mtctr r7 +112: dcbt r3,r4 + addi r3,r3,CACHELINE_BYTES + bdnz 112b +#else /* MAX_COPY_PREFETCH == 1 */ + li r3,CACHELINE_BYTES + 4 + dcbt r11,r4 +#endif /* MAX_COPY_PREFETCH */ + + mtctr r0 +53: + dcbt r3,r4 + dcbz r11,r6 +/* had to move these to keep extable in order */ + .section __ex_table,"a" + .align 3 + .llong 70b,100f + .llong 71b,101f + .llong 72b,102f + .llong 73b,103f + .llong 53b,105f + .text +/* the main body of the cacheline loop */ + COPY_16_BYTES_WITHEX(0) +#if CACHE_LINE_SIZE >= 32 + COPY_16_BYTES_WITHEX(1) +#if CACHE_LINE_SIZE >= 64 + COPY_16_BYTES_WITHEX(2) + COPY_16_BYTES_WITHEX(3) +#if CACHE_LINE_SIZE >= 128 + COPY_16_BYTES_WITHEX(4) + COPY_16_BYTES_WITHEX(5) + COPY_16_BYTES_WITHEX(6) + COPY_16_BYTES_WITHEX(7) +#endif +#endif +#endif + bdnz 53b + +63: srwi. r0,r5,2 + mtctr r0 + beq 64f +30: lwzu r0,4(r4) +31: stwu r0,4(r6) + bdnz 30b + +64: andi. r0,r5,3 + mtctr r0 + beq+ 65f +40: lbz r0,4(r4) +41: stb r0,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 40b +65: li r3,0 + blr + +/* read fault, initial single-byte copy */ +100: li r4,0 + b 90f +/* write fault, initial single-byte copy */ +101: li r4,1 +90: subf r5,r8,r5 + li r3,0 + b 99f +/* read fault, initial word copy */ +102: li r4,0 + b 91f +/* write fault, initial word copy */ +103: li r4,1 +91: li r3,2 + b 99f + +/* + * this stuff handles faults in the cacheline loop and branches to either + * 104f (if in read part) or 105f (if in write part), after updating r5 + */ + COPY_16_BYTES_EXCODE(0) +#if CACHE_LINE_SIZE >= 32 + COPY_16_BYTES_EXCODE(1) +#if CACHE_LINE_SIZE >= 64 + COPY_16_BYTES_EXCODE(2) + COPY_16_BYTES_EXCODE(3) +#if CACHE_LINE_SIZE >= 128 + COPY_16_BYTES_EXCODE(4) + COPY_16_BYTES_EXCODE(5) + COPY_16_BYTES_EXCODE(6) + COPY_16_BYTES_EXCODE(7) +#endif +#endif +#endif + +/* read fault in cacheline loop */ +104: li r4,0 + b 92f +/* fault on dcbz (effectively a write fault) */ +/* or write fault in cacheline loop */ +105: li r4,1 +92: li r3,LG_CACHELINE_BYTES + b 99f +/* read fault in final word loop */ +108: li r4,0 + b 93f +/* write fault in final word loop */ +109: li r4,1 +93: andi. r5,r5,3 + li r3,2 + b 99f +/* read fault in final byte loop */ +110: li r4,0 + b 94f +/* write fault in final byte loop */ +111: li r4,1 +94: li r5,0 + li r3,0 +/* + * At this stage the number of bytes not copied is + * r5 + (ctr << r3), and r4 is 0 for read or 1 for write. + */ +99: mfctr r0 + slw r3,r0,r3 + add r3,r3,r5 + cmpwi 0,r4,0 + bne 120f +/* for read fault, clear out the destination: r3 bytes starting at 4(r6) */ + srwi. r0,r3,2 + li r9,0 + mtctr r0 + beq 113f +112: stwu r9,4(r6) + bdnz 112b +113: andi. r0,r3,3 + mtctr r0 + beq 120f +114: stb r9,4(r6) + addi r6,r6,1 + bdnz 114b +120: blr + + .section __ex_table,"a" + .align 3 + .llong 30b,108b + .llong 31b,109b + .llong 40b,110b + .llong 41b,111b + .llong 112b,120b + .llong 114b,120b + .text + +_GLOBAL(__clear_user) + addi r6,r3,-4 + li r3,0 + li r5,0 + cmplwi 0,r4,4 + blt 7f + /* clear a single word */ +11: stwu r5,4(r6) + beqlr + /* clear word sized chunks */ + andi. r0,r6,3 + add r4,r0,r4 + subf r6,r0,r6 + srwi r0,r4,2 + mtctr r0 + bdz 6f +1: stwu r5,4(r6) + bdnz 1b +6: andi. r4,r4,3 + /* clear byte sized chunks */ +7: cmpwi 0,r4,0 + beqlr + mtctr r4 + addi r6,r6,3 +8: stbu r5,1(r6) + bdnz 8b + blr +99: li r3,-EFAULT + blr + + .section __ex_table,"a" + .align 3 + .llong 11b,99b + .llong 1b,99b + .llong 8b,99b + .text + +_GLOBAL(__strncpy_from_user) + addi r6,r3,-1 + addi r4,r4,-1 + cmpwi 0,r5,0 + beq 2f + mtctr r5 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + beq 3f +2: addi r6,r6,1 +3: subf r3,r3,r6 + blr +99: li r3,-EFAULT + blr + + .section __ex_table,"a" + .align 3 + .llong 1b,99b + .text + +/* r3 = str, r4 = len (> 0), r5 = top (highest addr) */ +_GLOBAL(__strnlen_user) + addi r7,r3,-1 + subf r6,r7,r5 /* top+1 - str */ + cmplw 0,r4,r6 + bge 0f + mr r6,r4 +0: mtctr r6 /* ctr = min(len, top - str) */ +1: lbzu r0,1(r7) /* get next byte */ + cmpwi 0,r0,0 + bdnzf 2,1b /* loop if --ctr != 0 && byte != 0 */ + addi r7,r7,1 + subf r3,r3,r7 /* number of bytes we have looked at */ + beqlr /* return if we found a 0 byte */ + cmpw 0,r3,r4 /* did we look at all len bytes? */ + blt 99f /* if not, must have hit top */ + addi r3,r4,1 /* return len + 1 to indicate no null found */ + blr +99: li r3,0 /* bad address, return 0 */ + blr + + .section __ex_table,"a" + .align 3 + .llong 1b,99b diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/mm/Makefile linux.ac/arch/ppc64/mm/Makefile --- linux.vanilla/arch/ppc64/mm/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/mm/Makefile Wed Oct 10 01:47:34 2001 @@ -0,0 +1,16 @@ +# +# Makefile for the linux ppc-specific parts of the memory manager. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +EXTRA_CFLAGS = -mno-minimal-toc + +O_TARGET := mm.o + +obj-y := fault.o init.o extable.o imalloc.o + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/mm/extable.c linux.ac/arch/ppc64/mm/extable.c --- linux.vanilla/arch/ppc64/mm/extable.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/mm/extable.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,48 @@ +/* + * linux/arch/ppc/mm/extable.c + * + * from linux/arch/i386/mm/extable.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline unsigned long +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) +{ + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return 0; +} + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret; + + ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); + if (ret) return ret; + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/mm/fault.c linux.ac/arch/ppc64/mm/fault.c --- linux.vanilla/arch/ppc64/mm/fault.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/mm/fault.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,239 @@ +/* + * arch/ppc/mm/fault.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/mm/fault.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * Modified by Cort Dougan and Paul Mackerras. + * + * Modified for PPC64 by Dave Engebretsen (engebret@ibm.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) +extern void (*debugger)(struct pt_regs *); +extern void (*debugger_fault_handler)(struct pt_regs *); +extern int (*debugger_dabr_match)(struct pt_regs *); +int debugger_kernel_faults = 1; +#endif + +unsigned long htab_reloads = 0; /* updated by head.S:hash_page() */ +unsigned long htab_evicts = 0; /* updated by head.S:hash_page() */ +unsigned long pte_misses = 0; /* updated by do_page_fault() */ +unsigned long pte_errors = 0; /* updated by do_page_fault() */ +unsigned int probingmem = 0; + +extern void die_if_kernel(char *, struct pt_regs *, long); +void bad_page_fault(struct pt_regs *, unsigned long); +void do_page_fault(struct pt_regs *, unsigned long, unsigned long); + +/* + * For 600- and 800-family processors, the error_code parameter is DSISR + * for a data fault, SRR1 for an instruction fault. + */ +void do_page_fault(struct pt_regs *regs, unsigned long address, + unsigned long error_code) +{ + struct vm_area_struct * vma; + struct mm_struct *mm = current->mm; + siginfo_t info; + unsigned long code = SEGV_MAPERR; + unsigned long is_write = error_code & 0x02000000; + unsigned long mm_fault_return; + + PPCDBG(PPCDBG_MM, "Entering do_page_fault: addr = 0x%16.16lx, error_code = %lx\n\tregs_trap = %lx, srr0 = %lx, srr1 = %lx\n", address, error_code, regs->trap, get_srr0(), get_srr1()); + /* + * Fortunately the bit assignments in SRR1 for an instruction + * fault and DSISR for a data fault are mostly the same for the + * bits we are interested in. But there are some bits which + * indicate errors in DSISR but can validly be set in SRR1. + */ + if (regs->trap == 0x400) + error_code &= 0x48200000; + +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) +#if 0 /* Crap??? PPPBBB */ + if (debugger_fault_handler && regs->trap == 0x300) { + debugger_fault_handler(regs); + return; + } +#endif + if (error_code & 0x00400000) { + /* DABR match */ + if (debugger_dabr_match(regs)) + return; + } +#endif /* CONFIG_XMON || CONFIG_KGDB */ + + if (in_interrupt() || mm == NULL) { + bad_page_fault(regs, address); + return; + } + down_read(&mm->mmap_sem); + vma = find_vma(mm, address); + PPCDBG(PPCDBG_MM, "\tdo_page_fault: vma = 0x%16.16lx\n", vma); + if (!vma) { + PPCDBG(PPCDBG_MM, "\tdo_page_fault: !vma\n"); + goto bad_area; + } + PPCDBG(PPCDBG_MM, "\tdo_page_fault: vma->vm_start = 0x%16.16lx, vma->vm_flags = 0x%16.16lx\n", vma->vm_start, vma->vm_flags); + if (vma->vm_start <= address) { + goto good_area; + } + if (!(vma->vm_flags & VM_GROWSDOWN)) { + PPCDBG(PPCDBG_MM, "\tdo_page_fault: vma->vm_flags = %lx, %lx\n", vma->vm_flags, VM_GROWSDOWN); + goto bad_area; + } + if (expand_stack(vma, address)) { + PPCDBG(PPCDBG_MM, "\tdo_page_fault: expand_stack\n"); + goto bad_area; + } + +good_area: + code = SEGV_ACCERR; + + /* a write */ + if (is_write) { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + /* a read */ + } else { + /* protection fault */ + if (error_code & 0x08000000) + goto bad_area; + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + PPCDBG(PPCDBG_MM, "\tdo_page_fault: calling handle_mm_fault\n"); + mm_fault_return = handle_mm_fault(mm, vma, address, is_write); + PPCDBG(PPCDBG_MM, "\tdo_page_fault: handle_mm_fault = 0x%lx\n", + mm_fault_return); + switch(mm_fault_return) { + case 1: + current->min_flt++; + break; + case 2: + current->maj_flt++; + break; + case 0: + goto do_sigbus; + default: + goto out_of_memory; + } + + up_read(&mm->mmap_sem); + /* + * keep track of tlb+htab misses that are good addrs but + * just need pte's created via handle_mm_fault() + * -- Cort + */ + pte_misses++; + return; + +bad_area: + up_read(&mm->mmap_sem); + pte_errors++; + + /* User mode accesses cause a SIGSEGV */ + if (user_mode(regs)) { + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = code; + info.si_addr = (void *) address; + PPCDBG(PPCDBG_SIGNAL, "Bad addr in user: 0x%lx\n", address); +#ifdef CONFIG_XMON + ifppcdebug(PPCDBG_SIGNALXMON) + PPCDBG_ENTER_DEBUGGER(); +#endif + + force_sig_info(SIGSEGV, &info, current); + return; + } + + bad_page_fault(regs, address); + return; + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: + up_read(&mm->mmap_sem); + printk("VM: killing process %s\n", current->comm); + if (user_mode(regs)) + do_exit(SIGKILL); + bad_page_fault(regs, address); + return; + +do_sigbus: + up_read(&mm->mmap_sem); + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *)address; + force_sig_info (SIGBUS, &info, current); + if (!user_mode(regs)) + bad_page_fault(regs, address); +} + +/* + * bad_page_fault is called when we have a bad access from the kernel. + * It is called from do_page_fault above and from some of the procedures + * in traps.c. + */ +void +bad_page_fault(struct pt_regs *regs, unsigned long address) +{ + unsigned long fixup; + + /* Are we prepared to handle this fault? */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + + /* kernel has accessed a bad area */ + show_regs(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_kernel_faults) + debugger(regs); +#endif + print_backtrace( (unsigned long *)regs->gpr[1] ); + panic("kernel access of bad area pc %lx lr %lx address %lX tsk %s/%d", + regs->nip,regs->link,address,current->comm,current->pid); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/mm/imalloc.c linux.ac/arch/ppc64/mm/imalloc.c --- linux.vanilla/arch/ppc64/mm/imalloc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/mm/imalloc.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,69 @@ +/* + * c 2001 PPC 64 Team, 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. + */ + +#include +#include +#include + +#include +#include + +rwlock_t imlist_lock = RW_LOCK_UNLOCKED; +struct vm_struct * imlist = NULL; + +struct vm_struct *get_im_area(unsigned long size) { + unsigned long addr; + struct vm_struct **p, *tmp, *area; + + area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL); + if (!area) + return NULL; + addr = IMALLOC_START; + write_lock(&imlist_lock); + for (p = &imlist; (tmp = *p) ; p = &tmp->next) { + if (size + addr < (unsigned long) tmp->addr) + break; + addr = tmp->size + (unsigned long) tmp->addr; + if (addr > IMALLOC_END-size) { + write_unlock(&imlist_lock); + kfree(area); + return NULL; + } + } + area->flags = 0; + area->addr = (void *)addr; + area->size = size; + area->next = *p; + *p = area; + write_unlock(&imlist_lock); + return area; +} + +void ifree(void * addr) { + struct vm_struct **p, *tmp; + + if (!addr) + return; + if ((PAGE_SIZE-1) & (unsigned long) addr) { + printk(KERN_ERR "Trying to ifree() bad address (%p)\n", addr); + return; + } + write_lock(&imlist_lock); + for (p = &imlist ; (tmp = *p) ; p = &tmp->next) { + if (tmp->addr == addr) { + *p = tmp->next; + kfree(tmp); + write_unlock(&imlist_lock); + return; + } + } + write_unlock(&imlist_lock); + printk(KERN_ERR "Trying to ifree() nonexistent area (%p)\n", addr); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/mm/init.c linux.ac/arch/ppc64/mm/init.c --- linux.vanilla/arch/ppc64/mm/init.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/mm/init.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,684 @@ +/* + * + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * Dave Engebretsen + * Rework for PPC64 port. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_BLK_DEV_INITRD +#include /* for initrd_* */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PGTOKB(pages) (((pages) * PAGE_SIZE) >> 10) + +#ifdef CONFIG_PPC_ISERIES +#include +#endif + +struct mmu_context_stack_t mmu_context_stack; +int mem_init_done; +unsigned long ioremap_bot = IMALLOC_BASE; + +static int boot_mapsize; +static unsigned long totalram_pages; +static struct device_node *memory_node; + +extern pgd_t swapper_pg_dir[]; +extern char __init_begin, __init_end; +extern char __chrp_begin, __chrp_end; +extern char __openfirmware_begin, __openfirmware_end; +extern struct _of_tce_table of_tce_table[]; +extern char _start[], _end[]; +extern char _stext[], etext[]; +extern struct task_struct *current_set[NR_CPUS]; +extern struct Naca *naca; + +void mm_init_ppc64(void); + +void make_pte(HPTE * htab, + unsigned long va, unsigned long pa, + int mode, unsigned long hash_mask); + +unsigned long *pmac_find_end_of_memory(void); +extern unsigned long *find_end_of_memory(void); + +extern pgd_t ioremap_dir[]; +extern pgd_t bolted_dir[]; +pgd_t * ioremap_pgd = (pgd_t *)&ioremap_dir; +pgd_t * bolted_pgd = (pgd_t *)&bolted_dir; + +static void map_io_page(unsigned long va, unsigned long pa, int flags); +extern void die_if_kernel(char *,struct pt_regs *,long); + +unsigned long klimit = (unsigned long)_end; + +HPTE *Hash=0; +unsigned long Hash_size=0; +unsigned long _SDR1=0; +unsigned long _ASR=0; + +/* max amount of RAM to use */ +unsigned long __max_memory; + +/* This is declared as we are using the more or less generic + * include/asm-ppc64/tlb.h file -- tgall + */ +mmu_gather_t mmu_gathers[NR_CPUS]; + +int do_check_pgt_cache(int low, int high) +{ + int freed = 0; + + if (pgtable_cache_size > high) { + do { + 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; +} + +void show_mem(void) +{ + int i,free = 0,total = 0,reserved = 0; + int shared = 0, cached = 0; + struct task_struct *p; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageReserved(mem_map+i)) + reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; + else if (!atomic_read(&mem_map[i].count)) + free++; + else + shared += atomic_read(&mem_map[i].count) - 1; + } + printk("%d pages of RAM\n",total); + printk("%d free pages\n",free); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%d pages in page table cache\n",(int)pgtable_cache_size); + show_buffers(); + printk("%-8s %3s %8s %8s %8s %9s %8s", "Process", "Pid", + "Ctx", "Ctx<<4", "Last Sys", "pc", "task"); +#ifdef CONFIG_SMP + printk(" %3s", "CPU"); +#endif /* CONFIG_SMP */ + printk("\n"); + for_each_task(p) + { + printk("%-8.8s %3d %8ld %8ld %8ld %c%08lx %08lx ", + p->comm,p->pid, + (p->mm)?p->mm->context:0, + (p->mm)?(p->mm->context<<4):0, + p->thread.last_syscall, + (p->thread.regs)?user_mode(p->thread.regs) ? 'u' : 'k' : '?', + (p->thread.regs)?p->thread.regs->nip:0, + (ulong)p); + { + int iscur = 0; +#ifdef CONFIG_SMP + printk("%3d ", p->processor); + if ( (p->processor != NO_PROC_ID) && + (p == current_set[p->processor]) ) + { + iscur = 1; + printk("current"); + } +#else + if ( p == current ) + { + iscur = 1; + printk("current"); + } + + if ( p == last_task_used_math ) + { + if ( iscur ) + printk(","); + printk("last math"); + } +#endif /* CONFIG_SMP */ + printk("\n"); + } + } +} + +void si_meminfo(struct sysinfo *val) +{ + val->totalram = totalram_pages; + val->sharedram = 0; + val->freeram = nr_free_pages(); + val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = 0; + val->freehigh = 0; + val->mem_unit = PAGE_SIZE; +} + +void * +ioremap(unsigned long addr, unsigned long size) +{ + return __ioremap(addr, size, _PAGE_NO_CACHE); +} + +extern struct vm_struct * get_im_area( unsigned long size ); + +void * +__ioremap(unsigned long addr, unsigned long size, unsigned long flags) +{ + unsigned long pa, ea, i; + + /* + * Choose an address to map it to. + * Once the imalloc system is running, we use it. + * Before that, we map using addresses going + * up from ioremap_bot. imalloc will use + * the addresses from ioremap_bot through + * IMALLOC_END (0xE000001fffffffff) + * + */ + pa = addr & PAGE_MASK; + size = PAGE_ALIGN(addr + size) - pa; + + // MIKEC: Do we still need to support this? + /* + * If the address lies within the first 16 MB, assume it's in ISA + * memory space + */ + if (pa < 16*1024*1024) + pa += _ISA_MEM_BASE; + + /* + * Don't allow anybody to remap normal RAM that we're using. + * mem_init() sets high_memory so only do the check after that. + */ +#if 0 /* DRENG / PPPBBB assert to not remap DRAM is not right when I/O + * space is in the middle of DRAM ranges ... + */ + if ( mem_init_done && (pa < virt_to_phys(high_memory)) ) + { + printk("__ioremap(): phys addr %0lx is RAM lr %p\n", pa, + __builtin_return_address(0)); + return NULL; + } +#endif + if (size == 0) + return NULL; + + if (mem_init_done) { + struct vm_struct *area; + area = get_im_area(size); + if (area == 0) + return NULL; + ea = (unsigned long)(area->addr); + } + else { + ea = ioremap_bot; + ioremap_bot += size; + } + + if ((flags & _PAGE_PRESENT) == 0) + flags |= pgprot_val(PAGE_KERNEL); + if (flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU)) + flags |= _PAGE_GUARDED; + + for (i = 0; i < size; i += PAGE_SIZE) { + map_io_page(ea+i, pa+i, flags); + } + + return (void *) (ea + (addr & ~PAGE_MASK)); +} + +void iounmap(void *addr) +{ + /* DRENG / PPPBBB todo */ +} + +unsigned long iopa(unsigned long addr) +{ + pmd_t *pd; + pte_t *pg; + + /* Do we have a page table? */ + if (init_mm.pgd == NULL) + return 0; + + /* Use upper 10 bits of addr to index the first level map */ + pd = (pmd_t *) (init_mm.pgd + (addr >> PGDIR_SHIFT)); + if (pmd_none(*pd)) + return 0; + + /* Use middle 10 bits of addr to index the second-level map */ + pg = pte_offset(pd, addr); + return (pte_val(*pg) & PAGE_MASK) | (addr & ~PAGE_MASK); +} + +/* + * map_io_page currently only called by __ioremap + * map_io_page adds an entry to the ioremap page table + * and adds an entry to the HPT, possibly bolting it + */ +static void map_io_page(unsigned long ea, unsigned long pa, int flags) +{ + pgd_t *pgdp; + pmd_t *pmdp; + 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); + } +} + +void +local_flush_tlb_all(void) +{ + /* 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 ); +} + +void +local_flush_tlb_mm(struct mm_struct *mm) +{ + if ( mm->map_count ) { + struct vm_area_struct *mp; + for ( mp = mm->mmap; mp != NULL; mp = mp->vm_next ) + local_flush_tlb_range( mm, mp->vm_start, mp->vm_end ); + } + else /* MIKEC: It is not clear why this is needed */ + local_flush_tlb_range( mm, USER_START, USER_END ); +} + + +/* + * Callers should hold the mm->page_table_lock + */ +void +local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) +{ + unsigned long context = 0; + pgd_t *pgd; + pmd_t *pmd; + pte_t *ptep; + pte_t pte; + + switch( REGION_ID(vmaddr) ) { + case VMALLOC_REGION_ID: + pgd = pgd_offset_k( vmaddr ); + break; + case IO_REGION_ID: + pgd = pgd_offset_i( vmaddr ); + break; + case BOLTED_REGION_ID: + pgd = pgd_offset_b( vmaddr ); + break; + case USER_REGION_ID: + pgd = pgd_offset( vma->vm_mm, vmaddr ); + context = vma->vm_mm->context; + break; + default: + panic("local_flush_tlb_page: invalid region 0x%016lx", vmaddr); + + } + + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, vmaddr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, vmaddr); + /* Check if HPTE might exist and flush it if so */ + pte = __pte(pte_update(ptep, _PAGE_HPTEFLAGS, 0)); + if ( pte_val(pte) & _PAGE_HASHPTE ) { + flush_hash_page(context, vmaddr, pte); + } + } + } +} + +void +local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *ptep; + pte_t pte; + unsigned long pgd_end, pmd_end; + unsigned long context; + + if ( start >= end ) + 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 ); + + context = 0; + + switch( REGION_ID(start) ) { + case VMALLOC_REGION_ID: + pgd = pgd_offset_k( start ); + break; + case IO_REGION_ID: + pgd = pgd_offset_i( start ); + break; + case BOLTED_REGION_ID: + pgd = pgd_offset_b( start ); + break; + case USER_REGION_ID: + pgd = pgd_offset( mm, start ); + context = mm->context; + break; + default: + panic("flush_tlb_range: invalid region for start (%016lx) and end (%016lx)\n", start, end); + + } + + do { + pgd_end = (start + PGDIR_SIZE) & PGDIR_MASK; + if ( pgd_end > end ) + pgd_end = end; + if ( !pgd_none( *pgd ) ) { + pmd = pmd_offset( pgd, start ); + do { + pmd_end = ( start + PMD_SIZE ) & PMD_MASK; + if ( pmd_end > end ) + pmd_end = end; + if ( !pmd_none( *pmd ) ) { + ptep = pte_offset( pmd, start ); + do { + if ( pte_val(*ptep) & _PAGE_HASHPTE ) { + pte = __pte(pte_update(ptep, _PAGE_HPTEFLAGS, 0)); + if ( pte_val(pte) & _PAGE_HASHPTE ) + flush_hash_page( context, start, pte ); + } + start += PAGE_SIZE; + ++ptep; + } while ( start < pmd_end ); + } + else + start = pmd_end; + ++pmd; + } while ( start < pgd_end ); + } + else + start = pgd_end; + ++pgd; + } while ( start < end ); +} + + +void __init free_initmem(void) +{ + unsigned long a; + unsigned long num_freed_pages = 0; +#define FREESEC(START,END,CNT) do { \ + a = (unsigned long)(&START); \ + for (; a < (unsigned long)(&END); a += PAGE_SIZE) { \ + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); \ + set_page_count(mem_map+MAP_NR(a), 1); \ + free_page(a); \ + CNT++; \ + } \ +} while (0) + + FREESEC(__init_begin,__init_end,num_freed_pages); + + printk ("Freeing unused kernel memory: %ldk init\n", + PGTOKB(num_freed_pages)); +} + +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long start, unsigned long end) +{ + for (; start < end; start += PAGE_SIZE) { + ClearPageReserved(mem_map + MAP_NR(start)); + set_page_count(mem_map+MAP_NR(start), 1); + free_page(start); + totalram_pages++; + } + printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10); +} +#endif + + +/* Reserve all contexts < FIRST_USER_CONTEXT for kernel use. + * The range of contexts [FIRST_USER_CONTEXT, NUM_USER_CONTEXT) + * are stored on a stack for easy allocation and deallocation. + */ +void +init_context_stack(void) +{ + mm_context_t top; + + mmu_context_stack.lock = SPIN_LOCK_UNLOCKED; + mmu_context_stack.top = FIRST_USER_CONTEXT; + for(top=0; top < FIRST_USER_CONTEXT ;top++) { + mmu_context_stack.stack[top] = NO_CONTEXT; + } + for(top=FIRST_USER_CONTEXT; top < NUM_USER_CONTEXT ;top++) { + mmu_context_stack.stack[top] = top; + } +} + + +/* + * Do very early mm setup. + */ +void __init mm_init_ppc64(void) { + ppc_md.progress("MM:init", 0); + + /* Reserve all contexts < FIRST_USER_CONTEXT for kernel use. + * The range of contexts [FIRST_USER_CONTEXT, NUM_USER_CONTEXT) + * are stored on a stack for easy allocation and deallocation. + */ + init_context_stack(); + + ppc_md.progress("MM:exit", 0x211); +} + + + +/* + * Initialize the bootmem system and give it all the memory we + * have available. + */ +void __init do_init_bootmem(void) +{ + unsigned long i; + unsigned long start, bootmap_pages; + unsigned long total_pages = lmb_end_of_DRAM() >> PAGE_SHIFT; + + PPCDBG(PPCDBG_MMINIT, "do_init_bootmem: start\n"); + /* + * Find an area to use for the bootmem bitmap. Calculate the size of + * bitmap required as (Total Memory) / PAGE_SIZE / BITS_PER_BYTE. + * Add 1 additional page in case the address isn't page-aligned. + */ + bootmap_pages = bootmem_bootmap_pages(total_pages); + + start = (unsigned long)__a2p(lmb_alloc(bootmap_pages<physicalMemorySize); + + boot_mapsize = init_bootmem(start >> PAGE_SHIFT, total_pages); + PPCDBG(PPCDBG_MMINIT, "\tboot_mapsize = 0x%lx\n", boot_mapsize); + + /* add all physical memory to the bootmem map */ + for (i=0; i < lmb.memory.cnt ;i++) { + unsigned long physbase = lmb.memory.region[i].physbase; + unsigned long size = lmb.memory.region[i].size; + free_bootmem(physbase, size); + } + /* reserve the sections we're already using */ + for (i=0; i < lmb.reserved.cnt ;i++) { + unsigned long physbase = lmb.reserved.region[i].physbase; + unsigned long size = lmb.reserved.region[i].size; +#if 0 /* PPPBBB */ + if ( (physbase == 0) && (size < (16<<20)) ) { + size = 16 << 20; + } +#endif + reserve_bootmem(physbase, size); + } + + PPCDBG(PPCDBG_MMINIT, "do_init_bootmem: end\n"); +} + +/* + * paging_init() sets up the page tables - in fact we've already done this. + */ +void __init paging_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES], i; + + /* + * All pages are DMA-able so we put them all in the DMA zone. + */ + zones_size[0] = lmb_end_of_DRAM() >> PAGE_SHIFT; + for (i = 1; i < MAX_NR_ZONES; i++) + zones_size[i] = 0; + free_area_init(zones_size); +} + +void __init mem_init(void) +{ + extern char *sysmap; + extern unsigned long sysmap_size; + unsigned long addr; + int codepages = 0; + int datapages = 0; + int initpages = 0; + unsigned long va_rtas_base = (unsigned long)__va(rtas.base); + max_mapnr = max_low_pfn; + high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); + num_physpages = max_mapnr; /* RAM is assumed contiguous */ + + totalram_pages += free_all_bootmem(); + + ifppcdebug(PPCDBG_MMINIT) { + udbg_printf("mem_init: totalram_pages = 0x%lx\n", totalram_pages); + udbg_printf("mem_init: va_rtas_base = 0x%lx\n", va_rtas_base); + udbg_printf("mem_init: va_rtas_end = 0x%lx\n", PAGE_ALIGN(va_rtas_base+rtas.size)); + udbg_printf("mem_init: pinned start = 0x%lx\n", __va(0)); + udbg_printf("mem_init: pinned end = 0x%lx\n", PAGE_ALIGN(klimit)); + } + + if ( sysmap_size ) + for (addr = (unsigned long)sysmap; + addr < PAGE_ALIGN((unsigned long)sysmap+sysmap_size) ; + addr += PAGE_SIZE) + SetPageReserved(mem_map + MAP_NR(addr)); + + for (addr = KERNELBASE; addr <= (unsigned long)__va(lmb_end_of_DRAM()); + addr += PAGE_SIZE) { + if (!PageReserved(mem_map + MAP_NR(addr))) + continue; + if (addr < (ulong) etext) + codepages++; + + else if (addr >= (unsigned long)&__init_begin + && addr < (unsigned long)&__init_end) + initpages++; + else if (addr < klimit) + datapages++; + } + + printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08lx,%08lx]\n", + (unsigned long)nr_free_pages()<< (PAGE_SHIFT-10), + codepages<< (PAGE_SHIFT-10), datapages<< (PAGE_SHIFT-10), + initpages<< (PAGE_SHIFT-10), + PAGE_OFFSET, __va(lmb_end_of_DRAM())); + mem_init_done = 1; + +#ifdef CONFIG_PPC_ISERIES + create_virtual_bus_tce_table(); +#endif +} + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/vmlinux.lds linux.ac/arch/ppc64/vmlinux.lds --- linux.vanilla/arch/ppc64/vmlinux.lds Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/vmlinux.lds Wed Oct 10 01:47:34 2001 @@ -0,0 +1,144 @@ +OUTPUT_ARCH(powerpc) +SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/local/powerpc-any-elf/lib); +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } +/* .init : { *(.init) } =0*/ + .plt : { *(.plt) } + .text : + { + *(.text) + *(.fixup) + *(.got1) + } + . = ALIGN(4096); + _etext = .; + PROVIDE (etext = .); + .rodata : + { + *(.rodata) + *(.rodata1) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFFFFFFFFFF000; + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.got.plt) *(.got) + *(.dynamic) + CONSTRUCTORS + } + . = ALIGN(4096); + _edata = .; + PROVIDE (edata = .); + + .fixup : { *(.fixup) } + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + + + . = ALIGN(4096); + .data.page_aligned : { *(.data.page_aligned) } + + . = ALIGN(128); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { + *(.data.init); + __vtop_table_begin = .; + *(.vtop_fixup); + __vtop_table_end = .; + __ptov_table_begin = .; + *(.ptov_fixup); + __ptov_table_end = .; + } + . = ALIGN(16); + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { *(.initcall.init) } + __initcall_end = .; + + + . = ALIGN(4096); + __init_end = .; + + . = ALIGN(4096); + __chrp_begin = .; + .text.chrp : { *(.text.chrp) } + .data.chrp : { *(.data.chrp) } + . = ALIGN(4096); + __chrp_end = .; + + . = ALIGN(4096); + __openfirmware_begin = .; + .text.openfirmware : { *(.text.openfirmware) } + .data.openfirmware : { *(.data.openfirmware) } + . = ALIGN(4096); + __openfirmware_end = .; + + + . = ALIGN(4096); + __toc_start = .; + .toc : + { + *(.toc) + } + . = ALIGN(4096); + __toc_end = .; + + . = ALIGN(4096); + __bss_start = .; + .bss : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + + . = ALIGN(4096); + _end = . ; + PROVIDE (end = .); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/xmon/Makefile linux.ac/arch/ppc64/xmon/Makefile --- linux.vanilla/arch/ppc64/xmon/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/xmon/Makefile Wed Oct 10 01:47:34 2001 @@ -0,0 +1,9 @@ +# Makefile for xmon + +EXTRA_CFLAGS = -mno-minimal-toc + +O_TARGET = x.o + +obj-y := start.o xmon.o ppc-dis.o ppc-opc.o subr_prf.o setjmp.o + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/xmon/adb.c linux.ac/arch/ppc64/xmon/adb.c --- linux.vanilla/arch/ppc64/xmon/adb.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/xmon/adb.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,217 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include "nonstdio.h" +#include "privinst.h" + +#define scanhex xmon_scanhex +#define skipbl xmon_skipbl + +#define ADB_B (*(volatile unsigned char *)0xf3016000) +#define ADB_SR (*(volatile unsigned char *)0xf3017400) +#define ADB_ACR (*(volatile unsigned char *)0xf3017600) +#define ADB_IFR (*(volatile unsigned char *)0xf3017a00) + +static inline void eieio(void) { asm volatile ("eieio" : :); } + +#define N_ADB_LOG 1000 +struct adb_log { + unsigned char b; + unsigned char ifr; + unsigned char acr; + unsigned int time; +} adb_log[N_ADB_LOG]; +int n_adb_log; + +void +init_adb_log(void) +{ + adb_log[0].b = ADB_B; + adb_log[0].ifr = ADB_IFR; + adb_log[0].acr = ADB_ACR; + adb_log[0].time = get_dec(); + n_adb_log = 0; +} + +void +dump_adb_log(void) +{ + unsigned t, t0; + struct adb_log *ap; + int i; + + ap = adb_log; + t0 = ap->time; + for (i = 0; i <= n_adb_log; ++i, ++ap) { + t = t0 - ap->time; + printf("b=%x ifr=%x acr=%x at %d.%.7d\n", ap->b, ap->ifr, ap->acr, + t / 1000000000, (t % 1000000000) / 100); + } +} + +void +adb_chklog(void) +{ + struct adb_log *ap = &adb_log[n_adb_log + 1]; + + ap->b = ADB_B; + ap->ifr = ADB_IFR; + ap->acr = ADB_ACR; + if (ap->b != ap[-1].b || (ap->ifr & 4) != (ap[-1].ifr & 4) + || ap->acr != ap[-1].acr) { + ap->time = get_dec(); + ++n_adb_log; + } +} + +int +adb_bitwait(int bmask, int bval, int fmask, int fval) +{ + int i; + struct adb_log *ap; + + for (i = 10000; i > 0; --i) { + adb_chklog(); + ap = &adb_log[n_adb_log]; + if ((ap->b & bmask) == bval && (ap->ifr & fmask) == fval) + return 0; + } + return -1; +} + +int +adb_wait(void) +{ + if (adb_bitwait(0, 0, 4, 4) < 0) { + printf("adb: ready wait timeout\n"); + return -1; + } + return 0; +} + +void +adb_readin(void) +{ + int i, j; + unsigned char d[64]; + + if (ADB_B & 8) { + printf("ADB_B: %x\n", ADB_B); + return; + } + i = 0; + adb_wait(); + j = ADB_SR; + eieio(); + ADB_B &= ~0x20; + eieio(); + for (;;) { + if (adb_wait() < 0) + break; + d[i++] = ADB_SR; + eieio(); + if (ADB_B & 8) + break; + ADB_B ^= 0x10; + eieio(); + } + ADB_B |= 0x30; + if (adb_wait() == 0) + j = ADB_SR; + for (j = 0; j < i; ++j) + printf("%.2x ", d[j]); + printf("\n"); +} + +int +adb_write(unsigned char *d, int i) +{ + int j; + unsigned x; + + if ((ADB_B & 8) == 0) { + printf("r: "); + adb_readin(); + } + for (;;) { + ADB_ACR = 0x1c; + eieio(); + ADB_SR = d[0]; + eieio(); + ADB_B &= ~0x20; + eieio(); + if (ADB_B & 8) + break; + ADB_ACR = 0xc; + eieio(); + ADB_B |= 0x20; + eieio(); + adb_readin(); + } + adb_wait(); + for (j = 1; j < i; ++j) { + ADB_SR = d[j]; + eieio(); + ADB_B ^= 0x10; + eieio(); + if (adb_wait() < 0) + break; + } + ADB_ACR = 0xc; + eieio(); + x = ADB_SR; + eieio(); + ADB_B |= 0x30; + return j; +} + +void +adbcmds(void) +{ + char cmd; + unsigned rtcu, rtcl, dec, pdec, x; + int i, j; + unsigned char d[64]; + + cmd = skipbl(); + switch (cmd) { + case 't': + for (;;) { + rtcl = get_rtcl(); + rtcu = get_rtcu(); + dec = get_dec(); + printf("rtc u=%u l=%u dec=%x (%d = %d.%.7d)\n", + rtcu, rtcl, dec, pdec - dec, (pdec - dec) / 1000000000, + ((pdec - dec) % 1000000000) / 100); + pdec = dec; + if (cmd == 'x') + break; + while (xmon_read(stdin, &cmd, 1) != 1) + ; + } + break; + case 'r': + init_adb_log(); + while (adb_bitwait(8, 0, 0, 0) == 0) + adb_readin(); + break; + case 'w': + i = 0; + while (scanhex(&x)) + d[i++] = x; + init_adb_log(); + j = adb_write(d, i); + printf("sent %d bytes\n", j); + while (adb_bitwait(8, 0, 0, 0) == 0) + adb_readin(); + break; + case 'l': + dump_adb_log(); + break; + } +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/xmon/ansidecl.h linux.ac/arch/ppc64/xmon/ansidecl.h --- linux.vanilla/arch/ppc64/xmon/ansidecl.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/xmon/ansidecl.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,141 @@ +/* ANSI and traditional C compatability macros + Copyright 1991, 1992 Free Software Foundation, Inc. + This file is part of the GNU C Library. + +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. */ + +/* ANSI and traditional C compatibility macros + + ANSI C is assumed if __STDC__ is #defined. + + Macro ANSI C definition Traditional C definition + ----- ---- - ---------- ----------- - ---------- + PTR `void *' `char *' + LONG_DOUBLE `long double' `double' + VOLATILE `volatile' `' + SIGNED `signed' `' + PTRCONST `void *const' `char *' + ANSI_PROTOTYPES 1 not defined + + CONST is also defined, but is obsolete. Just use const. + + DEFUN (name, arglist, args) + + Defines function NAME. + + ARGLIST lists the arguments, separated by commas and enclosed in + parentheses. ARGLIST becomes the argument list in traditional C. + + ARGS list the arguments with their types. It becomes a prototype in + ANSI C, and the type declarations in traditional C. Arguments should + be separated with `AND'. For functions with a variable number of + arguments, the last thing listed should be `DOTS'. + + DEFUN_VOID (name) + + Defines a function NAME, which takes no arguments. + + obsolete -- EXFUN (name, (prototype)) -- obsolete. + + Replaced by PARAMS. Do not use; will disappear someday soon. + Was used in external function declarations. + In ANSI C it is `NAME PROTOTYPE' (so PROTOTYPE should be enclosed in + parentheses). In traditional C it is `NAME()'. + For a function that takes no arguments, PROTOTYPE should be `(void)'. + + PARAMS ((args)) + + We could use the EXFUN macro to handle prototype declarations, but + the name is misleading and the result is ugly. So we just define a + simple macro to handle the parameter lists, as in: + + static int foo PARAMS ((int, char)); + + This produces: `static int foo();' or `static int foo (int, char);' + + EXFUN would have done it like this: + + static int EXFUN (foo, (int, char)); + + but the function is not external...and it's hard to visually parse + the function name out of the mess. EXFUN should be considered + obsolete; new code should be written to use PARAMS. + + For example: + extern int printf PARAMS ((CONST char *format DOTS)); + int DEFUN(fprintf, (stream, format), + FILE *stream AND CONST char *format DOTS) { ... } + void DEFUN_VOID(abort) { ... } +*/ + +#ifndef _ANSIDECL_H + +#define _ANSIDECL_H 1 + + +/* Every source file includes this file, + so they will all get the switch for lint. */ +/* LINTLIBRARY */ + + +#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(WIN32) +/* All known AIX compilers implement these things (but don't always + define __STDC__). The RISC/OS MIPS compiler defines these things + in SVR4 mode, but does not define __STDC__. */ + +#define PTR void * +#define PTRCONST void *CONST +#define LONG_DOUBLE long double + +#define AND , +#define NOARGS void +#define CONST const +#define VOLATILE volatile +#define SIGNED signed +#define DOTS , ... + +#define EXFUN(name, proto) name proto +#define DEFUN(name, arglist, args) name(args) +#define DEFUN_VOID(name) name(void) + +#define PROTO(type, name, arglist) type name arglist +#define PARAMS(paramlist) paramlist +#define ANSI_PROTOTYPES 1 + +#else /* Not ANSI C. */ + +#define PTR char * +#define PTRCONST PTR +#define LONG_DOUBLE double + +#define AND ; +#define NOARGS +#define CONST +#ifndef const /* some systems define it in header files for non-ansi mode */ +#define const +#endif +#define VOLATILE +#define SIGNED +#define DOTS + +#define EXFUN(name, proto) name() +#define DEFUN(name, arglist, args) name arglist args; +#define DEFUN_VOID(name) name() +#define PROTO(type, name, arglist) type name () +#define PARAMS(paramlist) () + +#endif /* ANSI C. */ + +#endif /* ansidecl.h */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/xmon/nonstdio.h linux.ac/arch/ppc64/xmon/nonstdio.h --- linux.vanilla/arch/ppc64/xmon/nonstdio.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/xmon/nonstdio.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,22 @@ +typedef int FILE; +extern FILE *xmon_stdin, *xmon_stdout; +#define EOF (-1) +#define stdin xmon_stdin +#define stdout xmon_stdout +#define printf xmon_printf +#define fprintf xmon_fprintf +#define fputs xmon_fputs +#define fgets xmon_fgets +#define putchar xmon_putchar +#define getchar xmon_getchar +#define putc xmon_putc +#define getc xmon_getc +#define fopen(n, m) NULL +#define fflush(f) do {} while (0) +#define fclose(f) do {} while (0) +extern char *fgets(char *, int, void *); +extern void xmon_printf(const char *, ...); +extern void xmon_fprintf(void *, const char *, ...); +extern void xmon_sprintf(char *, const char *, ...); + +#define perror(s) printf("%s: no files!\n", (s)) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/xmon/ppc-dis.c linux.ac/arch/ppc64/xmon/ppc-dis.c --- linux.vanilla/arch/ppc64/xmon/ppc-dis.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/xmon/ppc-dis.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,190 @@ +/* ppc-dis.c -- Disassemble PowerPC instructions + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +2, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +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 file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "nonstdio.h" +#include "ansidecl.h" +#include "ppc.h" + +static int print_insn_powerpc PARAMS ((FILE *, unsigned long insn, + unsigned long memaddr, int dialect)); + +extern void print_address PARAMS((unsigned long memaddr)); + +/* Print a big endian PowerPC instruction. For convenience, also + disassemble instructions supported by the Motorola PowerPC 601. */ + +int +print_insn_big_powerpc (FILE *out, unsigned long insn, unsigned long memaddr) +{ + return print_insn_powerpc (out, insn, memaddr, + PPC_OPCODE_PPC | PPC_OPCODE_601); +} + +/* Print a PowerPC or POWER instruction. */ + +static int +print_insn_powerpc (FILE *out, unsigned long insn, unsigned long memaddr, + int dialect) +{ + const struct powerpc_opcode *opcode; + const struct powerpc_opcode *opcode_end; + unsigned long op; + + /* Get the major opcode of the instruction. */ + op = PPC_OP (insn); + + /* Find the first match in the opcode table. We could speed this up + a bit by doing a binary search on the major opcode. */ + opcode_end = powerpc_opcodes + powerpc_num_opcodes; + for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++) + { + unsigned long table_op; + const unsigned char *opindex; + const struct powerpc_operand *operand; + int invalid; + int need_comma; + int need_paren; + + table_op = PPC_OP (opcode->opcode); + if (op < table_op) + break; + if (op > table_op) + continue; + + if ((insn & opcode->mask) != opcode->opcode + || (opcode->flags & dialect) == 0) + continue; + + /* Make two passes over the operands. First see if any of them + have extraction functions, and, if they do, make sure the + instruction is valid. */ + invalid = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + operand = powerpc_operands + *opindex; + if (operand->extract) + (*operand->extract) (insn, &invalid); + } + if (invalid) + continue; + + /* The instruction is valid. */ + fprintf(out, "%s", opcode->name); + if (opcode->operands[0] != 0) + fprintf(out, "\t"); + + /* Now extract and print the operands. */ + need_comma = 0; + need_paren = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + long value; + + operand = powerpc_operands + *opindex; + + /* Operands that are marked FAKE are simply ignored. We + already made sure that the extract function considered + the instruction to be valid. */ + if ((operand->flags & PPC_OPERAND_FAKE) != 0) + continue; + + /* Extract the value from the instruction. */ + if (operand->extract) + value = (*operand->extract) (insn, (int *) 0); + else + { + value = (insn >> operand->shift) & ((1 << operand->bits) - 1); + if ((operand->flags & PPC_OPERAND_SIGNED) != 0 + && (value & (1 << (operand->bits - 1))) != 0) + value -= 1 << operand->bits; + } + + /* If the operand is optional, and the value is zero, don't + print anything. */ + if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 + && (operand->flags & PPC_OPERAND_NEXT) == 0 + && value == 0) + continue; + + if (need_comma) + { + fprintf(out, ","); + need_comma = 0; + } + + /* Print the operand as directed by the flags. */ + if ((operand->flags & PPC_OPERAND_GPR) != 0) + fprintf(out, "r%ld", value); + else if ((operand->flags & PPC_OPERAND_FPR) != 0) + fprintf(out, "f%ld", value); + else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0) + print_address (memaddr + value); + else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) + print_address (value & 0xffffffff); + else if ((operand->flags & PPC_OPERAND_CR) == 0 + || (dialect & PPC_OPCODE_PPC) == 0) + fprintf(out, "%ld", value); + else + { + if (operand->bits == 3) + fprintf(out, "cr%d", value); + else + { + static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; + int cr; + int cc; + + cr = value >> 2; + if (cr != 0) + fprintf(out, "4*cr%d", cr); + cc = value & 3; + if (cc != 0) + { + if (cr != 0) + fprintf(out, "+"); + fprintf(out, "%s", cbnames[cc]); + } + } + } + + if (need_paren) + { + fprintf(out, ")"); + need_paren = 0; + } + + if ((operand->flags & PPC_OPERAND_PARENS) == 0) + need_comma = 1; + else + { + fprintf(out, "("); + need_paren = 1; + } + } + + /* We have found and printed an instruction; return. */ + return 4; + } + + /* We could not find a match. */ + fprintf(out, ".long 0x%lx", insn); + + return 4; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/xmon/ppc-opc.c linux.ac/arch/ppc64/xmon/ppc-opc.c --- linux.vanilla/arch/ppc64/xmon/ppc-opc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/xmon/ppc-opc.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,2816 @@ +/* ppc-opc.c -- PowerPC opcode list + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +2, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +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 file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include +#include "ansidecl.h" +#include "ppc.h" + +/* This file holds the PowerPC opcode table. The opcode table + includes almost all of the extended instruction mnemonics. This + permits the disassembler to use them, and simplifies the assembler + logic, at the cost of increasing the table size. The table is + strictly constant data, so the compiler should be able to put it in + the .text section. + + This file also holds the operand table. All knowledge about + inserting operands into instructions and vice-versa is kept in this + file. */ + +/* Local insertion and extraction functions. */ + +static unsigned long insert_bat PARAMS ((unsigned long, long, const char **)); +static long extract_bat PARAMS ((unsigned long, int *)); +static unsigned long insert_bba PARAMS ((unsigned long, long, const char **)); +static long extract_bba PARAMS ((unsigned long, int *)); +static unsigned long insert_bd PARAMS ((unsigned long, long, const char **)); +static long extract_bd PARAMS ((unsigned long, int *)); +static unsigned long insert_bdm PARAMS ((unsigned long, long, const char **)); +static long extract_bdm PARAMS ((unsigned long, int *)); +static unsigned long insert_bdp PARAMS ((unsigned long, long, const char **)); +static long extract_bdp PARAMS ((unsigned long, int *)); +static unsigned long insert_bo PARAMS ((unsigned long, long, const char **)); +static long extract_bo PARAMS ((unsigned long, int *)); +static unsigned long insert_boe PARAMS ((unsigned long, long, const char **)); +static long extract_boe PARAMS ((unsigned long, int *)); +static unsigned long insert_ds PARAMS ((unsigned long, long, const char **)); +static long extract_ds PARAMS ((unsigned long, int *)); +static unsigned long insert_li PARAMS ((unsigned long, long, const char **)); +static long extract_li PARAMS ((unsigned long, int *)); +static unsigned long insert_mbe PARAMS ((unsigned long, long, const char **)); +static long extract_mbe PARAMS ((unsigned long, int *)); +static unsigned long insert_mb6 PARAMS ((unsigned long, long, const char **)); +static long extract_mb6 PARAMS ((unsigned long, int *)); +static unsigned long insert_nb PARAMS ((unsigned long, long, const char **)); +static long extract_nb PARAMS ((unsigned long, int *)); +static unsigned long insert_nsi PARAMS ((unsigned long, long, const char **)); +static long extract_nsi PARAMS ((unsigned long, int *)); +static unsigned long insert_ral PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_ram PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_ras PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_rbs PARAMS ((unsigned long, long, const char **)); +static long extract_rbs PARAMS ((unsigned long, int *)); +static unsigned long insert_sh6 PARAMS ((unsigned long, long, const char **)); +static long extract_sh6 PARAMS ((unsigned long, int *)); +static unsigned long insert_spr PARAMS ((unsigned long, long, const char **)); +static long extract_spr PARAMS ((unsigned long, int *)); +static unsigned long insert_tbr PARAMS ((unsigned long, long, const char **)); +static long extract_tbr PARAMS ((unsigned long, int *)); + +/* The operands table. + + The fields are bits, shift, signed, insert, extract, flags. */ + +const struct powerpc_operand powerpc_operands[] = +{ + /* The zero index is used to indicate the end of the list of + operands. */ +#define UNUSED (0) + { 0, 0, 0, 0, 0 }, + + /* The BA field in an XL form instruction. */ +#define BA (1) +#define BA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_CR }, + + /* The BA field in an XL form instruction when it must be the same + as the BT field in the same instruction. */ +#define BAT (2) + { 5, 16, insert_bat, extract_bat, PPC_OPERAND_FAKE }, + + /* The BB field in an XL form instruction. */ +#define BB (3) +#define BB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_CR }, + + /* The BB field in an XL form instruction when it must be the same + as the BA field in the same instruction. */ +#define BBA (4) + { 5, 11, insert_bba, extract_bba, PPC_OPERAND_FAKE }, + + /* The BD field in a B form instruction. The lower two bits are + forced to zero. */ +#define BD (5) + { 16, 0, insert_bd, extract_bd, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when absolute addressing is + used. */ +#define BDA (6) + { 16, 0, insert_bd, extract_bd, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDM (7) + { 16, 0, insert_bdm, extract_bdm, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used + and absolute address is used. */ +#define BDMA (8) + { 16, 0, insert_bdm, extract_bdm, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDP (9) + { 16, 0, insert_bdp, extract_bdp, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used + and absolute addressing is used. */ +#define BDPA (10) + { 16, 0, insert_bdp, extract_bdp, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BF field in an X or XL form instruction. */ +#define BF (11) + { 3, 23, 0, 0, PPC_OPERAND_CR }, + + /* An optional BF field. This is used for comparison instructions, + in which an omitted BF field is taken as zero. */ +#define OBF (12) + { 3, 23, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The BFA field in an X or XL form instruction. */ +#define BFA (13) + { 3, 18, 0, 0, PPC_OPERAND_CR }, + + /* The BI field in a B form or XL form instruction. */ +#define BI (14) +#define BI_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_CR }, + + /* The BO field in a B form instruction. Certain values are + illegal. */ +#define BO (15) +#define BO_MASK (0x1f << 21) + { 5, 21, insert_bo, extract_bo, 0 }, + + /* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. */ +#define BOE (16) + { 5, 21, insert_boe, extract_boe, 0 }, + + /* The BT field in an X or XL form instruction. */ +#define BT (17) + { 5, 21, 0, 0, PPC_OPERAND_CR }, + + /* The condition register number portion of the BI field in a B form + or XL form instruction. This is used for the extended + conditional branch mnemonics, which set the lower two bits of the + BI field. This field is optional. */ +#define CR (18) + { 3, 18, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The D field in a D form instruction. This is a displacement off + a register, and implies that the next operand is a register in + parentheses. */ +#define D (19) + { 16, 0, 0, 0, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ +#define DS (20) + { 16, 0, insert_ds, extract_ds, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The FL1 field in a POWER SC form instruction. */ +#define FL1 (21) + { 4, 12, 0, 0, 0 }, + + /* The FL2 field in a POWER SC form instruction. */ +#define FL2 (22) + { 3, 2, 0, 0, 0 }, + + /* The FLM field in an XFL form instruction. */ +#define FLM (23) + { 8, 17, 0, 0, 0 }, + + /* The FRA field in an X or A form instruction. */ +#define FRA (24) +#define FRA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_FPR }, + + /* The FRB field in an X or A form instruction. */ +#define FRB (25) +#define FRB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_FPR }, + + /* The FRC field in an A form instruction. */ +#define FRC (26) +#define FRC_MASK (0x1f << 6) + { 5, 6, 0, 0, PPC_OPERAND_FPR }, + + /* The FRS field in an X form instruction or the FRT field in a D, X + or A form instruction. */ +#define FRS (27) +#define FRT (FRS) + { 5, 21, 0, 0, PPC_OPERAND_FPR }, + + /* The FXM field in an XFX instruction. */ +#define FXM (28) +#define FXM_MASK (0xff << 12) + { 8, 12, 0, 0, 0 }, + + /* The L field in a D or X form instruction. */ +#define L (29) + { 1, 21, 0, 0, PPC_OPERAND_OPTIONAL }, + + /* The LEV field in a POWER SC form instruction. */ +#define LEV (30) + { 7, 5, 0, 0, 0 }, + + /* The LI field in an I form instruction. The lower two bits are + forced to zero. */ +#define LI (31) + { 26, 0, insert_li, extract_li, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The LI field in an I form instruction when used as an absolute + address. */ +#define LIA (32) + { 26, 0, insert_li, extract_li, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The MB field in an M form instruction. */ +#define MB (33) +#define MB_MASK (0x1f << 6) + { 5, 6, 0, 0, 0 }, + + /* The ME field in an M form instruction. */ +#define ME (34) +#define ME_MASK (0x1f << 1) + { 5, 1, 0, 0, 0 }, + + /* The MB and ME fields in an M form instruction expressed a single + operand which is a bitmask indicating which bits to select. This + is a two operand form using PPC_OPERAND_NEXT. See the + description in opcode/ppc.h for what this means. */ +#define MBE (35) + { 5, 6, 0, 0, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT }, + { 32, 0, insert_mbe, extract_mbe, 0 }, + + /* The MB or ME field in an MD or MDS form instruction. The high + bit is wrapped to the low end. */ +#define MB6 (37) +#define ME6 (MB6) +#define MB6_MASK (0x3f << 5) + { 6, 5, insert_mb6, extract_mb6, 0 }, + + /* The NB field in an X form instruction. The value 32 is stored as + 0. */ +#define NB (38) + { 6, 11, insert_nb, extract_nb, 0 }, + + /* The NSI field in a D form instruction. This is the same as the + SI field, only negated. */ +#define NSI (39) + { 16, 0, insert_nsi, extract_nsi, + PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED }, + + /* The RA field in an D, DS, X, XO, M, or MDS form instruction. */ +#define RA (40) +#define RA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_GPR }, + + /* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ +#define RAL (41) + { 5, 16, insert_ral, 0, PPC_OPERAND_GPR }, + + /* The RA field in an lmw instruction, which has special value + restrictions. */ +#define RAM (42) + { 5, 16, insert_ram, 0, PPC_OPERAND_GPR }, + + /* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ +#define RAS (43) + { 5, 16, insert_ras, 0, PPC_OPERAND_GPR }, + + /* The RB field in an X, XO, M, or MDS form instruction. */ +#define RB (44) +#define RB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_GPR }, + + /* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. */ +#define RBS (45) + { 5, 1, insert_rbs, extract_rbs, PPC_OPERAND_FAKE }, + + /* The RS field in a D, DS, X, XFX, XS, M, MD or MDS form + instruction or the RT field in a D, DS, X, XFX or XO form + instruction. */ +#define RS (46) +#define RT (RS) +#define RT_MASK (0x1f << 21) + { 5, 21, 0, 0, PPC_OPERAND_GPR }, + + /* The SH field in an X or M form instruction. */ +#define SH (47) +#define SH_MASK (0x1f << 11) + { 5, 11, 0, 0, 0 }, + + /* The SH field in an MD form instruction. This is split. */ +#define SH6 (48) +#define SH6_MASK ((0x1f << 11) | (1 << 1)) + { 6, 1, insert_sh6, extract_sh6, 0 }, + + /* The SI field in a D form instruction. */ +#define SI (49) + { 16, 0, 0, 0, PPC_OPERAND_SIGNED }, + + /* The SI field in a D form instruction when we accept a wide range + of positive values. */ +#define SISIGNOPT (50) + { 16, 0, 0, 0, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT }, + + /* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ +#define SPR (51) +#define SPR_MASK (0x3ff << 11) + { 10, 11, insert_spr, extract_spr, 0 }, + + /* The BAT index number in an XFX form m[ft]ibat[lu] instruction. */ +#define SPRBAT (52) +#define SPRBAT_MASK (0x3 << 17) + { 2, 17, 0, 0, 0 }, + + /* The SPRG register number in an XFX form m[ft]sprg instruction. */ +#define SPRG (53) +#define SPRG_MASK (0x3 << 16) + { 2, 16, 0, 0, 0 }, + + /* The SR field in an X form instruction. */ +#define SR (54) + { 4, 16, 0, 0, 0 }, + + /* The SV field in a POWER SC form instruction. */ +#define SV (55) + { 14, 2, 0, 0, 0 }, + + /* The TBR field in an XFX form instruction. This is like the SPR + field, but it is optional. */ +#define TBR (56) + { 10, 11, insert_tbr, extract_tbr, PPC_OPERAND_OPTIONAL }, + + /* The TO field in a D or X form instruction. */ +#define TO (57) +#define TO_MASK (0x1f << 21) + { 5, 21, 0, 0, 0 }, + + /* The U field in an X form instruction. */ +#define U (58) + { 4, 12, 0, 0, 0 }, + + /* The UI field in a D form instruction. */ +#define UI (59) + { 16, 0, 0, 0, 0 }, +}; + +/* The functions used to insert and extract complicated operands. */ + +/* The BA field in an XL form instruction when it must be the same as + the BT field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BT field into the BA field, + and the extraction function just checks that the fields are the + same. */ + +/*ARGSUSED*/ +static unsigned long +insert_bat (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (((insn >> 21) & 0x1f) << 16); +} + +static long +extract_bat (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BB field in an XL form instruction when it must be the same as + the BA field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BA field into the BB field, + and the extraction function just checks that the fields are the + same. */ + +/*ARGSUSED*/ +static unsigned long +insert_bba (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (((insn >> 16) & 0x1f) << 11); +} + +static long +extract_bba (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 16) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BD field in a B form instruction. The lower two bits are + forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_bd (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (value & 0xfffc); +} + +/*ARGSUSED*/ +static long +extract_bd (insn, invalid) + unsigned long insn; + int *invalid; +{ + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The BD field in a B form instruction when the - modifier is used. + This modifier means that the branch is not expected to be taken. + We must set the y bit of the BO field to 1 if the offset is + negative. When extracting, we require that the y bit be 1 and that + the offset be positive, since if the y bit is 0 we just want to + print the normal form of the instruction. */ + +/*ARGSUSED*/ +static unsigned long +insert_bdm (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if ((value & 0x8000) != 0) + insn |= 1 << 21; + return insn | (value & 0xfffc); +} + +static long +extract_bdm (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn & (1 << 21)) == 0 + || (insn & (1 << 15)) == 0)) + *invalid = 1; + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The BD field in a B form instruction when the + modifier is used. + This is like BDM, above, except that the branch is expected to be + taken. */ + +/*ARGSUSED*/ +static unsigned long +insert_bdp (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if ((value & 0x8000) == 0) + insn |= 1 << 21; + return insn | (value & 0xfffc); +} + +static long +extract_bdp (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn & (1 << 21)) == 0 + || (insn & (1 << 15)) != 0)) + *invalid = 1; + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* Check for legal values of a BO field. */ + +static int +valid_bo (long value) +{ + /* Certain encodings have bits that are required to be zero. These + are (z must be zero, y may be anything): + 001zy + 011zy + 1z00y + 1z01y + 1z1zz + */ + switch (value & 0x14) + { + default: + case 0: + return 1; + case 0x4: + return (value & 0x2) == 0; + case 0x10: + return (value & 0x8) == 0; + case 0x14: + return value == 0x14; + } +} + +/* The BO field in a B form instruction. Warn about attempts to set + the field to an illegal value. */ + +static unsigned long +insert_bo (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (errmsg != (const char **) NULL + && ! valid_bo (value)) + *errmsg = "invalid conditional option"; + return insn | ((value & 0x1f) << 21); +} + +static long +extract_bo (insn, invalid) + unsigned long insn; + int *invalid; +{ + long value; + + value = (insn >> 21) & 0x1f; + if (invalid != (int *) NULL + && ! valid_bo (value)) + *invalid = 1; + return value; +} + +/* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. When + extracting it, we force it to be even. */ + +static unsigned long +insert_boe (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (errmsg != (const char **) NULL) + { + if (! valid_bo (value)) + *errmsg = "invalid conditional option"; + else if ((value & 1) != 0) + *errmsg = "attempt to set y bit when using + or - modifier"; + } + return insn | ((value & 0x1f) << 21); +} + +static long +extract_boe (insn, invalid) + unsigned long insn; + int *invalid; +{ + long value; + + value = (insn >> 21) & 0x1f; + if (invalid != (int *) NULL + && ! valid_bo (value)) + *invalid = 1; + return value & 0x1e; +} + +/* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_ds (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (value & 0xfffc); +} + +/*ARGSUSED*/ +static long +extract_ds (insn, invalid) + unsigned long insn; + int *invalid; +{ + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The LI field in an I form instruction. The lower two bits are + forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_li (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (value & 0x3fffffc); +} + +/*ARGSUSED*/ +static long +extract_li (insn, invalid) + unsigned long insn; + int *invalid; +{ + if ((insn & 0x2000000) != 0) + return (insn & 0x3fffffc) - 0x4000000; + else + return insn & 0x3fffffc; +} + +/* The MB and ME fields in an M form instruction expressed as a single + operand which is itself a bitmask. The extraction function always + marks it as invalid, since we never want to recognize an + instruction which uses a field of this type. */ + +static unsigned long +insert_mbe (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + unsigned long uval; + int mb, me; + + uval = value; + + if (uval == 0) + { + if (errmsg != (const char **) NULL) + *errmsg = "illegal bitmask"; + return insn; + } + + me = 31; + while ((uval & 1) == 0) + { + uval >>= 1; + --me; + } + + mb = me; + uval >>= 1; + while ((uval & 1) != 0) + { + uval >>= 1; + --mb; + } + + if (uval != 0) + { + if (errmsg != (const char **) NULL) + *errmsg = "illegal bitmask"; + } + + return insn | (mb << 6) | (me << 1); +} + +static long +extract_mbe (insn, invalid) + unsigned long insn; + int *invalid; +{ + long ret; + int mb, me; + int i; + + if (invalid != (int *) NULL) + *invalid = 1; + + ret = 0; + mb = (insn >> 6) & 0x1f; + me = (insn >> 1) & 0x1f; + for (i = mb; i < me; i++) + ret |= 1 << (31 - i); + return ret; +} + +/* The MB or ME field in an MD or MDS form instruction. The high bit + is wrapped to the low end. */ + +/*ARGSUSED*/ +static unsigned long +insert_mb6 (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((value & 0x1f) << 6) | (value & 0x20); +} + +/*ARGSUSED*/ +static long +extract_mb6 (insn, invalid) + unsigned long insn; + int *invalid; +{ + return ((insn >> 6) & 0x1f) | (insn & 0x20); +} + +/* The NB field in an X form instruction. The value 32 is stored as + 0. */ + +static unsigned long +insert_nb (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value < 0 || value > 32) + *errmsg = "value out of range"; + if (value == 32) + value = 0; + return insn | ((value & 0x1f) << 11); +} + +/*ARGSUSED*/ +static long +extract_nb (insn, invalid) + unsigned long insn; + int *invalid; +{ + long ret; + + ret = (insn >> 11) & 0x1f; + if (ret == 0) + ret = 32; + return ret; +} + +/* The NSI field in a D form instruction. This is the same as the SI + field, only negated. The extraction function always marks it as + invalid, since we never want to recognize an instruction which uses + a field of this type. */ + +/*ARGSUSED*/ +static unsigned long +insert_nsi (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((- value) & 0xffff); +} + +static long +extract_nsi (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL) + *invalid = 1; + if ((insn & 0x8000) != 0) + return - ((insn & 0xffff) - 0x10000); + else + return - (insn & 0xffff); +} + +/* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ + +static unsigned long +insert_ral (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0 + || value == ((insn >> 21) & 0x1f)) + *errmsg = "invalid register operand when updating"; + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in an lmw instruction, which has special value + restrictions. */ + +static unsigned long +insert_ram (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value >= ((insn >> 21) & 0x1f)) + *errmsg = "index register in load range"; + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ + +static unsigned long +insert_ras (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0) + *errmsg = "invalid register operand when updating"; + return insn | ((value & 0x1f) << 16); +} + +/* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. This operand is marked FAKE. The insertion + function just copies the BT field into the BA field, and the + extraction function just checks that the fields are the same. */ + +/*ARGSUSED*/ +static unsigned long +insert_rbs (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (((insn >> 21) & 0x1f) << 11); +} + +static long +extract_rbs (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The SH field in an MD form instruction. This is split. */ + +/*ARGSUSED*/ +static unsigned long +insert_sh6 (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4); +} + +/*ARGSUSED*/ +static long +extract_sh6 (insn, invalid) + unsigned long insn; + int *invalid; +{ + return ((insn >> 11) & 0x1f) | ((insn << 4) & 0x20); +} + +/* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ + +static unsigned long +insert_spr (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_spr (insn, invalid) + unsigned long insn; + int *invalid; +{ + return ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); +} + +/* The TBR field in an XFX instruction. This is just like SPR, but it + is optional. When TBR is omitted, it must be inserted as 268 (the + magic number of the TB register). These functions treat 0 + (indicating an omitted optional operand) as 268. This means that + ``mftb 4,0'' is not handled correctly. This does not matter very + much, since the architecture manual does not define mftb as + accepting any values other than 268 or 269. */ + +#define TB (268) + +static unsigned long +insert_tbr (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0) + value = TB; + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_tbr (insn, invalid) + unsigned long insn; + int *invalid; +{ + long ret; + + ret = ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); + if (ret == TB) + ret = 0; + return ret; +} + +/* Macros used to form opcodes. */ + +/* The main opcode. */ +#define OP(x) (((x) & 0x3f) << 26) +#define OP_MASK OP (0x3f) + +/* The main opcode combined with a trap code in the TO field of a D + form instruction. Used for extended mnemonics for the trap + instructions. */ +#define OPTO(x,to) (OP (x) | (((to) & 0x1f) << 21)) +#define OPTO_MASK (OP_MASK | TO_MASK) + +/* The main opcode combined with a comparison size bit in the L field + of a D form or X form instruction. Used for extended mnemonics for + the comparison instructions. */ +#define OPL(x,l) (OP (x) | (((l) & 1) << 21)) +#define OPL_MASK OPL (0x3f,1) + +/* An A form instruction. */ +#define A(op, xop, rc) (OP (op) | (((xop) & 0x1f) << 1) | ((rc) & 1)) +#define A_MASK A (0x3f, 0x1f, 1) + +/* An A_MASK with the FRB field fixed. */ +#define AFRB_MASK (A_MASK | FRB_MASK) + +/* An A_MASK with the FRC field fixed. */ +#define AFRC_MASK (A_MASK | FRC_MASK) + +/* An A_MASK with the FRA and FRC fields fixed. */ +#define AFRAFRC_MASK (A_MASK | FRA_MASK | FRC_MASK) + +/* A B form instruction. */ +#define B(op, aa, lk) (OP (op) | (((aa) & 1) << 1) | ((lk) & 1)) +#define B_MASK B (0x3f, 1, 1) + +/* A B form instruction setting the BO field. */ +#define BBO(op, bo, aa, lk) (B ((op), (aa), (lk)) | (((bo) & 0x1f) << 21)) +#define BBO_MASK BBO (0x3f, 0x1f, 1, 1) + +/* A BBO_MASK with the y bit of the BO field removed. This permits + matching a conditional branch regardless of the setting of the y + bit. */ +#define Y_MASK (1 << 21) +#define BBOY_MASK (BBO_MASK &~ Y_MASK) + +/* A B form instruction setting the BO field and the condition bits of + the BI field. */ +#define BBOCB(op, bo, cb, aa, lk) \ + (BBO ((op), (bo), (aa), (lk)) | (((cb) & 0x3) << 16)) +#define BBOCB_MASK BBOCB (0x3f, 0x1f, 0x3, 1, 1) + +/* A BBOCB_MASK with the y bit of the BO field removed. */ +#define BBOYCB_MASK (BBOCB_MASK &~ Y_MASK) + +/* A BBOYCB_MASK in which the BI field is fixed. */ +#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK) + +/* The main opcode mask with the RA field clear. */ +#define DRA_MASK (OP_MASK | RA_MASK) + +/* A DS form instruction. */ +#define DSO(op, xop) (OP (op) | ((xop) & 0x3)) +#define DS_MASK DSO (0x3f, 3) + +/* An M form instruction. */ +#define M(op, rc) (OP (op) | ((rc) & 1)) +#define M_MASK M (0x3f, 1) + +/* An M form instruction with the ME field specified. */ +#define MME(op, me, rc) (M ((op), (rc)) | (((me) & 0x1f) << 1)) + +/* An M_MASK with the MB and ME fields fixed. */ +#define MMBME_MASK (M_MASK | MB_MASK | ME_MASK) + +/* An M_MASK with the SH and ME fields fixed. */ +#define MSHME_MASK (M_MASK | SH_MASK | ME_MASK) + +/* An MD form instruction. */ +#define MD(op, xop, rc) (OP (op) | (((xop) & 0x7) << 2) | ((rc) & 1)) +#define MD_MASK MD (0x3f, 0x7, 1) + +/* An MD_MASK with the MB field fixed. */ +#define MDMB_MASK (MD_MASK | MB6_MASK) + +/* An MD_MASK with the SH field fixed. */ +#define MDSH_MASK (MD_MASK | SH6_MASK) + +/* An MDS form instruction. */ +#define MDS(op, xop, rc) (OP (op) | (((xop) & 0xf) << 1) | ((rc) & 1)) +#define MDS_MASK MDS (0x3f, 0xf, 1) + +/* An MDS_MASK with the MB field fixed. */ +#define MDSMB_MASK (MDS_MASK | MB6_MASK) + +/* An SC form instruction. */ +#define SC(op, sa, lk) (OP (op) | (((sa) & 1) << 1) | ((lk) & 1)) +#define SC_MASK (OP_MASK | (0x3ff << 16) | (1 << 1) | 1) + +/* An X form instruction. */ +#define X(op, xop) (OP (op) | (((xop) & 0x3ff) << 1)) + +/* An X form instruction with the RC bit specified. */ +#define XRC(op, xop, rc) (X ((op), (xop)) | ((rc) & 1)) + +/* The mask for an X form instruction. */ +#define X_MASK XRC (0x3f, 0x3ff, 1) + +/* An X_MASK with the RA field fixed. */ +#define XRA_MASK (X_MASK | RA_MASK) + +/* An X_MASK with the RB field fixed. */ +#define XRB_MASK (X_MASK | RB_MASK) + +/* An X_MASK with the RT field fixed. */ +#define XRT_MASK (X_MASK | RT_MASK) + +/* An X_MASK with the RA and RB fields fixed. */ +#define XRARB_MASK (X_MASK | RA_MASK | RB_MASK) + +/* An X_MASK with the RT and RA fields fixed. */ +#define XRTRA_MASK (X_MASK | RT_MASK | RA_MASK) + +/* An X form comparison instruction. */ +#define XCMPL(op, xop, l) (X ((op), (xop)) | (((l) & 1) << 21)) + +/* The mask for an X form comparison instruction. */ +#define XCMP_MASK (X_MASK | (1 << 22)) + +/* The mask for an X form comparison instruction with the L field + fixed. */ +#define XCMPL_MASK (XCMP_MASK | (1 << 21)) + +/* An X form trap instruction with the TO field specified. */ +#define XTO(op, xop, to) (X ((op), (xop)) | (((to) & 0x1f) << 21)) +#define XTO_MASK (X_MASK | TO_MASK) + +/* An XFL form instruction. */ +#define XFL(op, xop, rc) (OP (op) | (((xop) & 0x3ff) << 1) | ((rc) & 1)) +#define XFL_MASK (XFL (0x3f, 0x3ff, 1) | (1 << 25) | (1 << 16)) + +/* An XL form instruction with the LK field set to 0. */ +#define XL(op, xop) (OP (op) | (((xop) & 0x3ff) << 1)) + +/* An XL form instruction which uses the LK field. */ +#define XLLK(op, xop, lk) (XL ((op), (xop)) | ((lk) & 1)) + +/* The mask for an XL form instruction. */ +#define XL_MASK XLLK (0x3f, 0x3ff, 1) + +/* An XL form instruction which explicitly sets the BO field. */ +#define XLO(op, bo, xop, lk) \ + (XLLK ((op), (xop), (lk)) | (((bo) & 0x1f) << 21)) +#define XLO_MASK (XL_MASK | BO_MASK) + +/* An XL form instruction which explicitly sets the y bit of the BO + field. */ +#define XLYLK(op, xop, y, lk) (XLLK ((op), (xop), (lk)) | (((y) & 1) << 21)) +#define XLYLK_MASK (XL_MASK | Y_MASK) + +/* An XL form instruction which sets the BO field and the condition + bits of the BI field. */ +#define XLOCB(op, bo, cb, xop, lk) \ + (XLO ((op), (bo), (xop), (lk)) | (((cb) & 3) << 16)) +#define XLOCB_MASK XLOCB (0x3f, 0x1f, 0x3, 0x3ff, 1) + +/* An XL_MASK or XLYLK_MASK or XLOCB_MASK with the BB field fixed. */ +#define XLBB_MASK (XL_MASK | BB_MASK) +#define XLYBB_MASK (XLYLK_MASK | BB_MASK) +#define XLBOCBBB_MASK (XLOCB_MASK | BB_MASK) + +/* An XL_MASK with the BO and BB fields fixed. */ +#define XLBOBB_MASK (XL_MASK | BO_MASK | BB_MASK) + +/* An XL_MASK with the BO, BI and BB fields fixed. */ +#define XLBOBIBB_MASK (XL_MASK | BO_MASK | BI_MASK | BB_MASK) + +/* An XO form instruction. */ +#define XO(op, xop, oe, rc) \ + (OP (op) | (((xop) & 0x1ff) << 1) | (((oe) & 1) << 10) | ((rc) & 1)) +#define XO_MASK XO (0x3f, 0x1ff, 1, 1) + +/* An XO_MASK with the RB field fixed. */ +#define XORB_MASK (XO_MASK | RB_MASK) + +/* An XS form instruction. */ +#define XS(op, xop, rc) (OP (op) | (((xop) & 0x1ff) << 2) | ((rc) & 1)) +#define XS_MASK XS (0x3f, 0x1ff, 1) + +/* A mask for the FXM version of an XFX form instruction. */ +#define XFXFXM_MASK (X_MASK | (1 << 20) | (1 << 11)) + +/* An XFX form instruction with the FXM field filled in. */ +#define XFXM(op, xop, fxm) \ + (X ((op), (xop)) | (((fxm) & 0xff) << 12)) + +/* An XFX form instruction with the SPR field filled in. */ +#define XSPR(op, xop, spr) \ + (X ((op), (xop)) | (((spr) & 0x1f) << 16) | (((spr) & 0x3e0) << 6)) +#define XSPR_MASK (X_MASK | SPR_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRBAT field. */ +#define XSPRBAT_MASK (XSPR_MASK &~ SPRBAT_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRG field. */ +#define XSPRG_MASK (XSPR_MASK &~ SPRG_MASK) + +/* The BO encodings used in extended conditional branch mnemonics. */ +#define BODNZF (0x0) +#define BODNZFP (0x1) +#define BODZF (0x2) +#define BODZFP (0x3) +#define BOF (0x4) +#define BOFP (0x5) +#define BODNZT (0x8) +#define BODNZTP (0x9) +#define BODZT (0xa) +#define BODZTP (0xb) +#define BOT (0xc) +#define BOTP (0xd) +#define BODNZ (0x10) +#define BODNZP (0x11) +#define BODZ (0x12) +#define BODZP (0x13) +#define BOU (0x14) + +/* The BI condition bit encodings used in extended conditional branch + mnemonics. */ +#define CBLT (0) +#define CBGT (1) +#define CBEQ (2) +#define CBSO (3) + +/* The TO encodings used in extended trap mnemonics. */ +#define TOLGT (0x1) +#define TOLLT (0x2) +#define TOEQ (0x4) +#define TOLGE (0x5) +#define TOLNL (0x5) +#define TOLLE (0x6) +#define TOLNG (0x6) +#define TOGT (0x8) +#define TOGE (0xc) +#define TONL (0xc) +#define TOLT (0x10) +#define TOLE (0x14) +#define TONG (0x14) +#define TONE (0x18) +#define TOU (0x1f) + +/* Smaller names for the flags so each entry in the opcodes table will + fit on a single line. */ +#undef PPC +#define PPC PPC_OPCODE_PPC +#define POWER PPC_OPCODE_POWER +#define POWER2 PPC_OPCODE_POWER2 +#define B32 PPC_OPCODE_32 +#define B64 PPC_OPCODE_64 +#define M601 PPC_OPCODE_601 + +/* The opcode table. + + The format of the opcode table is: + + NAME OPCODE MASK FLAGS { OPERANDS } + + NAME is the name of the instruction. + OPCODE is the instruction opcode. + MASK is the opcode mask; this is used to tell the disassembler + which bits in the actual opcode must match OPCODE. + FLAGS are flags indicated what processors support the instruction. + OPERANDS is the list of operands. + + The disassembler reads the table in order and prints the first + instruction which matches, so this table is sorted to put more + specific instructions before more general instructions. It is also + sorted by major opcode. */ + +const struct powerpc_opcode powerpc_opcodes[] = { +{ "tdlgti", OPTO(2,TOLGT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdllti", OPTO(2,TOLLT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdeqi", OPTO(2,TOEQ), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlgei", OPTO(2,TOLGE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlnli", OPTO(2,TOLNL), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdllei", OPTO(2,TOLLE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlngi", OPTO(2,TOLNG), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdgti", OPTO(2,TOGT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdgei", OPTO(2,TOGE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdnli", OPTO(2,TONL), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlti", OPTO(2,TOLT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlei", OPTO(2,TOLE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdngi", OPTO(2,TONG), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdnei", OPTO(2,TONE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdi", OP(2), OP_MASK, PPC|B64, { TO, RA, SI } }, + +{ "twlgti", OPTO(3,TOLGT), OPTO_MASK, PPC, { RA, SI } }, +{ "tlgti", OPTO(3,TOLGT), OPTO_MASK, POWER, { RA, SI } }, +{ "twllti", OPTO(3,TOLLT), OPTO_MASK, PPC, { RA, SI } }, +{ "tllti", OPTO(3,TOLLT), OPTO_MASK, POWER, { RA, SI } }, +{ "tweqi", OPTO(3,TOEQ), OPTO_MASK, PPC, { RA, SI } }, +{ "teqi", OPTO(3,TOEQ), OPTO_MASK, POWER, { RA, SI } }, +{ "twlgei", OPTO(3,TOLGE), OPTO_MASK, PPC, { RA, SI } }, +{ "tlgei", OPTO(3,TOLGE), OPTO_MASK, POWER, { RA, SI } }, +{ "twlnli", OPTO(3,TOLNL), OPTO_MASK, PPC, { RA, SI } }, +{ "tlnli", OPTO(3,TOLNL), OPTO_MASK, POWER, { RA, SI } }, +{ "twllei", OPTO(3,TOLLE), OPTO_MASK, PPC, { RA, SI } }, +{ "tllei", OPTO(3,TOLLE), OPTO_MASK, POWER, { RA, SI } }, +{ "twlngi", OPTO(3,TOLNG), OPTO_MASK, PPC, { RA, SI } }, +{ "tlngi", OPTO(3,TOLNG), OPTO_MASK, POWER, { RA, SI } }, +{ "twgti", OPTO(3,TOGT), OPTO_MASK, PPC, { RA, SI } }, +{ "tgti", OPTO(3,TOGT), OPTO_MASK, POWER, { RA, SI } }, +{ "twgei", OPTO(3,TOGE), OPTO_MASK, PPC, { RA, SI } }, +{ "tgei", OPTO(3,TOGE), OPTO_MASK, POWER, { RA, SI } }, +{ "twnli", OPTO(3,TONL), OPTO_MASK, PPC, { RA, SI } }, +{ "tnli", OPTO(3,TONL), OPTO_MASK, POWER, { RA, SI } }, +{ "twlti", OPTO(3,TOLT), OPTO_MASK, PPC, { RA, SI } }, +{ "tlti", OPTO(3,TOLT), OPTO_MASK, POWER, { RA, SI } }, +{ "twlei", OPTO(3,TOLE), OPTO_MASK, PPC, { RA, SI } }, +{ "tlei", OPTO(3,TOLE), OPTO_MASK, POWER, { RA, SI } }, +{ "twngi", OPTO(3,TONG), OPTO_MASK, PPC, { RA, SI } }, +{ "tngi", OPTO(3,TONG), OPTO_MASK, POWER, { RA, SI } }, +{ "twnei", OPTO(3,TONE), OPTO_MASK, PPC, { RA, SI } }, +{ "tnei", OPTO(3,TONE), OPTO_MASK, POWER, { RA, SI } }, +{ "twi", OP(3), OP_MASK, PPC, { TO, RA, SI } }, +{ "ti", OP(3), OP_MASK, POWER, { TO, RA, SI } }, + +{ "mulli", OP(7), OP_MASK, PPC, { RT, RA, SI } }, +{ "muli", OP(7), OP_MASK, POWER, { RT, RA, SI } }, + +{ "subfic", OP(8), OP_MASK, PPC, { RT, RA, SI } }, +{ "sfi", OP(8), OP_MASK, POWER, { RT, RA, SI } }, + +{ "dozi", OP(9), OP_MASK, POWER|M601, { RT, RA, SI } }, + +{ "cmplwi", OPL(10,0), OPL_MASK, PPC, { OBF, RA, UI } }, +{ "cmpldi", OPL(10,1), OPL_MASK, PPC|B64, { OBF, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, PPC, { BF, L, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, POWER, { BF, RA, UI } }, + +{ "cmpwi", OPL(11,0), OPL_MASK, PPC, { OBF, RA, SI } }, +{ "cmpdi", OPL(11,1), OPL_MASK, PPC|B64, { OBF, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, PPC, { BF, L, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, POWER, { BF, RA, SI } }, + +{ "addic", OP(12), OP_MASK, PPC, { RT, RA, SI } }, +{ "ai", OP(12), OP_MASK, POWER, { RT, RA, SI } }, +{ "subic", OP(12), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "addic.", OP(13), OP_MASK, PPC, { RT, RA, SI } }, +{ "ai.", OP(13), OP_MASK, POWER, { RT, RA, SI } }, +{ "subic.", OP(13), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "li", OP(14), DRA_MASK, PPC, { RT, SI } }, +{ "lil", OP(14), DRA_MASK, POWER, { RT, SI } }, +{ "addi", OP(14), OP_MASK, PPC, { RT, RA, SI } }, +{ "cal", OP(14), OP_MASK, POWER, { RT, D, RA } }, +{ "subi", OP(14), OP_MASK, PPC, { RT, RA, NSI } }, +{ "la", OP(14), OP_MASK, PPC, { RT, D, RA } }, + +{ "lis", OP(15), DRA_MASK, PPC, { RT, SISIGNOPT } }, +{ "liu", OP(15), DRA_MASK, POWER, { RT, SISIGNOPT } }, +{ "addis", OP(15), OP_MASK, PPC, { RT,RA,SISIGNOPT } }, +{ "cau", OP(15), OP_MASK, POWER, { RT,RA,SISIGNOPT } }, +{ "subis", OP(15), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "bdnz-", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BDM } }, +{ "bdnz+", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BDP } }, +{ "bdnz", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BD } }, +{ "bdn", BBO(16,BODNZ,0,0), BBOYBI_MASK, POWER, { BD } }, +{ "bdnzl-", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BDM } }, +{ "bdnzl+", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BDP } }, +{ "bdnzl", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BD } }, +{ "bdnl", BBO(16,BODNZ,0,1), BBOYBI_MASK, POWER, { BD } }, +{ "bdnza-", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdnza+", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdnza", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDA } }, +{ "bdna", BBO(16,BODNZ,1,0), BBOYBI_MASK, POWER, { BDA } }, +{ "bdnzla-", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdnzla+", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdnzla", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDA } }, +{ "bdnla", BBO(16,BODNZ,1,1), BBOYBI_MASK, POWER, { BDA } }, +{ "bdz-", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC, { BDM } }, +{ "bdz+", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC, { BDP } }, +{ "bdz", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC|POWER, { BD } }, +{ "bdzl-", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC, { BDM } }, +{ "bdzl+", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC, { BDP } }, +{ "bdzl", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC|POWER, { BD } }, +{ "bdza-", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdza+", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdza", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC|POWER, { BDA } }, +{ "bdzla-", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdzla+", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdzla", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC|POWER, { BDA } }, +{ "blt-", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "blt+", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "blt", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bltl-", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bltl+", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bltl", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blta-", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blta+", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blta", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bltla-", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bltla+", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bltla", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgt-", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgt+", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgt", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgtl-", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgtl+", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgtl", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgta-", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgta+", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgta", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgtla-", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgtla+", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgtla", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "beq-", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "beq+", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "beq", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "beql-", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "beql+", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "beql", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "beqa-", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "beqa+", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "beqa", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "beqla-", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "beqla+", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "beqla", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bso-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bso+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bso", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bsol-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bsol+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bsol", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bsoa-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bsoa+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bsoa", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bsola-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bsola+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bsola", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bun-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bun+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bun", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bunl-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bunl+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bunl", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BD } }, +{ "buna-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "buna+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "buna", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bunla-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bunla+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bunla", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bge-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bge+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bge", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgel-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgel+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgel", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgea-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgea+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgea", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgela-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgela+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgela", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnl-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnl+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnl", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnll-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnll+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnll", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnla-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnla+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnla", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnlla-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnlla+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnlla", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "ble-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "ble+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "ble", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blel-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "blel+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "blel", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blea-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blea+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blea", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "blela-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blela+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blela", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bng-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bng+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bng", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bngl-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bngl+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bngl", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnga-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnga+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnga", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bngla-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bngla+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bngla", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bne-", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bne+", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bne", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnel-", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnel+", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnel", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnea-", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnea+", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnea", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnela-", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnela+", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnela", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bns-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bns+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bns", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnsl-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnsl+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnsl", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnsa-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnsa+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnsa", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnsla-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnsla+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnsla", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnu-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnu+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnu", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bnul-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnul+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnul", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bnua-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnua+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnua", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bnula-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnula+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnula", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bdnzt-", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzt+", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzt", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnztl-", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnztl+", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnztl", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzta-", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzta+", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzta", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnztla-",BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnztla+",BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnztla", BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnzf-", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzf+", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzf", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzfl-", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzfl+", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzfl", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzfa-", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzfa+", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzfa", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnzfla-",BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzfla+",BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzfla", BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bt-", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bt+", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bt", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bbt", BBO(16,BOT,0,0), BBOY_MASK, POWER, { BI, BD } }, +{ "btl-", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "btl+", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "btl", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bbtl", BBO(16,BOT,0,1), BBOY_MASK, POWER, { BI, BD } }, +{ "bta-", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bta+", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bta", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbta", BBO(16,BOT,1,0), BBOY_MASK, POWER, { BI, BDA } }, +{ "btla-", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "btla+", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "btla", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbtla", BBO(16,BOT,1,1), BBOY_MASK, POWER, { BI, BDA } }, +{ "bf-", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bf+", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bf", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bbf", BBO(16,BOF,0,0), BBOY_MASK, POWER, { BI, BD } }, +{ "bfl-", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bfl+", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bfl", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bbfl", BBO(16,BOF,0,1), BBOY_MASK, POWER, { BI, BD } }, +{ "bfa-", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bfa+", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bfa", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbfa", BBO(16,BOF,1,0), BBOY_MASK, POWER, { BI, BDA } }, +{ "bfla-", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bfla+", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bfla", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbfla", BBO(16,BOF,1,1), BBOY_MASK, POWER, { BI, BDA } }, +{ "bdzt-", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzt+", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzt", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdztl-", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdztl+", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdztl", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzta-", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzta+", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzta", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdztla-", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdztla+", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdztla", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdzf-", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzf+", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzf", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzfl-", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzfl+", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzfl", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzfa-", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzfa+", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzfa", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdzfla-", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzfla+", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzfla", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bc-", B(16,0,0), B_MASK, PPC, { BOE, BI, BDM } }, +{ "bc+", B(16,0,0), B_MASK, PPC, { BOE, BI, BDP } }, +{ "bc", B(16,0,0), B_MASK, PPC|POWER, { BO, BI, BD } }, +{ "bcl-", B(16,0,1), B_MASK, PPC, { BOE, BI, BDM } }, +{ "bcl+", B(16,0,1), B_MASK, PPC, { BOE, BI, BDP } }, +{ "bcl", B(16,0,1), B_MASK, PPC|POWER, { BO, BI, BD } }, +{ "bca-", B(16,1,0), B_MASK, PPC, { BOE, BI, BDMA } }, +{ "bca+", B(16,1,0), B_MASK, PPC, { BOE, BI, BDPA } }, +{ "bca", B(16,1,0), B_MASK, PPC|POWER, { BO, BI, BDA } }, +{ "bcla-", B(16,1,1), B_MASK, PPC, { BOE, BI, BDMA } }, +{ "bcla+", B(16,1,1), B_MASK, PPC, { BOE, BI, BDPA } }, +{ "bcla", B(16,1,1), B_MASK, PPC|POWER, { BO, BI, BDA } }, + +{ "sc", SC(17,1,0), 0xffffffff, PPC, { 0 } }, +{ "svc", SC(17,0,0), SC_MASK, POWER, { LEV, FL1, FL2 } }, +{ "svcl", SC(17,0,1), SC_MASK, POWER, { LEV, FL1, FL2 } }, +{ "svca", SC(17,1,0), SC_MASK, POWER, { SV } }, +{ "svcla", SC(17,1,1), SC_MASK, POWER, { SV } }, + +{ "b", B(18,0,0), B_MASK, PPC|POWER, { LI } }, +{ "bl", B(18,0,1), B_MASK, PPC|POWER, { LI } }, +{ "ba", B(18,1,0), B_MASK, PPC|POWER, { LIA } }, +{ "bla", B(18,1,1), B_MASK, PPC|POWER, { LIA } }, + +{ "mcrf", XL(19,0), XLBB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } }, + +{ "blr", XLO(19,BOU,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "br", XLO(19,BOU,16,0), XLBOBIBB_MASK, POWER, { 0 } }, +{ "blrl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "brl", XLO(19,BOU,16,1), XLBOBIBB_MASK, POWER, { 0 } }, +{ "bdnzlr", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlr-", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlr+", XLO(19,BODNZP,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl", XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl-",XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl+",XLO(19,BODNZP,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr-", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr+", XLO(19,BODZP,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl-", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl+", XLO(19,BODZP,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bltlr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlr-", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlr+", XLOCB(19,BOTP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bltlrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlrl-", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlrl+", XLOCB(19,BOTP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgtlr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlr-", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlr+", XLOCB(19,BOTP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgtlrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlrl-", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlrl+", XLOCB(19,BOTP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "beqlr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlr-", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlr+", XLOCB(19,BOTP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "beqlrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlrl-", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlrl+", XLOCB(19,BOTP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bsolr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsor", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bsolrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsorl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bunlr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bger", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgelrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgerl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnllr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnllrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "blelr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bler", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "blelrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blerl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnglr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnglrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnelr", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelr-", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelr+", XLOCB(19,BOFP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bner", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnelrl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelrl-", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelrl+", XLOCB(19,BOFP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnerl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnslr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnslrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnulr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "btlr", XLO(19,BOT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "btlr-", XLO(19,BOT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "btlr+", XLO(19,BOTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bbtr", XLO(19,BOT,16,0), XLBOBB_MASK, POWER, { BI } }, +{ "btlrl", XLO(19,BOT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "btlrl-", XLO(19,BOT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "btlrl+", XLO(19,BOTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bbtrl", XLO(19,BOT,16,1), XLBOBB_MASK, POWER, { BI } }, +{ "bflr", XLO(19,BOF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bflr-", XLO(19,BOF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bflr+", XLO(19,BOFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bbfr", XLO(19,BOF,16,0), XLBOBB_MASK, POWER, { BI } }, +{ "bflrl", XLO(19,BOF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bflrl-", XLO(19,BOF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bflrl+", XLO(19,BOFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bbfrl", XLO(19,BOF,16,1), XLBOBB_MASK, POWER, { BI } }, +{ "bdnztlr", XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlr-",XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlr+",XLO(19,BODNZTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl-",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl+",XLO(19,BODNZTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr", XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr-",XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr+",XLO(19,BODNZFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl-",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl+",XLO(19,BODNZFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr-", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr+", XLO(19,BODZTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl", XLO(19,BODZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl-",XLO(19,BODZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl+",XLO(19,BODZTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr-", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr+", XLO(19,BODZFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl", XLO(19,BODZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl-",XLO(19,BODZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl+",XLO(19,BODZFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bclr", XLLK(19,16,0), XLYBB_MASK, PPC, { BO, BI } }, +{ "bclrl", XLLK(19,16,1), XLYBB_MASK, PPC, { BO, BI } }, +{ "bclr+", XLYLK(19,16,1,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclrl+", XLYLK(19,16,1,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclr-", XLYLK(19,16,0,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclrl-", XLYLK(19,16,0,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcr", XLLK(19,16,0), XLBB_MASK, POWER, { BO, BI } }, +{ "bcrl", XLLK(19,16,1), XLBB_MASK, POWER, { BO, BI } }, + +{ "crnot", XL(19,33), XL_MASK, PPC, { BT, BA, BBA } }, +{ "crnor", XL(19,33), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "rfi", XL(19,50), 0xffffffff, PPC|POWER, { 0 } }, +{ "rfci", XL(19,51), 0xffffffff, PPC, { 0 } }, + +{ "rfsvc", XL(19,82), 0xffffffff, POWER, { 0 } }, + +{ "crandc", XL(19,129), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "isync", XL(19,150), 0xffffffff, PPC, { 0 } }, +{ "ics", XL(19,150), 0xffffffff, POWER, { 0 } }, + +{ "crclr", XL(19,193), XL_MASK, PPC, { BT, BAT, BBA } }, +{ "crxor", XL(19,193), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crnand", XL(19,225), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crand", XL(19,257), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crset", XL(19,289), XL_MASK, PPC, { BT, BAT, BBA } }, +{ "creqv", XL(19,289), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crorc", XL(19,417), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crmove", XL(19,449), XL_MASK, PPC, { BT, BA, BBA } }, +{ "cror", XL(19,449), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "bctr", XLO(19,BOU,528,0), XLBOBIBB_MASK, PPC|POWER, { 0 } }, +{ "bctrl", XLO(19,BOU,528,1), XLBOBIBB_MASK, PPC|POWER, { 0 } }, +{ "bltctr", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctr-", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctr+", XLOCB(19,BOTP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl", XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl-",XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl+",XLOCB(19,BOTP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr-", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr+", XLOCB(19,BOTP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl", XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl-",XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl+",XLOCB(19,BOTP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr-", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr+", XLOCB(19,BOTP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl", XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl-",XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl+",XLOCB(19,BOTP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr-", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr+", XLOCB(19,BOFP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl", XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl-",XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl+",XLOCB(19,BOFP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "btctr", XLO(19,BOT,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctr-", XLO(19,BOT,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctr+", XLO(19,BOTP,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl", XLO(19,BOT,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl-", XLO(19,BOT,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl+", XLO(19,BOTP,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr", XLO(19,BOF,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr-", XLO(19,BOF,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr+", XLO(19,BOFP,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl", XLO(19,BOF,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl-", XLO(19,BOF,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl+", XLO(19,BOFP,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bcctr", XLLK(19,528,0), XLYBB_MASK, PPC, { BO, BI } }, +{ "bcctr-", XLYLK(19,528,0,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctr+", XLYLK(19,528,1,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctrl", XLLK(19,528,1), XLYBB_MASK, PPC, { BO, BI } }, +{ "bcctrl-", XLYLK(19,528,0,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctrl+", XLYLK(19,528,1,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcc", XLLK(19,528,0), XLBB_MASK, POWER, { BO, BI } }, +{ "bccl", XLLK(19,528,1), XLBB_MASK, POWER, { BO, BI } }, + +{ "rlwimi", M(20,0), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlimi", M(20,0), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rlwimi.", M(20,1), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlimi.", M(20,1), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rotlwi", MME(21,31,0), MMBME_MASK, PPC, { RA, RS, SH } }, +{ "clrlwi", MME(21,31,0), MSHME_MASK, PPC, { RA, RS, MB } }, +{ "rlwinm", M(21,0), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlinm", M(21,0), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, +{ "rotlwi.", MME(21,31,1), MMBME_MASK, PPC, { RA,RS,SH } }, +{ "clrlwi.", MME(21,31,1), MSHME_MASK, PPC, { RA, RS, MB } }, +{ "rlwinm.", M(21,1), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlinm.", M(21,1), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rlmi", M(22,0), M_MASK, POWER|M601, { RA,RS,RB,MBE,ME } }, +{ "rlmi.", M(22,1), M_MASK, POWER|M601, { RA,RS,RB,MBE,ME } }, + +{ "rotlw", MME(23,31,0), MMBME_MASK, PPC, { RA, RS, RB } }, +{ "rlwnm", M(23,0), M_MASK, PPC, { RA,RS,RB,MBE,ME } }, +{ "rlnm", M(23,0), M_MASK, POWER, { RA,RS,RB,MBE,ME } }, +{ "rotlw.", MME(23,31,1), MMBME_MASK, PPC, { RA, RS, RB } }, +{ "rlwnm.", M(23,1), M_MASK, PPC, { RA,RS,RB,MBE,ME } }, +{ "rlnm.", M(23,1), M_MASK, POWER, { RA,RS,RB,MBE,ME } }, + +{ "nop", OP(24), 0xffffffff, PPC, { 0 } }, +{ "ori", OP(24), OP_MASK, PPC, { RA, RS, UI } }, +{ "oril", OP(24), OP_MASK, POWER, { RA, RS, UI } }, + +{ "oris", OP(25), OP_MASK, PPC, { RA, RS, UI } }, +{ "oriu", OP(25), OP_MASK, POWER, { RA, RS, UI } }, + +{ "xori", OP(26), OP_MASK, PPC, { RA, RS, UI } }, +{ "xoril", OP(26), OP_MASK, POWER, { RA, RS, UI } }, + +{ "xoris", OP(27), OP_MASK, PPC, { RA, RS, UI } }, +{ "xoriu", OP(27), OP_MASK, POWER, { RA, RS, UI } }, + +{ "andi.", OP(28), OP_MASK, PPC, { RA, RS, UI } }, +{ "andil.", OP(28), OP_MASK, POWER, { RA, RS, UI } }, + +{ "andis.", OP(29), OP_MASK, PPC, { RA, RS, UI } }, +{ "andiu.", OP(29), OP_MASK, POWER, { RA, RS, UI } }, + +{ "rotldi", MD(30,0,0), MDMB_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "clrldi", MD(30,0,0), MDSH_MASK, PPC|B64, { RA, RS, MB6 } }, +{ "rldicl", MD(30,0,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rotldi.", MD(30,0,1), MDMB_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "clrldi.", MD(30,0,1), MDSH_MASK, PPC|B64, { RA, RS, MB6 } }, +{ "rldicl.", MD(30,0,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rldicr", MD(30,1,0), MD_MASK, PPC|B64, { RA, RS, SH6, ME6 } }, +{ "rldicr.", MD(30,1,1), MD_MASK, PPC|B64, { RA, RS, SH6, ME6 } }, + +{ "rldic", MD(30,2,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rldic.", MD(30,2,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rldimi", MD(30,3,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rldimi.", MD(30,3,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rotld", MDS(30,8,0), MDSMB_MASK, PPC|B64, { RA, RS, RB } }, +{ "rldcl", MDS(30,8,0), MDS_MASK, PPC|B64, { RA, RS, RB, MB6 } }, +{ "rotld.", MDS(30,8,1), MDSMB_MASK, PPC|B64, { RA, RS, RB } }, +{ "rldcl.", MDS(30,8,1), MDS_MASK, PPC|B64, { RA, RS, RB, MB6 } }, + +{ "rldcr", MDS(30,9,0), MDS_MASK, PPC|B64, { RA, RS, RB, ME6 } }, +{ "rldcr.", MDS(30,9,1), MDS_MASK, PPC|B64, { RA, RS, RB, ME6 } }, + +{ "cmpw", XCMPL(31,0,0), XCMPL_MASK, PPC, { OBF, RA, RB } }, +{ "cmpd", XCMPL(31,0,1), XCMPL_MASK, PPC|B64, { OBF, RA, RB } }, +{ "cmp", X(31,0), XCMP_MASK, PPC, { BF, L, RA, RB } }, +{ "cmp", X(31,0), XCMPL_MASK, POWER, { BF, RA, RB } }, + +{ "twlgt", XTO(31,4,TOLGT), XTO_MASK, PPC, { RA, RB } }, +{ "tlgt", XTO(31,4,TOLGT), XTO_MASK, POWER, { RA, RB } }, +{ "twllt", XTO(31,4,TOLLT), XTO_MASK, PPC, { RA, RB } }, +{ "tllt", XTO(31,4,TOLLT), XTO_MASK, POWER, { RA, RB } }, +{ "tweq", XTO(31,4,TOEQ), XTO_MASK, PPC, { RA, RB } }, +{ "teq", XTO(31,4,TOEQ), XTO_MASK, POWER, { RA, RB } }, +{ "twlge", XTO(31,4,TOLGE), XTO_MASK, PPC, { RA, RB } }, +{ "tlge", XTO(31,4,TOLGE), XTO_MASK, POWER, { RA, RB } }, +{ "twlnl", XTO(31,4,TOLNL), XTO_MASK, PPC, { RA, RB } }, +{ "tlnl", XTO(31,4,TOLNL), XTO_MASK, POWER, { RA, RB } }, +{ "twlle", XTO(31,4,TOLLE), XTO_MASK, PPC, { RA, RB } }, +{ "tlle", XTO(31,4,TOLLE), XTO_MASK, POWER, { RA, RB } }, +{ "twlng", XTO(31,4,TOLNG), XTO_MASK, PPC, { RA, RB } }, +{ "tlng", XTO(31,4,TOLNG), XTO_MASK, POWER, { RA, RB } }, +{ "twgt", XTO(31,4,TOGT), XTO_MASK, PPC, { RA, RB } }, +{ "tgt", XTO(31,4,TOGT), XTO_MASK, POWER, { RA, RB } }, +{ "twge", XTO(31,4,TOGE), XTO_MASK, PPC, { RA, RB } }, +{ "tge", XTO(31,4,TOGE), XTO_MASK, POWER, { RA, RB } }, +{ "twnl", XTO(31,4,TONL), XTO_MASK, PPC, { RA, RB } }, +{ "tnl", XTO(31,4,TONL), XTO_MASK, POWER, { RA, RB } }, +{ "twlt", XTO(31,4,TOLT), XTO_MASK, PPC, { RA, RB } }, +{ "tlt", XTO(31,4,TOLT), XTO_MASK, POWER, { RA, RB } }, +{ "twle", XTO(31,4,TOLE), XTO_MASK, PPC, { RA, RB } }, +{ "tle", XTO(31,4,TOLE), XTO_MASK, POWER, { RA, RB } }, +{ "twng", XTO(31,4,TONG), XTO_MASK, PPC, { RA, RB } }, +{ "tng", XTO(31,4,TONG), XTO_MASK, POWER, { RA, RB } }, +{ "twne", XTO(31,4,TONE), XTO_MASK, PPC, { RA, RB } }, +{ "tne", XTO(31,4,TONE), XTO_MASK, POWER, { RA, RB } }, +{ "trap", XTO(31,4,TOU), 0xffffffff, PPC, { 0 } }, +{ "tw", X(31,4), X_MASK, PPC, { TO, RA, RB } }, +{ "t", X(31,4), X_MASK, POWER, { TO, RA, RB } }, + +{ "subfc", XO(31,8,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sf", XO(31,8,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subc", XO(31,8,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfc.", XO(31,8,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sf.", XO(31,8,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subc.", XO(31,8,0,1), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfco", XO(31,8,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfo", XO(31,8,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subco", XO(31,8,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfo.", XO(31,8,1,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "mulhdu", XO(31,9,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulhdu.", XO(31,9,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "addc", XO(31,10,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "a", XO(31,10,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addc.", XO(31,10,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "a.", XO(31,10,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addco", XO(31,10,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "ao", XO(31,10,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addco.", XO(31,10,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "ao.", XO(31,10,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mulhwu", XO(31,11,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhwu.", XO(31,11,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mfcr", X(31,19), XRARB_MASK, POWER|PPC, { RT } }, + +{ "lwarx", X(31,20), X_MASK, PPC, { RT, RA, RB } }, + +{ "ldx", X(31,21), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "lwzx", X(31,23), X_MASK, PPC, { RT, RA, RB } }, +{ "lx", X(31,23), X_MASK, POWER, { RT, RA, RB } }, + +{ "slw", XRC(31,24,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sl", XRC(31,24,0), X_MASK, POWER, { RA, RS, RB } }, +{ "slw.", XRC(31,24,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sl.", XRC(31,24,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "cntlzw", XRC(31,26,0), XRB_MASK, PPC, { RA, RS } }, +{ "cntlz", XRC(31,26,0), XRB_MASK, POWER, { RA, RS } }, +{ "cntlzw.", XRC(31,26,1), XRB_MASK, PPC, { RA, RS } }, +{ "cntlz.", XRC(31,26,1), XRB_MASK, POWER, { RA, RS } }, + +{ "sld", XRC(31,27,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "sld.", XRC(31,27,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "and", XRC(31,28,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "and.", XRC(31,28,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "maskg", XRC(31,29,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "maskg.", XRC(31,29,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "cmplw", XCMPL(31,32,0), XCMPL_MASK, PPC, { OBF, RA, RB } }, +{ "cmpld", XCMPL(31,32,1), XCMPL_MASK, PPC|B64, { OBF, RA, RB } }, +{ "cmpl", X(31,32), XCMP_MASK, PPC, { BF, L, RA, RB } }, +{ "cmpl", X(31,32), XCMPL_MASK, POWER, { BF, RA, RB } }, + +{ "subf", XO(31,40,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub", XO(31,40,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subf.", XO(31,40,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub.", XO(31,40,0,1), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo", XO(31,40,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo", XO(31,40,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "ldux", X(31,53), X_MASK, PPC|B64, { RT, RAL, RB } }, + +{ "dcbst", X(31,54), XRT_MASK, PPC, { RA, RB } }, + +{ "lwzux", X(31,55), X_MASK, PPC, { RT, RAL, RB } }, +{ "lux", X(31,55), X_MASK, POWER, { RT, RA, RB } }, + +{ "cntlzd", XRC(31,58,0), XRB_MASK, PPC|B64, { RA, RS } }, +{ "cntlzd.", XRC(31,58,1), XRB_MASK, PPC|B64, { RA, RS } }, + +{ "andc", XRC(31,60,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "andc.", XRC(31,60,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "tdlgt", XTO(31,68,TOLGT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdllt", XTO(31,68,TOLLT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdeq", XTO(31,68,TOEQ), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlge", XTO(31,68,TOLGE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlnl", XTO(31,68,TOLNL), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlle", XTO(31,68,TOLLE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlng", XTO(31,68,TOLNG), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdgt", XTO(31,68,TOGT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdge", XTO(31,68,TOGE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdnl", XTO(31,68,TONL), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlt", XTO(31,68,TOLT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdle", XTO(31,68,TOLE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdng", XTO(31,68,TONG), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdne", XTO(31,68,TONE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "td", X(31,68), X_MASK, PPC|B64, { TO, RA, RB } }, + +{ "mulhd", XO(31,73,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulhd.", XO(31,73,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "mulhw", XO(31,75,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhw.", XO(31,75,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mfmsr", X(31,83), XRARB_MASK, PPC|POWER, { RT } }, + +{ "ldarx", X(31,84), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "dcbf", X(31,86), XRT_MASK, PPC, { RA, RB } }, + +{ "lbzx", X(31,87), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "neg", XO(31,104,0,0), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "neg.", XO(31,104,0,1), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "nego", XO(31,104,1,0), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "nego.", XO(31,104,1,1), XORB_MASK, PPC|POWER, { RT, RA } }, + +{ "mul", XO(31,107,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mul.", XO(31,107,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mulo", XO(31,107,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mulo.", XO(31,107,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "clf", X(31,118), XRB_MASK, POWER, { RT, RA } }, + +{ "lbzux", X(31,119), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "not", XRC(31,124,0), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "nor", XRC(31,124,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "not.", XRC(31,124,1), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "nor.", XRC(31,124,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "subfe", XO(31,136,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfe", XO(31,136,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfe.", XO(31,136,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfe.", XO(31,136,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfeo", XO(31,136,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfeo", XO(31,136,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfeo.", XO(31,136,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfeo.", XO(31,136,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "adde", XO(31,138,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "ae", XO(31,138,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "adde.", XO(31,138,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "ae.", XO(31,138,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addeo", XO(31,138,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "aeo", XO(31,138,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addeo.", XO(31,138,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "aeo.", XO(31,138,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mtcr", XFXM(31,144,0xff), XFXFXM_MASK|FXM_MASK, PPC|POWER, { RS }}, +{ "mtcrf", X(31,144), XFXFXM_MASK, PPC|POWER, { FXM, RS } }, + +{ "mtmsr", X(31,146), XRARB_MASK, PPC|POWER, { RS } }, +{ "mtmsrd", X(31,178), XRARB_MASK, PPC|POWER, { RS } }, + +{ "stdx", X(31,149), X_MASK, PPC|B64, { RS, RA, RB } }, + +{ "stwcx.", XRC(31,150,1), X_MASK, PPC, { RS, RA, RB } }, + +{ "stwx", X(31,151), X_MASK, PPC, { RS, RA, RB } }, +{ "stx", X(31,151), X_MASK, POWER, { RS, RA, RB } }, + +{ "slq", XRC(31,152,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "slq.", XRC(31,152,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sle", XRC(31,153,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sle.", XRC(31,153,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stdux", X(31,181), X_MASK, PPC|B64, { RS, RAS, RB } }, + +{ "stwux", X(31,183), X_MASK, PPC, { RS, RAS, RB } }, +{ "stux", X(31,183), X_MASK, POWER, { RS, RA, RB } }, + +{ "sliq", XRC(31,184,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sliq.", XRC(31,184,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "subfze", XO(31,200,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfze", XO(31,200,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfze.", XO(31,200,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfze.", XO(31,200,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "subfzeo", XO(31,200,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfzeo", XO(31,200,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfzeo.",XO(31,200,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfzeo.", XO(31,200,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "addze", XO(31,202,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "aze", XO(31,202,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "addze.", XO(31,202,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "aze.", XO(31,202,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "addzeo", XO(31,202,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "azeo", XO(31,202,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "addzeo.", XO(31,202,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "azeo.", XO(31,202,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mtsr", X(31,210), XRB_MASK|(1<<20), PPC|POWER|B32, { SR, RS } }, + +{ "stdcx.", XRC(31,214,1), X_MASK, PPC|B64, { RS, RA, RB } }, + +{ "stbx", X(31,215), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "sllq", XRC(31,216,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sllq.", XRC(31,216,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sleq", XRC(31,217,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sleq.", XRC(31,217,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "subfme", XO(31,232,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfme", XO(31,232,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfme.", XO(31,232,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfme.", XO(31,232,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "subfmeo", XO(31,232,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfmeo", XO(31,232,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfmeo.",XO(31,232,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfmeo.", XO(31,232,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mulld", XO(31,233,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulld.", XO(31,233,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulldo", XO(31,233,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulldo.", XO(31,233,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "addme", XO(31,234,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "ame", XO(31,234,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "addme.", XO(31,234,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "ame.", XO(31,234,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "addmeo", XO(31,234,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "ameo", XO(31,234,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "addmeo.", XO(31,234,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "ameo.", XO(31,234,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mullw", XO(31,235,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "muls", XO(31,235,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullw.", XO(31,235,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "muls.", XO(31,235,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullwo", XO(31,235,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulso", XO(31,235,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullwo.", XO(31,235,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulso.", XO(31,235,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mtsrin", X(31,242), XRA_MASK, PPC|B32, { RS, RB } }, +{ "mtsri", X(31,242), XRA_MASK, POWER|B32, { RS, RB } }, + +{ "dcbtst", X(31,246), XRT_MASK, PPC, { RA, RB } }, + +{ "stbux", X(31,247), X_MASK, PPC|POWER, { RS, RAS, RB } }, + +{ "slliq", XRC(31,248,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "slliq.", XRC(31,248,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "doz", XO(31,264,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "doz.", XO(31,264,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "dozo", XO(31,264,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "dozo.", XO(31,264,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "add", XO(31,266,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "cax", XO(31,266,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "add.", XO(31,266,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "cax.", XO(31,266,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addo", XO(31,266,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "caxo", XO(31,266,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addo.", XO(31,266,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "caxo.", XO(31,266,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "lscbx", XRC(31,277,0), X_MASK, POWER|M601, { RT, RA, RB } }, +{ "lscbx.", XRC(31,277,1), X_MASK, POWER|M601, { RT, RA, RB } }, + +{ "dcbt", X(31,278), XRT_MASK, PPC, { RA, RB } }, + +{ "lhzx", X(31,279), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "icbt", X(31,262), XRT_MASK, PPC, { RA, RB } }, + +{ "eqv", XRC(31,284,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "eqv.", XRC(31,284,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "tlbie", X(31,306), XRTRA_MASK, PPC, { RB } }, +{ "tlbi", X(31,306), XRTRA_MASK, POWER, { RB } }, + +{ "eciwx", X(31,310), X_MASK, PPC, { RT, RA, RB } }, + +{ "lhzux", X(31,311), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "xor", XRC(31,316,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "xor.", XRC(31,316,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "mfdcr", X(31,323), X_MASK, PPC, { RT, SPR } }, + +{ "div", XO(31,331,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "div.", XO(31,331,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divo", XO(31,331,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divo.", XO(31,331,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "mfmq", XSPR(31,339,0), XSPR_MASK, POWER|M601, { RT } }, +{ "mfxer", XSPR(31,339,1), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfrtcu", XSPR(31,339,4), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfrtcl", XSPR(31,339,5), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdec", XSPR(31,339,6), XSPR_MASK, POWER|M601, { RT } }, +{ "mflr", XSPR(31,339,8), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfctr", XSPR(31,339,9), XSPR_MASK, PPC|POWER, { RT } }, +{ "mftid", XSPR(31,339,17), XSPR_MASK, POWER, { RT } }, +{ "mfdsisr", XSPR(31,339,18), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdar", XSPR(31,339,19), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdec", XSPR(31,339,22), XSPR_MASK, PPC, { RT } }, +{ "mfsdr0", XSPR(31,339,24), XSPR_MASK, POWER, { RT } }, +{ "mfsdr1", XSPR(31,339,25), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsrr0", XSPR(31,339,26), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsrr1", XSPR(31,339,27), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsprg", XSPR(31,339,272), XSPRG_MASK, PPC, { RT, SPRG } }, +{ "mfasr", XSPR(31,339,280), XSPR_MASK, PPC|B64, { RT } }, +{ "mfear", XSPR(31,339,282), XSPR_MASK, PPC, { RT } }, +{ "mfpvr", XSPR(31,339,287), XSPR_MASK, PPC, { RT } }, +{ "mfibatu", XSPR(31,339,528), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfibatl", XSPR(31,339,529), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatu", XSPR(31,339,536), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatl", XSPR(31,339,537), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfspr", X(31,339), X_MASK, PPC|POWER, { RT, SPR } }, + +{ "lwax", X(31,341), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "lhax", X(31,343), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "dccci", X(31,454), XRT_MASK, PPC, { RA, RB } }, + +{ "abs", XO(31,360,0,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abs.", XO(31,360,0,1), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abso", XO(31,360,1,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abso.", XO(31,360,1,1), XORB_MASK, POWER|M601, { RT, RA } }, + +{ "divs", XO(31,363,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divs.", XO(31,363,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divso", XO(31,363,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divso.", XO(31,363,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "tlbia", X(31,370), 0xffffffff, PPC, { 0 } }, + +{ "mftbu", XSPR(31,371,269), XSPR_MASK, PPC, { RT } }, +{ "mftb", X(31,371), X_MASK, PPC, { RT, TBR } }, + +{ "lwaux", X(31,373), X_MASK, PPC|B64, { RT, RAL, RB } }, + +{ "lhaux", X(31,375), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "sthx", X(31,407), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "lfqx", X(31,791), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "lfqux", X(31,823), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "stfqx", X(31,919), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "stfqux", X(31,951), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "orc", XRC(31,412,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "orc.", XRC(31,412,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "sradi", XS(31,413,0), XS_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "sradi.", XS(31,413,1), XS_MASK, PPC|B64, { RA, RS, SH6 } }, + +{ "slbie", X(31,434), XRTRA_MASK, PPC|B64, { RB } }, + +{ "ecowx", X(31,438), X_MASK, PPC, { RT, RA, RB } }, + +{ "sthux", X(31,439), X_MASK, PPC|POWER, { RS, RAS, RB } }, + +{ "mr", XRC(31,444,0), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "or", XRC(31,444,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "mr.", XRC(31,444,1), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "or.", XRC(31,444,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "mtdcr", X(31,451), X_MASK, PPC, { SPR, RS } }, + +{ "divdu", XO(31,457,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdu.", XO(31,457,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divduo", XO(31,457,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divduo.", XO(31,457,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "divwu", XO(31,459,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwu.", XO(31,459,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo", XO(31,459,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo.", XO(31,459,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mtmq", XSPR(31,467,0), XSPR_MASK, POWER|M601, { RS } }, +{ "mtxer", XSPR(31,467,1), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtlr", XSPR(31,467,8), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtctr", XSPR(31,467,9), XSPR_MASK, PPC|POWER, { RS } }, +{ "mttid", XSPR(31,467,17), XSPR_MASK, POWER, { RS } }, +{ "mtdsisr", XSPR(31,467,18), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtdar", XSPR(31,467,19), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtrtcu", XSPR(31,467,20), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtrtcl", XSPR(31,467,21), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtdec", XSPR(31,467,22), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsdr0", XSPR(31,467,24), XSPR_MASK, POWER, { RS } }, +{ "mtsdr1", XSPR(31,467,25), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsrr0", XSPR(31,467,26), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsrr1", XSPR(31,467,27), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsprg", XSPR(31,467,272), XSPRG_MASK, PPC, { SPRG, RS } }, +{ "mtasr", XSPR(31,467,280), XSPR_MASK, PPC|B64, { RS } }, +{ "mtear", XSPR(31,467,282), XSPR_MASK, PPC, { RS } }, +{ "mttbl", XSPR(31,467,284), XSPR_MASK, PPC, { RS } }, +{ "mttbu", XSPR(31,467,285), XSPR_MASK, PPC, { RS } }, +{ "mtibatu", XSPR(31,467,528), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtibatl", XSPR(31,467,529), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatu", XSPR(31,467,536), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatl", XSPR(31,467,537), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtspr", X(31,467), X_MASK, PPC|POWER, { SPR, RS } }, + +{ "dcbi", X(31,470), XRT_MASK, PPC, { RA, RB } }, + +{ "nand", XRC(31,476,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "nand.", XRC(31,476,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "nabs", XO(31,488,0,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabs.", XO(31,488,0,1), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabso", XO(31,488,1,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabso.", XO(31,488,1,1), XORB_MASK, POWER|M601, { RT, RA } }, + +{ "divd", XO(31,489,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divd.", XO(31,489,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdo", XO(31,489,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdo.", XO(31,489,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "divw", XO(31,491,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divw.", XO(31,491,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo", XO(31,491,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo.", XO(31,491,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "slbia", X(31,498), 0xffffffff, PPC|B64, { 0 } }, + +{ "cli", X(31,502), XRB_MASK, POWER, { RT, RA } }, + +{ "mcrxr", X(31,512), XRARB_MASK|(3<<21), PPC|POWER, { BF } }, + +{ "clcs", X(31,531), XRB_MASK, POWER|M601, { RT, RA } }, + +{ "lswx", X(31,533), X_MASK, PPC, { RT, RA, RB } }, +{ "lsx", X(31,533), X_MASK, POWER, { RT, RA, RB } }, + +{ "lwbrx", X(31,534), X_MASK, PPC, { RT, RA, RB } }, +{ "lbrx", X(31,534), X_MASK, POWER, { RT, RA, RB } }, + +{ "lfsx", X(31,535), X_MASK, PPC|POWER, { FRT, RA, RB } }, + +{ "srw", XRC(31,536,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sr", XRC(31,536,0), X_MASK, POWER, { RA, RS, RB } }, +{ "srw.", XRC(31,536,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sr.", XRC(31,536,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "rrib", XRC(31,537,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "rrib.", XRC(31,537,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "srd", XRC(31,539,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "srd.", XRC(31,539,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "maskir", XRC(31,541,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "maskir.", XRC(31,541,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "tlbsync", X(31,566), 0xffffffff, PPC, { 0 } }, + +{ "lfsux", X(31,567), X_MASK, PPC|POWER, { FRT, RAS, RB } }, + +{ "mfsr", X(31,595), XRB_MASK|(1<<20), PPC|POWER|B32, { RT, SR } }, + +{ "lswi", X(31,597), X_MASK, PPC, { RT, RA, NB } }, +{ "lsi", X(31,597), X_MASK, POWER, { RT, RA, NB } }, + +{ "sync", X(31,598), 0xffffffff, PPC, { 0 } }, +{ "dcs", X(31,598), 0xffffffff, POWER, { 0 } }, + +{ "lfdx", X(31,599), X_MASK, PPC|POWER, { FRT, RA, RB } }, + +{ "mfsri", X(31,627), X_MASK, POWER, { RT, RA, RB } }, + +{ "dclst", X(31,630), XRB_MASK, POWER, { RS, RA } }, + +{ "lfdux", X(31,631), X_MASK, PPC|POWER, { FRT, RAS, RB } }, + +{ "mfsrin", X(31,659), XRA_MASK, PPC|B32, { RT, RB } }, + +{ "stswx", X(31,661), X_MASK, PPC, { RS, RA, RB } }, +{ "stsx", X(31,661), X_MASK, POWER, { RS, RA, RB } }, + +{ "stwbrx", X(31,662), X_MASK, PPC, { RS, RA, RB } }, +{ "stbrx", X(31,662), X_MASK, POWER, { RS, RA, RB } }, + +{ "stfsx", X(31,663), X_MASK, PPC|POWER, { FRS, RA, RB } }, + +{ "srq", XRC(31,664,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srq.", XRC(31,664,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sre", XRC(31,665,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sre.", XRC(31,665,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stfsux", X(31,695), X_MASK, PPC|POWER, { FRS, RAS, RB } }, + +{ "sriq", XRC(31,696,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sriq.", XRC(31,696,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "stswi", X(31,725), X_MASK, PPC, { RS, RA, NB } }, +{ "stsi", X(31,725), X_MASK, POWER, { RS, RA, NB } }, + +{ "stfdx", X(31,727), X_MASK, PPC|POWER, { FRS, RA, RB } }, + +{ "srlq", XRC(31,728,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srlq.", XRC(31,728,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sreq", XRC(31,729,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sreq.", XRC(31,729,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stfdux", X(31,759), X_MASK, PPC|POWER, { FRS, RAS, RB } }, + +{ "srliq", XRC(31,760,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "srliq.", XRC(31,760,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "lhbrx", X(31,790), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "sraw", XRC(31,792,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sra", XRC(31,792,0), X_MASK, POWER, { RA, RS, RB } }, +{ "sraw.", XRC(31,792,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sra.", XRC(31,792,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "srad", XRC(31,794,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "srad.", XRC(31,794,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "rac", X(31,818), X_MASK, POWER, { RT, RA, RB } }, + +{ "srawi", XRC(31,824,0), X_MASK, PPC, { RA, RS, SH } }, +{ "srai", XRC(31,824,0), X_MASK, POWER, { RA, RS, SH } }, +{ "srawi.", XRC(31,824,1), X_MASK, PPC, { RA, RS, SH } }, +{ "srai.", XRC(31,824,1), X_MASK, POWER, { RA, RS, SH } }, + +{ "eieio", X(31,854), 0xffffffff, PPC, { 0 } }, + +{ "sthbrx", X(31,918), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "sraq", XRC(31,920,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sraq.", XRC(31,920,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "srea", XRC(31,921,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srea.", XRC(31,921,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "extsh", XRC(31,922,0), XRB_MASK, PPC, { RA, RS } }, +{ "exts", XRC(31,922,0), XRB_MASK, POWER, { RA, RS } }, +{ "extsh.", XRC(31,922,1), XRB_MASK, PPC, { RA, RS } }, +{ "exts.", XRC(31,922,1), XRB_MASK, POWER, { RA, RS } }, + +{ "sraiq", XRC(31,952,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sraiq.", XRC(31,952,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "extsb", XRC(31,954,0), XRB_MASK, PPC, { RA, RS} }, +{ "extsb.", XRC(31,954,1), XRB_MASK, PPC, { RA, RS} }, + +{ "iccci", X(31,966), XRT_MASK, PPC, { RA, RB } }, + +{ "icbi", X(31,982), XRT_MASK, PPC, { RA, RB } }, + +{ "stfiwx", X(31,983), X_MASK, PPC, { FRS, RA, RB } }, + +{ "extsw", XRC(31,986,0), XRB_MASK, PPC, { RA, RS } }, +{ "extsw.", XRC(31,986,1), XRB_MASK, PPC, { RA, RS } }, + +{ "dcbz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, +{ "dclz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, + +{ "lwz", OP(32), OP_MASK, PPC, { RT, D, RA } }, +{ "l", OP(32), OP_MASK, POWER, { RT, D, RA } }, + +{ "lwzu", OP(33), OP_MASK, PPC, { RT, D, RAL } }, +{ "lu", OP(33), OP_MASK, POWER, { RT, D, RA } }, + +{ "lbz", OP(34), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lbzu", OP(35), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "stw", OP(36), OP_MASK, PPC, { RS, D, RA } }, +{ "st", OP(36), OP_MASK, POWER, { RS, D, RA } }, + +{ "stwu", OP(37), OP_MASK, PPC, { RS, D, RAS } }, +{ "stu", OP(37), OP_MASK, POWER, { RS, D, RA } }, + +{ "stb", OP(38), OP_MASK, PPC|POWER, { RS, D, RA } }, + +{ "stbu", OP(39), OP_MASK, PPC|POWER, { RS, D, RAS } }, + +{ "lhz", OP(40), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lhzu", OP(41), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "lha", OP(42), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lhau", OP(43), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "sth", OP(44), OP_MASK, PPC|POWER, { RS, D, RA } }, + +{ "sthu", OP(45), OP_MASK, PPC|POWER, { RS, D, RAS } }, + +{ "lmw", OP(46), OP_MASK, PPC, { RT, D, RAM } }, +{ "lm", OP(46), OP_MASK, POWER, { RT, D, RA } }, + +{ "stmw", OP(47), OP_MASK, PPC, { RS, D, RA } }, +{ "stm", OP(47), OP_MASK, POWER, { RS, D, RA } }, + +{ "lfs", OP(48), OP_MASK, PPC|POWER, { FRT, D, RA } }, + +{ "lfsu", OP(49), OP_MASK, PPC|POWER, { FRT, D, RAS } }, + +{ "lfd", OP(50), OP_MASK, PPC|POWER, { FRT, D, RA } }, + +{ "lfdu", OP(51), OP_MASK, PPC|POWER, { FRT, D, RAS } }, + +{ "stfs", OP(52), OP_MASK, PPC|POWER, { FRS, D, RA } }, + +{ "stfsu", OP(53), OP_MASK, PPC|POWER, { FRS, D, RAS } }, + +{ "stfd", OP(54), OP_MASK, PPC|POWER, { FRS, D, RA } }, + +{ "stfdu", OP(55), OP_MASK, PPC|POWER, { FRS, D, RAS } }, + +{ "lfq", OP(56), OP_MASK, POWER2, { FRT, D, RA } }, + +{ "lfqu", OP(57), OP_MASK, POWER2, { FRT, D, RA } }, + +{ "ld", DSO(58,0), DS_MASK, PPC|B64, { RT, DS, RA } }, + +{ "ldu", DSO(58,1), DS_MASK, PPC|B64, { RT, DS, RAL } }, + +{ "lwa", DSO(58,2), DS_MASK, PPC|B64, { RT, DS, RA } }, + +{ "fdivs", A(59,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fdivs.", A(59,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsubs", A(59,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fsubs.", A(59,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fadds", A(59,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fadds.", A(59,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsqrts", A(59,22,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fsqrts.", A(59,22,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fres", A(59,24,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fres.", A(59,24,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fmuls", A(59,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fmuls.", A(59,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } }, + +{ "fmsubs", A(59,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmsubs.", A(59,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmadds", A(59,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmadds.", A(59,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmsubs", A(59,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmsubs.",A(59,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmadds", A(59,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmadds.",A(59,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "stfq", OP(60), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "stfqu", OP(61), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "std", DSO(62,0), DS_MASK, PPC|B64, { RS, DS, RA } }, + +{ "stdu", DSO(62,1), DS_MASK, PPC|B64, { RS, DS, RAS } }, + +{ "fcmpu", X(63,0), X_MASK|(3<<21), PPC|POWER, { BF, FRA, FRB } }, + +{ "frsp", XRC(63,12,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "frsp.", XRC(63,12,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "fctiw", XRC(63,14,0), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcir", XRC(63,14,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiw.", XRC(63,14,1), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcir.", XRC(63,14,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fctiwz", XRC(63,15,0), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcirz", XRC(63,15,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiwz.", XRC(63,15,1), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcirz.", XRC(63,15,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fdiv", A(63,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fd", A(63,18,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fdiv.", A(63,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fd.", A(63,18,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fsub", A(63,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fs", A(63,20,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fsub.", A(63,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fs.", A(63,20,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fadd", A(63,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fa", A(63,21,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fadd.", A(63,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fa.", A(63,21,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fsqrt", A(63,22,0), AFRAFRC_MASK, PPC|POWER2, { FRT, FRB } }, +{ "fsqrt.", A(63,22,1), AFRAFRC_MASK, PPC|POWER2, { FRT, FRB } }, + +{ "fsel", A(63,23,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fsel.", A(63,23,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmul", A(63,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fm", A(63,25,0), AFRB_MASK, POWER, { FRT, FRA, FRC } }, +{ "fmul.", A(63,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fm.", A(63,25,1), AFRB_MASK, POWER, { FRT, FRA, FRC } }, + +{ "frsqrte", A(63,26,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "frsqrte.",A(63,26,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fmsub", A(63,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fms", A(63,28,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fmsub.", A(63,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fms.", A(63,28,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fmadd", A(63,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fma", A(63,29,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fmadd.", A(63,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fma.", A(63,29,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fnmsub", A(63,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnms", A(63,30,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fnmsub.", A(63,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnms.", A(63,30,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fnmadd", A(63,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnma", A(63,31,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fnmadd.", A(63,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnma.", A(63,31,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fcmpo", X(63,30), X_MASK|(3<<21), PPC|POWER, { BF, FRA, FRB } }, + +{ "mtfsb1", XRC(63,38,0), XRARB_MASK, PPC|POWER, { BT } }, +{ "mtfsb1.", XRC(63,38,1), XRARB_MASK, PPC|POWER, { BT } }, + +{ "fneg", XRC(63,40,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fneg.", XRC(63,40,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mcrfs", X(63,64), XRB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } }, + +{ "mtfsb0", XRC(63,70,0), XRARB_MASK, PPC|POWER, { BT } }, +{ "mtfsb0.", XRC(63,70,1), XRARB_MASK, PPC|POWER, { BT } }, + +{ "fmr", XRC(63,72,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fmr.", XRC(63,72,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mtfsfi", XRC(63,134,0), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } }, +{ "mtfsfi.", XRC(63,134,1), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } }, + +{ "fnabs", XRC(63,136,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fnabs.", XRC(63,136,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "fabs", XRC(63,264,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fabs.", XRC(63,264,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mffs", XRC(63,583,0), XRARB_MASK, PPC|POWER, { FRT } }, +{ "mffs.", XRC(63,583,1), XRARB_MASK, PPC|POWER, { FRT } }, + +{ "mtfsf", XFL(63,711,0), XFL_MASK, PPC|POWER, { FLM, FRB } }, +{ "mtfsf.", XFL(63,711,1), XFL_MASK, PPC|POWER, { FLM, FRB } }, + +{ "fctid", XRC(63,814,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fctid.", XRC(63,814,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +{ "fctidz", XRC(63,815,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fctidz.", XRC(63,815,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +{ "fcfid", XRC(63,846,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fcfid.", XRC(63,846,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +}; + +const int powerpc_num_opcodes = + sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]); + +/* The macro table. This is only used by the assembler. */ + +const struct powerpc_macro powerpc_macros[] = { +{ "extldi", 4, PPC|B64, "rldicr %0,%1,%3,(%2)-1" }, +{ "extldi.", 4, PPC|B64, "rldicr. %0,%1,%3,(%2)-1" }, +{ "extrdi", 4, PPC|B64, "rldicl %0,%1,(%2)+(%3),64-(%2)" }, +{ "extrdi.", 4, PPC|B64, "rldicl. %0,%1,(%2)+(%3),64-(%2)" }, +{ "insrdi", 4, PPC|B64, "rldimi %0,%1,64-((%2)+(%3)),%3" }, +{ "insrdi.", 4, PPC|B64, "rldimi. %0,%1,64-((%2)+(%3)),%3" }, +{ "rotrdi", 3, PPC|B64, "rldicl %0,%1,64-(%2),0" }, +{ "rotrdi.", 3, PPC|B64, "rldicl. %0,%1,64-(%2),0" }, +{ "sldi", 3, PPC|B64, "rldicr %0,%1,%2,63-(%2)" }, +{ "sldi.", 3, PPC|B64, "rldicr. %0,%1,%2,63-(%2)" }, +{ "srdi", 3, PPC|B64, "rldicl %0,%1,64-(%2),%2" }, +{ "srdi.", 3, PPC|B64, "rldicl. %0,%1,64-(%2),%2" }, +{ "clrrdi", 3, PPC|B64, "rldicr %0,%1,0,63-(%2)" }, +{ "clrrdi.", 3, PPC|B64, "rldicr. %0,%1,0,63-(%2)" }, +{ "clrlsldi",4, PPC|B64, "rldic %0,%1,%3,(%2)-(%3)" }, +{ "clrlsldi.",4, PPC|B64, "rldic. %0,%1,%3,(%2)-(%3)" }, + +{ "extlwi", 4, PPC, "rlwinm %0,%1,%3,0,(%2)-1" }, +{ "extlwi.", 4, PPC, "rlwinm. %0,%1,%3,0,(%2)-1" }, +{ "extrwi", 4, PPC, "rlwinm %0,%1,(%2)+(%3),32-(%2),31" }, +{ "extrwi.", 4, PPC, "rlwinm. %0,%1,(%2)+(%3),32-(%2),31" }, +{ "inslwi", 4, PPC, "rlwimi %0,%1,32-(%3),%3,(%2)+(%3)-1" }, +{ "inslwi.", 4, PPC, "rlwimi. %0,%1,32-(%3),%3,(%2)+(%3)-1" }, +{ "insrwi", 4, PPC, "rlwimi %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1" }, +{ "insrwi.", 4, PPC, "rlwimi. %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1"}, +{ "rotrwi", 3, PPC, "rlwinm %0,%1,32-(%2),0,31" }, +{ "rotrwi.", 3, PPC, "rlwinm. %0,%1,32-(%2),0,31" }, +{ "slwi", 3, PPC, "rlwinm %0,%1,%2,0,31-(%2)" }, +{ "sli", 3, POWER, "rlinm %0,%1,%2,0,31-(%2)" }, +{ "slwi.", 3, PPC, "rlwinm. %0,%1,%2,0,31-(%2)" }, +{ "sli.", 3, POWER, "rlinm. %0,%1,%2,0,31-(%2)" }, +{ "srwi", 3, PPC, "rlwinm %0,%1,32-(%2),%2,31" }, +{ "sri", 3, POWER, "rlinm %0,%1,32-(%2),%2,31" }, +{ "srwi.", 3, PPC, "rlwinm. %0,%1,32-(%2),%2,31" }, +{ "sri.", 3, POWER, "rlinm. %0,%1,32-(%2),%2,31" }, +{ "clrrwi", 3, PPC, "rlwinm %0,%1,0,0,31-(%2)" }, +{ "clrrwi.", 3, PPC, "rlwinm. %0,%1,0,0,31-(%2)" }, +{ "clrlslwi",4, PPC, "rlwinm %0,%1,%3,(%2)-(%3),31-(%3)" }, +{ "clrlslwi.",4, PPC, "rlwinm. %0,%1,%3,(%2)-(%3),31-(%3)" }, + +}; + +const int powerpc_num_macros = + sizeof (powerpc_macros) / sizeof (powerpc_macros[0]); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/xmon/ppc.h linux.ac/arch/ppc64/xmon/ppc.h --- linux.vanilla/arch/ppc64/xmon/ppc.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/xmon/ppc.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,240 @@ +/* ppc.h -- Header file for PowerPC opcode table + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +1, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +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 file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef PPC_H +#define PPC_H + +/* The opcode table is an array of struct powerpc_opcode. */ + +struct powerpc_opcode +{ + /* The opcode name. */ + const char *name; + + /* The opcode itself. Those bits which will be filled in with + operands are zeroes. */ + unsigned long opcode; + + /* The opcode mask. This is used by the disassembler. This is a + mask containing ones indicating those bits which must match the + opcode field, and zeroes indicating those bits which need not + match (and are presumably filled in by operands). */ + unsigned long mask; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The defined values + are listed below. */ + unsigned long flags; + + /* An array of operand codes. Each code is an index into the + operand table. They appear in the order which the operands must + appear in assembly code, and are terminated by a zero. */ + unsigned char operands[8]; +}; + +/* The table itself is sorted by major opcode number, and is otherwise + in the order in which the disassembler should consider + instructions. */ +extern const struct powerpc_opcode powerpc_opcodes[]; +extern const int powerpc_num_opcodes; + +/* Values defined for the flags field of a struct powerpc_opcode. */ + +/* Opcode is defined for the PowerPC architecture. */ +#define PPC_OPCODE_PPC (01) + +/* Opcode is defined for the POWER (RS/6000) architecture. */ +#define PPC_OPCODE_POWER (02) + +/* Opcode is defined for the POWER2 (Rios 2) architecture. */ +#define PPC_OPCODE_POWER2 (04) + +/* Opcode is only defined on 32 bit architectures. */ +#define PPC_OPCODE_32 (010) + +/* Opcode is only defined on 64 bit architectures. */ +#define PPC_OPCODE_64 (020) + +/* Opcode is supported by the Motorola PowerPC 601 processor. The 601 + is assumed to support all PowerPC (PPC_OPCODE_PPC) instructions, + but it also supports many additional POWER instructions. */ +#define PPC_OPCODE_601 (040) + +/* A macro to extract the major opcode from an instruction. */ +#define PPC_OP(i) (((i) >> 26) & 0x3f) + +/* The operands table is an array of struct powerpc_operand. */ + +struct powerpc_operand +{ + /* The number of bits in the operand. */ + int bits; + + /* How far the operand is left shifted in the instruction. */ + int shift; + + /* Insertion function. This is used by the assembler. To insert an + operand value into an instruction, check this field. + + If it is NULL, execute + i |= (op & ((1 << o->bits) - 1)) << o->shift; + (i is the instruction which we are filling in, o is a pointer to + this structure, and op is the opcode value; this assumes twos + complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction and the operand value. It will return the new value + of the instruction. If the ERRMSG argument is not NULL, then if + the operand value is illegal, *ERRMSG will be set to a warning + string (the operand will be inserted in any case). If the + operand value is legal, *ERRMSG will be unchanged (most operands + can accept any value). */ + unsigned long (*insert) PARAMS ((unsigned long instruction, long op, + const char **errmsg)); + + /* Extraction function. This is used by the disassembler. To + extract this operand type from an instruction, check this field. + + If it is NULL, compute + op = ((i) >> o->shift) & ((1 << o->bits) - 1); + if ((o->flags & PPC_OPERAND_SIGNED) != 0 + && (op & (1 << (o->bits - 1))) != 0) + op -= 1 << o->bits; + (i is the instruction, o is a pointer to this structure, and op + is the result; this assumes twos complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction value. It will return the value of the operand. If + the INVALID argument is not NULL, *INVALID will be set to + non-zero if this operand type can not actually be extracted from + this operand (i.e., the instruction does not match). If the + operand is valid, *INVALID will not be changed. */ + long (*extract) PARAMS ((unsigned long instruction, int *invalid)); + + /* One bit syntax flags. */ + unsigned long flags; +}; + +/* Elements in the table are retrieved by indexing with values from + the operands field of the powerpc_opcodes table. */ + +extern const struct powerpc_operand powerpc_operands[]; + +/* Values defined for the flags field of a struct powerpc_operand. */ + +/* This operand takes signed values. */ +#define PPC_OPERAND_SIGNED (01) + +/* This operand takes signed values, but also accepts a full positive + range of values when running in 32 bit mode. That is, if bits is + 16, it takes any value from -0x8000 to 0xffff. In 64 bit mode, + this flag is ignored. */ +#define PPC_OPERAND_SIGNOPT (02) + +/* This operand does not actually exist in the assembler input. This + is used to support extended mnemonics such as mr, for which two + operands fields are identical. The assembler should call the + insert function with any op value. The disassembler should call + the extract function, ignore the return value, and check the value + placed in the valid argument. */ +#define PPC_OPERAND_FAKE (04) + +/* The next operand should be wrapped in parentheses rather than + separated from this one by a comma. This is used for the load and + store instructions which want their operands to look like + reg,displacement(reg) + */ +#define PPC_OPERAND_PARENS (010) + +/* This operand may use the symbolic names for the CR fields, which + are + lt 0 gt 1 eq 2 so 3 un 3 + cr0 0 cr1 1 cr2 2 cr3 3 + cr4 4 cr5 5 cr6 6 cr7 7 + These may be combined arithmetically, as in cr2*4+gt. These are + only supported on the PowerPC, not the POWER. */ +#define PPC_OPERAND_CR (020) + +/* This operand names a register. The disassembler uses this to print + register names with a leading 'r'. */ +#define PPC_OPERAND_GPR (040) + +/* This operand names a floating point register. The disassembler + prints these with a leading 'f'. */ +#define PPC_OPERAND_FPR (0100) + +/* This operand is a relative branch displacement. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_RELATIVE (0200) + +/* This operand is an absolute branch address. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_ABSOLUTE (0400) + +/* This operand is optional, and is zero if omitted. This is used for + the optional BF and L fields in the comparison instructions. The + assembler must count the number of operands remaining on the line, + and the number of operands remaining for the opcode, and decide + whether this operand is present or not. The disassembler should + print this operand out only if it is not zero. */ +#define PPC_OPERAND_OPTIONAL (01000) + +/* This flag is only used with PPC_OPERAND_OPTIONAL. If this operand + is omitted, then for the next operand use this operand value plus + 1, ignoring the next operand field for the opcode. This wretched + hack is needed because the Power rotate instructions can take + either 4 or 5 operands. The disassembler should print this operand + out regardless of the PPC_OPERAND_OPTIONAL field. */ +#define PPC_OPERAND_NEXT (02000) + +/* This operand should be regarded as a negative number for the + purposes of overflow checking (i.e., the normal most negative + number is disallowed and one more than the normal most positive + number is allowed). This flag will only be set for a signed + operand. */ +#define PPC_OPERAND_NEGATIVE (04000) + +/* The POWER and PowerPC assemblers use a few macros. We keep them + with the operands table for simplicity. The macro table is an + array of struct powerpc_macro. */ + +struct powerpc_macro +{ + /* The macro name. */ + const char *name; + + /* The number of operands the macro takes. */ + unsigned int operands; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The values are the + same as those for the struct powerpc_opcode flags field. */ + unsigned long flags; + + /* A format string to turn the macro into a normal instruction. + Each %N in the string is replaced with operand number N (zero + based). */ + const char *format; +}; + +extern const struct powerpc_macro powerpc_macros[]; +extern const int powerpc_num_macros; + +#endif /* PPC_H */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/xmon/privinst.h linux.ac/arch/ppc64/xmon/privinst.h --- linux.vanilla/arch/ppc64/xmon/privinst.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/xmon/privinst.h Wed Oct 10 01:47:34 2001 @@ -0,0 +1,102 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include + +#define GETREG(reg) \ + static inline unsigned long get_ ## reg (void) \ + { unsigned long ret; asm volatile ("mf" #reg " %0" : "=r" (ret) :); return ret; } + +#define SETREG(reg) \ + static inline void set_ ## reg (unsigned long val) \ + { asm volatile ("mt" #reg " %0" : : "r" (val)); } + +GETREG(msr) +SETREG(msrd) +GETREG(cr) + +#define GSETSPR(n, name) \ + static inline long get_ ## name (void) \ + { long ret; asm volatile ("mfspr %0," #n : "=r" (ret) : ); return ret; } \ + static inline void set_ ## name (long val) \ + { asm volatile ("mtspr " #n ",%0" : : "r" (val)); } + +GSETSPR(0, mq) +GSETSPR(1, xer) +GSETSPR(4, rtcu) +GSETSPR(5, rtcl) +GSETSPR(8, lr) +GSETSPR(9, ctr) +GSETSPR(18, dsisr) +GSETSPR(19, dar) +GSETSPR(22, dec) +GSETSPR(25, sdr1) +GSETSPR(26, srr0) +GSETSPR(27, srr1) +GSETSPR(272, sprg0) +GSETSPR(273, sprg1) +GSETSPR(274, sprg2) +GSETSPR(275, sprg3) +GSETSPR(282, ear) +GSETSPR(287, pvr) +#ifndef CONFIG_8xx +GSETSPR(528, bat0u) +GSETSPR(529, bat0l) +GSETSPR(530, bat1u) +GSETSPR(531, bat1l) +GSETSPR(532, bat2u) +GSETSPR(533, bat2l) +GSETSPR(534, bat3u) +GSETSPR(535, bat3l) +GSETSPR(1008, hid0) +GSETSPR(1009, hid1) +GSETSPR(1010, iabr) +GSETSPR(1013, dabr) +GSETSPR(1023, pir) +#else +GSETSPR(144, cmpa) +GSETSPR(145, cmpb) +GSETSPR(146, cmpc) +GSETSPR(147, cmpd) +GSETSPR(158, ictrl) +#endif + +static inline int get_sr(int n) +{ + int ret; + +#if 0 +// DRENG does not assemble + asm (" mfsrin %0,%1" : "=r" (ret) : "r" (n << 28)); +#endif + return ret; +} + +static inline void set_sr(int n, int val) +{ +#if 0 +// DRENG does not assemble + asm ("mtsrin %0,%1" : : "r" (val), "r" (n << 28)); +#endif +} + +static inline void store_inst(void *p) +{ + asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p)); +} + +static inline void cflush(void *p) +{ + asm volatile ("dcbf 0,%0; icbi 0,%0" : : "r" (p)); +} + +static inline void cinval(void *p) +{ + asm volatile ("dcbi 0,%0; icbi 0,%0" : : "r" (p)); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/xmon/setjmp.c linux.ac/arch/ppc64/xmon/setjmp.c --- linux.vanilla/arch/ppc64/xmon/setjmp.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/xmon/setjmp.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,77 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * NB this file must be compiled with -O2. + */ + +int +xmon_setjmp(long *buf) /* NOTE: assert(sizeof(buf) > 184) */ +{ + /* XXX should save fp regs as well */ + asm volatile ( + "mflr 0; std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + mfcr 0; std 0,24(%0)\n\ + std 13,32(%0)\n\ + std 14,40(%0)\n\ + std 15,48(%0)\n\ + std 16,56(%0)\n\ + std 17,64(%0)\n\ + std 18,72(%0)\n\ + std 19,80(%0)\n\ + std 20,88(%0)\n\ + std 21,96(%0)\n\ + std 22,104(%0)\n\ + std 23,112(%0)\n\ + std 24,120(%0)\n\ + std 25,128(%0)\n\ + std 26,136(%0)\n\ + std 27,144(%0)\n\ + std 28,152(%0)\n\ + std 29,160(%0)\n\ + std 30,168(%0)\n\ + std 31,176(%0)\n\ + " : : "r" (buf)); + return 0; +} + +void +xmon_longjmp(long *buf, int val) +{ + if (val == 0) + val = 1; + asm volatile ( + "ld 13,32(%0)\n\ + ld 14,40(%0)\n\ + ld 15,48(%0)\n\ + ld 16,56(%0)\n\ + ld 17,64(%0)\n\ + ld 18,72(%0)\n\ + ld 19,80(%0)\n\ + ld 20,88(%0)\n\ + ld 21,96(%0)\n\ + ld 22,104(%0)\n\ + ld 23,112(%0)\n\ + ld 24,120(%0)\n\ + ld 25,128(%0)\n\ + ld 26,136(%0)\n\ + ld 27,144(%0)\n\ + ld 28,152(%0)\n\ + ld 29,160(%0)\n\ + ld 30,168(%0)\n\ + ld 31,176(%0)\n\ + ld 0,24(%0)\n\ + mtcrf 0x38,0\n\ + ld 0,0(%0)\n\ + ld 1,8(%0)\n\ + ld 2,16(%0)\n\ + mtlr 0\n\ + mr 3,%1\n\ + " : : "r" (buf), "r" (val)); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/xmon/start.c linux.ac/arch/ppc64/xmon/start.c --- linux.vanilla/arch/ppc64/xmon/start.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/xmon/start.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,320 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static volatile unsigned char *sccc, *sccd; +unsigned long TXRDY, RXRDY; +extern void xmon_printf(const char *fmt, ...); +static int xmon_expect(const char *str, unsigned int timeout); + +static int console = 0; +static int via_modem = 0; +/* static int xmon_use_sccb = 0; --Unused */ + +#define TB_SPEED 25000000 + +extern void *comport1; +static inline unsigned int readtb(void) +{ + unsigned int ret; + + asm volatile("mftb %0" : "=r" (ret) :); + return ret; +} + +void buf_access(void) +{ + if ( _machine == _MACH_chrp ) + sccd[3] &= ~0x80; /* reset DLAB */ +} + +extern int adb_init(void); + +static void sysrq_handle_xmon(int key, struct pt_regs *pt_regs, struct kbd_struct *kbd, struct tty_struct *tty) +{ + xmon(pt_regs); +} +static struct sysrq_key_op sysrq_xmon_op = +{ + handler: sysrq_handle_xmon, + help_msg: "xmon", + action_msg: "Entering xmon\n", +}; + +void +xmon_map_scc(void) +{ + /* should already be mapped by the kernel boot */ + sccd = (volatile unsigned char *) (((unsigned long)comport1)); + sccc = (volatile unsigned char *) (((unsigned long)comport1)+5); + TXRDY = 0x20; + RXRDY = 1; + /* This maybe isn't the best place to register sysrq 'x' */ + __sysrq_put_key_op('x', &sysrq_xmon_op); +} + +static int scc_initialized = 0; + +void xmon_init_scc(void); +extern void pmu_poll(void); + +int +xmon_write(void *handle, void *ptr, int nb) +{ + char *p = ptr; + int i, c, ct; + + if (!scc_initialized) + xmon_init_scc(); + ct = 0; + for (i = 0; i < nb; ++i) { + while ((*sccc & TXRDY) == 0) { + } + c = p[i]; + if (c == '\n' && !ct) { + c = '\r'; + ct = 1; + --i; + } else { + if (console) + printk("%c", c); + ct = 0; + } + buf_access(); + *sccd = c; + } + return i; +} + +int xmon_wants_key; +int xmon_adb_keycode; + +int +xmon_read(void *handle, void *ptr, int nb) +{ + char *p = ptr; + int i, c; + + if (!scc_initialized) + xmon_init_scc(); + for (i = 0; i < nb; ++i) { + do { + while ((*sccc & RXRDY) == 0) + ; + buf_access(); + c = *sccd; + } while (c == 0x11 || c == 0x13); + *p++ = c; + } + return i; +} + +int +xmon_read_poll(void) +{ + if ((*sccc & RXRDY) == 0) { +#ifdef CONFIG_ADB_PMU + if (sys_ctrler == SYS_CTRLER_PMU) + pmu_poll(); +#endif /* CONFIG_ADB_PMU */ + return -1; + } + buf_access(); + return *sccd; +} + +void +xmon_init_scc() +{ + if ( _machine == _MACH_chrp ) + { + sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */ + sccd[0] = 12; eieio(); /* DLL = 9600 baud */ + sccd[1] = 0; eieio(); + sccd[2] = 0; eieio(); /* FCR = 0 */ + sccd[3] = 3; eieio(); /* LCR = 8N1 */ + sccd[1] = 0; eieio(); /* IER = 0 */ + } + + scc_initialized = 1; + if (via_modem) { + for (;;) { + xmon_write(0, "ATE1V1\r", 7); + if (xmon_expect("OK", 5)) { + xmon_write(0, "ATA\r", 4); + if (xmon_expect("CONNECT", 40)) + break; + } + xmon_write(0, "+++", 3); + xmon_expect("OK", 3); + } + } +} + +void *xmon_stdin; +void *xmon_stdout; +void *xmon_stderr; + +void +xmon_init(void) +{ +} + +int +xmon_putc(int c, void *f) +{ + char ch = c; + + if (c == '\n') + xmon_putc('\r', f); + return xmon_write(f, &ch, 1) == 1? c: -1; +} + +int +xmon_putchar(int c) +{ + return xmon_putc(c, xmon_stdout); +} + +int +xmon_fputs(char *str, void *f) +{ + int n = strlen(str); + + return xmon_write(f, str, n) == n? 0: -1; +} + +int +xmon_readchar(void) +{ + char ch; + + for (;;) { + switch (xmon_read(xmon_stdin, &ch, 1)) { + case 1: + return ch; + case -1: + xmon_printf("read(stdin) returned -1\r\n", 0, 0); + return -1; + } + } +} + +static char line[256]; +static char *lineptr; +static int lineleft; + +int xmon_expect(const char *str, unsigned int timeout) +{ + int c; + unsigned int t0; + + timeout *= TB_SPEED; + t0 = readtb(); + do { + lineptr = line; + for (;;) { + c = xmon_read_poll(); + if (c == -1) { + if (readtb() - t0 > timeout) + return 0; + continue; + } + if (c == '\n') + break; + if (c != '\r' && lineptr < &line[sizeof(line) - 1]) + *lineptr++ = c; + } + *lineptr = 0; + } while (strstr(line, str) == NULL); + return 1; +} + +int +xmon_getchar(void) +{ + int c; + + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = xmon_readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + xmon_putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + xmon_putchar('\a'); + else { + xmon_putchar(c); + *lineptr++ = c; + } + } + } + lineleft = lineptr - line; + lineptr = line; + } + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; +} + +char * +xmon_fgets(char *str, int nb, void *f) +{ + char *p; + int c; + + for (p = str; p < str + nb - 1; ) { + c = xmon_getchar(); + if (c == -1) { + if (p == str) + return 0; + break; + } + *p++ = c; + if (c == '\n') + break; + } + *p = 0; + return str; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/xmon/subr_prf.c linux.ac/arch/ppc64/xmon/subr_prf.c --- linux.vanilla/arch/ppc64/xmon/subr_prf.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/xmon/subr_prf.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,55 @@ +/* + * Written by Cort Dougan to replace the version originally used + * by Paul Mackerras, which came from NetBSD and thus had copyright + * conflicts with Linux. + * + * This file makes liberal use of the standard linux utility + * routines to reduce the size of the binary. We assume we can + * trust some parts of Linux inside the debugger. + * -- Cort (cort@cs.nmt.edu) + * + * Copyright (C) 1999 Cort Dougan. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include "nonstdio.h" + +extern int xmon_write(void *, void *, int); + +void +xmon_vfprintf(void *f, const char *fmt, va_list ap) +{ + static char xmon_buf[2048]; + int n; + + n = vsprintf(xmon_buf, fmt, ap); + xmon_write(f, xmon_buf, n); +} + +void +xmon_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xmon_vfprintf(stdout, fmt, ap); + va_end(ap); +} + +void +xmon_fprintf(void *f, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xmon_vfprintf(f, fmt, ap); + va_end(ap); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc64/xmon/xmon.c linux.ac/arch/ppc64/xmon/xmon.c --- linux.vanilla/arch/ppc64/xmon/xmon.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/ppc64/xmon/xmon.c Wed Oct 10 01:47:34 2001 @@ -0,0 +1,3022 @@ +/* + * Routines providing a simple monitor for use on the PowerMac. + * + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nonstdio.h" +#include "privinst.h" +#include + +#include + +#ifndef _PACA_H +#include +#endif + +#define scanhex xmon_scanhex +#define skipbl xmon_skipbl + +static unsigned long adrs; +static int size = 1; +static unsigned long ndump = 64; +static unsigned long nidump = 16; +static unsigned long ncsum = 4096; +static int termch; + +static unsigned long xmon_running = 0; + +static u_int bus_error_jmp[100]; +#define setjmp xmon_setjmp +#define longjmp xmon_longjmp + +#define memlist_entry list_entry +#define memlist_next(x) ((x)->next) +#define memlist_prev(x) ((x)->prev) + + +/* Max number of stack frames we are willing to produce on a backtrace. */ +#define MAXFRAMECOUNT 50 + +/* Breakpoint stuff */ +struct bpt { + unsigned long address; + unsigned instr; + unsigned long count; + unsigned char enabled; + char funcname[64]; /* function name for humans */ +}; + +#define NBPTS 16 +static struct bpt bpts[NBPTS]; +static struct bpt dabr; +static struct bpt iabr; +static unsigned bpinstr = 0x7fe00008; /* trap */ + +/* Prototypes */ +extern void (*debugger_fault_handler)(struct pt_regs *); +static int cmds(struct pt_regs *); +static int mread(unsigned long, void *, int); +static int mwrite(unsigned long, void *, int); +static void handle_fault(struct pt_regs *); +static void byterev(unsigned char *, int); +static void memex(void); +static int bsesc(void); +static void dump(void); +static void prdump(unsigned long, long); +#ifdef __MWERKS__ +static void prndump(unsigned, int); +static int nvreadb(unsigned); +#endif +static int ppc_inst_dump(unsigned long, long); +void print_address(unsigned long); +static int getsp(void); +static void dump_hash_table(void); +static void backtrace(struct pt_regs *); +static void excprint(struct pt_regs *); +static void prregs(struct pt_regs *); +static void memops(int); +static void memlocate(void); +static void memzcan(void); +static void memdiffs(unsigned char *, unsigned char *, unsigned, unsigned); +int skipbl(void); +int scanhex(unsigned long *valp); +static void scannl(void); +static int hexdigit(int); +void getstring(char *, int); +static void flush_input(void); +static int inchar(void); +static void take_input(char *); +/* static void openforth(void); */ +static unsigned long read_spr(int); +static void write_spr(int, unsigned long); +static void super_regs(void); +static void print_sysmap(void); +static void remove_bpts(void); +static void insert_bpts(void); +static struct bpt *at_breakpoint(unsigned long pc); +static void bpt_cmds(void); +static void cacheflush(void); +/* static char *pretty_print_addr(unsigned long addr); -Unused -aglitke */ +static char *lookup_name(unsigned long addr); +static void csum(void); +static void mem_translate(void); +static void mem_check(void); +static void mem_find_real(void); +static void mem_find_vsid(void); +static void mem_check_full_group(void); +static void mem_check_zones(void); +static void mem_check_pagetable_vsids (void); + +static void mem_map_check_slab(void); +static void mem_map_lock_pages(void); +static void mem_map_check_hash(void); +static void mem_check_dup_rpn (void); +static void show_task(struct task_struct * p); +static void show_state(void); +static void debug_trace(void); + +extern int print_insn_big_powerpc(FILE *, unsigned long, unsigned long); +extern void printf(const char *fmt, ...); +extern int putchar(int ch); +extern int setjmp(u_int *); +extern void longjmp(u_int *, int); +extern unsigned long _ASR; +extern struct Naca *naca; + +pte_t *find_linux_pte(pgd_t *pgdir, unsigned long va); /* from htab.c */ + +#define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3]) + +static char *help_string = "\ +Commands:\n\ + b show breakpoints\n\ + bd set data breakpoint\n\ + bi set instruction breakpoint\n\ + bc clear breakpoint\n\ + d dump bytes\n\ + dd dump double values\n\ + df dump float values\n\ + di dump instructions\n\ + e print exception information\n\ + f flush cache\n\ + h dump hash table\n\ + m examine/change memory\n\ + mm move a block of memory\n\ + ms set a block of memory\n\ + md compare two blocks of memory\n\ + ml locate a block of memory\n\ + mz zero a block of memory\n\ + mx translation information for an effective address\n\ + mi show information about memory allocation\n\ + M print System.map\n\ + p show the task list\n\ + r print registers\n\ + s single step\n\ + S print special registers\n\ + t print backtrace\n\ + T Enable/Disable PPCDBG flags\n\ + x exit monitor\n\ +"; + + +static int xmon_trace; +#define SSTEP 1 /* stepping because of 's' command */ +#define BRSTEP 2 /* stepping over breakpoint */ + + +/* (Ref: 64-bit PowerPC ELF ABI Spplement; Ian Lance Taylor, Zembu Labs). + A PPC stack frame looks like this: + + High Address + Back Chain + FP reg save area + GP reg save area + Local var space + Parameter save area (SP+48) + TOC save area (SP+40) + link editor doubleword (SP+32) + compiler doubleword (SP+24) + LR save (SP+16) + CR save (SP+8) + Back Chain (SP+0) + + Note that the LR (ret addr) may not be saved in the current frame if + no functions have been called from the current function. + */ + + +/* + A traceback table typically follows each function. + The find_tb_table() func will fill in this struct. Note that the struct + is not an exact match with the encoded table defined by the ABI. It is + defined here more for programming convenience. + */ +struct tbtable { + unsigned long flags; /* flags: */ +#define TBTAB_FLAGSGLOBALLINK (1L<<47) +#define TBTAB_FLAGSISEPROL (1L<<46) +#define TBTAB_FLAGSHASTBOFF (1L<<45) +#define TBTAB_FLAGSINTPROC (1L<<44) +#define TBTAB_FLAGSHASCTL (1L<<43) +#define TBTAB_FLAGSTOCLESS (1L<<42) +#define TBTAB_FLAGSFPPRESENT (1L<<41) +#define TBTAB_FLAGSNAMEPRESENT (1L<<38) +#define TBTAB_FLAGSUSESALLOCA (1L<<37) +#define TBTAB_FLAGSSAVESCR (1L<<33) +#define TBTAB_FLAGSSAVESLR (1L<<32) +#define TBTAB_FLAGSSTORESBC (1L<<31) +#define TBTAB_FLAGSFIXUP (1L<<30) +#define TBTAB_FLAGSPARMSONSTK (1L<<0) + unsigned char fp_saved; /* num fp regs saved f(32-n)..f31 */ + unsigned char gpr_saved; /* num gpr's saved */ + unsigned char fixedparms; /* num fixed point parms */ + unsigned char floatparms; /* num float parms */ + unsigned char parminfo[32]; /* types of args. null terminated */ +#define TBTAB_PARMFIXED 1 +#define TBTAB_PARMSFLOAT 2 +#define TBTAB_PARMDFLOAT 3 + unsigned int tb_offset; /* offset from start of func */ + unsigned long funcstart; /* addr of start of function */ + char name[64]; /* name of function (null terminated)*/ +}; +static int find_tb_table(unsigned long codeaddr, struct tbtable *tab); + +void +xmon(struct pt_regs *excp) +{ + struct pt_regs regs; + int cmd; + unsigned long msr; + + if (xmon_running) + /* If we're already in xmon, just return */ + return; + + xmon_running = 1; + + if (excp == NULL) { + /* Ok, grab regs as they are now. + This won't do a particularily good job because the + prologue has already been executed. + ToDo: We could reach back into the callers save + area to do a better job of representing the + caller's state. + */ + asm volatile ("std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + std 3,24(%0)\n\ + std 4,32(%0)\n\ + std 5,40(%0)\n\ + std 6,48(%0)\n\ + std 7,56(%0)\n\ + std 8,64(%0)\n\ + std 9,72(%0)\n\ + std 10,80(%0)\n\ + std 11,88(%0)\n\ + std 12,96(%0)\n\ + std 13,104(%0)\n\ + std 14,112(%0)\n\ + std 15,120(%0)\n\ + std 16,128(%0)\n\ + std 17,136(%0)\n\ + std 18,144(%0)\n\ + std 19,152(%0)\n\ + std 20,160(%0)\n\ + std 21,168(%0)\n\ + std 22,176(%0)\n\ + std 23,184(%0)\n\ + std 24,192(%0)\n\ + std 25,200(%0)\n\ + std 26,208(%0)\n\ + std 27,216(%0)\n\ + std 28,224(%0)\n\ + std 29,232(%0)\n\ + std 30,240(%0)\n\ + std 31,248(%0)" : : "b" (®s)); + printf("xmon called\n"); + /* Fetch the link reg for this stack frame. + NOTE: the prev printf fills in the lr. */ + regs.nip = regs.link = ((unsigned long *)(regs.gpr[1]))[2]; + regs.msr = get_msr(); + regs.ctr = get_ctr(); + regs.xer = get_xer(); + regs.ccr = get_cr(); + regs.trap = 0; + excp = ®s; + } + msr = get_msr(); + set_msrd(msr & ~MSR_EE); /* disable interrupts */ + remove_bpts(); + excprint(excp); + cmd = cmds(excp); + if (cmd == 's') { + xmon_trace = SSTEP; + excp->msr |= 0x400; + } else if (at_breakpoint(excp->nip)) { + xmon_trace = BRSTEP; + excp->msr |= 0x400; + } else { + xmon_trace = 0; + insert_bpts(); + } + set_msrd(msr); /* restore interrupt enable */ + xmon_running = 0; +} + +/* Code can call this to get a backtrace and continue. */ +void +xmon_backtrace(const char *fmt, ...) +{ + va_list ap; + struct pt_regs regs; + + + /* Ok, grab regs as they are now. + This won't do a particularily good job because the + prologue has already been executed. + ToDo: We could reach back into the callers save + area to do a better job of representing the + caller's state. + */ + asm volatile ("std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + std 3,24(%0)\n\ + std 4,32(%0)\n\ + std 5,40(%0)\n\ + std 6,48(%0)\n\ + std 7,56(%0)\n\ + std 8,64(%0)\n\ + std 9,72(%0)\n\ + std 10,80(%0)\n\ + std 11,88(%0)\n\ + std 12,96(%0)\n\ + std 13,104(%0)\n\ + std 14,112(%0)\n\ + std 15,120(%0)\n\ + std 16,128(%0)\n\ + std 17,136(%0)\n\ + std 18,144(%0)\n\ + std 19,152(%0)\n\ + std 20,160(%0)\n\ + std 21,168(%0)\n\ + std 22,176(%0)\n\ + std 23,184(%0)\n\ + std 24,192(%0)\n\ + std 25,200(%0)\n\ + std 26,208(%0)\n\ + std 27,216(%0)\n\ + std 28,224(%0)\n\ + std 29,232(%0)\n\ + std 30,240(%0)\n\ + std 31,248(%0)" : : "b" (®s)); + /* Fetch the link reg for this stack frame. + NOTE: the prev printf fills in the lr. */ + regs.nip = regs.link = ((unsigned long *)(regs.gpr[1]))[2]; + regs.msr = get_msr(); + regs.ctr = get_ctr(); + regs.xer = get_xer(); + regs.ccr = get_cr(); + regs.trap = 0; + + va_start(ap, fmt); + xmon_vfprintf(stdout, fmt, ap); + xmon_putc('\n', stdout); + va_end(ap); + take_input("\n"); + backtrace(®s); +} + +/* Call this to poll for ^C during busy operations. + * Returns true if the user has hit ^C. + */ +int +xmon_interrupted(void) +{ + int ret = xmon_read_poll(); + if (ret == 3) { + printf("\n^C interrupted.\n"); + return 1; + } + return 0; +} + + +void +xmon_irq(int irq, void *d, struct pt_regs *regs) +{ + printf("Keyboard interrupt\n"); + xmon(regs); +} + +int +xmon_bpt(struct pt_regs *regs) +{ + struct bpt *bp; + + bp = at_breakpoint(regs->nip); + if (!bp) + return 0; + if (bp->count) { + --bp->count; + remove_bpts(); + excprint(regs); + xmon_trace = BRSTEP; + regs->msr |= 0x400; + } else { + printf("Stopped at breakpoint %x (%lx %s)\n", (bp - bpts)+1, bp->address, bp->funcname); + xmon(regs); + } + return 1; +} + +int +xmon_sstep(struct pt_regs *regs) +{ + if (!xmon_trace) + return 0; + if (xmon_trace == BRSTEP) { + xmon_trace = 0; + insert_bpts(); + } else { + xmon(regs); + } + return 1; +} + +int +xmon_dabr_match(struct pt_regs *regs) +{ + if (dabr.enabled && dabr.count) { + --dabr.count; + remove_bpts(); + excprint(regs); + xmon_trace = BRSTEP; + regs->msr |= 0x400; + } else { + dabr.instr = regs->nip; + xmon(regs); + } + return 1; +} + +int +xmon_iabr_match(struct pt_regs *regs) +{ + if (iabr.enabled && iabr.count) { + --iabr.count; + remove_bpts(); + excprint(regs); + xmon_trace = BRSTEP; + regs->msr |= 0x400; + } else { + xmon(regs); + } + return 1; +} + +static struct bpt * +at_breakpoint(unsigned long pc) +{ + int i; + struct bpt *bp; + + if (dabr.enabled && pc == dabr.instr) + return &dabr; + if (iabr.enabled && pc == iabr.address) + return &iabr; + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) + if (bp->enabled && pc == bp->address) + return bp; + return 0; +} + +static void +insert_bpts() +{ + int i; + struct bpt *bp; + + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) { + if (!bp->enabled) + continue; + if (mread(bp->address, &bp->instr, 4) != 4 + || mwrite(bp->address, &bpinstr, 4) != 4) { + printf("Couldn't insert breakpoint at %x, disabling\n", + bp->address); + bp->enabled = 0; + } else { + store_inst((void *)bp->address); + } + } + + if (!__is_processor(PV_POWER4)) { + if (dabr.enabled) + set_dabr(dabr.address); + if (iabr.enabled) + set_iabr(iabr.address); + } +} + +static void +remove_bpts() +{ + int i; + struct bpt *bp; + unsigned instr; + + if (!__is_processor(PV_POWER4)) { + set_dabr(0); + set_iabr(0); + } + + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) { + if (!bp->enabled) + continue; + if (mread(bp->address, &instr, 4) == 4 + && instr == bpinstr + && mwrite(bp->address, &bp->instr, 4) != 4) + printf("Couldn't remove breakpoint at %x\n", + bp->address); + else + store_inst((void *)bp->address); + } +} + +static char *last_cmd; + +/* Command interpreting routine */ +static int +cmds(struct pt_regs *excp) +{ + int cmd; + + last_cmd = NULL; + for(;;) { + printf("mon> "); + fflush(stdout); + flush_input(); + termch = 0; + cmd = skipbl(); + if( cmd == '\n' ) { + if (last_cmd == NULL) + continue; + take_input(last_cmd); + last_cmd = NULL; + cmd = inchar(); + } + switch (cmd) { + case 'm': + cmd = inchar(); + switch (cmd) { + case 'm': + case 's': + case 'd': + memops(cmd); + break; + case 'l': + memlocate(); + break; + case 'z': + memzcan(); + break; + case 'x': + mem_translate(); + break; + case 'c': + mem_check(); + break; + case 'g': + mem_check_full_group(); + break; + case 'j': + mem_map_check_slab(); + break; + case 'h': + mem_map_check_hash(); + break; + case 'f': + mem_find_real(); + break; + case 'e': + mem_find_vsid(); + break; + case 'r': + mem_check_dup_rpn(); + break; + case 'i': + show_mem(); + break; + case 'k': + mem_check_zones(); + break; + case 'o': + mem_check_pagetable_vsids (); + break; + case 'q': + mem_map_lock_pages() ; + break; + + + default: + termch = cmd; + memex(); + } + break; + case 'd': + dump(); + break; + case 'r': + if (excp != NULL) + prregs(excp); /* print regs */ + break; + case 'e': + if (excp == NULL) + printf("No exception information\n"); + else + excprint(excp); + break; + case 'M': + print_sysmap(); + case 'S': + super_regs(); + break; + case 't': + backtrace(excp); + break; + case 'f': + cacheflush(); + break; + case 'h': + dump_hash_table(); + break; + case 's': + case 'x': + case EOF: + return cmd; + case '?': + printf(help_string); + break; + case 'p': + show_state(); + break; + case 'b': + bpt_cmds(); + break; + case 'c': + csum(); + break; + case 'T': + debug_trace(); + break; + default: + printf("Unrecognized command: "); + do { + if( ' ' < cmd && cmd <= '~' ) + putchar(cmd); + else + printf("\\x%x", cmd); + cmd = inchar(); + } while (cmd != '\n'); + printf(" (type ? for help)\n"); + break; + } + } +} + +static unsigned short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +#define FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +static void +csum(void) +{ + unsigned int i; + unsigned short fcs; + unsigned char v; + + scanhex((void *)&adrs); + scanhex((void *)&ncsum); + fcs = 0xffff; + for (i = 0; i < ncsum; ++i) { + if (mread(adrs+i, &v, 1) == 0) { + printf("csum stopped at %x\n", adrs+i); + break; + } + fcs = FCS(fcs, v); + } + printf("%x\n", fcs); +} + +static char *breakpoint_help_string = + "Breakpoint command usage:\n" + "b show breakpoints\n" + "b [cnt] set breakpoint at given instr addr\n" + "bc clear all breakpoints\n" + "bc clear breakpoint number n or at addr\n" + "bi [cnt] set hardware instr breakpoint (broken?)\n" + "bd [cnt] set hardware data breakpoint (broken?)\n" + ""; + +static void +bpt_cmds(void) +{ + int cmd; + unsigned long a; + int mode, i; + struct bpt *bp; + struct tbtable tab; + + cmd = inchar(); + switch (cmd) { + case 'd': /* bd - hardware data breakpoint */ + if (__is_processor(PV_POWER4)) { + printf("Not implemented on POWER4\n"); + break; + } + mode = 7; + cmd = inchar(); + if (cmd == 'r') + mode = 5; + else if (cmd == 'w') + mode = 6; + else + termch = cmd; + dabr.address = 0; + dabr.count = 0; + dabr.enabled = scanhex(&dabr.address); + scanhex(&dabr.count); + if (dabr.enabled) + dabr.address = (dabr.address & ~7) | mode; + break; + case 'i': /* bi - hardware instr breakpoint */ + if (__is_processor(PV_POWER4)) { + printf("Not implemented on POWER4\n"); + break; + } + iabr.address = 0; + iabr.count = 0; + iabr.enabled = scanhex(&iabr.address); + if (iabr.enabled) + iabr.address |= 3; + scanhex(&iabr.count); + break; + case 'c': + if (!scanhex(&a)) { + /* clear all breakpoints */ + for (i = 0; i < NBPTS; ++i) + bpts[i].enabled = 0; + iabr.enabled = 0; + dabr.enabled = 0; + printf("All breakpoints cleared\n"); + } else { + if (a <= NBPTS && a >= 1) { + /* assume a breakpoint number */ + --a; /* bp nums are 1 based */ + bp = &bpts[a]; + } else { + /* assume a breakpoint address */ + bp = at_breakpoint(a); + } + if (bp == 0) { + printf("No breakpoint at %x\n", a); + } else { + printf("Cleared breakpoint %x (%lx %s)\n", (bp - bpts)+1, bp->address, bp->funcname); + bp->enabled = 0; + } + } + break; + case '?': + printf(breakpoint_help_string); + break; + default: + termch = cmd; + cmd = skipbl(); + if (cmd == '?') { + printf(breakpoint_help_string); + break; + } + termch = cmd; + if (!scanhex(&a)) { + /* print all breakpoints */ + int bpnum; + + printf(" type address count\n"); + if (dabr.enabled) { + printf(" data %.16lx %8x [", dabr.address & ~7, + dabr.count); + if (dabr.address & 1) + printf("r"); + if (dabr.address & 2) + printf("w"); + printf("]\n"); + } + if (iabr.enabled) + printf(" inst %.16lx %8x\n", iabr.address & ~3, + iabr.count); + for (bp = bpts, bpnum = 1; bp < &bpts[NBPTS]; ++bp, ++bpnum) + if (bp->enabled) + printf("%2x trap %.16lx %8x %s\n", bpnum, bp->address, bp->count, bp->funcname); + break; + } + bp = at_breakpoint(a); + if (bp == 0) { + for (bp = bpts; bp < &bpts[NBPTS]; ++bp) + if (!bp->enabled) + break; + if (bp >= &bpts[NBPTS]) { + printf("Sorry, no free breakpoints. Please clear one first.\n"); + break; + } + } + bp->enabled = 1; + bp->address = a; + bp->count = 0; + scanhex(&bp->count); + /* Find the function name just once. */ + bp->funcname[0] = '\0'; + if (find_tb_table(bp->address, &tab) && tab.name[0]) { + /* Got a nice name for it. */ + int delta = bp->address - tab.funcstart; + sprintf(bp->funcname, "%s+0x%x", tab.name, delta); + } + printf("Set breakpoint %2x trap %.16lx %8x %s\n", (bp-bpts)+1, bp->address, bp->count, bp->funcname); + break; + } +} + +/* Very cheap human name for vector lookup. */ +static +const char *getvecname(unsigned long vec) +{ + char *ret; + switch (vec) { + case 0x100: ret = "(System Reset)"; break; + case 0x200: ret = "(Machine Check)"; break; + case 0x300: ret = "(Data Access)"; break; + case 0x400: ret = "(Instruction Access)"; break; + case 0x500: ret = "(Hardware Interrupt)"; break; + case 0x600: ret = "(Alignment)"; break; + case 0x700: ret = "(Program Check)"; break; + case 0x800: ret = "(FPU Unavailable)"; break; + case 0x900: ret = "(Decrementer)"; break; + case 0xc00: ret = "(System Call)"; break; + case 0xd00: ret = "(Single Step)"; break; + case 0xf00: ret = "(Performance Monitor)"; break; + default: ret = ""; + } + return ret; +} + +static void +backtrace(struct pt_regs *excp) +{ + unsigned long sp; + unsigned long lr; + unsigned long stack[3]; + struct pt_regs regs; + struct tbtable tab; + int framecount; + char *funcname; + /* declare these as raw ptrs so we don't get func descriptors */ + extern void *ret_from_except, *ret_from_syscall_1; + + if (excp != NULL) { + lr = excp->link; + sp = excp->gpr[1]; + } else { + /* Use care not to call any function before this point + so the saved lr has a chance of being good. */ + asm volatile ("mflr %0" : "=r" (lr) :); + sp = getsp(); + } + scanhex(&sp); + scannl(); + for (framecount = 0; + sp != 0 && framecount < MAXFRAMECOUNT; + sp = stack[0], framecount++) { + if (mread(sp, stack, sizeof(stack)) != sizeof(stack)) + break; +#if 0 + if (lr != 0) { + stack[2] = lr; /* fake out the first saved lr. It may not be saved yet. */ + lr = 0; + } +#endif + printf("%.16lx %.16lx", sp, stack[2]); + /* TAI -- for now only the ones cast to unsigned long will match. + * Need to test the rest... + */ + if ((stack[2] == (unsigned long)ret_from_except && + (funcname = "ret_from_except")) + || (stack[2] == (unsigned long)ret_from_syscall_1 && + (funcname = "ret_from_syscall_1")) +#if 0 + || stack[2] == (unsigned) &ret_from_intercept + || stack[2] == (unsigned) &ret_from_syscall_2 + || stack[2] == (unsigned) &do_bottom_half_ret + || stack[2] == (unsigned) &do_signal_ret +#endif + ) { + printf(" %s\n", funcname); + if (mread(sp+112, ®s, sizeof(regs)) != sizeof(regs)) + break; + printf("exception: %lx %s regs %lx\n", regs.trap, getvecname(regs.trap), sp+112); + printf(" %.16lx", regs.nip); + if ((regs.nip & 0xffffffff00000000UL) && + find_tb_table(regs.nip, &tab)) { + int delta = regs.nip-tab.funcstart; + if (delta < 0) + printf(" "); + else + 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; + } else { + if (stack[2] && find_tb_table(stack[2], &tab)) { + int delta = stack[2]-tab.funcstart; + if (delta < 0) + printf(" "); + else + printf(" %s+0x%x", tab.name, delta); + } + printf("\n"); + } + if (stack[0] && stack[0] <= sp) { + if ((stack[0] & 0xffffffff00000000UL) == 0) + printf("\n", stack[0]); + else + printf("\n", stack[0]); + break; + } + } + if (framecount >= MAXFRAMECOUNT) + printf("\n"); +} + +int +getsp() +{ + int x; + + asm("mr %0,1" : "=r" (x) :); + return x; +} + + +void +excprint(struct pt_regs *fp) +{ + struct task_struct *c; + unsigned long p; + struct tbtable tab; + + printf("Vector: %lx %s at [%lx]\n", fp->trap, getvecname(fp->trap), fp); + printf(" pc: %lx", fp->nip); + if (find_tb_table(fp->nip, &tab) && tab.name[0]) { + /* Got a nice name for it */ + int delta = fp->nip - tab.funcstart; + printf(" (%s+0x%x)", tab.name, delta); + } + printf("\n"); + printf(" lr: %lx", fp->link); + if (find_tb_table(fp->link, &tab) && tab.name[0]) { + /* Got a nice name for it */ + int delta = fp->link - tab.funcstart; + printf(" (%s+0x%x)", tab.name, delta); + } + printf("\n"); + printf(" sp: %lx\n", fp->gpr[1]); + printf(" msr: %lx\n", fp->msr); + + if (fp->trap == 0x300 || fp->trap == 0x600) { + printf(" dar: %lx\n", fp->dar); + printf(" dsisr: %lx\n", fp->dsisr); + } + + /* XXX: need to copy current or we die. Why? */ + c = current; + p = current_paca; + printf(" current = 0x%lx\n", c); + printf(" paca = 0x%lx\n", p); + if (c) { + printf(" current = %lx, pid = %ld, comm = %s\n", + c, c->pid, c->comm); + } +} + +void +prregs(struct pt_regs *fp) +{ + int n; + unsigned long base; + + if (scanhex((void *)&base)) + fp = (struct pt_regs *) base; + for (n = 0; n < 16; ++n) + printf("R%.2ld = %.16lx R%.2ld = %.16lx\n", n, fp->gpr[n], + n+16, fp->gpr[n+16]); + printf("pc = %.16lx msr = %.16lx\nlr = %.16lx cr = %.16lx\n", + fp->nip, fp->msr, fp->link, fp->ccr); + printf("ctr = %.16lx xer = %.16lx trap = %8lx\n", + fp->ctr, fp->xer, fp->trap); +} + +void +cacheflush(void) +{ + int cmd; + unsigned long nflush; + + cmd = inchar(); + if (cmd != 'i') + termch = cmd; + scanhex((void *)&adrs); + if (termch != '\n') + termch = 0; + nflush = 1; + scanhex(&nflush); + nflush = (nflush + 31) / 32; + if (cmd != 'i') { + for (; nflush > 0; --nflush, adrs += 0x20) + cflush((void *) adrs); + } else { + for (; nflush > 0; --nflush, adrs += 0x20) + cinval((void *) adrs); + } +} + +unsigned long +read_spr(int n) +{ + unsigned int instrs[2]; + unsigned long (*code)(void); + + instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6); + instrs[1] = 0x4e800020; + store_inst(instrs); + store_inst(instrs+1); + code = (unsigned long (*)(void)) instrs; + return code(); +} + +void +write_spr(int n, unsigned long val) +{ + unsigned int instrs[2]; + unsigned long (*code)(unsigned long); + + instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6); + instrs[1] = 0x4e800020; + store_inst(instrs); + store_inst(instrs+1); + code = (unsigned long (*)(unsigned long)) instrs; + code(val); +} + +static unsigned long regno; +extern char exc_prolog; +extern char dec_exc; + +void +print_sysmap(void) +{ + extern char *sysmap; + if ( sysmap ) + printf("System.map: \n%s", sysmap); +} + +void +super_regs() +{ + int i, cmd; + unsigned long val; + struct Paca* ptrPaca = NULL; + struct ItLpPaca* ptrLpPaca = NULL; + struct ItLpRegSave* ptrLpRegSave = NULL; + + cmd = skipbl(); + if (cmd == '\n') { + unsigned long sp, toc; + asm("mr %0,1" : "=r" (sp) :); + asm("mr %0,2" : "=r" (toc) :); + + printf("msr = %.16lx sprg0= %.16lx\n", get_msr(), get_sprg0()); + printf("pvr = %.16lx sprg1= %.16lx\n", get_pvr(), get_sprg1()); + printf("dec = %.16lx sprg2= %.16lx\n", get_dec(), get_sprg2()); + printf("sp = %.16lx sprg3= %.16lx\n", sp, get_sprg3()); + printf("toc = %.16lx dar = %.16lx\n", toc, get_dar()); + printf("srr0 = %.16lx srr1 = %.16lx\n", get_srr0(), get_srr1()); + printf("asr = %.16lx\n", mfasr()); + for (i = 0; i < 8; ++i) + printf("sr%.2ld = %.16lx sr%.2ld = %.16lx\n", i, get_sr(i), i+8, get_sr(i+8)); + + // Dump out relevant Paca data areas. + printf("Paca: \n"); + ptrPaca = (struct Paca*)get_sprg3(); + printf(" Saved Gpr21=%.16lx Saved Gpr22 =%.16lx \n", ptrPaca->xR21, ptrPaca->xR22); + + printf(" Local Processor Control Area (LpPaca): \n"); + ptrLpPaca = ptrPaca->xLpPacaPtr; + printf(" Saved Srr0=%.16lx Saved Srr1=%.16lx \n", ptrLpPaca->xSavedSrr0, ptrLpPaca->xSavedSrr1); + printf(" Saved Gpr3=%.16lx Saved Gpr4=%.16lx \n", ptrLpPaca->xSavedGpr3, ptrLpPaca->xSavedGpr4); + printf(" Saved Gpr5=%.16lx \n", ptrLpPaca->xSavedGpr5); + + printf(" Local Processor Register Save Area (LpRegSave): \n"); + ptrLpRegSave = ptrPaca->xLpRegSavePtr; + printf(" Saved Sprg0=%.16lx Saved Sprg1=%.16lx \n", ptrLpRegSave->xSPRG0, ptrLpRegSave->xSPRG0); + printf(" Saved Sprg2=%.16lx Saved Sprg3=%.16lx \n", ptrLpRegSave->xSPRG2, ptrLpRegSave->xSPRG3); + printf(" Saved Msr =%.16lx Saved Nia =%.16lx \n", ptrLpRegSave->xMSR, ptrLpRegSave->xNIA); + + return; + } + + scanhex(®no); + switch (cmd) { + case 'w': + val = read_spr(regno); + scanhex(&val); + write_spr(regno, val); + /* fall through */ + case 'r': + printf("spr %lx = %lx\n", regno, read_spr(regno)); + break; + case 's': + val = get_sr(regno); + scanhex(&val); + set_sr(regno, val); + break; + case 'm': + val = get_msr(); + scanhex(&val); + set_msrd(val); + break; + } + scannl(); +} + +#if 0 +static void +openforth() +{ + int c; + char *p; + char cmd[1024]; + int args[5]; + extern int (*prom_entry)(int *); + + p = cmd; + c = skipbl(); + while (c != '\n') { + *p++ = c; + c = inchar(); + } + *p = 0; + args[0] = (int) "interpret"; + args[1] = 1; + args[2] = 1; + args[3] = (int) cmd; + (*prom_entry)(args); + printf("\n"); + if (args[4] != 0) + printf("error %x\n", args[4]); +} +#endif + +#ifndef CONFIG_PPC64BRIDGE +static void +dump_hash_table_seg(unsigned seg, unsigned start, unsigned end) +{ + extern void *Hash; + extern unsigned long Hash_size; + unsigned *htab = Hash; + unsigned hsize = Hash_size; + unsigned v, hmask, va, last_va; + int found, last_found, i; + unsigned *hg, w1, last_w2, last_va0; + + last_found = 0; + hmask = hsize / 64 - 1; + va = start; + start = (start >> 12) & 0xffff; + end = (end >> 12) & 0xffff; + for (v = start; v < end; ++v) { + found = 0; + hg = htab + (((v ^ seg) & hmask) * 16); + w1 = 0x80000000 | (seg << 7) | (v >> 10); + for (i = 0; i < 8; ++i, hg += 2) { + if (*hg == w1) { + found = 1; + break; + } + } + if (!found) { + w1 ^= 0x40; + hg = htab + ((~(v ^ seg) & hmask) * 16); + for (i = 0; i < 8; ++i, hg += 2) { + if (*hg == w1) { + found = 1; + break; + } + } + } + if (!(last_found && found && (hg[1] & ~0x180) == last_w2 + 4096)) { + if (last_found) { + if (last_va != last_va0) + printf(" ... %x", last_va); + printf("\n"); + } + if (found) { + printf("%x to %x", va, hg[1]); + last_va0 = va; + } + last_found = found; + } + if (found) { + last_w2 = hg[1] & ~0x180; + last_va = va; + } + va += 4096; + } + if (last_found) + printf(" ... %x\n", last_va); +} + +#else /* CONFIG_PPC64BRIDGE */ +static void +dump_hash_table_seg(unsigned seg, unsigned start, unsigned end) +{ + extern void *Hash; + extern unsigned long Hash_size; + unsigned *htab = Hash; + unsigned hsize = Hash_size; + unsigned v, hmask, va, last_va; + int found, last_found, i; + unsigned *hg, w1, last_w2, last_va0; + + last_found = 0; + hmask = hsize / 128 - 1; + va = start; + start = (start >> 12) & 0xffff; + end = (end >> 12) & 0xffff; + for (v = start; v < end; ++v) { + found = 0; + hg = htab + (((v ^ seg) & hmask) * 32); + w1 = 1 | (seg << 12) | ((v & 0xf800) >> 4); + for (i = 0; i < 8; ++i, hg += 4) { + if (hg[1] == w1) { + found = 1; + break; + } + } + if (!found) { + w1 ^= 2; + hg = htab + ((~(v ^ seg) & hmask) * 32); + for (i = 0; i < 8; ++i, hg += 4) { + if (hg[1] == w1) { + found = 1; + break; + } + } + } + if (!(last_found && found && (hg[3] & ~0x180) == last_w2 + 4096)) { + if (last_found) { + if (last_va != last_va0) + printf(" ... %x", last_va); + printf("\n"); + } + if (found) { + printf("%x to %x", va, hg[3]); + last_va0 = va; + } + last_found = found; + } + if (found) { + last_w2 = hg[3] & ~0x180; + last_va = va; + } + va += 4096; + } + if (last_found) + printf(" ... %x\n", last_va); +} +#endif /* CONFIG_PPC64BRIDGE */ + +static unsigned long hash_ctx; +static unsigned long hash_start; +static unsigned long hash_end; + +static void +dump_hash_table() +{ + int seg; + unsigned seg_start, seg_end; + + hash_ctx = 0; + hash_start = 0; + hash_end = 0xfffff000; + scanhex(&hash_ctx); + scanhex(&hash_start); + scanhex(&hash_end); + printf("Mappings for context %x\n", hash_ctx); + seg_start = hash_start; + for (seg = hash_start >> 28; seg <= hash_end >> 28; ++seg) { + seg_end = (seg << 28) | 0x0ffff000; + if (seg_end > hash_end) + seg_end = hash_end; + dump_hash_table_seg((hash_ctx << 4) + seg, seg_start, seg_end); + seg_start = seg_end + 0x1000; + } +} + +/* + * Stuff for reading and writing memory safely + */ +extern inline void sync(void) +{ + asm volatile("sync; isync"); +} + +extern inline void __delay(unsigned int loops) +{ + if (loops != 0) + __asm__ __volatile__("mtctr %0; 1: bdnz 1b" : : + "r" (loops) : "ctr"); +} + +int +mread(unsigned long adrs, void *buf, int size) +{ + volatile int n; + char *p, *q; + + n = 0; + if( setjmp(bus_error_jmp) == 0 ){ + debugger_fault_handler = handle_fault; + sync(); + p = (char *) adrs; + q = (char *) buf; + switch (size) { + case 2: *(short *)q = *(short *)p; break; + case 4: *(int *)q = *(int *)p; break; + default: + for( ; n < size; ++n ) { + *q++ = *p++; + sync(); + } + } + sync(); + /* wait a little while to see if we get a machine check */ + __delay(200); + n = size; + } + debugger_fault_handler = 0; + return n; +} + +int +mwrite(unsigned long adrs, void *buf, int size) +{ + volatile int n; + char *p, *q; + + n = 0; + if( setjmp(bus_error_jmp) == 0 ){ + debugger_fault_handler = handle_fault; + sync(); + p = (char *) adrs; + q = (char *) buf; + switch (size) { + case 2: *(short *)p = *(short *)q; break; + case 4: *(int *)p = *(int *)q; break; + default: + for( ; n < size; ++n ) { + *p++ = *q++; + sync(); + } + } + sync(); + /* wait a little while to see if we get a machine check */ + __delay(200); + n = size; + } else { + printf("*** Error writing address %x\n", adrs + n); + } + debugger_fault_handler = 0; + return n; +} + +static int fault_type; +static char *fault_chars[] = { "--", "**", "##" }; + +static void +handle_fault(struct pt_regs *regs) +{ + fault_type = regs->trap == 0x200? 0: regs->trap == 0x300? 1: 2; + longjmp(bus_error_jmp, 1); +} + +#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t)) + +void +byterev(unsigned char *val, int size) +{ + int t; + + switch (size) { + case 2: + SWAP(val[0], val[1], t); + break; + case 4: + SWAP(val[0], val[3], t); + SWAP(val[1], val[2], t); + break; + case 8: /* is there really any use for this? */ + SWAP(val[0], val[7], t); + SWAP(val[1], val[6], t); + SWAP(val[2], val[5], t); + SWAP(val[3], val[4], t); + break; + } +} + +static int brev; +static int mnoread; + +static char *memex_help_string = + "Memory examine command usage:\n" + "m [addr] [flags] examine/change memory\n" + " addr is optional. will start where left off.\n" + " flags may include chars from this set:\n" + " b modify by bytes (default)\n" + " w modify by words (2 byte)\n" + " l modify by longs (4 byte)\n" + " d modify by doubleword (8 byte)\n" + " r toggle reverse byte order mode\n" + " n do not read memory (for i/o spaces)\n" + " . ok to read (default)\n" + "NOTE: flags are saved as defaults\n" + ""; + +static char *memex_subcmd_help_string = + "Memory examine subcommands:\n" + " hexval write this val to current location\n" + " 'string' write chars from string to this location\n" + " ' increment address\n" + " ^ decrement address\n" + " / increment addr by 0x10. //=0x100, ///=0x1000, etc\n" + " \\ decrement addr by 0x10. \\\\=0x100, \\\\\\=0x1000, etc\n" + " ` clear no-read flag\n" + " ; stay at this addr\n" + " v change to byte mode\n" + " w change to word (2 byte) mode\n" + " l change to long (4 byte) mode\n" + " u change to doubleword (8 byte) mode\n" + " m addr change current addr\n" + " n toggle no-read flag\n" + " r toggle byte reverse flag\n" + " < count back up count bytes\n" + " > count skip forward count bytes\n" + " x exit this mode\n" + ""; + +void +memex() +{ + int cmd, inc, i, nslash; + unsigned long n; + unsigned char val[16]; + + scanhex((void *)&adrs); + cmd = skipbl(); + if (cmd == '?') { + printf(memex_help_string); + return; + } else { + termch = cmd; + } + last_cmd = "m\n"; + while ((cmd = skipbl()) != '\n') { + switch( cmd ){ + case 'b': size = 1; break; + case 'w': size = 2; break; + case 'l': size = 4; break; + case 'd': size = 8; break; + case 'r': brev = !brev; break; + case 'n': mnoread = 1; break; + case '.': mnoread = 0; break; + } + } + if( size <= 0 ) + size = 1; + else if( size > 8 ) + size = 8; + for(;;){ + if (!mnoread) + n = mread(adrs, val, size); + printf("%.16x%c", adrs, brev? 'r': ' '); + if (!mnoread) { + if (brev) + byterev(val, size); + putchar(' '); + for (i = 0; i < n; ++i) + printf("%.2x", val[i]); + for (; i < size; ++i) + printf("%s", fault_chars[fault_type]); + } + putchar(' '); + inc = size; + nslash = 0; + for(;;){ + if( scanhex(&n) ){ + for (i = 0; i < size; ++i) + val[i] = n >> (i * 8); + if (!brev) + byterev(val, size); + mwrite(adrs, val, size); + inc = size; + } + cmd = skipbl(); + if (cmd == '\n') + break; + inc = 0; + switch (cmd) { + case '\'': + for(;;){ + n = inchar(); + if( n == '\\' ) + n = bsesc(); + else if( n == '\'' ) + break; + for (i = 0; i < size; ++i) + val[i] = n >> (i * 8); + if (!brev) + byterev(val, size); + mwrite(adrs, val, size); + adrs += size; + } + adrs -= size; + inc = size; + break; + case ',': + adrs += size; + break; + case '.': + mnoread = 0; + break; + case ';': + break; + case 'x': + case EOF: + scannl(); + return; + case 'b': + case 'v': + size = 1; + break; + case 'w': + size = 2; + break; + case 'l': + size = 4; + break; + case 'u': + size = 8; + break; + case '^': + adrs -= size; + break; + break; + case '/': + if (nslash > 0) + adrs -= 1 << nslash; + else + nslash = 0; + nslash += 4; + adrs += 1 << nslash; + break; + case '\\': + if (nslash < 0) + adrs += 1 << -nslash; + else + nslash = 0; + nslash -= 4; + adrs -= 1 << -nslash; + break; + case 'm': + scanhex((void *)&adrs); + break; + case 'n': + mnoread = 1; + break; + case 'r': + brev = !brev; + break; + case '<': + n = size; + scanhex(&n); + adrs -= n; + break; + case '>': + n = size; + scanhex(&n); + adrs += n; + break; + case '?': + printf(memex_subcmd_help_string); + break; + } + } + adrs += inc; + } +} + +int +bsesc() +{ + int c; + + c = inchar(); + switch( c ){ + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 'b': c = '\b'; break; + case 't': c = '\t'; break; + } + return c; +} + +#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ + || ('a' <= (c) && (c) <= 'f') \ + || ('A' <= (c) && (c) <= 'F')) +void +dump() +{ + int c; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex((void *)&adrs); + if( termch != '\n') + termch = 0; + if( c == 'i' ){ + scanhex(&nidump); + if( nidump == 0 ) + nidump = 16; + adrs += ppc_inst_dump(adrs, nidump); + last_cmd = "di\n"; + } else { + scanhex(&ndump); + if( ndump == 0 ) + ndump = 64; + prdump(adrs, ndump); + adrs += ndump; + last_cmd = "d\n"; + } +} + +void +prdump(unsigned long adrs, long ndump) +{ + long n, m, c, r, nr; + unsigned char temp[16]; + + for( n = ndump; n > 0; ){ + printf("%.16lx", adrs); + putchar(' '); + r = n < 16? n: 16; + nr = mread(adrs, temp, r); + adrs += nr; + for( m = 0; m < r; ++m ){ + if ((m & 7) == 0 && m > 0) + putchar(' '); + if( m < nr ) + printf("%.2x", temp[m]); + else + printf("%s", fault_chars[fault_type]); + } + for(; m < 16; ++m ) + printf(" "); + printf(" |"); + for( m = 0; m < r; ++m ){ + if( m < nr ){ + c = temp[m]; + putchar(' ' <= c && c <= '~'? c: '.'); + } else + putchar(' '); + } + n -= r; + for(; m < 16; ++m ) + putchar(' '); + printf("|\n"); + if( nr < r ) + break; + } +} + +int +ppc_inst_dump(unsigned long adr, long count) +{ + int nr, dotted; + unsigned long first_adr; + unsigned long inst, last_inst; + unsigned char val[4]; + + dotted = 0; + for (first_adr = adr; count > 0; --count, adr += 4){ + nr = mread(adr, val, 4); + if( nr == 0 ){ + const char *x = fault_chars[fault_type]; + printf("%.16lx %s%s%s%s\n", adr, x, x, x, x); + break; + } + inst = GETWORD(val); + if (adr > first_adr && inst == last_inst) { + if (!dotted) { + printf(" ...\n"); + dotted = 1; + } + continue; + } + dotted = 0; + last_inst = inst; + printf("%.16lx ", adr); + printf("%.8x\t", inst); + print_insn_big_powerpc(stdout, inst, adr); /* always returns 4 */ + printf("\n"); + } + return adr - first_adr; +} + +void +print_address(unsigned long addr) +{ + printf("0x%lx", addr); +} + +/* + * Memory operations - move, set, print differences + */ +static unsigned long mdest; /* destination address */ +static unsigned long msrc; /* source address */ +static unsigned long mval; /* byte value to set memory to */ +static unsigned long mcount; /* # bytes to affect */ +static unsigned long mdiffs; /* max # differences to print */ + +void +memops(int cmd) +{ + scanhex((void *)&mdest); + if( termch != '\n' ) + termch = 0; + scanhex((void *)(cmd == 's'? &mval: &msrc)); + if( termch != '\n' ) + termch = 0; + scanhex((void *)&mcount); + switch( cmd ){ + case 'm': + memmove((void *)mdest, (void *)msrc, mcount); + break; + case 's': + memset((void *)mdest, mval, mcount); + break; + case 'd': + if( termch != '\n' ) + termch = 0; + scanhex((void *)&mdiffs); + memdiffs((unsigned char *)mdest, (unsigned char *)msrc, mcount, mdiffs); + break; + } +} + +void +memdiffs(unsigned char *p1, unsigned char *p2, unsigned nb, unsigned maxpr) +{ + unsigned n, prt; + + prt = 0; + for( n = nb; n > 0; --n ) + if( *p1++ != *p2++ ) + if( ++prt <= maxpr ) + printf("%.16x %.2x # %.16x %.2x\n", p1 - 1, + p1[-1], p2 - 1, p2[-1]); + if( prt > maxpr ) + printf("Total of %d differences\n", prt); +} + +static unsigned mend; +static unsigned mask; + +void +memlocate() +{ + unsigned a, n; + unsigned char val[4]; + + last_cmd = "ml"; + scanhex((void *)&mdest); + if (termch != '\n') { + termch = 0; + scanhex((void *)&mend); + if (termch != '\n') { + termch = 0; + scanhex((void *)&mval); + mask = ~0; + if (termch != '\n') termch = 0; + scanhex((void *)&mask); + } + } + n = 0; + for (a = mdest; a < mend; a += 4) { + if (mread(a, val, 4) == 4 + && ((GETWORD(val) ^ mval) & mask) == 0) { + printf("%.16x: %.16x\n", a, GETWORD(val)); + if (++n >= 10) + break; + } + } +} + +static unsigned long mskip = 0x1000; +static unsigned long mlim = 0xffffffff; + +void +memzcan() +{ + unsigned char v; + unsigned a; + int ok, ook; + + scanhex(&mdest); + if (termch != '\n') termch = 0; + scanhex(&mskip); + if (termch != '\n') termch = 0; + scanhex(&mlim); + ook = 0; + for (a = mdest; a < mlim; a += mskip) { + ok = mread(a, &v, 1); + if (ok && !ook) { + printf("%.8x .. ", a); + fflush(stdout); + } else if (!ok && ook) + printf("%.8x\n", a - mskip); + ook = ok; + if (a + mskip < a) + break; + } + if (ook) + printf("%.8x\n", a - mskip); +} + +/* Input scanning routines */ +int +skipbl() +{ + int c; + + if( termch != 0 ){ + c = termch; + termch = 0; + } else + c = inchar(); + while( c == ' ' || c == '\t' ) + c = inchar(); + return c; +} + +int +scanhex(vp) +unsigned long *vp; +{ + int c, d; + unsigned long v; + + c = skipbl(); + d = hexdigit(c); + if( d == EOF ){ + termch = c; + return 0; + } + v = 0; + do { + v = (v << 4) + d; + c = inchar(); + d = hexdigit(c); + } while( d != EOF ); + termch = c; + *vp = v; + return 1; +} + +void +scannl() +{ + int c; + + c = termch; + termch = 0; + while( c != '\n' ) + c = inchar(); +} + +int +hexdigit(c) +{ + if( '0' <= c && c <= '9' ) + return c - '0'; + if( 'A' <= c && c <= 'F' ) + return c - ('A' - 10); + if( 'a' <= c && c <= 'f' ) + return c - ('a' - 10); + return EOF; +} + +void +getstring(char *s, int size) +{ + int c; + + c = skipbl(); + do { + if( size > 1 ){ + *s++ = c; + --size; + } + c = inchar(); + } while( c != ' ' && c != '\t' && c != '\n' ); + termch = c; + *s = 0; +} + +static char line[256]; +static char *lineptr; + +void +flush_input() +{ + lineptr = NULL; +} + +int +inchar() +{ + if (lineptr == NULL || *lineptr == 0) { + if (fgets(line, sizeof(line), stdin) == NULL) { + lineptr = NULL; + return EOF; + } + lineptr = line; + } + return *lineptr++; +} + +void +take_input(str) +char *str; +{ + lineptr = str; +} + +/*static char *pretty_print_addr(unsigned long addr) --Unused + *{ + * printf("%08x", addr); + * if ( lookup_name(addr) ) + * printf(" %s", lookup_name(addr) ); + * return NULL; + *} + */ +static char *lookup_name(unsigned long addr) +{ +return NULL; +#if 0 + /* XXX crap, we don't want the whole of the rest of the map - paulus */ + extern char *sysmap; + extern unsigned long sysmap_size; + char *c = sysmap; + unsigned long cmp; + + if ( !sysmap || !sysmap_size ) + return NULL; + cmp = simple_strtoul(c, &c, 8); + strcpy( last, strsep( &c, "\n")); + while ( c < (sysmap+sysmap_size) ) + { + cmp = simple_strtoul(c, &c, 8); + if ( cmp < addr ) + break; + strcpy( last, strsep( &c, "\n")); + } + return last; +#endif +} + +/* Starting at codeaddr scan forward for a tbtable and fill in the + given table. Return non-zero if successful at doing something. + */ +static int +find_tb_table(unsigned long codeaddr, struct tbtable *tab) +{ + unsigned long codeaddr_max; + unsigned long tbtab_start; + int nr; + int instr; + int num_parms; + + if (tab == NULL) + return 0; + memset(tab, 0, sizeof(tab)); + + /* Scan instructions starting at codeaddr for 128k max */ + for (codeaddr_max = codeaddr + 128*1024*4; + codeaddr < codeaddr_max; + codeaddr += 4) { + nr = mread(codeaddr, &instr, 4); + if (nr != 4) + return 0; /* Bad read. Give up promptly. */ + if (instr == 0) { + /* table should follow. */ + int version; + unsigned long flags; + tbtab_start = codeaddr; /* save it to compute func start addr */ + codeaddr += 4; + nr = mread(codeaddr, &flags, 8); + if (nr != 8) + return 0; /* Bad read or no tb table. */ + tab->flags = flags; + version = (flags >> 56) & 0xff; + if (version != 0) + continue; /* No tb table here. */ + /* Now, like the version, some of the flags are values + that are more conveniently extracted... */ + tab->fp_saved = (flags >> 24) & 0x3f; + tab->gpr_saved = (flags >> 16) & 0x3f; + tab->fixedparms = (flags >> 8) & 0xff; + tab->floatparms = (flags >> 1) & 0x7f; + codeaddr += 8; + num_parms = tab->fixedparms + tab->floatparms; + if (num_parms) { + unsigned int parminfo; + int parm; + if (num_parms > 32) + return 1; /* incomplete */ + nr = mread(codeaddr, &parminfo, 4); + if (nr != 4) + return 1; /* incomplete */ + /* decode parminfo...32 bits. + A zero means fixed. A one means float and the + following bit determines single (0) or double (1). + */ + for (parm = 0; parm < num_parms; parm++) { + if (parminfo & 0x80000000) { + parminfo <<= 1; + if (parminfo & 0x80000000) + tab->parminfo[parm] = TBTAB_PARMDFLOAT; + else + tab->parminfo[parm] = TBTAB_PARMSFLOAT; + } else { + tab->parminfo[parm] = TBTAB_PARMFIXED; + } + parminfo <<= 1; + } + codeaddr += 4; + } + if (flags & TBTAB_FLAGSHASTBOFF) { + nr = mread(codeaddr, &tab->tb_offset, 4); + if (nr != 4) + return 1; /* incomplete */ + if (tab->tb_offset > 0) { + tab->funcstart = tbtab_start - tab->tb_offset; + } + codeaddr += 4; + } + /* hand_mask appears to be always be omitted. */ + if (flags & TBTAB_FLAGSHASCTL) { + /* Assume this will never happen for C or asm */ + return 1; /* incomplete */ + } + if (flags & TBTAB_FLAGSNAMEPRESENT) { + short namlen; + nr = mread(codeaddr, &namlen, 2); + if (nr != 2) + return 1; /* incomplete */ + if (namlen >= sizeof(tab->name)) + namlen = sizeof(tab->name)-1; + codeaddr += 2; + nr = mread(codeaddr, tab->name, namlen); + tab->name[namlen] = '\0'; + codeaddr += namlen; + } + return 1; + } + } + return 0; /* hit max...sorry. */ +} + +void +mem_translate() { + int c; + unsigned long ea, va, vsid, vpn, page, hpteg_slot_primary, hpteg_slot_secondary, primary_hash, i, *steg, esid, stabl; + HPTE * hpte; + struct mm_struct * mm; + pte_t *ptep = NULL; + void * pgdir; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex((void *)&ea); + + if ( (( ea >= KRANGE_START ) && ( ea <= (KRANGE_START + (1UL<<60) ))) || + (( ea >= BTMALLOC_START) && ( ea <= BTMALLOC_END)) ) { + ptep = 0; + vsid = get_kernel_vsid(ea); + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + } else { + // if in vmalloc range, use the vmalloc page directory + if ( ( ea >= VMALLOC_START ) && ( ea <= VMALLOC_END ) ) { + mm = &init_mm; + vsid = get_kernel_vsid( ea ); + } + // if in ioremap range, use the ioremap page directory + else if ( ( ea >= IMALLOC_START ) && ( ea <= IMALLOC_END ) ) { + mm = &ioremap_mm; + vsid = get_kernel_vsid( ea ); + } + // if in user range, use the current task's page directory + else if ( ( ea >= USER_START ) && ( ea <= USER_END ) ) { + mm = current->mm; + vsid = get_vsid(mm->context, ea ); + } + pgdir = mm->pgd; + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + ptep = find_linux_pte( pgdir, ea ); + } + + vpn = ((vsid << 28) | (((ea) & 0xFFFF000))) >> 12; + page = vpn & 0xffff; + esid = (ea >> 28) & 0xFFFFFFFFF; + + // Search the primary group for an available slot + primary_hash = ( vsid & 0x7fffffffff ) ^ page; + hpteg_slot_primary = ( primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + hpteg_slot_secondary = ( ~primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + + printf("ea : %.16lx\n", ea); + printf("esid : %.16lx\n", esid); + printf("vsid : %.16lx\n", vsid); + + printf("\nSoftware Page Table\n-------------------\n"); + printf("ptep : %.16lx\n", ((unsigned long *)ptep)); + if(ptep) { + printf("*ptep : %.16lx\n", *((unsigned long *)ptep)); + } + + hpte = htab_data.htab + hpteg_slot_primary; + printf("\nHardware Page Table\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("slot primary : %.16lx\n", hpteg_slot_primary); + printf("slot secondary : %.16lx\n", hpteg_slot_secondary); + printf("\nPrimary Group\n"); + for (i=0; i<8; ++i) { + if ( hpte->dw0.dw0.v != 0 ) { + printf("%d: (hpte)%.16lx %.16lx\n", i, hpte->dw0.dword0, hpte->dw1.dword1); + printf(" vsid: %.13lx api: %.2lx hash: %.1lx\n", + (hpte->dw0.dw0.avpn)>>5, + (hpte->dw0.dw0.avpn) & 0x1f, + (hpte->dw0.dw0.h)); + printf(" rpn: %.13lx \n", (hpte->dw1.dw1.rpn)); + printf(" pp: %.1lx \n", + ((hpte->dw1.dw1.pp0)<<2)|(hpte->dw1.dw1.pp)); + printf(" wimgn: %.2lx reference: %.1lx change: %.1lx\n", + ((hpte->dw1.dw1.w)<<4)| + ((hpte->dw1.dw1.i)<<3)| + ((hpte->dw1.dw1.m)<<2)| + ((hpte->dw1.dw1.g)<<1)| + ((hpte->dw1.dw1.n)<<0), + hpte->dw1.dw1.r, hpte->dw1.dw1.c); + } + hpte++; + } + + printf("\nSecondary Group\n"); + // Search the secondary group + hpte = htab_data.htab + hpteg_slot_secondary; + for (i=0; i<8; ++i) { + if(hpte->dw0.dw0.v) { + printf("%d: (hpte)%.16lx %.16lx\n", i, hpte->dw0.dword0, hpte->dw1.dword1); + printf(" vsid: %.13lx api: %.2lx hash: %.1lx\n", + (hpte->dw0.dw0.avpn)>>5, + (hpte->dw0.dw0.avpn) & 0x1f, + (hpte->dw0.dw0.h)); + printf(" rpn: %.13lx \n", (hpte->dw1.dw1.rpn)); + printf(" pp: %.1lx \n", + ((hpte->dw1.dw1.pp0)<<2)|(hpte->dw1.dw1.pp)); + printf(" wimgn: %.2lx reference: %.1lx change: %.1lx\n", + ((hpte->dw1.dw1.w)<<4)| + ((hpte->dw1.dw1.i)<<3)| + ((hpte->dw1.dw1.m)<<2)| + ((hpte->dw1.dw1.g)<<1)| + ((hpte->dw1.dw1.n)<<0), + hpte->dw1.dw1.r, hpte->dw1.dw1.c); + } + hpte++; + } + + printf("\nHardware Segment Table\n-----------------------\n"); + stabl = (unsigned long)(KERNELBASE+(_ASR&0xFFFFFFFFFFFFFFFE)); + steg = (unsigned long *)((stabl) | ((esid & 0x1f) << 7)); + + printf("stab base : %.16lx\n", stabl); + printf("slot : %.16lx\n", steg); + + for (i=0; i<8; ++i) { + printf("%d: (ste) %.16lx %.16lx\n", i, + *((unsigned long *)(steg+i*2)),*((unsigned long *)(steg+i*2+1)) ); + } +} + +void mem_check() { + unsigned long htab_size_bytes; + unsigned long htab_end; + unsigned long last_rpn; + HPTE *hpte1, *hpte2; + unsigned long linux_pte; + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + // last_rpn = (naca->physicalMemorySize-1) >> PAGE_SHIFT; + last_rpn = 0xfffff; + + printf("\nHardware Page Table Check\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + +#if 1 + for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + if ( hpte1->dw1.dw1.rpn <= last_rpn ) { + for(hpte2 = hpte1+1; hpte2 < htab_end; hpte2++) { + if ( hpte2->dw0.dw0.v != 0 ) { + if(hpte1->dw1.dw1.rpn == hpte2->dw1.dw1.rpn) { + printf(" Duplicate rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" hpte1: %16.16lx *hpte1: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + printf(" hpte2: %16.16lx *hpte2: %16.16lx %16.16lx\n", + hpte2, hpte2->dw0.dword0, hpte2->dw1.dword1); + } + } + } + } else { + printf(" Bogus rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" hpte: %16.16lx *hpte: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + } + } + } +#endif + printf("\nDone -------------------\n"); +} + +void mem_find_real() { + unsigned long htab_size_bytes; + unsigned long htab_end; + unsigned long last_rpn; + HPTE *hpte1; + unsigned long pa, rpn; + int c; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex((void *)&pa); + rpn = pa >> 12; + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + // last_rpn = (naca->physicalMemorySize-1) >> PAGE_SHIFT; + last_rpn = 0xfffff; + + printf("\nMem Find RPN\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + if ( hpte1->dw1.dw1.rpn == rpn ) { + printf(" Found rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" hpte: %16.16lx *hpte1: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + } + } + } + printf("\nDone -------------------\n"); +} + +void mem_find_vsid() { + unsigned long htab_size_bytes; + unsigned long htab_end; + HPTE *hpte1; + unsigned long vsid; + int c; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex((void *)&vsid); + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + + printf("\nMem Find VSID\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + if ( ((hpte1->dw0.dw0.avpn)>>5) == vsid ) { + printf(" Found vsid: %.16lx \n", ((hpte1->dw0.dw0.avpn) >> 5)); + printf(" hpte: %16.16lx *hpte1: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + } + } + } + printf("\nDone -------------------\n"); +} + +void mem_map_check_slab() { + int i, slab_count; + + i = max_mapnr; + slab_count = 0; + + while (i-- > 0) { + if (PageSlab(mem_map+i)){ + printf(" slab entry - mem_map entry =%p \n", mem_map+i); + slab_count ++; + } + + } + + printf(" count of pages for slab = %d \n", slab_count); + + +} + +void mem_map_lock_pages() { + int i, lock_count; + + i = max_mapnr; + lock_count = 0; + + while (i-- > 0) { + if (PageLocked(mem_map+i)){ + printf(" locked entry - mem_map entry =%p \n", mem_map+i); + lock_count ++; + } + + } + + printf(" count of locked pages = %d \n", lock_count); + + +} + + + +void mem_map_check_hash() { + int i; + + i = max_mapnr; + + + while (i-- > 0) { + // skip the reserved + if (!PageReserved(mem_map+i)) + { + + if (((mem_map+i)->next_hash) != NULL){ + if (((unsigned long)((mem_map+i)->next_hash) & 0xf000000000000000) != 0xC000000000000000){ + printf(" mem_map check hash - non c0 entry - address/value = %p %lx \n", + mem_map+i,(mem_map+i)->next_hash); + } + if (((mem_map+i)->next_hash) == 0xC000000000000000){ + printf(" mem_map check hash - 0xC000000000000000 entry = %p \n", mem_map+i); + } + } + } + else + { + if (page_count(mem_map+i) < 0) + { + printf(" reserved page with negative count- entry = %lx \n", mem_map + i); + } + } + + } + + printf(" mem_map check hash completed \n"); + +} + + +void mem_check_zones() +{ + pg_data_t *pgdat; + unsigned long order; + unsigned type; + int i, mem_map_zone_count, mem_map_zone_flag_zero_count, mem_map_reserved_count; + int mem_map_zone_count_reserved, mem_map_zone_flag_nonzero_count; + + pgdat = pgdat_list; + + + printf(" nr_inactive_clean_pages = %lx, nr_free_pages = %lx \n", + nr_inactive_clean_pages(), + nr_free_pages() + ); + + for (type = 0; type < MAX_NR_ZONES; type++) { + struct list_head *head, *curr; + zone_t *zone = pgdat->node_zones + type; + unsigned long nr, total, flags; + + + printf("zone info zone = %p free pages = %lx \n", + zone, zone->free_pages); + printf(" inactive_clean_pages = %lx \n", zone -> inactive_clean_pages); + printf(" inactive_dirty_pages = %lx \n", zone -> inactive_dirty_pages); + printf(" pages_min = %lx \n", zone -> pages_min); + printf(" pages_low = %lx \n" , zone -> pages_low); + printf(" pages_high = %lx \n" , zone -> pages_high); + + total = 0; + if (zone->size) { + spin_lock_irqsave(&zone->lock, flags); + for (order = 0; order < MAX_ORDER; order++) { + head = &(zone->free_area + order)->free_list; + curr = head; + nr = 0; + printf(" pages on free list for order = %d \n", order); + for (;;) { + curr = memlist_next(curr); + + if (curr == head) + break; + printf(" page = %lx flags= %lx \n", curr, ((struct page *)curr)->flags); + nr++; + // add code to check zone map to assure pages are free + } + total += nr * (1 << order); + printf(" entries of order = %d order size in pages = %lx \n", order, 1< 0) { + // skip the reserved + if (!PageReserved(mem_map+i)) + { + if (((mem_map+i)->zone) == zone){ + mem_map_zone_count ++; + if (((mem_map+i)->flags) == 0){ + mem_map_zone_flag_zero_count ++; + + } + else + { + mem_map_zone_flag_nonzero_count ++; + } + + } + // all non reserved pages should be in zone 0 + if (((mem_map+i)->zone) != pgdat->node_zones){ + printf( " page in wrong zone - entry = %p zone = %lx \n", mem_map+i,(mem_map+i)->zone); + } + + } + else + { + mem_map_reserved_count ++; + if (((mem_map+i)->zone) == zone){ + mem_map_zone_count_reserved ++; + } + + } + } + + + printf(" mem_map zone= %p mem_map count for zone = %lx",zone, mem_map_zone_count); + printf(" mem_map_flag_zero_count = %lx\n",mem_map_zone_flag_zero_count); + printf(" mem_map_flag_nonzero_count = %lx\n",mem_map_zone_flag_nonzero_count); + printf(" mem_map_reserved_count = %lx\n",mem_map_reserved_count); + printf(" mem_map_zone_count_reserved = %lx\n",mem_map_zone_count_reserved); + + + spin_unlock_irqrestore(&zone->lock, flags); + } + + } + + + +} + + +void mem_check_dup_rpn () { + unsigned long htab_size_bytes; + unsigned long htab_end; + unsigned long last_rpn; + HPTE *hpte1, *hpte2; + unsigned long linux_pte; + int dup_count; + struct task_struct *p; + unsigned long kernel_vsid_c0,kernel_vsid_c1,kernel_vsid_c2,kernel_vsid_c3; + unsigned long kernel_vsid_c4,kernel_vsid_c5,kernel_vsid_d,kernel_vsid_e; + unsigned long kernel_vsid_f; + unsigned long vsid0,vsid1,vsidB,vsid2; + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + // last_rpn = (naca->physicalMemorySize-1) >> PAGE_SHIFT; + last_rpn = 0xfffff; + + printf("\nHardware Page Table Check\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + + for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + if ( hpte1->dw1.dw1.rpn <= last_rpn ) { + dup_count = 0; + for(hpte2 = hpte1+1; hpte2 < htab_end; hpte2++) { + if ( hpte2->dw0.dw0.v != 0 ) { + if(hpte1->dw1.dw1.rpn == hpte2->dw1.dw1.rpn) { + dup_count++; + } + } + } + if(dup_count > 5) { + printf(" Duplicate rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" mem map array entry %p count = %d \n", + (mem_map+(hpte1->dw1.dw1.rpn)), (mem_map+(hpte1->dw1.dw1.rpn))->count); + for(hpte2 = hpte1+1; hpte2 < htab_end; hpte2++) { + if ( hpte2->dw0.dw0.v != 0 ) { + if(hpte1->dw1.dw1.rpn == hpte2->dw1.dw1.rpn) { + printf(" hpte2: %16.16lx *hpte2: %16.16lx %16.16lx\n", + hpte2, hpte2->dw0.dword0, hpte2->dw1.dword1); + } + } + } + } + } else { + printf(" Bogus rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" hpte: %16.16lx *hpte: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + } + } + if (xmon_interrupted()) + return; + } + + + + // print the kernel vsids + kernel_vsid_c0 = get_kernel_vsid(0xC000000000000000); + kernel_vsid_c1 = get_kernel_vsid(0xC000000010000000); + kernel_vsid_c2 = get_kernel_vsid(0xC000000020000000); + kernel_vsid_c3 = get_kernel_vsid(0xC000000030000000); + kernel_vsid_c4 = get_kernel_vsid(0xC000000040000000); + kernel_vsid_c5 = get_kernel_vsid(0xC000000050000000); + kernel_vsid_d = get_kernel_vsid(0xD000000000000000); + kernel_vsid_e = get_kernel_vsid(0xE000000000000000); + kernel_vsid_f = get_kernel_vsid(0xF000000000000000); + + printf(" kernel vsid - seg c0 = %lx\n", kernel_vsid_c0 ); + printf(" kernel vsid - seg c1 = %lx\n", kernel_vsid_c1 ); + printf(" kernel vsid - seg c2 = %lx\n", kernel_vsid_c2 ); + printf(" kernel vsid - seg c3 = %lx\n", kernel_vsid_c3 ); + printf(" kernel vsid - seg c4 = %lx\n", kernel_vsid_c4 ); + printf(" kernel vsid - seg c5 = %lx\n", kernel_vsid_c5 ); + printf(" kernel vsid - seg d = %lx\n", kernel_vsid_d ); + printf(" kernel vsid - seg e = %lx\n", kernel_vsid_e ); + printf(" kernel vsid - seg f = %lx\n", kernel_vsid_f ); + + + // print a list of valid vsids for the tasks + read_lock(&tasklist_lock); + for_each_task(p) + if(p->mm) { + struct mm_struct *mm = p->mm; + printf(" task = %p mm = %lx pgd %lx\n", + p, mm, mm->pgd); + vsid0 = get_vsid( mm->context, 0 ); + vsid1 = get_vsid( mm->context, 0x10000000 ); + vsid2 = get_vsid( mm->context, 0x20000000 ); + vsidB = get_vsid( mm->context, 0xB0000000 ); + printf(" context = %lx vsid seg 0 = %lx\n", mm->context, vsid0 ); + printf(" vsid seg 1 = %lx\n", vsid1 ); + printf(" vsid seg 2 = %lx\n", vsid2 ); + printf(" vsid seg 2 = %lx\n", vsidB ); + + printf("\n"); + }; + read_unlock(&tasklist_lock); + + + printf("\nDone -------------------\n"); + +} + + + +void mem_check_pagetable_vsids () { + unsigned long htab_size_bytes; + unsigned long htab_end; + unsigned long last_rpn; + struct task_struct *p; + unsigned long valid_table_count,invalid_table_count,bogus_rpn_count; + int found; + unsigned long user_address_table_count,kernel_page_table_count; + unsigned long pt_vsid; + unsigned long linux_pte; + HPTE *hpte1; + + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + // last_rpn = (naca->physicalMemorySize-1) >> PAGE_SHIFT; + last_rpn = 0xfffff; + + printf("\nHardware Page Table Check\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + valid_table_count = 0; + invalid_table_count = 0; + bogus_rpn_count = 0; + user_address_table_count = 0; + kernel_page_table_count = 0; + for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + valid_table_count++; + if ( hpte1->dw1.dw1.rpn <= last_rpn ) { + pt_vsid = (hpte1->dw0.dw0.avpn) >> 5; + if ((pt_vsid == get_kernel_vsid(0xC000000000000000)) | + (pt_vsid == get_kernel_vsid(0xC000000010000000)) | + (pt_vsid == get_kernel_vsid(0xC000000020000000)) | + (pt_vsid == get_kernel_vsid(0xC000000030000000)) | + (pt_vsid == get_kernel_vsid(0xC000000040000000)) | + (pt_vsid == get_kernel_vsid(0xC000000050000000)) | + (pt_vsid == get_kernel_vsid(0xD000000000000000)) | + (pt_vsid == get_kernel_vsid(0xE000000000000000)) | + (pt_vsid == get_kernel_vsid(0xF000000000000000)) ) + + { + kernel_page_table_count ++; + } + else + { + read_lock(&tasklist_lock); + found = 0; + for_each_task(p) { + + if(p->mm && (found == 0)) { + struct mm_struct *mm = p->mm; + + if ((pt_vsid == get_vsid( mm->context, 0 )) | + (pt_vsid == get_vsid( mm->context, 0x10000000 )) | + (pt_vsid == get_vsid( mm->context, 0x20000000 )) | + (pt_vsid == get_vsid( mm->context, 0x30000000 )) | + (pt_vsid == get_vsid( mm->context, 0x40000000 )) | + (pt_vsid == get_vsid( mm->context, 0x50000000 )) | + (pt_vsid == get_vsid( mm->context, 0x60000000 )) | + (pt_vsid == get_vsid( mm->context, 0x70000000 )) | + (pt_vsid == get_vsid( mm->context, 0x80000000 )) | + (pt_vsid == get_vsid( mm->context, 0x90000000 )) | + (pt_vsid == get_vsid( mm->context, 0xA0000000 )) | + (pt_vsid == get_vsid( mm->context, 0xB0000000 ))) + { + user_address_table_count ++; + found = 1; + } + } + } + read_unlock(&tasklist_lock); + if (found == 0) + { + printf(" vsid not found vsid = %lx, hpte = %p \n", + pt_vsid,hpte1); + printf(" rpn in entry = %lx \n", hpte1->dw1.dw1.rpn); + printf(" mem map address = %lx \n", mem_map + (hpte1->dw1.dw1.rpn)); + + } + else // found + { + } + + } // good rpn + + } + else + { + bogus_rpn_count ++; + + } + } + else + { + invalid_table_count++; + } + } + + + printf(" page table valid counts - valid entries = %lx invalid entries = %lx \n", + valid_table_count, invalid_table_count); + + printf(" bogus rpn entries ( probably io) = %lx \n", bogus_rpn_count); + + + + printf(" page table counts - kernel entries = %lx user entries = %lx \n", + kernel_page_table_count, user_address_table_count); + + printf("\nDone -------------------\n"); + +} + + +void mem_check_full_group() { + unsigned long htab_size_bytes; + unsigned count; + unsigned count_array[] = {0,0,0,0,0,0,0,0,0}; + unsigned i; + unsigned long htab_end; + HPTE *hpte1, *hpte2, *hpte3; + u64 rpn = 0; + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + + printf("\nHardware Page Find full groups \n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + for (hpte1 = htab_data.htab; (unsigned long)hpte1 < htab_end; hpte1= hpte1 + 8) + { + count = 0; + hpte2 = hpte1; + for (i=0; i<8; ++i) + { + if ( hpte2->dw0.dw0.v != 0 ) + { + count++; + } + hpte2++; + } + if (count == 8 ) + { + printf(" full group starting with entry %lx \n", hpte1); + hpte3 = hpte1; + for (i=0; i<8; ++i) + { + if ( hpte3->dw0.dw0.v != 0 ) + { + printf(" entry number %d \n",i); + printf(" vsid: %.13lx api: %.2lx hash: %.1lx\n", + (hpte3->dw0.dw0.avpn)>>5, + (hpte3->dw0.dw0.avpn) & 0x1f, + (hpte3->dw0.dw0.h)); + printf(" rpn: %.13lx \n", (hpte3->dw1.dw1.rpn)); + // Dump out the memmap array entry address, corresponding virtual address, and reference count. + rpn = hpte3->dw1.dw1.rpn; + printf(" mem_map+rpn=%p, virtual@=%p, count=%lx \n", mem_map+rpn, (mem_map+rpn)->virtual, (mem_map+rpn)->count); + } + hpte3++; + } + if (xmon_interrupted()) + return; + } + + count_array[count]++; + } + for (i=1; i<9; ++i) + { + printf(" group count for size %i = %lx \n", i, count_array[i]); + } + + printf("\nDone -------------------\n"); +} + + + +static void show_task(struct task_struct * p) +{ + /* unsigned long free = 0; --Unused */ + int state; + static const char * stat_nam[] = { "R", "S", "D", "Z", "T", "W" }; + + printf("--------------------------------------------------------------------------\n"); + printf("%-11.11s pid: %5.5lx ppid: %5.5lx state: ", + p->comm, p->pid, p->p_pptr->pid); + state = p->state ? ffz(~p->state) + 1 : 0; + if (((unsigned) state) < sizeof(stat_nam)/sizeof(char *)) + printf(stat_nam[state]); + else + printf(" "); + if (p == current) + printf(" pc: current task "); + else + printf(" pc: 0x%16.16lx ", thread_saved_pc(&p->thread)); + + if (p->p_cptr) + printf("%5d ", p->p_cptr->pid); + else + printf(" "); + if (!p->mm) + printf(" (L-TLB) "); + else + printf(" (NOTLB) "); + if (p->p_ysptr) + printf("%7d", p->p_ysptr->pid); + else + printf(" "); + if (p->p_osptr) + printf(" %5d\n", p->p_osptr->pid); + else + printf("\n"); + + { + struct sigqueue *q; + char s[sizeof(sigset_t)*2+1], b[sizeof(sigset_t)*2+1]; + + render_sigset_t(&p->pending.signal, s); + render_sigset_t(&p->blocked, b); + printf(" sig: %d %s %s :", signal_pending(p), s, b); + for (q = p->pending.head; q ; q = q->next) + printf(" %d", q->info.si_signo); + printf(" X\n"); + } + + printf(" pers : %lx current : %lx", + p->personality, p); + printf("\n"); + + printf(" thread : 0x%16.16lx ksp : 0x%16.16lx\n", + &(p->thread), (p->thread.ksp)); + printf(" pgdir : 0x%16.16lx\n", (p->thread.pgdir)); + printf(" regs : 0x%16.16lx sysc : 0x%16.16lx\n", + (p->thread.regs), (p->thread.last_syscall)); + if(p->thread.regs) { + printf(" nip : 0x%16.16lx msr : 0x%16.16lx\n", + ((p->thread.regs)->nip), ((p->thread.regs)->msr)); + printf(" ctr : 0x%16.16lx link : 0x%16.16lx\n", + ((p->thread.regs)->ctr), ((p->thread.regs)->link)); + printf(" xer : 0x%16.16lx ccr : 0x%16.16lx\n", + ((p->thread.regs)->xer), ((p->thread.regs)->ccr)); + printf(" mq : 0x%16.16lx trap : 0x%16.16lx\n", + ((p->thread.regs)->mq), ((p->thread.regs)->trap)); + printf(" dar : 0x%16.16lx dsis : 0x%16.16lx\n", + ((p->thread.regs)->dar), ((p->thread.regs)->dsisr)); + printf(" rslt : 0x%16.16lx org3 : 0x%16.16lx\n", + ((p->thread.regs)->result), (p->thread.regs->orig_gpr3)); + } + + if(p->mm) { + struct mm_struct *mm = p->mm; + printf(" mm : 0x%16.16lx pgd : 0x%16.16lx\n", + mm, mm->pgd); + printf(" context: 0x%16.16lx mmap : 0x%16.16lx\n", + mm->context, mm->mmap); + + printf("\n"); + } + +} + +static void show_state(void) +{ + struct task_struct *p; + +#if (BITS_PER_LONG == 32) + printf("\n" + " free sibling\n"); + printf("task name st PC stack pid father child younger older\n"); +#else + printf("\n" + " free sibling\n"); + printf(" task PC stack pid father child younger older\n"); +#endif + read_lock(&tasklist_lock); + for_each_task(p) + show_task(p); + read_unlock(&tasklist_lock); +} + +static void debug_trace(void) { + unsigned long val, cmd, on; + + cmd = skipbl(); + if (cmd == '\n') { + /* show current state */ + unsigned long i; + printf("naca->debug_switch = 0x%lx\n", naca->debug_switch); + for (i = 0; i < PPCDBG_NUM_FLAGS ;i++) { + on = PPCDBG_BITVAL(i) & naca->debug_switch; + printf("%02x %s %12s ", i, on ? "on " : "off", trace_names[i] ? trace_names[i] : ""); + if (((i+1) % 3) == 0) + printf("\n"); + } + printf("\n"); + return; + } + while (cmd != '\n') { + on = 1; /* default if no sign given */ + while (cmd == '+' || cmd == '-') { + on = (cmd == '+'); + cmd = inchar(); + if (cmd == ' ' || cmd == '\n') { /* Turn on or off based on + or - */ + naca->debug_switch = on ? PPCDBG_ALL:PPCDBG_NONE; + printf("Setting all values to %s...\n", on ? "on" : "off"); + if (cmd == '\n') return; + else cmd = skipbl(); + } + else + termch = cmd; + } + termch = cmd; /* not +/- ... let scanhex see it */ + scanhex((void *)&val); + if (val >= 64) { + printf("Value %x out of range:\n", val); + return; + } + if (on) { + naca->debug_switch |= PPCDBG_BITVAL(val); + printf("enable debug %x %s\n", val, trace_names[val] ? trace_names[val] : ""); + } else { + naca->debug_switch &= ~PPCDBG_BITVAL(val); + printf("disable debug %x %s\n", val, trace_names[val] ? trace_names[val] : ""); + } + cmd = skipbl(); + } +} + + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/Makefile linux.ac/arch/s390/Makefile --- linux.vanilla/arch/s390/Makefile Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390/Makefile Wed Oct 10 01:47:34 2001 @@ -55,16 +55,6 @@ MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot -MAKESILO = $(MAKE) -C arch/$(ARCH)/tools/silo - -MAKEDASDFMT = $(MAKE) -C arch/$(ARCH)/tools/dasdfmt - -silo: - @$(MAKESILO) silo - -dasdfmt: - @$(MAKEDASDFMT) dasdfmt - image: vmlinux @$(MAKEBOOT) image diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/config.in linux.ac/arch/s390/config.in --- linux.vanilla/arch/s390/config.in Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390/config.in Wed Oct 10 01:47:34 2001 @@ -9,6 +9,7 @@ define_bool CONFIG_UID16 y define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n +define_bool CONFIG_GENERIC_BUST_SPINLOCK n mainmenu_name "Linux Kernel Configuration" define_bool CONFIG_ARCH_S390 y diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/defconfig linux.ac/arch/s390/defconfig --- linux.vanilla/arch/s390/defconfig Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390/defconfig Wed Oct 10 01:47:34 2001 @@ -7,6 +7,7 @@ CONFIG_UID16=y CONFIG_RWSEM_GENERIC_SPINLOCK=y # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_GENERIC_BUST_SPINLOCK=n CONFIG_ARCH_S390=y # @@ -72,6 +73,7 @@ CONFIG_MD_RAID0=m CONFIG_MD_RAID1=m CONFIG_MD_RAID5=m +# CONFIG_MD_MULTIPATH is not set CONFIG_BLK_DEV_LVM=m # @@ -247,6 +249,7 @@ CONFIG_IBM_PARTITION=y # CONFIG_MAC_PARTITION is not set # CONFIG_MSDOS_PARTITION is not set +# CONFIG_LDM_PARTITION is not set # CONFIG_SGI_PARTITION is not set # CONFIG_ULTRIX_PARTITION is not set # CONFIG_SUN_PARTITION is not set diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/Makefile linux.ac/arch/s390/kernel/Makefile --- linux.vanilla/arch/s390/kernel/Makefile Thu Apr 12 03:02:27 2001 +++ linux.ac/arch/s390/kernel/Makefile Wed Oct 10 01:47:34 2001 @@ -15,7 +15,7 @@ O_TARGET := kernel.o export-objs := debug.o ebcdic.o irq.o s390_ext.o smp.o s390_ksyms.o -obj-y := lowcore.o entry.o bitmap.o traps.o time.o process.o irq.o \ +obj-y := entry.o bitmap.o traps.o time.o process.o irq.o \ setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ semaphore.o s390fpu.o reipl.o s390_ext.o debug.o diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/debug.c linux.ac/arch/s390/kernel/debug.c --- linux.vanilla/arch/s390/kernel/debug.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390/kernel/debug.c Wed Oct 10 01:47:34 2001 @@ -83,6 +83,9 @@ static int debug_input_level_fn(debug_info_t * id, struct debug_view *view, struct file *file, const char *user_buf, size_t user_buf_size, loff_t * offset); +static int debug_input_flush_fn(debug_info_t * id, struct debug_view *view, + struct file *file, const char *user_buf, + size_t user_buf_size, loff_t * offset); static int debug_hex_ascii_format_fn(debug_info_t * id, struct debug_view *view, char *out_buf, const char *in_buf); static int debug_raw_format_fn(debug_info_t * id, @@ -123,6 +126,15 @@ NULL }; +struct debug_view debug_flush_view = { + "flush", + NULL, + NULL, + NULL, + &debug_input_flush_fn, + NULL +}; + struct debug_view debug_sprintf_view = { "sprintf", NULL, @@ -664,6 +676,7 @@ if(!rc) goto out; debug_register_view(rc, &debug_level_view); + debug_register_view(rc, &debug_flush_view); printk(KERN_INFO "debug: reserved %d areas of %d pages for debugging %s\n", nr_areas, 1 << page_order, rc->name); @@ -1027,6 +1040,73 @@ out: *offset += in_buf_size; return rc; /* number of input characters */ +} + + +/* + * flushes debug areas + */ + +void debug_flush(debug_info_t* id, int area) +{ + unsigned long flags; + int i; + + if(!id) + return; + spin_lock_irqsave(&id->lock,flags); + if(area == DEBUG_FLUSH_ALL){ + id->active_area = 0; + memset(id->active_entry, 0, id->nr_areas * sizeof(int)); + for (i = 0; i < id->nr_areas; i++) + memset(id->areas[i], 0, PAGE_SIZE << id->page_order); + printk(KERN_INFO "debug: %s: all areas flushed\n",id->name); + } else if(area >= 0 && area < id->nr_areas) { + id->active_entry[area] = 0; + memset(id->areas[area], 0, PAGE_SIZE << id->page_order); + printk(KERN_INFO + "debug: %s: area %i has been flushed\n", + id->name, area); + } else { + printk(KERN_INFO + "debug: %s: area %i cannot be flushed (range: %i - %i)\n", + id->name, area, 0, id->nr_areas-1); + } + spin_unlock_irqrestore(&id->lock,flags); +} + +/* + * view function: flushes debug areas + */ + +static int debug_input_flush_fn(debug_info_t * id, struct debug_view *view, + struct file *file, const char *user_buf, + size_t in_buf_size, loff_t * offset) +{ + char input_buf[1]; + int rc = in_buf_size; + + if (*offset != 0) + goto out; + if (copy_from_user(input_buf, user_buf, 1)){ + rc = -EFAULT; + goto out; + } + if(input_buf[0] == '-') { + debug_flush(id, DEBUG_FLUSH_ALL); + goto out; + } + if (isdigit(input_buf[0])) { + int area = ((int) input_buf[0] - (int) '0'); + debug_flush(id, area); + goto out; + } + + printk(KERN_INFO "debug: area `%c` is not valid\n", input_buf[0]); + + out: + *offset += in_buf_size; + return rc; /* number of input characters */ } /* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/entry.S linux.ac/arch/s390/kernel/entry.S --- linux.vanilla/arch/s390/kernel/entry.S Thu Oct 11 13:52:07 2001 +++ linux.ac/arch/s390/kernel/entry.S Wed Oct 10 01:47:34 2001 @@ -97,17 +97,27 @@ * R15 - kernel stack pointer */ - .macro SAVE_ALL psworg # system entry macro + .macro SAVE_ALL psworg,sync # system entry macro stm %r13,%r15,__LC_SAVE_AREA - stam %a2,%a4,__LC_SAVE_AREA+12 basr %r13,0 # temp base pointer l %r13,.Lentry_base-.(%r13) # load &entry_base to %r13 tm \psworg+1,0x01 # test problem state bit - bz BASED(.+12) # skip stack setup save - l %r15,__LC_KERNEL_STACK # problem state -> load ksp - lam %a2,%a4,BASED(.Lc_ac) # set ac.reg. 2 to primary space - # and access reg. 4 to home space -0: s %r15,BASED(.Lc_spsize) # make room for registers & psw + stam %a2,%a4,__LC_SAVE_AREA+12 + .if \sync + bz BASED(1f) # skip stack setup save + .else + bnz BASED(0f) # from user -> load kernel stack + l %r14,__LC_ASYNC_STACK # are we already on the async stack ? + slr %r14,%r15 + sra %r14,13 + be BASED(1f) + l %r15,__LC_ASYNC_STACK # load async. stack + b BASED(1f) + .endif +0: l %r15,__LC_KERNEL_STACK # problem state -> load ksp + lam %a2,%a4,BASED(.Lc_ac) # set ac.reg. 2 to primary space + # and ac.reg. 4 to home space +1: s %r15,BASED(.Lc_spsize) # make room for registers & psw n %r15,BASED(.Lc0xfffffff8) # align stack pointer to 8 stm %r0,%r12,SP_R0(%r15) # store gprs 0-12 to kernel stack st %r2,SP_ORIG_R2(%r15) # store original content of gpr 2 @@ -120,7 +130,7 @@ xc 0(4,%r15),0(%r15) # clear back chain .endm - .macro RESTORE_ALL # system exit macro + .macro RESTORE_ALL sync # system exit macro mvc __LC_RETURN_PSW(8),SP_PSW(%r15) # move user PSW to lowcore lam %a0,%a15,SP_AREGS(%r15) # load the access registers lm %r0,%r15,SP_R0(%r15) # load gprs 0-15 of user @@ -129,8 +139,8 @@ .endm .macro GET_CURRENT - lr %r9,%r15 # load pointer to task_struct to %r9 - n %r9,BASED(.Lc0xffffe000) + l %r9,BASED(.Lc0xffffe000) # load pointer to task_struct to %r9 + al %r9,__LC_KERNEL_STACK .endm @@ -171,13 +181,35 @@ br %r14 /* + * do_softirq calling function. We want to run the softirq functions on the + * asynchronous interrupt stack. + */ + .global do_call_softirq +do_call_softirq: + stm %r12,%r15,24(%r15) + lr %r12,%r15 + basr %r13,0 +do_call_base: + l %r0,__LC_ASYNC_STACK + slr %r0,%r15 + sra %r0,13 + be 0f-do_call_base(%r13) + l %r15,__LC_ASYNC_STACK +0: sl %r15,.Lc_overhead-do_call_base(%r13) + st %r12,0(%r15) # store backchain + l %r1,.Ldo_softirq-do_call_base(%r13) + basr %r14,%r1 + lm %r12,%r15,24(%r12) + br %r14 + +/* * SVC interrupt handler routine. System calls are synchronous events and * are executed with interrupts enabled. */ .globl system_call system_call: - SAVE_ALL __LC_SVC_OLD_PSW + SAVE_ALL __LC_SVC_OLD_PSW,1 mvi SP_PGM_OLD_ILC(%r15),1 # mark PGM_OLD_ILC as invalid pgm_system_call: GET_CURRENT # load pointer to task_struct to R9 @@ -207,7 +239,7 @@ tm SP_PGM_OLD_ILC(%r15),0xff bz BASED(pgm_svcret) stnsm 24(%r15),0xfc # disable I/O and ext. interrupts - RESTORE_ALL + RESTORE_ALL 1 # # call do_signal before return @@ -557,7 +589,10 @@ .long sys_madvise .long sys_getdents64 /* 220 */ .long sys_fcntl64 - .rept 255-221 + .long sys_ni_syscall /* 222 - reserved for posix_acl */ + .long sys_ni_syscall /* 223 - reserved for posix_acl */ + .long sys_ni_syscall /* 224 - reserved for posix_acl */ + .rept 255-224 .long sys_ni_syscall .endr @@ -581,10 +616,10 @@ * for LPSW?). */ stm %r13,%r15,__LC_SAVE_AREA - stam %a2,%a4,__LC_SAVE_AREA+12 basr %r13,0 # temp base pointer l %r13,.Lentry_base-.(%r13)# load &entry_base to %r13 tm __LC_PGM_INT_CODE+1,0x80 # check whether we got a per exception + stam %a2,%a4,__LC_SAVE_AREA+12 bz BASED(pgm_sv) # skip if not tm __LC_PGM_OLD_PSW,0x40 # test if per event recording is on bnz BASED(pgm_sv) # skip if it is @@ -677,7 +712,7 @@ .globl io_int_handler io_int_handler: - SAVE_ALL __LC_IO_OLD_PSW + SAVE_ALL __LC_IO_OLD_PSW,0 GET_CURRENT # load pointer to task_struct to R9 la %r2,SP_PTREGS(%r15) # address of register-save area sr %r3,%r3 @@ -710,7 +745,7 @@ bnz BASED(io_signal_return) io_leave: stnsm 24(%r15),0xfc # disable I/O and ext. interrupts - RESTORE_ALL + RESTORE_ALL 0 # # call do_softirq @@ -744,7 +779,7 @@ .globl ext_int_handler ext_int_handler: - SAVE_ALL __LC_EXT_OLD_PSW + SAVE_ALL __LC_EXT_OLD_PSW,0 GET_CURRENT # load pointer to task_struct to R9 la %r2,SP_PTREGS(%r15) # address of register-save area lh %r3,__LC_EXT_INT_CODE # error code @@ -772,11 +807,11 @@ .globl mcck_int_handler mcck_int_handler: - SAVE_ALL __LC_MCK_OLD_PSW + SAVE_ALL __LC_MCK_OLD_PSW,0 l %r1,BASED(.Ls390_mcck) basr %r14,%r1 # call machine check handler mcck_return: - RESTORE_ALL + RESTORE_ALL 0 #ifdef CONFIG_SMP /* @@ -784,7 +819,7 @@ */ .globl restart_int_handler restart_int_handler: - l %r15,__LC_KERNEL_STACK # load ksp + l %r15,__LC_SAVE_AREA+60 # load ksp lctl %c0,%c15,__LC_CREGS_SAVE_AREA # get new ctl regs lam %a0,%a15,__LC_AREGS_SAVE_AREA stosm 0(%r15),0x04 # now we can turn dat on @@ -817,6 +852,7 @@ .Lc0xffffe000: .long -8192 # to round stack pointer to &task_struct .Lc8191: .long 8191 .Lc_spsize: .long SP_SIZE +.Lc_overhead: .long STACK_FRAME_OVERHEAD .Lc_ac: .long 0,0,1 .Lc_ENOSYS: .long -ENOSYS .Lc4: .long 4 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/gdb-stub.c linux.ac/arch/s390/kernel/gdb-stub.c --- linux.vanilla/arch/s390/kernel/gdb-stub.c Fri May 12 19:41:44 2000 +++ linux.ac/arch/s390/kernel/gdb-stub.c Wed Oct 10 01:47:34 2001 @@ -65,7 +65,8 @@ * $m0,10#2a +$00010203040506070809101112131415#42 * */ - +#define TRUE 1 +#define FALSE 0 #include #include #include @@ -74,14 +75,15 @@ #include #include #include +#include +#define S390_REGS_COMMON_SIZE offsetof(struct gdb_pt_regs,orig_gpr2) /* * external low-level support routines */ -extern int putDebugChar(char c); /* write a single character */ -extern char getDebugChar(void); /* read and return a single char */ + extern void fltr_set_mem_err(void); extern void trap_low(void); @@ -247,8 +249,10 @@ while (count-- > 0) { ch = *(mem++); +#if 0 if (mem_err) return 0; +#endif *buf++ = hexchars[ch >> 4]; *buf++ = hexchars[ch & 0xf]; } @@ -276,8 +280,10 @@ ch = hex(*buf++) << 4; ch |= hex(*buf++); *(mem++) = ch; +#if 0 if (mem_err) return 0; +#endif } /* set_mem_fault_trap(0); */ @@ -349,7 +355,7 @@ return (numChars); } -void gdb_stub_get_non_pt_regs(gdb_pt_regs *regs) +void gdb_stub_get_non_pt_regs(struct gdb_pt_regs *regs) { s390_fp_regs *fpregs=®s->fp_regs; int has_ieee=save_fp_regs1(fpregs); @@ -365,7 +371,7 @@ } } -void gdb_stub_set_non_pt_regs(gdb_pt_regs *regs) +void gdb_stub_set_non_pt_regs(struct gdb_pt_regs *regs) { restore_fp_regs1(®s->fp_regs); } @@ -390,7 +396,7 @@ * returns 1 if you should skip the instruction at the trap address, 0 * otherwise. */ -void gdb_stub_handle_exception(gdb_pt_regs *regs,int sigval) +void gdb_stub_handle_exception(struct gdb_pt_regs *regs,int sigval) { int trap; /* Trap type */ int addr; @@ -402,19 +408,23 @@ /* * reply to host that an exception has occurred */ +#if 0 send_signal(sigval); - +#endif /* * Wait for input from remote GDB */ - while (1) { + while (1) + { output_buffer[0] = 0; getpacket(input_buffer); switch (input_buffer[0]) { case '?': +#if 0 send_signal(sigval); +#endif continue; case 'd': @@ -427,9 +437,9 @@ case 'g': gdb_stub_get_non_pt_regs(regs); ptr = output_buffer; - ptr= mem2hex((char *)regs,ptr,sizeof(s390_regs_common),FALSE); + ptr= mem2hex((char *)regs,ptr,S390_REGS_COMMON_SIZE,FALSE); ptr= mem2hex((char *)®s->crs[0],ptr,NUM_CRS*CR_SIZE,FALSE); - ptr = mem2hex((char *)®s->fp_regs, ptr,sizeof(s390_fp_regs)); + ptr = mem2hex((char *)®s->fp_regs, ptr,sizeof(s390_fp_regs),FALSE); break; /* @@ -438,11 +448,11 @@ */ case 'G': ptr=input_buffer; - hex2mem (ptr, (char *)regs,sizeof(s390_regs_common), FALSE); - ptr+=sizeof(s390_regs_common)*2; + hex2mem (ptr, (char *)regs,S390_REGS_COMMON_SIZE, FALSE); + ptr+=S390_REGS_COMMON_SIZE*2; hex2mem (ptr, (char *)regs->crs[0],NUM_CRS*CR_SIZE, FALSE); ptr+=NUM_CRS*CR_SIZE*2; - hex2mem (ptr, (char *)regs->fp_regs,sizeof(s390_fp_regs), FALSE); + hex2mem (ptr, (char *)®s->fp_regs,sizeof(s390_fp_regs), FALSE); gdb_stub_set_non_pt_regs(regs); strcpy(output_buffer,"OK"); break; @@ -472,7 +482,8 @@ if (hexToInt(&ptr, &addr) && *ptr++ == ',' && hexToInt(&ptr, &length) - && *ptr++ == ':') { + && *ptr++ == ':') + { if (hex2mem(ptr, (char *)addr, length, 1)) strcpy(output_buffer, "OK"); else @@ -490,8 +501,7 @@ ptr = &input_buffer[1]; if (hexToInt(&ptr, &addr)) - regs->cp0_epc = addr; - + regs->psw.addr = addr; /* * Need to flush the instruction cache here, as we may * have deposited a breakpoint, and the icache probably @@ -529,22 +539,22 @@ * There is no single step insn in the MIPS ISA, so we * use breakpoints and continue, instead. */ +#if 0 single_step(regs); +#endif flush_cache_all(); return; /* NOTREACHED */ + break; - } - break; - - } /* switch */ - - /* - * reply to the request - */ + } /* switch */ + /* + * reply to the request + */ + putpacket(output_buffer); - + } /* while */ } @@ -558,13 +568,9 @@ { if (!gdb_stub_initialised) return; - __asm__ __volatile__( - ".globl breakinst\n" - "breakinst:\t.word %0\n\t" - : - : "i" (S390_BREAKPOINT_U16) - : - ); + asm volatile (".globl breakinst\n" + "breakinst:\t.word %0" + : : "i" (S390_BREAKPOINT_U16) ); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/head.S linux.ac/arch/s390/kernel/head.S --- linux.vanilla/arch/s390/kernel/head.S Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390/kernel/head.S Wed Oct 10 01:47:34 2001 @@ -461,28 +461,52 @@ .org 0x10000 startup:basr %r13,0 # get base .LPG1: lctl %c0,%c15,.Lctl-.LPG1(%r13) # load control registers - la %r12,parmarea-.LPG1(%r13) # pointer to parameter area + la %r12,_pstart-.LPG1(%r13) # pointer to parameter area # move IPL device to lowcore mvc __LC_IPLDEV(4),IPL_DEVICE-PARMAREA(%r12) # -# find out memory size. +# find memory chunks. # mvc __LC_PGM_NEW_PSW(8),.Lpcmem-.LPG1(%r13) - lhi %r2,1 - sll %r2,17 # test in increments of 128KB - lr %r1,%r2 - ahi %r1,-4 # test last word in the segment -.Lloop: - l %r0,0(%r1) # test 128KB segment - st %r0,0(%r1) - ar %r1,%r2 # add 128KB - bnm .Lloop-.LPG1(%r13) # r1 < 0x80000000 -> loop -.Lchkmem: - n %r1,.L4malign-.LPG1(%r13) # align to multiples of 4M - l %r2,.Lmemsize-.LPG1(%r13) # address of variable memory_size - st %r1,0(%r2) # store memory size - + la %r1,1 # test in increments of 128KB + sll %r1,17 + l %r3,.Lmchunk-.LPG1(%r13) # get pointer to memory_chunk array + slr %r4,%r4 # set start of chunk to zero + slr %r5,%r5 # set end of chunk to zero + slr %r6,%r6 # set access code to zero +.Lloop: + tprot 0(%r5),0 # test protection of first byte + ipm %r7 + srl %r7,28 + clr %r6,%r7 # compare cc with last access code + be .Lsame-.LPG1(%r13) + clr %r4,%r5 # chunk size > 0? + be .Lsize0-.LPG1(%r13) + st %r4,0(%r3) # store start address of chunk + lr %r0,%r5 + slr %r0,%r4 + st %r0,4(%r3) # store size of chunk + st %r6,8(%r3) # store type of chunk + la %r3,12(%r3) + lr %r4,%r5 # set start to end +.Lsize0: + lr %r6,%r7 # set access code to last cc +.Lsame: + ar %r5,%r1 # add 128KB to end of chunk + bno .Lloop-.LPG1(%r13) # r1 < 0x80000000 -> loop +.Lchkmem: # > 2GB or tprot got a program check + clr %r4,%r5 # chunk size > 0? + be .Ldonemem-.LPG1(%r13) + st %r4,0(%r3) # store start address of chunk + lr %r0,%r5 + slr %r0,%r4 + st %r0,4(%r3) # store size of chunk + st %r6,8(%r3) # store type of chunk +.Ldonemem: + l %r1,.Lmemsize-.LPG1(%r13) # address of variable memory_size + st %r5,0(%r1) # store last end to memory size + l %r12,.Lmflags-.LPG1(%r13) # get address of machine_flags # # find out if we are running under VM @@ -534,7 +558,7 @@ .Lentry:.long 0x00080000,0x80000000 + _stext .Lctl: .long 0x04b50002 # cr0: various things .long 0 # cr1: primary space segment table - .long 0 # cr2: access register translation + .long .Lduct # cr2: dispatchable unit control table .long 0 # cr3: instruction authorization .long 0 # cr4: instruction authorization .long 0 # cr5: various things @@ -552,47 +576,50 @@ .Lpcfpu:.long 0x00080000,0x80000000 + .Lchkfpu .Lpccsp:.long 0x00080000,0x80000000 + .Lchkcsp .Lpcmvpg:.long 0x00080000,0x80000000 + .Lchkmvpg -.L4malign:.long 0xffc00000 .Lmemsize:.long memory_size +.Lmchunk:.long memory_chunk .Lmflags:.long machine_flags + .org PARMAREA-64 +.Lduct: .long 0,0,0,0,0,0,0,0 + .long 0,0,0,0,0,0,0,0 + # # params at 10400 (setup.h) # .org PARMAREA -parmarea: + .global _pstart +_pstart: .long 0,0 # IPL_DEVICE .long 0,RAMDISK_ORIGIN # INITRD_START - .long 0,0x800000 # INITRD_SIZE + .long 0,RAMDISK_SIZE # INITRD_SIZE .org COMMAND_LINE .byte "root=/dev/ram0 ro" .byte 0 + .org 0x11000 + .global _pend +_pend: -# -# startup-code, running in virtual mode -# #ifdef CONFIG_SHARED_KERNEL .org 0x100000 -#else - .org 0x10800 #endif + +# +# startup-code, running in virtual mode +# .globl _stext _stext: basr %r13,0 # get base .LPG2: # -# Setup lowcore +# Setup stack # - l %r1,__LC_IPLDEV # load ipl device number - spx .Lprefix-.LPG2(%r13) # set prefix to linux lowcore - st %r1,__LC_IPLDEV # store ipl device number l %r15,.Linittu-.LPG2(%r13) - ahi %r15,8192 # init_task_union + 8191 + ahi %r15,8192 # init_task_union + 8192 st %r15,__LC_KERNEL_STACK # set end of kernel stack ahi %r15,-96 xc 0(4,%r15),0(%r15) # set backchain to zero - lhi %r0,-1 - st %r0,__LC_KERNEL_LEVEL # set interrupt count to -1 + # # clear bss memory # @@ -622,7 +649,6 @@ # .align 8 .Ldw: .long 0x000a0000,0x00000000 -.Lprefix: .long init_S390_lowcore .Linittu: .long init_task_union .Lstart: .long start_kernel .Lbss_bgn: .long __bss_start diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/init_task.c linux.ac/arch/s390/kernel/init_task.c --- linux.vanilla/arch/s390/kernel/init_task.c Mon Sep 17 23:29:09 2001 +++ linux.ac/arch/s390/kernel/init_task.c Wed Oct 10 01:47:34 2001 @@ -12,6 +12,7 @@ #include #include +static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/lowcore.S linux.ac/arch/s390/kernel/lowcore.S --- linux.vanilla/arch/s390/kernel/lowcore.S Fri May 12 19:41:44 2000 +++ linux.ac/arch/s390/kernel/lowcore.S Thu Jan 1 01:00:00 1970 @@ -1,60 +0,0 @@ -/* - * arch/s390/kernel/lowcore.S - * S390 lowcore definition. - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Hartmut Penner (hp@de.ibm.com) - * Martin Schwidefsky (schwidefsky@de.ibm.com), - */ - -#include - .align 4096 - .globl init_S390_lowcore -init_S390_lowcore: - .long _RESTART_PSW_MASK - .long restart_int_handler + _ADDR_31 - .long 0,0 - .long 0,0 -EXT_OLD: .long 0,0 -SVC_OLD: .long 0,0 -PGM_OLD: .long 0,0 -MCCK_OLD:.long 0,0 -IO_OLD: .long 0,0 - .long 0,0,0,0,0,0 -# -# new psws need all to be physical -# because we start with dat off -# -EXT_PSW: .long _EXT_PSW_MASK - .long ext_int_handler + _ADDR_31 -# -SVC_PSW: .long _SVC_PSW_MASK - .long system_call + _ADDR_31 -# -PGM_PSW: .long _PGM_PSW_MASK - .long pgm_check_handler + _ADDR_31 -# -MCCK_PSW:.long _MCCK_PSW_MASK - .long mcck_int_handler + _ADDR_31 -# -IO_PSW: .long _IO_PSW_MASK - .long io_int_handler + _ADDR_31 -# -# -# -EXTERNAL_PARAMETER: .long 0 -CPU_ADDRESS: .word 0 -EXT_INTERRUPT_CODE: .word 0 -SVC_ILC: .word 0 -SVC_CODE: .word 0 -PGM_ILC: .word 0 -PGM_CODE: .word 0 -TRANS_EXC_ADDR: .long 0 # 090 - .fill 0xC00-0x094,1,0 -SAVE_AREA: .fill 0x40,1,0 # C00 -KERNEL_STACK: .long 0 # C40 -KERNEL_LEVEL: .long 0 # C44 -CPUID: .long 0,0 # C48 - .fill 0x1000-0xC50,1,0 - diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/process.c linux.ac/arch/s390/kernel/process.c --- linux.vanilla/arch/s390/kernel/process.c Thu Oct 11 13:52:08 2001 +++ linux.ac/arch/s390/kernel/process.c Wed Oct 10 01:47:34 2001 @@ -44,8 +44,6 @@ #include #include -spinlock_t semaphore_wake_lock = SPIN_LOCK_UNLOCKED; - asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); /* @@ -209,7 +207,7 @@ void show_regs(struct pt_regs *regs) { char buff[80]; - int line; + int i, line; printk("CPU: %d\n",smp_processor_id()); printk("Process %s (pid: %d, stackpage=%08X)\n", @@ -217,6 +215,17 @@ for (line = 0; sprintf_regs(line, buff, current, regs); line++) printk(buff); + + if (regs->psw.mask & PSW_PROBLEM_STATE) + { + printk("User Code:\n"); + memset(buff, 0, 20); + copy_from_user(buff, + (char *) (regs->psw.addr & PSW_ADDR_MASK), 20); + for (i = 0; i < 20; i++) + printk("%02x ", buff[i]); + printk("\n"); + } } char *task_show_regs(struct task_struct *task, char *buffer) @@ -324,28 +333,19 @@ asmlinkage int sys_fork(struct pt_regs regs) { - int ret; - - lock_kernel(); - ret = do_fork(SIGCHLD, regs.gprs[15], ®s, 0); - unlock_kernel(); - return ret; + return do_fork(SIGCHLD, regs.gprs[15], ®s, 0); } asmlinkage int sys_clone(struct pt_regs regs) { unsigned long clone_flags; unsigned long newsp; - int ret; - lock_kernel(); clone_flags = regs.gprs[3]; newsp = regs.orig_gpr2; if (!newsp) newsp = regs.gprs[15]; - ret = do_fork(clone_flags, newsp, ®s, 0); - unlock_kernel(); - return ret; + return do_fork(clone_flags, newsp, ®s, 0); } /* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/s390_ksyms.c linux.ac/arch/s390/kernel/s390_ksyms.c --- linux.vanilla/arch/s390/kernel/s390_ksyms.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390/kernel/s390_ksyms.c Wed Oct 10 01:47:34 2001 @@ -15,11 +15,11 @@ /* * memory management */ -EXPORT_SYMBOL(_oi_bitmap); -EXPORT_SYMBOL(_ni_bitmap); -EXPORT_SYMBOL(_zb_findmap); -EXPORT_SYMBOL(__copy_from_user_fixup); -EXPORT_SYMBOL(__copy_to_user_fixup); +EXPORT_SYMBOL_NOVERS(_oi_bitmap); +EXPORT_SYMBOL_NOVERS(_ni_bitmap); +EXPORT_SYMBOL_NOVERS(_zb_findmap); +EXPORT_SYMBOL_NOVERS(__copy_from_user_fixup); +EXPORT_SYMBOL_NOVERS(__copy_to_user_fixup); /* * semaphore ops @@ -56,10 +56,7 @@ EXPORT_SYMBOL(csum_fold); EXPORT_SYMBOL(console_mode); EXPORT_SYMBOL(console_device); +EXPORT_SYMBOL_NOVERS(do_call_softirq); -#if CONFIG_IP_MULTICAST -/* Required for lcs gigabit ethernet multicast support */ -EXPORT_SYMBOL(arp_mc_map); -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/setup.c linux.ac/arch/s390/kernel/setup.c --- linux.vanilla/arch/s390/kernel/setup.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390/kernel/setup.c Wed Oct 10 01:47:34 2001 @@ -47,6 +47,9 @@ unsigned int console_device = -1; unsigned long memory_size = 0; unsigned long machine_flags = 0; +struct { unsigned long addr, size, type; } memory_chunk[16]; +#define CHUNK_READ_WRITE 0 +#define CHUNK_READ_ONLY 1 __u16 boot_cpu_addr; int cpus_initialized = 0; unsigned long cpu_initialized = 0; @@ -258,6 +261,8 @@ * Setup function called from init/main.c just after the banner * was printed. */ +extern char _pstart, _pend, _stext; + void __init setup_arch(char **cmdline_p) { unsigned long bootmap_size; @@ -267,19 +272,14 @@ unsigned long start_pfn, end_pfn; static unsigned int smptrap=0; unsigned long delay = 0; + struct _lowcore *lowcore; + int i; if (smptrap) return; smptrap=1; /* - * Setup lowcore information for boot cpu - */ - cpu_init(); - boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; - __cpu_logical_map[0] = boot_cpu_addr; - - /* * print what head.S has found out about the machine */ printk((MACHINE_IS_VM) ? @@ -291,7 +291,7 @@ ROOT_DEV = to_kdev_t(0x0100); memory_start = (unsigned long) &_end; /* fixit if use $CODELO etc*/ - memory_end = memory_size; + memory_end = memory_size & ~0x400000UL; /* align memory end to 4MB */ /* * We need some free virtual space to be able to do vmalloc. * On a machine with 2GB memory we make sure that we have at @@ -373,10 +373,25 @@ bootmap_size = init_bootmem(start_pfn, end_pfn); /* - * Register RAM pages with the bootmem allocator. + * Register RAM areas with the bootmem allocator. */ - free_bootmem(start_pfn << PAGE_SHIFT, - (end_pfn - start_pfn) << PAGE_SHIFT); + for (i = 0; i < 16 && memory_chunk[i].size > 0; i++) { + unsigned long start_chunk, end_chunk; + + if (memory_chunk[i].type != CHUNK_READ_WRITE) + continue; + start_chunk = (memory_chunk[i].addr + PAGE_SIZE - 1); + start_chunk >>= PAGE_SHIFT; + end_chunk = (memory_chunk[i].addr + memory_chunk[i].size); + end_chunk >>= PAGE_SHIFT; + if (start_chunk < start_pfn) + start_chunk = start_pfn; + if (end_chunk > end_pfn) + end_chunk = end_pfn; + if (start_chunk < end_chunk) + free_bootmem(start_chunk << PAGE_SHIFT, + (end_chunk - start_chunk) << PAGE_SHIFT); + } /* * Reserve the bootmem bitmap itself as well. We do this in two @@ -401,6 +416,36 @@ } #endif + /* + * Setup lowcore for boot cpu + */ + lowcore = (struct _lowcore *) + __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0); + memset(lowcore, 0, PAGE_SIZE); + lowcore->restart_psw.mask = _RESTART_PSW_MASK; + lowcore->restart_psw.addr = _ADDR_31 + (addr_t) &restart_int_handler; + lowcore->external_new_psw.mask = _EXT_PSW_MASK; + lowcore->external_new_psw.addr = _ADDR_31 + (addr_t) &ext_int_handler; + lowcore->svc_new_psw.mask = _SVC_PSW_MASK; + lowcore->svc_new_psw.addr = _ADDR_31 + (addr_t) &system_call; + lowcore->program_new_psw.mask = _PGM_PSW_MASK; + lowcore->program_new_psw.addr = _ADDR_31 + (addr_t) &pgm_check_handler; + lowcore->mcck_new_psw.mask = _MCCK_PSW_MASK; + lowcore->mcck_new_psw.addr = _ADDR_31 + (addr_t) &mcck_int_handler; + lowcore->io_new_psw.mask = _IO_PSW_MASK; + lowcore->io_new_psw.addr = _ADDR_31 + (addr_t) &io_int_handler; + lowcore->ipl_device = S390_lowcore.ipl_device; + lowcore->kernel_stack = ((__u32) &init_task_union) + 8192; + lowcore->async_stack = (__u32) + __alloc_bootmem(2*PAGE_SIZE, 2*PAGE_SIZE, 0) + 8192; + set_prefix((__u32) lowcore); + cpu_init(); + boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; + __cpu_logical_map[0] = boot_cpu_addr; + + /* + * Create kernel page tables and switch to virtual addressing. + */ paging_init(); res = alloc_bootmem_low(sizeof(struct resource)); @@ -433,30 +478,49 @@ } /* - * Get CPU information for use by the procfs. + * get_cpuinfo - Get information on one CPU for use by procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. */ -int get_cpuinfo(char * buffer) +int get_cpuinfo(char *buffer, unsigned *cpu_np) { struct cpuinfo_S390 *cpuinfo; char *p = buffer; - int i; + unsigned n; - p += sprintf(p,"vendor_id : IBM/S390\n" - "# processors : %i\n" - "bogomips per cpu: %lu.%02lu\n", - smp_num_cpus, loops_per_jiffy/(500000/HZ), - (loops_per_jiffy/(5000/HZ))%100); - for (i = 0; i < smp_num_cpus; i++) { - cpuinfo = &safe_get_cpu_lowcore(i).cpu_data; - p += sprintf(p,"processor %i: " - "version = %02X, " - "identification = %06X, " - "machine = %04X\n", - i, cpuinfo->cpu_id.version, - cpuinfo->cpu_id.ident, - cpuinfo->cpu_id.machine); - } + n = *cpu_np; + while (n < NR_CPUS && (cpu_online_map & (1 << n)) == 0) + n++; + if (n >= NR_CPUS) { + *cpu_np = NR_CPUS; + return (0); + } + *cpu_np = n + 1; + + if (n == 0) { + p += sprintf(p,"vendor_id : IBM/S390\n" + "# processors : %i\n" + "bogomips per cpu: %lu.%02lu\n", + smp_num_cpus, loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ))%100); + } + cpuinfo = &safe_get_cpu_lowcore(n).cpu_data; + p += sprintf(p,"processor %i: " + "version = %02X, " + "identification = %06X, " + "machine = %04X\n", + n, cpuinfo->cpu_id.version, + cpuinfo->cpu_id.ident, + cpuinfo->cpu_id.machine); return p - buffer; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/signal.c linux.ac/arch/s390/kernel/signal.c --- linux.vanilla/arch/s390/kernel/signal.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390/kernel/signal.c Wed Oct 10 01:47:34 2001 @@ -12,6 +12,7 @@ * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson */ +#include #include #include #include @@ -23,31 +24,25 @@ #include #include #include +#include #include #include -#define DEBUG_SIG 0 - #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -/* pretcode & sig are used to store the return addr on Intel - & the signal no as the first parameter we do this differently - using gpr14 & gpr2. */ - -#define SIGFRAME_COMMON \ -__u8 callee_used_stack[__SIGNAL_FRAMESIZE]; \ -struct sigcontext sc; \ -_sigregs sregs; \ -__u8 retcode[S390_SYSCALL_SIZE]; typedef struct { - SIGFRAME_COMMON + __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; + struct sigcontext sc; + _sigregs sregs; + __u8 retcode[S390_SYSCALL_SIZE]; } sigframe; typedef struct { - SIGFRAME_COMMON + __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; + __u8 retcode[S390_SYSCALL_SIZE]; struct siginfo info; struct ucontext uc; } rt_sigframe; @@ -205,7 +200,7 @@ err=__copy_from_user(regs,&sregs->regs,sizeof(_s390_regs_common)); if(!err) { - regs->orig_gpr2 = -1; /* disable syscall checks */ + regs->trap = -1; /* disable syscall checks */ regs->psw.mask=(saved_psw.mask&~PSW_MASK_DEBUGCHANGE)| (regs->psw.mask&PSW_MASK_DEBUGCHANGE); regs->psw.addr=(saved_psw.addr&~PSW_ADDR_DEBUGCHANGE)| @@ -217,53 +212,51 @@ return(err); } -static int -restore_sigcontext(struct sigcontext *sc, struct pt_regs *regs, - _sigregs *sregs,sigset_t *set) -{ - unsigned int err; - - err=restore_sigregs(regs,sregs); - if(!err) - err=__copy_from_user(&set->sig,&sc->oldmask,_SIGMASK_COPY_SIZE); - return(err); -} - -int sigreturn_common(struct pt_regs *regs,int framesize) +asmlinkage long sys_sigreturn(struct pt_regs *regs) { sigframe *frame = (sigframe *)regs->gprs[15]; sigset_t set; if (verify_area(VERIFY_READ, frame, sizeof(*frame))) - return -1; - if (restore_sigcontext(&frame->sc,regs,&frame->sregs,&set)) - return -1; + goto badframe; + if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE)) + goto badframe; + sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); - return 0; -} - -asmlinkage int sys_sigreturn(struct pt_regs *regs) -{ - if (sigreturn_common(regs,sizeof(sigframe))) + if (restore_sigregs(regs, &frame->sregs)) goto badframe; + return regs->gprs[2]; badframe: force_sig(SIGSEGV, current); return 0; -} +} -asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) +asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) { rt_sigframe *frame = (rt_sigframe *)regs->gprs[15]; + sigset_t set; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); - if (sigreturn_common(regs,sizeof(rt_sigframe))) + if (restore_sigregs(regs, &frame->uc.uc_mcontext)) goto badframe; + /* It is more difficult to avoid calling this function than to call it and ignore errors. */ do_sigaltstack(&frame->uc.uc_stack, NULL, regs->gprs[15]); @@ -272,7 +265,7 @@ badframe: force_sig(SIGSEGV, current); return 0; -} +} /* * Set up a signal frame. @@ -306,58 +299,48 @@ return (void *)((sp - frame_size) & -8ul); } -static void *setup_frame_common(int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs * regs, - int frame_size,u16 retcode) +static inline int map_signal(int sig) { - sigframe *frame; - int err; + if (current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32) + return current->exec_domain->signal_invmap[sig]; + else + return sig; +} - frame = get_sigframe(ka, regs,frame_size); - if (!access_ok(VERIFY_WRITE, frame,frame_size)) - return 0; - err = save_sigregs(regs,&frame->sregs); - if(!err) - err=__put_user(&frame->sregs,&frame->sc.sregs); - if(!err) +static void setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) +{ + sigframe *frame = get_sigframe(ka, regs, sizeof(sigframe)); + if (!access_ok(VERIFY_WRITE, frame, sizeof(sigframe))) + goto give_sigsegv; + + if (__copy_to_user(&frame->sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE)) + goto give_sigsegv; + + if (save_sigregs(regs, &frame->sregs)) + goto give_sigsegv; + if (__put_user(&frame->sregs, &frame->sc.sregs)) + goto give_sigsegv; - err=__copy_to_user(&frame->sc.oldmask,&set->sig,_SIGMASK_COPY_SIZE); - if(!err) - { - regs->gprs[2]=(current->exec_domain - && current->exec_domain->signal_invmap - && sig < 32 - ? current->exec_domain->signal_invmap[sig] - : sig); - /* Set up registers for signal handler */ - regs->gprs[15] = (addr_t)frame; - regs->psw.addr = FIX_PSW(ka->sa.sa_handler); - regs->psw.mask = _USER_PSW_MASK; - } /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa.sa_flags & SA_RESTORER) { regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); } else { regs->gprs[14] = FIX_PSW(frame->retcode); - err |= __put_user(retcode, (u16 *)(frame->retcode)); + if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, + (u16 *)(frame->retcode))) + goto give_sigsegv; } - return(err ? 0:frame); -} -static void setup_frame(int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs * regs) -{ - sigframe *frame; + /* Set up registers for signal handler */ + regs->gprs[15] = (addr_t)frame; + regs->psw.addr = FIX_PSW(ka->sa.sa_handler); + regs->psw.mask = _USER_PSW_MASK; - if((frame=setup_frame_common(sig,ka,set,regs,sizeof(sigframe), - (S390_SYSCALL_OPCODE|__NR_sigreturn)))==0) - goto give_sigsegv; -#if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", - current->comm, current->pid, frame, regs->eip, frame->pretcode); -#endif - /* Martin wants this for pthreads */ + regs->gprs[2] = map_signal(sig); regs->gprs[3] = (addr_t)&frame->sc; /* We forgot to include these in the sigcontext. @@ -375,34 +358,44 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs * regs) { - rt_sigframe *frame; - addr_t orig_sp=regs->gprs[15]; - int err; + int err = 0; + rt_sigframe *frame = get_sigframe(ka, regs, sizeof(rt_sigframe)); + if (!access_ok(VERIFY_WRITE, frame, sizeof(rt_sigframe))) + goto give_sigsegv; - if((frame=setup_frame_common(sig,ka,set,regs,sizeof(rt_sigframe), - (S390_SYSCALL_OPCODE|__NR_rt_sigreturn)))==0) + if (copy_siginfo_to_user(&frame->info, info)) goto give_sigsegv; - - err = copy_siginfo_to_user(&frame->info, info); /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); err |= __put_user(0, &frame->uc.uc_link); err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); - err |= __put_user(sas_ss_flags(orig_sp), + err |= __put_user(sas_ss_flags(regs->gprs[15]), &frame->uc.uc_stack.ss_flags); err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); - err |= __put_user(&frame->sc,&frame->uc.sc); - regs->gprs[3] = (addr_t)&frame->info; - regs->gprs[4] = (addr_t)&frame->uc; - + err |= save_sigregs(regs, &frame->uc.uc_mcontext); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); if (err) goto give_sigsegv; -#if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", - current->comm, current->pid, frame, regs->eip, frame->pretcode); -#endif + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); + } else { + regs->gprs[14] = FIX_PSW(frame->retcode); + err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, + (u16 *)(frame->retcode)); + } + + /* Set up registers for signal handler */ + regs->gprs[15] = (addr_t)frame; + regs->psw.addr = FIX_PSW(ka->sa.sa_handler); + regs->psw.mask = _USER_PSW_MASK; + + regs->gprs[2] = map_signal(sig); + regs->gprs[3] = (addr_t)&frame->info; + regs->gprs[4] = (addr_t)&frame->uc; return; give_sigsegv: @@ -551,13 +544,16 @@ continue; /* FALLTHRU */ - case SIGSTOP: + case SIGSTOP: { + struct signal_struct *sig; set_current_state(TASK_STOPPED); current->exit_code = signr; - if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + sig = current->p_pptr->sig; + if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) notify_parent(current, SIGCHLD); schedule(); continue; + } case SIGQUIT: case SIGILL: case SIGTRAP: case SIGABRT: case SIGFPE: case SIGSEGV: diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/smp.c linux.ac/arch/s390/kernel/smp.c --- linux.vanilla/arch/s390/kernel/smp.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390/kernel/smp.c Wed Oct 10 01:47:34 2001 @@ -57,6 +57,8 @@ spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; +unsigned long cpu_online_map; + /* * Setup routine for controlling SMP activation * @@ -92,6 +94,95 @@ extern void reipl(unsigned long devno); +static sigp_ccode smp_ext_bitcall(int, ec_bit_sig); +static void smp_ext_bitcall_others(ec_bit_sig); + +/* + * Structure and data for smp_call_function(). This is designed to minimise + * static memory requirements. It also looks cleaner. + */ +static spinlock_t call_lock = SPIN_LOCK_UNLOCKED; + +struct call_data_struct { + void (*func) (void *info); + void *info; + atomic_t started; + atomic_t finished; + int wait; +}; + +static struct call_data_struct * call_data; + +/* + * 'Call function' interrupt callback + */ +static void do_call_function(void) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + atomic_inc(&call_data->started); + (*func)(info); + if (wait) + atomic_inc(&call_data->finished); +} + +/* + * this function sends a 'generic call function' IPI to all other CPUs + * in the system. + */ + +int smp_call_function (void (*func) (void *info), void *info, int nonatomic, + int wait) +/* + * [SUMMARY] Run a function on all other CPUs. + * The function to run. This must be fast and non-blocking. + * An arbitrary pointer to pass to the function. + * currently unused. + * If true, wait (atomically) until function has completed on other CPUs. + * [RETURNS] 0 on success, else a negative status code. Does not return until + * remote CPUs are nearly ready to execute <> or are or have executed. + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler, you may call it from a bottom half handler. + */ +{ + struct call_data_struct data; + int cpus = smp_num_cpus-1; + + if (!cpus || !atomic_read(&smp_commenced)) + return 0; + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + spin_lock_bh(&call_lock); + call_data = &data; + /* Send a message to all other CPUs and wait for them to respond */ + smp_ext_bitcall_others(ec_call_function); + + /* Wait for response */ + while (atomic_read(&data.started) != cpus) + barrier(); + + if (wait) + while (atomic_read(&data.finished) != cpus) + barrier(); + spin_unlock_bh(&call_lock); + + return 0; +} + + +/* + * Various special callbacks + */ + void do_machine_restart(void) { smp_send_stop(); @@ -148,7 +239,6 @@ void do_ext_call_interrupt(struct pt_regs *regs, __u16 code) { - ec_ext_call *ec, *next; int bits; /* @@ -169,131 +259,15 @@ do_machine_halt(); if (test_bit(ec_power_off, &bits)) do_machine_power_off(); - if (test_bit(ec_ptlb, &bits)) - local_flush_tlb(); - - /* - * Handle external call commands with a parameter area - */ - do { - ec = (ec_ext_call *) atomic_read(&S390_lowcore.ext_call_queue); - } while (atomic_compare_and_swap((int) ec, 0, - &S390_lowcore.ext_call_queue)); - if (ec == NULL) - return; /* no command signals */ - - /* Make a fifo out of the lifo */ - next = ec->next; - ec->next = NULL; - while (next != NULL) { - ec_ext_call *tmp = next->next; - next->next = ec; - ec = next; - next = tmp; - } - - /* Execute every sigp command on the queue */ - while (ec != NULL) { - switch (ec->cmd) { - case ec_callback_async: { - void (*func)(void *info); - void *info; - - func = ec->func; - info = ec->info; - atomic_set(&ec->status,ec_executing); - (func)(info); - return; - } - case ec_callback_sync: - atomic_set(&ec->status,ec_executing); - (ec->func)(ec->info); - atomic_set(&ec->status,ec_done); - return; - default: - } - ec = ec->next; - } -} - -/* - * Send a callback sigp to another cpu. - */ -sigp_ccode -smp_ext_call(int cpu, void (*func)(void *info), void *info, int wait) -{ - struct _lowcore *lowcore = &get_cpu_lowcore(cpu); - sigp_ccode ccode; - ec_ext_call ec; - - ec.cmd = wait ? ec_callback_sync : ec_callback_async; - atomic_set(&ec.status, ec_pending); - ec.func = func; - ec.info = info; - do { - ec.next = (ec_ext_call*) atomic_read(&lowcore->ext_call_queue); - } while (atomic_compare_and_swap((int) ec.next, (int)(&ec), - &lowcore->ext_call_queue)); - /* - * We try once to deliver the signal. There are four possible - * return codes: - * 0) Order code accepted - can't show up on an external call - * 1) Status stored - fine, wait for completion. - * 2) Busy - there is another signal pending. Thats fine too, because - * do_ext_call from the pending signal will execute all signals on - * the queue. We wait for completion. - * 3) Not operational - something very bad has happened to the cpu. - * do not wait for completion. - */ - ccode = signal_processor(cpu, sigp_external_call); - - if (ccode != sigp_not_operational) - /* wait for completion, FIXME: possible seed of a deadlock */ - while (atomic_read(&ec.status) != (wait?ec_done:ec_executing)); - - return ccode; -} - -/* - * Send a callback sigp to every other cpu in the system. - */ -void smp_ext_call_others(void (*func)(void *info), void *info, int wait) -{ - struct _lowcore *lowcore; - ec_ext_call ec[NR_CPUS]; - sigp_ccode ccode; - int i; - - for (i = 0; i < smp_num_cpus; i++) { - if (smp_processor_id() == i) - continue; - lowcore = &get_cpu_lowcore(i); - ec[i].cmd = wait ? ec_callback_sync : ec_callback_async; - atomic_set(&ec[i].status, ec_pending); - ec[i].func = func; - ec[i].info = info; - do { - ec[i].next = (ec_ext_call *) - atomic_read(&lowcore->ext_call_queue); - } while (atomic_compare_and_swap((int) ec[i].next, (int)(ec+i), - &lowcore->ext_call_queue)); - ccode = signal_processor(i, sigp_external_call); - } - - /* wait for completion, FIXME: possible seed of a deadlock */ - for (i = 0; i < smp_num_cpus; i++) { - if (smp_processor_id() == i) - continue; - while (atomic_read(&ec[i].status) != - (wait ? ec_done:ec_executing)); - } + if (test_bit(ec_call_function, &bits)) + do_call_function(); } /* * Send an external call sigp to another cpu and return without waiting * for its completion. */ -sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig) +static sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig) { struct _lowcore *lowcore = &get_cpu_lowcore(cpu); sigp_ccode ccode; @@ -310,7 +284,7 @@ * Send an external call sigp to every other cpu in the system and * return without waiting for its completion. */ -void smp_ext_bitcall_others(ec_bit_sig sig) +static void smp_ext_bitcall_others(ec_bit_sig sig) { struct _lowcore *lowcore; sigp_ccode ccode; @@ -329,51 +303,6 @@ } /* - * cycles through all the cpus, - * returns early if info is not NULL & the processor has something - * of intrest to report in the info structure. - * it returns the next cpu to check if it returns early. - * i.e. it should be used as follows if you wish to receive info. - * next_cpu=0; - * do - * { - * info->cpu=next_cpu; - * next_cpu=smp_signal_others(order_code,parameter,1,info); - * ... check info here - * } while(next_cpu<=smp_num_cpus) - * - * if you are lazy just use it like - * smp_signal_others(order_code,parameter,0,1,NULL); - */ -int smp_signal_others(sigp_order_code order_code, u32 parameter, - int spin, sigp_info *info) -{ - sigp_ccode ccode; - u32 dummy; - u16 i; - - if (info) - info->intresting = 0; - for (i = (info ? info->cpu : 0); i < smp_num_cpus; i++) { - if (smp_processor_id() != i) { - do { - ccode = signal_processor_ps( - (info ? &info->status : &dummy), - parameter, i, order_code); - } while(spin && ccode == sigp_busy); - if (info && ccode != sigp_order_code_accepted) { - info->intresting = 1; - info->cpu = i; - info->ccode = ccode; - i++; - break; - } - } - } - return i; -} - -/* * this function sends a 'stop' sigp to all other CPUs in the system. * it goes straight through. */ @@ -390,7 +319,18 @@ /* stop all processors */ - smp_signal_others(sigp_stop, 0, 1, NULL); + for (i = 0; i < smp_num_cpus; i++) { + if (smp_processor_id() != i) { + int ccode; + do { + ccode = signal_processor_ps( + &dummy, + 0, + i, + sigp_stop); + } while(ccode == sigp_busy); + } + } /* store status of all processors in their lowcores (real 0) */ @@ -419,7 +359,7 @@ void smp_ptlb_all(void) { - smp_ext_call_others(smp_ptlb_callback, NULL, 1); + smp_call_function(smp_ptlb_callback, NULL, 0, 1); local_flush_tlb(); } @@ -482,7 +422,7 @@ parms.end_ctl = cr; parms.orvals[cr] = 1 << bit; parms.andvals[cr] = 0xFFFFFFFF; - smp_ext_call_others(smp_ctl_bit_callback, &parms, 1); + smp_call_function(smp_ctl_bit_callback, &parms, 0, 1); } __ctl_set_bit(cr, bit); } @@ -498,36 +438,12 @@ parms.end_ctl = cr; parms.orvals[cr] = 0x00000000; parms.andvals[cr] = ~(1 << bit); - smp_ext_call_others(smp_ctl_bit_callback, &parms, 1); + smp_call_function(smp_ctl_bit_callback, &parms, 0, 1); } __ctl_clear_bit(cr, bit); } /* - * Call a function on all other processors - */ - -int -smp_call_function(void (*func)(void *info), void *info, int retry, int wait) -/* - * [SUMMARY] Run a function on all other CPUs. - * The function to run. This must be fast and non-blocking. - * An arbitrary pointer to pass to the function. - * currently unused. - * If true, wait (atomically) until function has completed on other CPUs. - * [RETURNS] 0 on success, else a negative status code. Does not return until - * remote CPUs are nearly ready to execute <> or are or have executed. - * - * You must not call this function with disabled interrupts or from a - * hardware interrupt handler, you may call it from a bottom half handler. - */ -{ - if (atomic_read(&smp_commenced) != 0) - smp_ext_call_others(func, info, wait); - return 0; -} - -/* * Lets check how many CPUs we have. */ @@ -537,6 +453,7 @@ current->processor = 0; smp_num_cpus = 1; + cpu_online_map = 1; for (curr_cpu = 0; curr_cpu <= 65535 && smp_num_cpus < max_cpus; curr_cpu++) { if ((__u16) curr_cpu == boot_cpu_addr) @@ -556,6 +473,7 @@ * Activate a secondary processor. */ extern void init_100hz_timer(void); +extern int pfault_init(void); extern int pfault_token(void); int __init start_secondary(void *cpuvoid) @@ -620,15 +538,20 @@ init_tasks[cpu] = idle; cpu_lowcore=&get_cpu_lowcore(cpu); - cpu_lowcore->kernel_stack=idle->thread.ksp; - __asm__ __volatile__("stctl 0,15,%0\n\t" - "stam 0,15,%1" + cpu_lowcore->save_area[15] = idle->thread.ksp; + cpu_lowcore->kernel_stack = (idle->thread.ksp | 8191) + 1; + __asm__ __volatile__("la 1,%0\n\t" + "stctl 0,15,0(1)\n\t" + "la 1,%1\n\t" + "stam 0,15,0(1)" : "=m" (cpu_lowcore->cregs_save_area[0]), "=m" (cpu_lowcore->access_regs_save_area[0]) - : : "memory"); + : : "1", "memory"); eieio(); signal_processor(cpu,sigp_restart); + /* Mark this cpu as online */ + set_bit(cpu, &cpu_online_map); } /* @@ -650,12 +573,12 @@ } /* - * Cycle through the processors sending APIC IPIs to boot each. + * Cycle through the processors sending sigp_restart to boot each. */ void __init smp_boot_cpus(void) { - struct _lowcore *curr_lowcore; + unsigned long async_stack; sigp_ccode ccode; int i; @@ -680,34 +603,37 @@ for(i = 0; i < smp_num_cpus; i++) { - curr_lowcore = (struct _lowcore *) - __get_free_page(GFP_KERNEL|GFP_DMA); - if (curr_lowcore == NULL) { - printk("smp_boot_cpus failed to allocate prefix memory\n"); - break; - } - lowcore_ptr[i] = curr_lowcore; - memcpy(curr_lowcore, &S390_lowcore, sizeof(struct _lowcore)); + lowcore_ptr[i] = (struct _lowcore *) + __get_free_page(GFP_KERNEL|GFP_DMA); + if (lowcore_ptr[i] == NULL) + panic("smp_boot_cpus failed to " + "allocate prefix memory\n"); + async_stack = __get_free_pages(GFP_KERNEL,1); + if (async_stack == 0) + panic("smp_boot_cpus failed to allocate " + "asyncronous interrupt stack\n"); + + memcpy(lowcore_ptr[i], &S390_lowcore, sizeof(struct _lowcore)); + lowcore_ptr[i]->async_stack = async_stack + (2 * PAGE_SIZE); /* * Most of the parameters are set up when the cpu is * started up. */ - if (smp_processor_id() == i) - set_prefix((u32) curr_lowcore); - else { - ccode = signal_processor_p((u32)(curr_lowcore), - i, sigp_set_prefix); - if(ccode) { - /* if this gets troublesome I'll have to do - * something about it. */ - printk("ccode %d for cpu %d returned when " - "setting prefix in smp_boot_cpus not good.\n", - (int) ccode, (int) i); - } - else - do_boot_cpu(i); - } - } + if (smp_processor_id() == i) + set_prefix((u32) lowcore_ptr[i]); + else { + ccode = signal_processor_p((u32)(lowcore_ptr[i]), + i, sigp_set_prefix); + if (ccode) + /* if this gets troublesome I'll have to do + * something about it. */ + printk("ccode %d for cpu %d returned when " + "setting prefix in smp_boot_cpus not good.\n", + (int) ccode, (int) i); + else + do_boot_cpu(i); + } + } } /* @@ -746,8 +672,6 @@ s390_do_profile(regs->psw.addr); if (!--prof_counter[cpu]) { - int system = 1-user; - struct task_struct * p = current; /* * The multiplier may have changed since the last time we got @@ -771,9 +695,7 @@ * WrongThing (tm) to do. */ - irq_enter(cpu, 0); update_process_times(user); - irq_exit(cpu, 0); } } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/time.c linux.ac/arch/s390/kernel/time.c --- linux.vanilla/arch/s390/kernel/time.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390/kernel/time.c Wed Oct 10 01:47:34 2001 @@ -151,15 +151,14 @@ void do_timer_interrupt(struct pt_regs *regs, __u16 error_code) { - unsigned long flags; + int cpu = smp_processor_id(); + + irq_enter(cpu, 0); /* * reset timer to 10ms minus time already elapsed * since timer-interrupt pending */ - - save_flags(flags); - cli(); #ifdef CONFIG_SMP if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr) { write_lock(&xtime_lock); @@ -195,8 +194,8 @@ write_unlock(&xtime_lock); #endif } - restore_flags(flags); + irq_exit(cpu, 0); } /* @@ -250,4 +249,7 @@ init_timer_cc -= 0x8126d60e46000000LL - (0x3c26700LL*1000000*4096); tod_to_timeval(init_timer_cc, &xtime); + + /* Set do_get_fast_time function pointer. */ + do_get_fast_time = do_gettimeofday; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/kernel/traps.c linux.ac/arch/s390/kernel/traps.c --- linux.vanilla/arch/s390/kernel/traps.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390/kernel/traps.c Wed Oct 10 01:47:34 2001 @@ -60,14 +60,16 @@ extern void pfault_interrupt(struct pt_regs *regs, __u16 error_code); #endif -spinlock_t die_lock; +spinlock_t die_lock = SPIN_LOCK_UNLOCKED; void die(const char * str, struct pt_regs * regs, long err) { console_verbose(); spin_lock_irq(&die_lock); + bust_spinlocks(1); printk("%s: %04lx\n", str, err & 0xffff); show_regs(regs); + bust_spinlocks(0); spin_unlock_irq(&die_lock); do_exit(SIGSEGV); } @@ -135,7 +137,7 @@ #if CONFIG_REMOTE_DEBUG if(gdb_stub_initialised) { - gdb_stub_handle_exception((gdb_pt_regs *)regs,signal); + gdb_stub_handle_exception((struct gdb_pt_regs *)regs,signal); return 0; } #endif @@ -211,7 +213,7 @@ specification_exception(struct pt_regs * regs, long interruption_code) { __u8 opcode[6]; - __u16 *location; + __u16 *location = NULL; int signal = 0; if (regs->psw.mask & PSW_PROBLEM_STATE) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/math-emu/math.c linux.ac/arch/s390/math-emu/math.c --- linux.vanilla/arch/s390/math-emu/math.c Thu Apr 12 20:16:35 2001 +++ linux.ac/arch/s390/math-emu/math.c Wed Oct 10 01:47:34 2001 @@ -1471,20 +1471,98 @@ } /* Test data class long double */ -static int emu_tcxb (int rx, double *val) { - display_emulation_not_implemented("tcxb"); +static int emu_tcxb (int rx, long val) { + FP_DECL_Q(QA); + mathemu_ldcv cvt; + int bit; + + cvt.w.high = current->thread.fp_regs.fprs[rx].ui; + cvt.w.low = current->thread.fp_regs.fprs[rx+2].ui; + FP_UNPACK_RAW_QP(QA, &cvt.ld); + switch (QA_e) { + default: + bit = 8; /* normalized number */ + break; + case 0: + if (_FP_FRAC_ZEROP_4(QA)) + bit = 10; /* zero */ + else + bit = 6; /* denormalized number */ + break; + case _FP_EXPMAX_Q: + if (_FP_FRAC_ZEROP_4(QA)) + bit = 4; /* infinity */ + else if (_FP_FRAC_HIGH_RAW_Q(QA) & _FP_QNANBIT_Q) + bit = 2; /* quiet NAN */ + else + bit = 0; /* signaling NAN */ + break; + } + if (!QA_s) + bit++; + emu_set_CC(((__u32) val >> bit) & 1); return 0; } /* Test data class double */ -static int emu_tcdb (int rx, double *val) { - display_emulation_not_implemented("tcdb"); +static int emu_tcdb (int rx, long val) { + FP_DECL_D(DA); + int bit; + + FP_UNPACK_RAW_DP(DA, ¤t->thread.fp_regs.fprs[rx].d); + switch (DA_e) { + default: + bit = 8; /* normalized number */ + break; + case 0: + if (_FP_FRAC_ZEROP_2(DA)) + bit = 10; /* zero */ + else + bit = 6; /* denormalized number */ + break; + case _FP_EXPMAX_D: + if (_FP_FRAC_ZEROP_2(DA)) + bit = 4; /* infinity */ + else if (_FP_FRAC_HIGH_RAW_D(DA) & _FP_QNANBIT_D) + bit = 2; /* quiet NAN */ + else + bit = 0; /* signaling NAN */ + break; + } + if (!DA_s) + bit++; + emu_set_CC(((__u32) val >> bit) & 1); return 0; } /* Test data class float */ -static int emu_tceb (int rx, __u32 val) { - display_emulation_not_implemented("tceb"); +static int emu_tceb (int rx, long val) { + FP_DECL_S(SA); + int bit; + + FP_UNPACK_RAW_SP(SA, ¤t->thread.fp_regs.fprs[rx].f); + switch (SA_e) { + default: + bit = 8; /* normalized number */ + break; + case 0: + if (_FP_FRAC_ZEROP_1(SA)) + bit = 10; /* zero */ + else + bit = 6; /* denormalized number */ + break; + case _FP_EXPMAX_S: + if (_FP_FRAC_ZEROP_1(SA)) + bit = 4; /* infinity */ + else if (_FP_FRAC_HIGH_RAW_S(SA) & _FP_QNANBIT_S) + bit = 2; /* quiet NAN */ + else + bit = 0; /* signaling NAN */ + break; + } + if (!SA_s) + bit++; + emu_set_CC(((__u32) val >> bit) & 1); return 0; } @@ -1796,13 +1874,13 @@ int _fex = 0; static const __u8 format_table[256] = { - [0x04] = 0x08,[0x05] = 0x07,[0x06] = 0x09,[0x07] = 0x07, - [0x08] = 0x03,[0x09] = 0x03,[0x0a] = 0x03,[0x0b] = 0x03, - [0x0c] = 0x08,[0x0d] = 0x03,[0x0e] = 0x06,[0x0f] = 0x06, - [0x10] = 0x03,[0x11] = 0x02,[0x12] = 0x01,[0x14] = 0x03, - [0x15] = 0x02,[0x17] = 0x03,[0x18] = 0x02,[0x19] = 0x02, - [0x1a] = 0x02,[0x1b] = 0x02,[0x1c] = 0x02,[0x1d] = 0x02, - [0x1e] = 0x05,[0x1f] = 0x05, + [0x04] = 0x06,[0x05] = 0x05,[0x06] = 0x07,[0x07] = 0x05, + [0x08] = 0x02,[0x09] = 0x02,[0x0a] = 0x02,[0x0b] = 0x02, + [0x0c] = 0x06,[0x0d] = 0x02,[0x0e] = 0x04,[0x0f] = 0x04, + [0x10] = 0x08,[0x11] = 0x09,[0x12] = 0x0a,[0x14] = 0x02, + [0x15] = 0x01,[0x17] = 0x02,[0x18] = 0x01,[0x19] = 0x01, + [0x1a] = 0x01,[0x1b] = 0x01,[0x1c] = 0x01,[0x1d] = 0x01, + [0x1e] = 0x03,[0x1f] = 0x03, }; static const void *jump_table[]= { [0x04] = emu_ldeb,[0x05] = emu_lxdb,[0x06] = emu_lxeb, @@ -1817,25 +1895,7 @@ }; switch (format_table[opcode[5]]) { - case 1: /* RXE format, long double constant */ { - __u64 *dxb, temp[2]; - __u32 opc; - - if ((opcode[1] >> 4) & 2) - return SIGILL; - emu_store_regd((opcode[1] >> 4) & 15); - emu_store_regd(((opcode[1] >> 4) & 15) + 2); - opc = *((__u32 *) opcode); - dxb = (__u64 *) calc_addr(regs, opc >> 16, opc >> 12, opc); - mathemu_copy_from_user(&temp, dxb, 16); - /* call the emulation function */ - _fex = ((int (*)(int, long double *)) jump_table[opcode[5]]) - (opcode[1] >> 4, (long double *) &temp); - emu_load_regd((opcode[1] >> 4) & 15); - emu_load_regd(((opcode[1] >> 4) & 15) + 2); - break; - } - case 2: /* RXE format, double constant */ { + case 1: /* RXE format, double constant */ { __u64 *dxb, temp; __u32 opc; @@ -1849,7 +1909,7 @@ emu_load_regd((opcode[1] >> 4) & 15); break; } - case 3: /* RXE format, float constant */ { + case 2: /* RXE format, float constant */ { __u32 *dxb, temp; __u32 opc; @@ -1863,27 +1923,7 @@ emu_load_rege((opcode[1] >> 4) & 15); break; } - case 4: /* RXF format, long double constant */ { - __u64 *dxb, temp[2]; - __u32 opc; - - if (((opcode[1] >> 4) & 0x20) || ((opcode[4] >> 4) & 0x20)) - return SIGILL; - emu_store_regd((opcode[1] >> 4) & 15); - emu_store_regd(((opcode[1] >> 4) & 15) + 2); - emu_store_regd((opcode[4] >> 4) & 15); - emu_store_regd(((opcode[4] >> 4) & 15) + 2); - opc = *((__u32 *) opcode); - dxb = (__u64 *) calc_addr(regs, opc >> 16, opc >> 12, opc); - mathemu_copy_from_user(&temp, dxb, 16); - /* call the emulation function */ - _fex = ((int (*)(int,long double *,int)) jump_table[opcode[5]]) - (opcode[1] >> 4, (double *) &temp, opcode[4] >> 4); - emu_load_regd((opcode[1] >> 4) & 15); - emu_load_regd(((opcode[1] >> 4) & 15) + 2); - break; - } - case 5: /* RXF format, double constant */ { + case 3: /* RXF format, double constant */ { __u64 *dxb, temp; __u32 opc; @@ -1898,7 +1938,7 @@ emu_load_regd((opcode[1] >> 4) & 15); break; } - case 6: /* RXF format, float constant */ { + case 4: /* RXF format, float constant */ { __u32 *dxb, temp; __u32 opc; @@ -1913,7 +1953,7 @@ emu_load_rege((opcode[4] >> 4) & 15); break; } - case 7: /* RXE format, double constant */ + case 5: /* RXE format, double constant */ /* store double and load long double */ { __u64 *dxb, temp; @@ -1931,7 +1971,7 @@ emu_load_regd(((opcode[1] >> 4) & 15) + 2); break; } - case 8: /* RXE format, float constant */ + case 6: /* RXE format, float constant */ /* store float and load double */ { __u32 *dxb, temp; @@ -1946,7 +1986,7 @@ emu_load_regd((opcode[1] >> 4) & 15); break; } - case 9: /* RXE format, float constant */ + case 7: /* RXE format, float constant */ /* store float and load long double */ { __u32 *dxb, temp; @@ -1962,6 +2002,45 @@ (opcode[1] >> 4, (float *) &temp); emu_load_regd((opcode[1] >> 4) & 15); emu_load_regd(((opcode[1] >> 4) & 15) + 2); + break; + } + case 8: /* RXE format, RX address used as int value */ { + __u64 dxb; + __u32 opc; + + emu_store_rege((opcode[1] >> 4) & 15); + opc = *((__u32 *) opcode); + dxb = (__u64) calc_addr(regs, opc >> 16, opc >> 12, opc); + /* call the emulation function */ + _fex = ((int (*)(int, long)) jump_table[opcode[5]]) + (opcode[1] >> 4, dxb); + break; + } + case 9: /* RXE format, RX address used as int value */ { + __u64 dxb; + __u32 opc; + + emu_store_regd((opcode[1] >> 4) & 15); + opc = *((__u32 *) opcode); + dxb = (__u64) calc_addr(regs, opc >> 16, opc >> 12, opc); + /* call the emulation function */ + _fex = ((int (*)(int, long)) jump_table[opcode[5]]) + (opcode[1] >> 4, dxb); + break; + } + case 10: /* RXE format, RX address used as int value */ { + __u64 dxb; + __u32 opc; + + if ((opcode[1] >> 4) & 2) + return SIGILL; + emu_store_regd((opcode[1] >> 4) & 15); + emu_store_regd(((opcode[1] >> 4) & 15) + 2); + opc = *((__u32 *) opcode); + dxb = (__u64) calc_addr(regs, opc >> 16, opc >> 12, opc); + /* call the emulation function */ + _fex = ((int (*)(int, long)) jump_table[opcode[5]]) + (opcode[1] >> 4, dxb); break; } default: /* invalid operation */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/mm/extable.c linux.ac/arch/s390/mm/extable.c --- linux.vanilla/arch/s390/mm/extable.c Fri May 12 19:41:45 2000 +++ linux.ac/arch/s390/mm/extable.c Wed Oct 10 01:47:35 2001 @@ -10,6 +10,7 @@ #include #include +#include #include extern const struct exception_table_entry __start___ex_table[]; @@ -36,28 +37,37 @@ return 0; } +extern spinlock_t modlist_lock; + unsigned long search_exception_table(unsigned long addr) { - unsigned long ret; + unsigned long ret = 0; + unsigned long flags; #ifndef CONFIG_MODULES addr &= 0x7fffffff; /* remove amode bit from address */ /* There is only the kernel to search. */ ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); - if (ret) return FIX_PSW(ret); + if (ret) ret = FIX_PSW(ret); + return ret; #else /* The kernel is the last "module" -- no need to treat it special. */ struct module *mp; addr &= 0x7fffffff; /* remove amode bit from address */ + + spin_lock_irqsave(&modlist_lock, flags); for (mp = module_list; mp != NULL; mp = mp->next) { - if (mp->ex_table_start == NULL) + if (mp->ex_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING))) continue; ret = search_one_table(mp->ex_table_start, mp->ex_table_end - 1, addr); - if (ret) return FIX_PSW(ret); + if (ret) { + ret = FIX_PSW(ret); + break; + } } + spin_unlock_irqrestore(&modlist_lock, flags); + return ret; #endif - - return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/mm/fault.c linux.ac/arch/s390/mm/fault.c --- linux.vanilla/arch/s390/mm/fault.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390/mm/fault.c Wed Oct 10 01:47:35 2001 @@ -4,6 +4,7 @@ * S390 version * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Hartmut Penner (hp@de.ibm.com) + * Ulrich Weigand (uweigand@de.ibm.com) * * Derived from "arch/i386/mm/fault.c" * Copyright (C) 1995 Linus Torvalds @@ -23,6 +24,7 @@ #include #include #include +#include #include #include @@ -34,6 +36,34 @@ #endif extern void die(const char *,struct pt_regs *,long); +static void force_sigsegv(struct task_struct *tsk, int code, void *address); + +extern spinlock_t timerlist_lock; + +/* + * Unlock any spinlocks which will prevent us from getting the + * message out (timerlist_lock is acquired through the + * console unblank code) + */ +void bust_spinlocks(int yes) +{ + spin_lock_init(&timerlist_lock); + if (yes) { + oops_in_progress = 1; + } else { + int loglevel_save = console_loglevel; + oops_in_progress = 0; + console_unblank(); + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; + printk(" "); + console_loglevel = loglevel_save; + } +} /* * This routine handles page faults. It determines the address, @@ -56,18 +86,36 @@ int si_code = SEGV_MAPERR; int kernel_address = 0; + tsk = current; + mm = tsk->mm; + + /* + * Check for low-address protection. This needs to be treated + * as a special case because the translation exception code + * field is not guaranteed to contain valid data in this case. + */ + if ((error_code & 0xff) == 4 && !(S390_lowcore.trans_exc_code & 4)) { + + /* Low-address protection hit in kernel mode means + NULL pointer write access in kernel mode. */ + if (!(regs->psw.mask & PSW_PROBLEM_STATE)) { + address = 0; + kernel_address = 1; + goto no_context; + } + + /* Low-address protection hit in user mode 'cannot happen'. */ + die ("Low-address protection", regs, error_code); + do_exit(SIGKILL); + } + /* * get the failing address * more specific the segment and page table portion of * the address */ - address = S390_lowcore.trans_exc_code&0x7ffff000; - tsk = current; - mm = tsk->mm; - - if (in_interrupt() || !mm) - goto no_context; + address = S390_lowcore.trans_exc_code&0x7ffff000; /* @@ -99,6 +147,7 @@ } } die("page fault via unknown access register", regs, error_code); + do_exit(SIGKILL); break; case 2: /* Secondary Segment Table Descriptor */ @@ -107,6 +156,11 @@ break; } + /* + * Check whether we have a user MM in the first place. + */ + if (in_interrupt() || !mm) + goto no_context; /* * When we get here, the fault happened in the current @@ -146,6 +200,7 @@ goto bad_area; } + survive: /* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo @@ -176,7 +231,6 @@ /* User mode accesses just cause a SIGSEGV */ if (regs->psw.mask & PSW_PROBLEM_STATE) { - struct siginfo si; tsk->thread.prot_addr = address; tsk->thread.trap_no = error_code; #ifndef CONFIG_SYSCTL @@ -193,10 +247,8 @@ show_regs(regs); } #endif - si.si_signo = SIGSEGV; - si.si_code = si_code; - si.si_addr = (void*) address; - force_sig_info(SIGSEGV, &si, tsk); + + force_sigsegv(tsk, si_code, (void *)address); return; } @@ -218,9 +270,6 @@ else printk(KERN_ALERT "Unable to handle kernel paging request" " at virtual user address %08lx\n", address); -/* - * need to define, which information is useful here - */ die("Oops", regs, error_code); do_exit(SIGKILL); @@ -232,6 +281,12 @@ */ out_of_memory: up_read(&mm->mmap_sem); + if (tsk->pid == 1) { + tsk->policy |= SCHED_YIELD; + schedule(); + down_read(&mm->mmap_sem); + goto survive; + } printk("VM: killing process %s\n", tsk->comm); if (regs->psw.mask & PSW_PROBLEM_STATE) do_exit(SIGKILL); @@ -253,6 +308,18 @@ goto no_context; } +/* + * Send SIGSEGV to task. This is an external routine + * to keep the stack usage of do_page_fault small. + */ +static void force_sigsegv(struct task_struct *tsk, int code, void *address) +{ + struct siginfo si; + si.si_signo = SIGSEGV; + si.si_code = code; + si.si_addr = address; + force_sig_info(SIGSEGV, &si, tsk); +} typedef struct _pseudo_wait_t { struct _pseudo_wait_t *next; @@ -434,7 +501,6 @@ asmlinkage void pfault_interrupt(struct pt_regs *regs, __u16 error_code) { - DECLARE_WAITQUEUE(wait, current); struct task_struct *tsk; wait_queue_head_t queue; wait_queue_head_t *qp; @@ -447,7 +513,7 @@ * external interrupt. */ subcode = S390_lowcore.cpu_addr; - if ((subcode & 0xff00) != 0x06) + if ((subcode & 0xff00) != 0x0600) return; /* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/mm/init.c linux.ac/arch/s390/mm/init.c --- linux.vanilla/arch/s390/mm/init.c Fri Sep 21 04:02:03 2001 +++ linux.ac/arch/s390/mm/init.c Wed Oct 10 01:47:35 2001 @@ -44,44 +44,23 @@ pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((__aligned__(PAGE_SIZE))); char empty_zero_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); -static int test_access(unsigned long loc) -{ - static const int ssm_mask = 0x07000000L; - int rc, i; - - rc = 0; - for (i=0; i<4; i++) { - __asm__ __volatile__( - " slr %0,%0\n" - " ssm %1\n" - " tprot 0(%2),0\n" - "0: jne 1f\n" - " lhi %0,1\n" - "1: ssm %3\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 0b,1b\n" - ".previous" - : "+&d" (rc) : "i" (0), "a" (loc), "m" (ssm_mask) - : "cc"); - if (rc == 0) - break; - loc += 0x100000; - } - return rc; -} - int do_check_pgt_cache(int low, int high) { int freed = 0; if(pgtable_cache_size > high) { do { - if(pgd_quicklist) - free_pgd_slow(get_pgd_fast()), freed += 2; - if(pmd_quicklist) - pmd_free_slow(pmd_alloc_one_fast(NULL, 0)), freed++; - if(pte_quicklist) - pte_free_slow(pte_alloc_one_fast(NULL, 0)), freed++; + if(pgd_quicklist) { + free_pgd_slow(get_pgd_fast()); + freed += 2; + } + if(pmd_quicklist) { + pmd_free_slow(pmd_alloc_one_fast(NULL, 0)); + freed++; + } + if(pte_quicklist) { + pte_free_slow(pte_alloc_one_fast(NULL, 0)); + freed++; + } } while(pgtable_cache_size > low); } return freed; @@ -200,7 +179,6 @@ void __init mem_init(void) { int codesize, reservedpages, datasize, initsize; - int tmp; max_mapnr = num_physpages = max_low_pfn; high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); @@ -211,24 +189,7 @@ /* this will put all low memory onto the freelists */ totalram_pages += free_all_bootmem(); - /* mark usable pages in the mem_map[] and count reserved pages */ reservedpages = 0; - tmp = 0; - do { - if (tmp && (tmp & 0x3ff) == 0 && - test_access(tmp * PAGE_SIZE) == 0) { - printk("4M Segment %lX not available\n",tmp*PAGE_SIZE); - do { - set_bit(PG_reserved, &mem_map[tmp].flags); - reservedpages++; - tmp++; - } while (tmp < max_low_pfn && (tmp & 0x3ff)); - } else { - if (PageReserved(mem_map+tmp)) - reservedpages++; - tmp++; - } - } while (tmp < max_low_pfn); codesize = (unsigned long) &_etext - (unsigned long) &_text; datasize = (unsigned long) &_edata - (unsigned long) &_etext; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/Makefile linux.ac/arch/s390x/Makefile --- linux.vanilla/arch/s390x/Makefile Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390x/Makefile Wed Oct 10 01:47:35 2001 @@ -52,16 +52,6 @@ MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot -MAKESILO = $(MAKE) -C arch/$(ARCH)/tools/silo - -MAKEDASDFMT = $(MAKE) -C arch/$(ARCH)/tools/dasdfmt - -silo: - @$(MAKE) -C arch/$(ARCH)/tools/silo - -dasdfmt: - @$(MAKE) -C arch/$(ARCH)/tools/dasdfmt - image: vmlinux @$(MAKEBOOT) image diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/config.in linux.ac/arch/s390x/config.in --- linux.vanilla/arch/s390x/config.in Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390x/config.in Wed Oct 10 01:47:35 2001 @@ -8,6 +8,7 @@ define_bool CONFIG_MCA n define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n +define_bool CONFIG_GENERIC_BUST_SPINLOCK n mainmenu_name "Linux Kernel Configuration" define_bool CONFIG_ARCH_S390 y diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/defconfig linux.ac/arch/s390x/defconfig --- linux.vanilla/arch/s390x/defconfig Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390x/defconfig Wed Oct 10 01:47:35 2001 @@ -6,6 +6,7 @@ # CONFIG_MCA is not set CONFIG_RWSEM_GENERIC_SPINLOCK=y # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_GENERIC_BUST_SPINLOCK=n CONFIG_ARCH_S390=y CONFIG_ARCH_S390X=y @@ -72,6 +73,7 @@ CONFIG_MD_RAID0=m CONFIG_MD_RAID1=m CONFIG_MD_RAID5=m +# CONFIG_MD_MULTIPATH is not set CONFIG_BLK_DEV_LVM=m # @@ -247,6 +249,7 @@ CONFIG_IBM_PARTITION=y # CONFIG_MAC_PARTITION is not set # CONFIG_MSDOS_PARTITION is not set +# CONFIG_LDM_PARTITION is not set # CONFIG_SGI_PARTITION is not set # CONFIG_ULTRIX_PARTITION is not set # CONFIG_SUN_PARTITION is not set diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/Makefile linux.ac/arch/s390x/kernel/Makefile --- linux.vanilla/arch/s390x/kernel/Makefile Thu Apr 12 03:02:29 2001 +++ linux.ac/arch/s390x/kernel/Makefile Wed Oct 10 01:47:35 2001 @@ -12,12 +12,14 @@ all: kernel.o head.o init_task.o -O_TARGET := kernel.o +O_TARGET := kernel.o -export-objs := debug.o ebcdic.o irq.o s390_ext.o smp.o s390_ksyms.o -obj-y := lowcore.o entry.o bitmap.o traps.o time.o process.o irq.o \ - setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ - semaphore.o s390fpu.o reipl.o s390_ext.o debug.o +export-objs := debug.o ebcdic.o irq.o s390_ext.o smp.o s390_ksyms.o \ + exec32.o + +obj-y := entry.o bitmap.o traps.o time.o process.o irq.o \ + setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ + semaphore.o s390fpu.o reipl.o s390_ext.o debug.o obj-$(CONFIG_MODULES) += s390_ksyms.o obj-$(CONFIG_SMP) += smp.o diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/cpcmd.c linux.ac/arch/s390x/kernel/cpcmd.c --- linux.vanilla/arch/s390x/kernel/cpcmd.c Thu Apr 12 03:02:29 2001 +++ linux.ac/arch/s390x/kernel/cpcmd.c Wed Oct 10 01:47:35 2001 @@ -6,21 +6,25 @@ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), */ -#include -#include #include -#include +#include #include +#include +#include + +static spinlock_t cpcmd_lock = SPIN_LOCK_UNLOCKED; +static char cpcmd_buf[128]; void cpcmd(char *cmd, char *response, int rlen) { const int mask = 0x40000000L; - char obuffer[128]; - int olen; + unsigned long flags; + int cmdlen; - olen = strlen(cmd); - strcpy(obuffer, cmd); - ASCEBC(obuffer,olen); + spin_lock_irqsave(&cpcmd_lock, flags); + cmdlen = strlen(cmd); + strcpy(cpcmd_buf, cmd); + ASCEBC(cpcmd_buf, cmdlen); if (response != NULL && rlen > 0) { asm volatile (" lrag 2,0(%0)\n" @@ -32,7 +36,7 @@ " .long 0x83240008 # Diagnose 83\n" " sam64" : /* no output */ - : "a" (obuffer), "d" (olen), + : "a" (cpcmd_buf), "d" (cmdlen), "a" (response), "d" (rlen), "m" (mask) : "2", "3", "4", "5" ); EBCASC(response, rlen); @@ -43,8 +47,9 @@ " .long 0x83230008 # Diagnose 83\n" " sam64" : /* no output */ - : "a" (obuffer), "d" (olen) + : "a" (cpcmd_buf), "d" (cmdlen) : "2", "3" ); } + spin_unlock_irqrestore(&cpcmd_lock, flags); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/debug.c linux.ac/arch/s390x/kernel/debug.c --- linux.vanilla/arch/s390x/kernel/debug.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390x/kernel/debug.c Wed Oct 10 01:47:35 2001 @@ -83,6 +83,9 @@ static int debug_input_level_fn(debug_info_t * id, struct debug_view *view, struct file *file, const char *user_buf, size_t user_buf_size, loff_t * offset); +static int debug_input_flush_fn(debug_info_t * id, struct debug_view *view, + struct file *file, const char *user_buf, + size_t user_buf_size, loff_t * offset); static int debug_hex_ascii_format_fn(debug_info_t * id, struct debug_view *view, char *out_buf, const char *in_buf); static int debug_raw_format_fn(debug_info_t * id, @@ -123,6 +126,15 @@ NULL }; +struct debug_view debug_flush_view = { + "flush", + NULL, + NULL, + NULL, + &debug_input_flush_fn, + NULL +}; + struct debug_view debug_sprintf_view = { "sprintf", NULL, @@ -664,6 +676,7 @@ if(!rc) goto out; debug_register_view(rc, &debug_level_view); + debug_register_view(rc, &debug_flush_view); printk(KERN_INFO "debug: reserved %d areas of %d pages for debugging %s\n", nr_areas, 1 << page_order, rc->name); @@ -1027,6 +1040,73 @@ out: *offset += in_buf_size; return rc; /* number of input characters */ +} + + +/* + * flushes debug areas + */ + +void debug_flush(debug_info_t* id, int area) +{ + unsigned long flags; + int i; + + if(!id) + return; + spin_lock_irqsave(&id->lock,flags); + if(area == DEBUG_FLUSH_ALL){ + id->active_area = 0; + memset(id->active_entry, 0, id->nr_areas * sizeof(int)); + for (i = 0; i < id->nr_areas; i++) + memset(id->areas[i], 0, PAGE_SIZE << id->page_order); + printk(KERN_INFO "debug: %s: all areas flushed\n",id->name); + } else if(area >= 0 && area < id->nr_areas) { + id->active_entry[area] = 0; + memset(id->areas[area], 0, PAGE_SIZE << id->page_order); + printk(KERN_INFO + "debug: %s: area %i has been flushed\n", + id->name, area); + } else { + printk(KERN_INFO + "debug: %s: area %i cannot be flushed (range: %i - %i)\n", + id->name, area, 0, id->nr_areas-1); + } + spin_unlock_irqrestore(&id->lock,flags); +} + +/* + * view function: flushes debug areas + */ + +static int debug_input_flush_fn(debug_info_t * id, struct debug_view *view, + struct file *file, const char *user_buf, + size_t in_buf_size, loff_t * offset) +{ + char input_buf[1]; + int rc = in_buf_size; + + if (*offset != 0) + goto out; + if (copy_from_user(input_buf, user_buf, 1)){ + rc = -EFAULT; + goto out; + } + if(input_buf[0] == '-') { + debug_flush(id, DEBUG_FLUSH_ALL); + goto out; + } + if (isdigit(input_buf[0])) { + int area = ((int) input_buf[0] - (int) '0'); + debug_flush(id, area); + goto out; + } + + printk(KERN_INFO "debug: area `%c` is not valid\n", input_buf[0]); + + out: + *offset += in_buf_size; + return rc; /* number of input characters */ } /* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/entry.S linux.ac/arch/s390x/kernel/entry.S --- linux.vanilla/arch/s390x/kernel/entry.S Thu Oct 11 13:52:08 2001 +++ linux.ac/arch/s390x/kernel/entry.S Wed Oct 10 01:47:35 2001 @@ -89,17 +89,25 @@ * R15 - kernel stack pointer */ - .macro SAVE_ALL psworg # system entry macro + .macro SAVE_ALL psworg,sync # system entry macro stmg %r14,%r15,__LC_SAVE_AREA - stam %a2,%a4,__LC_SAVE_AREA+16 tm \psworg+1,0x01 # test problem state bit - jz 0f # skip stack setup save - lg %r15,__LC_KERNEL_STACK # problem state -> load ksp - slr %r14,%r14 - sar %a2,%r14 # set ac.reg. 2 to primary space - lhi %r14,1 - sar %a4,%r14 # set access reg. 4 to home space -0: aghi %r15,-SP_SIZE # make room for registers & psw + stam %a2,%a4,__LC_SAVE_AREA+16 + .if \sync + jz 1f # skip stack setup save + .else + jnz 0f # from user -> load kernel stack + lg %r14,__LC_ASYNC_STACK # are we already on the async. stack ? + slgr %r14,%r15 + srag %r14,%r14,14 + jz 1f + lg %r15,__LC_ASYNC_STACK # load async. stack + j 1f + .endif +0: lg %r15,__LC_KERNEL_STACK # problem state -> load ksp + larl %r14,.Lc_ac + lam %a2,%a4,0(%r14) +1: aghi %r15,-SP_SIZE # make room for registers & psw nill %r15,0xfff8 # align stack pointer to 8 stmg %r0,%r14,SP_R0(%r15) # store gprs 0-14 to kernel stack stg %r2,SP_ORIG_R2(%r15) # store original content of gpr 2 @@ -112,7 +120,7 @@ xc 0(8,%r15),0(%r15) # clear back chain .endm - .macro RESTORE_ALL # system exit macro + .macro RESTORE_ALL sync # system exit macro mvc __LC_RETURN_PSW(16),SP_PSW(%r15) # move user PSW to lowcore lam %a0,%a15,SP_AREGS(%r15) # load the access registers lmg %r0,%r15,SP_R0(%r15) # load gprs 0-15 of user @@ -121,8 +129,8 @@ .endm .macro GET_CURRENT - lghi %r9,-16384 # load pointer to task_struct to %r9 - ngr %r9,15 + lg %r9,__LC_KERNEL_STACK # load pointer to task_struct to %r9 + aghi %r9,-16384 .endm @@ -161,13 +169,32 @@ br %r14 /* + * do_softirq calling function. We want to run the softirq functions on the + * asynchronous interrupt stack. + */ + .global do_call_softirq +do_call_softirq: + stmg %r12,%r15,48(%r15) + lgr %r12,%r15 + lg %r0,__LC_ASYNC_STACK + slgr %r0,%r15 + srag %r0,%r0,14 + je 0f + lg %r15,__LC_ASYNC_STACK +0: aghi %r15,-STACK_FRAME_OVERHEAD + stg %r12,0(%r15) # store back chain + brasl %r14,do_softirq + lmg %r12,%r15,48(%r12) + br %r14 + +/* * SVC interrupt handler routine. System calls are synchronous events and * are executed with interrupts enabled. */ .globl system_call system_call: - SAVE_ALL __LC_SVC_OLD_PSW + SAVE_ALL __LC_SVC_OLD_PSW,1 mvi SP_PGM_OLD_ILC(%r15),1 # mark PGM_OLD_ILC as invalid pgm_system_call: GET_CURRENT # load pointer to task_struct to R9 @@ -202,7 +229,7 @@ tm SP_PGM_OLD_ILC(%r15),0xff jz pgm_svcret stnsm 48(%r15),0xfc # disable I/O and ext. interrupts - RESTORE_ALL + RESTORE_ALL 1 # # call do_signal before return @@ -565,9 +592,9 @@ .long SYSCALL(sys_mmap2,sys32_mmap2_wrapper) .long SYSCALL(sys_ni_syscall,sys32_truncate64_wrapper) .long SYSCALL(sys_ni_syscall,sys32_ftruncate64_wrapper) - .long SYSCALL(sys_ni_syscall,sys32_stat64) /* 195 */ - .long SYSCALL(sys_ni_syscall,sys32_lstat64) - .long SYSCALL(sys_ni_syscall,sys32_fstat64) + .long SYSCALL(sys_ni_syscall,sys32_stat64_wrapper) /* 195 */ + .long SYSCALL(sys_ni_syscall,sys32_lstat64_wrapper) + .long SYSCALL(sys_ni_syscall,sys32_fstat64_wrapper) .long SYSCALL(sys_lchown,sys32_lchown_wrapper) .long SYSCALL(sys_getuid,sys_getuid) .long SYSCALL(sys_getgid,sys_getgid) /* 200 */ @@ -592,7 +619,10 @@ .long SYSCALL(sys_madvise,sys32_madvise_wrapper) .long SYSCALL(sys_getdents64,sys32_getdents64_wrapper)/* 220 */ .long SYSCALL(sys_ni_syscall,sys32_fcntl64_wrapper) - .rept 255-221 + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 222 - reserved for posix_acl */ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 223 - reserved for posix_acl */ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 224 - reserved for posix_acl */ + .rept 255-224 .long SYSCALL(sys_ni_syscall,sys_ni_syscall) .endr @@ -626,7 +656,7 @@ lpswe __LC_PGM_OLD_PSW # it was a single stepped SVC that is causing all the trouble pgm_svcper: - SAVE_ALL __LC_SVC_OLD_PSW + SAVE_ALL __LC_SVC_OLD_PSW,1 mvc SP_PGM_OLD_ILC(4,%r15),__LC_PGM_ILC # save program check information j pgm_system_call # now do the svc pgm_svcret: @@ -636,7 +666,7 @@ mvi SP_PGM_OLD_ILC(%r15),1 # mark PGM_OLD_ILC as invalid j pgm_no_sv pgm_sv: - SAVE_ALL __LC_PGM_OLD_PSW + SAVE_ALL __LC_PGM_OLD_PSW,1 mvi SP_PGM_OLD_ILC(%r15),1 # mark PGM_OLD_ILC as invalid llgh %r7,__LC_PGM_ILC # load instruction length GET_CURRENT @@ -668,7 +698,7 @@ */ .globl io_int_handler io_int_handler: - SAVE_ALL __LC_IO_OLD_PSW + SAVE_ALL __LC_IO_OLD_PSW,0 GET_CURRENT # load pointer to task_struct to R9 la %r2,SP_PTREGS(%r15) # address of register-save area llgh %r3,__LC_SUBCHANNEL_NR # load subchannel number @@ -701,7 +731,7 @@ jnz io_signal_return io_leave: stnsm 48(%r15),0xfc # disable I/O and ext. interrupts - RESTORE_ALL + RESTORE_ALL 0 # # call do_softirq and return from syscall, if interrupt-level @@ -732,7 +762,7 @@ */ .globl ext_int_handler ext_int_handler: - SAVE_ALL __LC_EXT_OLD_PSW + SAVE_ALL __LC_EXT_OLD_PSW,0 GET_CURRENT # load pointer to task_struct to R9 la %r2,SP_PTREGS(%r15) # address of register-save area llgh %r3,__LC_EXT_INT_CODE # error code @@ -760,10 +790,10 @@ */ .globl mcck_int_handler mcck_int_handler: - SAVE_ALL __LC_MCK_OLD_PSW + SAVE_ALL __LC_MCK_OLD_PSW,0 brasl %r14,s390_do_machine_check mcck_return: - RESTORE_ALL + RESTORE_ALL 0 #ifdef CONFIG_SMP /* @@ -771,10 +801,10 @@ */ .globl restart_int_handler restart_int_handler: - lg %r15,__LC_KERNEL_STACK # load ksp - lhi %r10,__LC_CREGS_SAVE_AREA + lg %r15,__LC_SAVE_AREA+120 # load ksp + lghi %r10,__LC_CREGS_SAVE_AREA lctlg %c0,%c15,0(%r10) # get new ctl regs - lhi %r10,__LC_AREGS_SAVE_AREA + lghi %r10,__LC_AREGS_SAVE_AREA lam %a0,%a15,0(%r10) stosm 0(%r15),0x04 # now we can turn dat on lmg %r6,%r15,48(%r15) # load registers from clone @@ -794,3 +824,8 @@ restart_go: #endif +/* + * Integer constants + */ + .align 4 +.Lc_ac: .long 0,0,1 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/head.S linux.ac/arch/s390x/kernel/head.S --- linux.vanilla/arch/s390x/kernel/head.S Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390x/kernel/head.S Wed Oct 10 01:47:35 2001 @@ -261,7 +261,7 @@ l %r1,0xb8 # load ipl subchannel number la %r2,IPL_BS # load start address bas %r14,.Lloader # load rest of ipl image - larl %r12,parmarea # pointer to parameter area + larl %r12,_pstart # pointer to parameter area st %r1,IPL_DEVICE+4-PARMAREA(%r12) # store ipl device number # @@ -464,7 +464,7 @@ sigp %r1,%r0,0x12 # switch to esame mode sam64 # switch to 64 bit mode lctlg %c0,%c15,.Lctl-.LPG1(%r13) # load control registers - larl %r12,parmarea # pointer to parameter area + larl %r12,_pstart # pointer to parameter area # move IPL device to lowcore mvc __LC_IPLDEV(4),IPL_DEVICE+4-PARMAREA(%r12) # set program check new psw mask @@ -472,42 +472,64 @@ # -# find out memory size. +# find memory chunks. # - la %r1,1f-.LPG1(%r13) # set program check address + larl %r1,.Lchkmem # set program check address stg %r1,__LC_PGM_NEW_PSW+8 - lghi %r2,1 - sllg %r2,%r2,17 # test in increments of 128KB - lgr %r1,%r2 - aghi %r1,-8 # test last word in the segment -0: lg %r0,0(%r1) # test 128KB segment - stg %r0,0(%r1) - algr %r1,%r2 # add 128KB - bc 12,0b-.LPG1(%r13) # r1 < 2^64 -> loop -1: ng %r1,.L4malign-.LPG1(%r13) # align to multiples of 4M - larl %r3,memory_size-. - stg %r1,0(%r3) # store memory size -# -# find out memory size part 2. Running native the HSA is located at -# 2GB and we will get an addressing exception trying to access it. -# We have to restart the scan at 2GB to find out if the machine has -# more than 2GB of storage. -# - la %r1,1f-.LPG1(%r13) # set program check address - stg %r1,__LC_PGM_NEW_PSW+8 - lg %r1,.Lscan2g-.LPG1(%r13) # restart scanning @ 2GB + 128K - 8 -0: lg %r0,0(%r1) # test 128KB segment - stg %r0,0(%r1) - algr %r1,%r2 # add 128 KB - bc 12,0b-.LPG1(%r13) # r1 < 2^64 -> loop -1: clg %r1,.Lscan2g-.LPG1(%r13) # program check @ 2GB + 128K - 8 ? - be 2f-.LPG1(%r13) - ng %r1,.L4malign-.LPG1(%r13) # align to multiples of 4M - larl %r3,memory_size-. - stg %r1,0(%r3) # store memory size -2: + la %r1,1 # test in increments of 128KB + sllg %r1,%r1,17 + larl %r3,memory_chunk + slgr %r4,%r4 # set start of chunk to zero + slgr %r5,%r5 # set end of chunk to zero + slr %r6,%r6 # set access code to zero +.Lloop: + tprot 0(%r5),0 # test protection of first byte + ipm %r7 + srl %r7,28 + clr %r6,%r7 # compare cc with last access code + je .Lsame + clgr %r4,%r5 # chunk size > 0? + je .Lsize0 + stg %r4,0(%r3) # store start address of chunk + lgr %r0,%r5 + slgr %r0,%r4 + stg %r0,8(%r3) # store size of chunk + st %r6,20(%r3) # store type of chunk + la %r3,24(%r3) + lgr %r4,%r5 # set start to end + larl %r8,memory_size + stg %r5,0(%r8) # store memory size +.Lsize0: + lr %r6,%r7 # set access code to last cc +.Lsame: + algr %r5,%r1 # add 128KB to end of chunk + brc 12,.Lloop +.Lchkmem: # > 16EB or tprot got a program check + clgr %r4,%r5 # chunk size > 0? + je .Ldonemem + stg %r4,0(%r3) # store start address of chunk + lgr %r0,%r5 + slgr %r0,%r4 + stg %r0,8(%r3) # store size of chunk + st %r6,20(%r3) # store type of chunk + la %r3,24(%r3) + lgr %r4,%r5 + larl %r8,memory_size + stg %r5,0(%r8) # store memory size +# +# Running native the HSA is located at 2GB and we will get an +# addressing exception trying to access it. We have to restart +# the scan at 2GB to find out if the machine has more than 2GB. +# + lghi %r4,1 + sllg %r4,%r4,31 + clgr %r5,%r4 + jhe .Ldonemem + lgr %r5,%r4 + j .Lloop +.Ldonemem: - larl %r12,machine_flags-. + larl %r12,machine_flags # # find out if we are running under VM # @@ -539,7 +561,7 @@ .Lentry:.quad 0x0000000180000000,_stext .Lctl: .quad 0x04b50002 # cr0: various things .quad 0 # cr1: primary space segment table - .quad 0 # cr2: access register translation + .quad .Lduct # cr2: dispatchable unit control table .quad 0 # cr3: instruction authorization .quad 0 # cr4: instruction authorization .quad 0 # cr5: various things @@ -557,11 +579,16 @@ .L4malign:.quad 0xffffffffffc00000 .Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8 + .org PARMAREA-64 +.Lduct: .long 0,0,0,0,0,0,0,0 + .long 0,0,0,0,0,0,0,0 + # # params at 10400 (setup.h) # .org PARMAREA -parmarea: + .global _pstart +_pstart: .quad 0 # IPL_DEVICE .quad RAMDISK_ORIGIN # INITRD_START .quad RAMDISK_SIZE # INITRD_SIZE @@ -569,31 +596,28 @@ .org COMMAND_LINE .byte "root=/dev/ram0 ro" .byte 0 + .org 0x11000 + .global _pend +_pend: -# -# startup-code, running in virtual mode -# #ifdef CONFIG_SHARED_KERNEL .org 0x100000 -#else - .org 0x10800 #endif + +# +# startup-code, running in virtual mode +# .globl _stext _stext: basr %r13,0 # get base .LPG2: # -# Setup lowcore +# Setup stack # - l %r1,__LC_IPLDEV # load ipl device number - spx .Lprefix-.LPG2(%r13) # set prefix to linux lowcore - st %r1,__LC_IPLDEV # store ipl device number larl %r15,init_task_union aghi %r15,16384 # init_task_union + 16384 stg %r15,__LC_KERNEL_STACK # set end of kernel stack aghi %r15,-160 xc 0(8,%r15),0(%r15) # set backchain to zero - lghi %r0,-1 - stg %r0,__LC_KERNEL_LEVEL # set interrupt count to -1 # # clear bss memory # @@ -621,6 +645,5 @@ # .align 8 .Ldw: .quad 0x0002000180000000,0x0000000000000000 -.Lprefix: .long init_S390_lowcore .Laregs: .long 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/init_task.c linux.ac/arch/s390x/kernel/init_task.c --- linux.vanilla/arch/s390x/kernel/init_task.c Mon Sep 17 23:29:09 2001 +++ linux.ac/arch/s390x/kernel/init_task.c Wed Oct 10 01:47:35 2001 @@ -12,6 +12,7 @@ #include #include +static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/ioctl32.c linux.ac/arch/s390x/kernel/ioctl32.c --- linux.vanilla/arch/s390x/kernel/ioctl32.c Sun Aug 12 18:38:48 2001 +++ linux.ac/arch/s390x/kernel/ioctl32.c Wed Oct 10 01:47:35 2001 @@ -27,6 +27,7 @@ #include #include #include +#include #include "linux32.h" @@ -451,6 +452,8 @@ IOCTL32_DEFAULT(VT_RESIZEX), IOCTL32_DEFAULT(VT_LOCKSWITCH), IOCTL32_DEFAULT(VT_UNLOCKSWITCH), + + IOCTL32_DEFAULT(SIOCGSTAMP), IOCTL32_HANDLER(SIOCGIFNAME, dev_ifname32), IOCTL32_HANDLER(SIOCGIFCONF, dev_ifconf), diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/linux32.c linux.ac/arch/s390x/kernel/linux32.c --- linux.vanilla/arch/s390x/kernel/linux32.c Sun Aug 12 23:06:07 2001 +++ linux.ac/arch/s390x/kernel/linux32.c Wed Oct 10 01:47:35 2001 @@ -2884,7 +2884,7 @@ err = copy_from_user(kaddr + offset, (char *)A(str), bytes_to_copy); flush_page_to_ram(page); - kunmap((unsigned long)kaddr); + kunmap(page); if (err) return -EFAULT; @@ -4038,57 +4038,55 @@ } struct stat64_emu31 { - unsigned short st_dev; - unsigned char __pad0[6]; - - long long st_ino; - unsigned int st_mode; - unsigned int st_nlink; - - __u32 st_uid; - __u32 st_gid; - - unsigned short st_rdev; - unsigned char __pad3[10]; - - long long st_size; - __u32 st_blksize; - - __u32 st_blocks; /* Number 512-byte blocks allocated. */ - __u32 __pad4; /* future possible st_blocks high bits */ - - __u32 st_atime; - __u32 __pad5; - - __u32 st_mtime; - __u32 __pad6; - - __u32 st_ctime; - __u32 __pad7; /* will be high 32 bits of ctime someday */ - - __u32 __unused1; - __u32 __unused2; -}; + unsigned char __pad0[6]; + unsigned short st_dev; + unsigned int __pad1; +#define STAT64_HAS_BROKEN_ST_INO 1 + u32 __st_ino; + unsigned int st_mode; + unsigned int st_nlink; + u32 st_uid; + u32 st_gid; + unsigned char __pad2[6]; + unsigned short st_rdev; + unsigned int __pad3; + long st_size; + u32 st_blksize; + unsigned char __pad4[4]; + u32 __pad5; /* future possible st_blocks high bits */ + u32 st_blocks; /* Number 512-byte blocks allocated. */ + u32 st_atime; + u32 __pad6; + u32 st_mtime; + u32 __pad7; + u32 st_ctime; + u32 __pad8; /* will be high 32 bits of ctime someday */ + unsigned long st_ino; +}; static inline int putstat64 (struct stat64_emu31 *ubuf, struct stat *kbuf) { - int err; - - err = put_user (kbuf->st_dev, &ubuf->st_dev); - err |= __put_user (kbuf->st_ino, &ubuf->st_ino); - err |= __put_user (kbuf->st_mode, &ubuf->st_mode); - err |= __put_user (kbuf->st_nlink, &ubuf->st_nlink); - err |= __put_user (kbuf->st_uid, &ubuf->st_uid); - err |= __put_user (kbuf->st_gid, &ubuf->st_gid); - err |= __put_user (kbuf->st_rdev, &ubuf->st_rdev); - err |= __put_user (kbuf->st_size, &ubuf->st_size); - err |= __put_user (kbuf->st_blksize, &ubuf->st_blksize); - err |= __put_user (kbuf->st_blocks, &ubuf->st_blocks); - err |= __put_user (kbuf->st_atime, &ubuf->st_atime); - err |= __put_user (kbuf->st_mtime, &ubuf->st_mtime); - err |= __put_user (kbuf->st_ctime, &ubuf->st_ctime); - return err; + struct stat64_emu31 tmp; + + memset(&tmp, 0, sizeof(tmp)); + + tmp.st_dev = (unsigned short)kbuf->st_dev; + tmp.st_ino = kbuf->st_ino; + tmp.__st_ino = (u32)kbuf->st_ino; + tmp.st_mode = kbuf->st_mode; + tmp.st_nlink = (unsigned int)kbuf->st_nlink; + tmp.st_uid = kbuf->st_uid; + tmp.st_gid = kbuf->st_gid; + tmp.st_rdev = (unsigned short)kbuf->st_rdev; + tmp.st_size = kbuf->st_size; + tmp.st_blksize = (u32)kbuf->st_blksize; + tmp.st_blocks = (u32)kbuf->st_blocks; + tmp.st_atime = (u32)kbuf->st_atime; + tmp.st_mtime = (u32)kbuf->st_mtime; + tmp.st_ctime = (u32)kbuf->st_ctime; + + return copy_to_user(ubuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } extern asmlinkage long sys_newstat(char * filename, struct stat * statbuf); @@ -4131,7 +4129,7 @@ return err; set_fs (KERNEL_DS); - ret = sys_newstat(tmp, &s); + ret = sys_newlstat(tmp, &s); set_fs (old_fs); putname(tmp); if (putstat64 (statbuf, &s)) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/linux32.h linux.ac/arch/s390x/kernel/linux32.h --- linux.vanilla/arch/s390x/kernel/linux32.h Tue Feb 13 22:13:44 2001 +++ linux.ac/arch/s390x/kernel/linux32.h Wed Oct 10 01:47:35 2001 @@ -237,8 +237,8 @@ __u32 uc_flags; __u32 uc_link; /* pointer */ stack_t32 uc_stack; + _sigregs32 uc_mcontext; sigset_t32 uc_sigmask; /* mask last for extensibility */ - __u32 sc; /* pointer */ }; #endif /* !CONFIG_S390_SUPPORT */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/lowcore.S linux.ac/arch/s390x/kernel/lowcore.S --- linux.vanilla/arch/s390x/kernel/lowcore.S Tue Feb 13 22:13:44 2001 +++ linux.ac/arch/s390x/kernel/lowcore.S Thu Jan 1 01:00:00 1970 @@ -1,28 +0,0 @@ -/* - * arch/s390/kernel/lowcore.S - * S390 lowcore definition. - * - * S390 64 bit Version - * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Hartmut Penner (hpenner@de.ibm.com) - * Martin Schwidefsky (schwidefsky@de.ibm.com), - */ -#include - - .align 8192 - .globl init_S390_lowcore -init_S390_lowcore: - .fill 0x1a0-0x000,1,0 - .quad _RESTART_PSW_MASK - .quad restart_int_handler - .quad _EXT_PSW_MASK - .quad ext_int_handler - .quad _SVC_PSW_MASK - .quad system_call - .quad _PGM_PSW_MASK - .quad pgm_check_handler - .quad _MCCK_PSW_MASK - .quad mcck_int_handler -EXT_PSW: .quad _IO_PSW_MASK - .quad io_int_handler - .fill 0x2000-0x200,1,0 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/mathemu.c linux.ac/arch/s390x/kernel/mathemu.c --- linux.vanilla/arch/s390x/kernel/mathemu.c Tue Feb 13 22:13:44 2001 +++ linux.ac/arch/s390x/kernel/mathemu.c Thu Jan 1 01:00:00 1970 @@ -1,920 +0,0 @@ -/* - * arch/s390/kernel/mathemu.c - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), - * - * 'mathemu.c' handles IEEE instructions on a S390 processor - * that does not have the IEEE fpu - */ - -#include -#include -#include -#include - -#include -#include - -#ifdef CONFIG_SYSCTL -int sysctl_ieee_emulation_warnings=1; -#endif - -static void display_emulation_not_implemented(char *instr) -{ - struct pt_regs *regs; - __u16 *location; - -#if CONFIG_SYSCTL - if(sysctl_ieee_emulation_warnings) -#endif - { - regs=current->thread.regs; - location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc); - printk("%s ieee fpu instruction not emulated process name: %s pid: %d \n", - instr, - current->comm, current->pid); - printk("%s's PSW: %08lx %08lx\n",instr, - (unsigned long) regs->psw.mask, - (unsigned long) location); - } -} - - -static void set_CC_df(__u64 val1,__u64 val2) { - int rc; - rc = __cmpdf2(val1,val2); - current->thread.regs->psw.mask &= 0xFFFFCFFFFFFFFFFFL; - switch (rc) { - case -1: - current->thread.regs->psw.mask |= 0x0000100000000000L; - break; - case 1: - current->thread.regs->psw.mask |= 0x0000200000000000L; - break; - } -} - -static void set_CC_sf(__u32 val1,__u32 val2) { - int rc; - rc = __cmpsf2(val1,val2); - current->thread.regs->psw.mask &= 0xFFFFCFFFFFFFFFFF; - switch (rc) { - case -1: - current->thread.regs->psw.mask |= 0x0000100000000000L; - break; - case 1: - current->thread.regs->psw.mask |= 0x0000200000000000L; - break; - } -} - - -static void emu_adb (int rx, __u64 val) { - current->thread.fp_regs.fprs[rx].d = __adddf3(current->thread.fp_regs.fprs[rx].d,val); - set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); -} - -static void emu_adbr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].d = __adddf3(current->thread.fp_regs.fprs[rx].d, - current->thread.fp_regs.fprs[ry].d); - set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); -} - -static void emu_aeb (int rx, __u32 val) { - current->thread.fp_regs.fprs[rx].f = __addsf3(current->thread.fp_regs.fprs[rx].f,val); - set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); -} - -static void emu_aebr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].f = __addsf3(current->thread.fp_regs.fprs[rx].f, - current->thread.fp_regs.fprs[ry].f); - set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); -} - -static void emu_axbr (int rx, int ry) { - display_emulation_not_implemented("axbr"); -} - -static void emu_cdb (int rx, __u64 val) { - set_CC_df(current->thread.fp_regs.fprs[rx].d,val); -} - -static void emu_cdbr (int rx, int ry) { - set_CC_df(current->thread.fp_regs.fprs[rx].d,current->thread.fp_regs.fprs[ry].d); -} - -static void emu_cdfbr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].d = - __floatsidf(current->thread.regs->gprs[ry]); -} - -static void emu_ceb (int rx, __u32 val) { - set_CC_sf(current->thread.fp_regs.fprs[rx].f,val); -} - -static void emu_cebr (int rx, int ry) { - set_CC_sf(current->thread.fp_regs.fprs[rx].f,current->thread.fp_regs.fprs[ry].f); -} - -static void emu_cefbr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].f = - __floatsisf(current->thread.regs->gprs[ry]); -} - -static void emu_cfdbr (int rx, int ry, int mask) { - current->thread.regs->gprs[rx] = - __fixdfsi(current->thread.fp_regs.fprs[ry].d); -} - -static void emu_cfebr (int rx, int ry, int mask) { - current->thread.regs->gprs[rx] = - __fixsfsi(current->thread.fp_regs.fprs[ry].f); -} - -static void emu_cfxbr (int rx, int ry, int mask) { - display_emulation_not_implemented("cfxbr"); -} - -static void emu_cxbr (int rx, int ry) { - display_emulation_not_implemented("cxbr"); -} - -static void emu_cxfbr (int rx, int ry) { - display_emulation_not_implemented("cxfbr"); -} - -static void emu_ddb (int rx, __u64 val) { - current->thread.fp_regs.fprs[rx].d = __divdf3(current->thread.fp_regs.fprs[rx].d,val); - set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); -} - -static void emu_ddbr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].d = __divdf3(current->thread.fp_regs.fprs[rx].d, - current->thread.fp_regs.fprs[ry].d); - set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); -} - -static void emu_deb (int rx, __u32 val) { - current->thread.fp_regs.fprs[rx].f = __divsf3(current->thread.fp_regs.fprs[rx].f,val); - set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); -} - -static void emu_debr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].f = __divsf3(current->thread.fp_regs.fprs[rx].f, - current->thread.fp_regs.fprs[ry].f); - set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); -} - -static void emu_didbr (int rx, int ry, int mask) { - display_emulation_not_implemented("didbr"); -} - -static void emu_diebr (int rx, int ry, int mask) { - display_emulation_not_implemented("diebr"); -} - -static void emu_dxbr (int rx, int ry) { - display_emulation_not_implemented("dxbr"); -} - -static void emu_efpc (int rx, int ry) { - display_emulation_not_implemented("efpc"); -} - -static void emu_fidbr (int rx, int ry, int mask) { - display_emulation_not_implemented("fidbr"); -} - -static void emu_fiebr (int rx, int ry, int mask) { - display_emulation_not_implemented("fiebr"); -} - -static void emu_fixbr (int rx, int ry, int mask) { - display_emulation_not_implemented("fixbr"); -} - -static void emu_kdb (int rx, __u64 val) { - display_emulation_not_implemented("kdb"); -} - -static void emu_kdbr (int rx, int ry) { - display_emulation_not_implemented("kdbr"); -} - -static void emu_keb (int rx, __u32 val) { - display_emulation_not_implemented("keb"); -} - -static void emu_kebr (int rx, int ry) { - display_emulation_not_implemented("kebr"); -} - -static void emu_kxbr (int rx, int ry) { - display_emulation_not_implemented("kxbr"); -} - -static void emu_lcdbr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].d = - __negdf2(current->thread.fp_regs.fprs[ry].d); - set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); -} - -static void emu_lcebr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].f = - __negsf2(current->thread.fp_regs.fprs[ry].f); - set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); -} - -static void emu_lcxbr (int rx, int ry) { - display_emulation_not_implemented("lcxbr"); -} - -static void emu_ldeb (int rx, __u32 val) { - current->thread.fp_regs.fprs[rx].d = __extendsfdf2(val); -} - -static void emu_ldebr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].d = - __extendsfdf2(current->thread.fp_regs.fprs[ry].f); -} - -static void emu_ldxbr (int rx, int ry) { - display_emulation_not_implemented("ldxbr"); -} - -static void emu_ledbr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].f = __truncdfsf2(current->thread.fp_regs.fprs[ry].d); - set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); -} - -static void emu_lexbr (int rx, int ry) { - display_emulation_not_implemented("lexbr"); -} - -static void emu_lndbr (int rx, int ry) { - display_emulation_not_implemented("lndbr"); -} - -static void emu_lnebr (int rx, int ry) { - display_emulation_not_implemented("lnebr"); -} - -static void emu_lnxbr (int rx, int ry) { - display_emulation_not_implemented("lnxbr"); -} - -static void emu_lpdbr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].d = __absdf2(current->thread.fp_regs.fprs[ry].d); - set_CC_df(current->thread.fp_regs.fprs[rx].d,0); -} - -static void emu_lpebr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].f = __abssf2(current->thread.fp_regs.fprs[ry].f); - set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); -} - -static void emu_lpxbr (int rx, int ry) { - display_emulation_not_implemented("lpxbr"); -} - -static void emu_ltdbr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].d = current->thread.fp_regs.fprs[ry].d; - set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); -} - -static void emu_ltebr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].f = current->thread.fp_regs.fprs[ry].f; - set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); -} - -static void emu_ltxbr (int rx, int ry) { - display_emulation_not_implemented("ltxbr"); -} - -static void emu_lxdb (int rx, __u64 val) { - display_emulation_not_implemented("lxdb"); -} - -static void emu_lxdbr (int rx, int ry) { - display_emulation_not_implemented("lxdbr"); -} - -static void emu_lxeb (int rx, __u32 val) { - display_emulation_not_implemented("lxeb"); -} - -static void emu_lxebr (int rx, int ry) { - display_emulation_not_implemented("lxebr"); -} - -static void emu_madb (int rx, __u64 val, int mask) { - display_emulation_not_implemented("madb"); -} - -static void emu_madbr (int rx, int ry, int mask) { - display_emulation_not_implemented("madbr"); -} - -static void emu_maeb (int rx, __u32 val, int mask) { - display_emulation_not_implemented("maeb"); -} - -static void emu_maebr (int rx, int ry, int mask) { - display_emulation_not_implemented("maebr"); -} - -static void emu_mdb (int rx, __u64 val) { - current->thread.fp_regs.fprs[rx].d = __muldf3(current->thread.fp_regs.fprs[rx].d,val); - set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); -} - -static void emu_mdbr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].d = __muldf3(current->thread.fp_regs.fprs[rx].d, - current->thread.fp_regs.fprs[ry].d); - set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); -} - -static void emu_mdeb (int rx, __u32 val) { - display_emulation_not_implemented("mdeb"); -} - -static void emu_mdebr (int rx, int ry) { - display_emulation_not_implemented("mdebr"); -} - -static void emu_meeb (int rx, __u32 val) { - current->thread.fp_regs.fprs[rx].f = __mulsf3(current->thread.fp_regs.fprs[rx].f, - val); - set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); -} - -static void emu_meebr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].f = __mulsf3(current->thread.fp_regs.fprs[rx].f, - current->thread.fp_regs.fprs[ry].f); - set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); -} - -static void emu_msdb (int rx, __u64 val, int mask) { - display_emulation_not_implemented("msdb"); -} - -static void emu_msdbr (int rx, int ry, int mask) { - display_emulation_not_implemented("msdbr"); -} - -static void emu_mseb (int rx, __u32 val, int mask) { - display_emulation_not_implemented("mseb"); -} - -static void emu_msebr (int rx, int ry, int mask) { - display_emulation_not_implemented("msebr"); -} - -static void emu_mxbr (int rx, int ry) { - display_emulation_not_implemented("mxbr"); -} - -static void emu_mxdb (int rx, __u64 val) { - display_emulation_not_implemented("mxdb"); -} - -static void emu_mxdbr (int rx, int ry) { - display_emulation_not_implemented("mxdbr"); -} - -static void emu_sdb (int rx, __u64 val) { - current->thread.fp_regs.fprs[rx].d = __subdf3(current->thread.fp_regs.fprs[rx].d, - val); - set_CC_sf(current->thread.fp_regs.fprs[rx].d,0ULL); -} - -static void emu_sdbr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].d = __subdf3(current->thread.fp_regs.fprs[rx].d, - current->thread.fp_regs.fprs[ry].d); - set_CC_sf(current->thread.fp_regs.fprs[rx].d,0ULL); -} - -static void emu_seb (int rx, __u32 val) { - current->thread.fp_regs.fprs[rx].f = __subsf3(current->thread.fp_regs.fprs[rx].f, - val); - set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); -} - -static void emu_sebr (int rx, int ry) { - current->thread.fp_regs.fprs[rx].f = __subsf3(current->thread.fp_regs.fprs[rx].f, - current->thread.fp_regs.fprs[ry].f); - set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); -} - -static void emu_sfpc (int rx, int ry) { - display_emulation_not_implemented("sfpc"); -} - -static void emu_sqdb (int rx, __u64 val) { - display_emulation_not_implemented("sqdb"); -} - -static void emu_sqdbr (int rx, int ry) { - display_emulation_not_implemented("sqdbr"); -} - -static void emu_sqeb (int rx, __u32 val) { - display_emulation_not_implemented("sqeb"); -} - -static void emu_sqebr (int rx, int ry) { - display_emulation_not_implemented("sqebr"); -} - -static void emu_sqxbr (int rx, int ry) { - display_emulation_not_implemented("sqxbr"); -} - -static void emu_sxbr (int rx, int ry) { - display_emulation_not_implemented("sxbr"); -} - -static void emu_tcdb (int rx, __u64 val) { - display_emulation_not_implemented("tcdb"); -} - -static void emu_tceb (int rx, __u32 val) { - display_emulation_not_implemented("tceb"); -} - -static void emu_tcxb (int rx, __u64 val) { - display_emulation_not_implemented("tcxb"); -} - - -static inline void emu_load_regd(int reg) { - if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ - __asm__ __volatile ( /* load reg from fp_regs.fprs[reg] */ - " bras 1,0f\n" - " ld 0,0(%1)\n" - "0: ex %0,0(1)" - : /* no output */ - : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].d) - : "1" ); - } -} - -static inline void emu_load_rege(int reg) { - if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ - __asm__ __volatile ( /* load reg from fp_regs.fprs[reg] */ - " bras 1,0f\n" - " le 0,0(%1)\n" - "0: ex %0,0(1)" - : /* no output */ - : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].f) - : "1" ); - } -} - -static inline void emu_store_regd(int reg) { - if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ - __asm__ __volatile ( /* store reg to fp_regs.fprs[reg] */ - " bras 1,0f\n" - " std 0,0(%1)\n" - "0: ex %0,0(1)" - : /* no output */ - : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].d) - : "1" ); - } -} - - -static inline void emu_store_rege(int reg) { - if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ - __asm__ __volatile ( /* store reg to fp_regs.fprs[reg] */ - " bras 1,0f\n" - " ste 0,0(%1)\n" - "0: ex %0,0(1)" - : /* no output */ - : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].f) - : "1" ); - } -} - -int math_emu_b3(__u8 *opcode, struct pt_regs * regs) { - static const __u8 format_table[] = { - 2, 2, 2, 2, 9, 1, 2, 1, 2, 2, 2, 2, 9, 2, 4, 4, - 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 3, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1,10, 1, 1, 3, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 3, 0, 0, 0, 3, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, - 0, 0, 0, 0, 5, 6, 6, 0, 7, 8, 8, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - static const void *jump_table[]= { - emu_lpebr, emu_lnebr, emu_ltebr, emu_lcebr, - emu_ldebr, emu_lxdbr, emu_lxebr, emu_mxdbr, - emu_kebr, emu_cebr, emu_aebr, emu_sebr, - emu_mdebr, emu_debr, emu_maebr, emu_msebr, - emu_lpdbr, emu_lndbr, emu_ltdbr, emu_lcdbr, - emu_sqebr, emu_sqdbr, emu_sqxbr, emu_meebr, - emu_kdbr, emu_cdbr, emu_adbr, emu_sdbr, - emu_mdbr, emu_ddbr, emu_madbr, emu_msdbr, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - emu_lpxbr, emu_lnxbr, emu_ltxbr, emu_lcxbr, - emu_ledbr, emu_ldxbr, emu_lexbr, emu_fixbr, - emu_kxbr, emu_cxbr, emu_axbr, emu_sxbr, - emu_mxbr, emu_dxbr, NULL, NULL, - NULL, NULL, NULL, emu_diebr, - NULL, NULL, NULL, emu_fiebr, - NULL, NULL, NULL, emu_didbr, - NULL, NULL, NULL, emu_fidbr, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - emu_sfpc, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - emu_efpc, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - emu_cefbr, emu_cdfbr, emu_cxfbr, NULL, - emu_cfebr, emu_cfdbr, emu_cfxbr - }; - - switch (format_table[opcode[1]]) { - case 1: /* RRE format, double operation */ - emu_store_regd((opcode[3]>>4)&15); - emu_store_regd(opcode[3]&15); - /* call the emulation function */ - ((void (*)(int, int))jump_table[opcode[1]]) - (opcode[3]>>4,opcode[3]&15); - emu_load_regd((opcode[3]>>4)&15); - emu_load_regd(opcode[3]&15); - return 0; - case 2: /* RRE format, float operation */ - emu_store_rege((opcode[3]>>4)&15); - emu_store_rege(opcode[3]&15); - /* call the emulation function */ - ((void (*)(int, int))jump_table[opcode[1]]) - (opcode[3]>>4,opcode[3]&15); - emu_load_rege((opcode[3]>>4)&15); - emu_load_rege(opcode[3]&15); - return 0; - case 3: /* RRF format, double operation */ - emu_store_regd((opcode[3]>>4)&15); - emu_store_regd(opcode[3]&15); - /* call the emulation function */ - ((void (*)(int, int, int))jump_table[opcode[1]]) - (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); - emu_load_regd((opcode[3]>>4)&15); - emu_load_regd(opcode[3]&15); - return 0; - case 4: /* RRF format, float operation */ - emu_store_rege((opcode[3]>>4)&15); - emu_store_rege(opcode[3]&15); - /* call the emulation function */ - ((void (*)(int, int, int))jump_table[opcode[1]]) - (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); - emu_load_rege((opcode[3]>>4)&15); - emu_load_rege(opcode[3]&15); - return 0; - case 5: /* RRE format, cefbr instruction */ - emu_store_rege((opcode[3]>>4)&15); - /* call the emulation function */ - ((void (*)(int, int))jump_table[opcode[1]]) - (opcode[3]>>4,opcode[3]&15); - emu_load_rege((opcode[3]>>4)&15); - return 0; - case 6: /* RRE format, cdfbr & cxfbr instruction */ - emu_store_regd((opcode[3]>>4)&15); - /* call the emulation function */ - ((void (*)(int, int))jump_table[opcode[1]]) - (opcode[3]>>4,opcode[3]&15); - emu_load_regd((opcode[3]>>4)&15); - return 0; - /* FIXME !! */ - return 0; - case 7: /* RRF format, cfebr instruction */ - emu_store_rege(opcode[3]&15); - /* call the emulation function */ - ((void (*)(int, int, int))jump_table[opcode[1]]) - (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); - return 0; - case 8: /* RRF format, cfdbr & cfxbr instruction */ - emu_store_regd(opcode[3]&15); - /* call the emulation function */ - ((void (*)(int, int, int))jump_table[opcode[1]]) - (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); - return 0; - case 9: /* RRE format, ldebr & mdebr instruction */ - /* float store but double load */ - emu_store_rege((opcode[3]>>4)&15); - emu_store_rege(opcode[3]&15); - /* call the emulation function */ - ((void (*)(int, int))jump_table[opcode[1]]) - (opcode[3]>>4,opcode[3]&15); - emu_load_regd((opcode[3]>>4)&15); - return 0; - case 10: /* RRE format, ledbr instruction */ - /* double store but float load */ - emu_store_regd((opcode[3]>>4)&15); - emu_store_regd(opcode[3]&15); - /* call the emulation function */ - ((void (*)(int, int))jump_table[opcode[1]]) - (opcode[3]>>4,opcode[3]&15); - emu_load_rege((opcode[3]>>4)&15); - return 0; - default: - return 1; - } -} - -static void* calc_addr(struct pt_regs *regs,int rx,int rb,int disp) -{ - rx &= 0xf; - rb &= 0xf; - disp &= 0xfff; - return (void*) ((rx != 0 ? regs->gprs[rx] : 0) + /* index */ - (rb != 0 ? regs->gprs[rb] : 0) + /* base */ - disp); -} - -int math_emu_ed(__u8 *opcode, struct pt_regs * regs) { - static const __u8 format_table[] = { - 0, 0, 0, 0, 5, 1, 2, 1, 2, 2, 2, 2, 5, 2, 4, 4, - 2, 1, 1, 0, 2, 1, 0, 2, 1, 1, 1, 1, 1, 1, 3, 3, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - static const void *jump_table[]= { - NULL, NULL, NULL, NULL, - emu_ldeb, emu_lxdb, emu_lxeb, emu_mxdb, - emu_keb, emu_ceb, emu_aeb, emu_seb, - emu_mdeb, emu_deb, emu_maeb, emu_mseb, - emu_tceb, emu_tcdb, emu_tcxb, NULL, - emu_sqeb, emu_sqdb, NULL, emu_meeb, - emu_kdb, emu_cdb, emu_adb, emu_sdb, - emu_mdb, emu_ddb, emu_madb, emu_msdb - }; - - switch (format_table[opcode[5]]) { - case 1: /* RXE format, __u64 constant */ { - __u64 *dxb, temp; - __u32 opc; - - emu_store_regd((opcode[1]>>4)&15); - opc = *((__u32 *) opcode); - dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if copy_from_user fails ? */ - copy_from_user(&temp, dxb, 8); - /* call the emulation function */ - ((void (*)(int, __u64))jump_table[opcode[5]]) - (opcode[1]>>4,temp); - emu_load_regd((opcode[1]>>4)&15); - return 0; - } - case 2: /* RXE format, __u32 constant */ { - __u32 *dxb, temp; - __u32 opc; - - emu_store_rege((opcode[1]>>4)&15); - opc = *((__u32 *) opcode); - dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if get_user fails ? */ - get_user(temp, dxb); - /* call the emulation function */ - ((void (*)(int, __u32))jump_table[opcode[5]]) - (opcode[1]>>4,temp); - emu_load_rege((opcode[1]>>4)&15); - return 0; - } - case 3: /* RXF format, __u64 constant */ { - __u32 *dxb, temp; - __u32 opc; - - emu_store_regd((opcode[1]>>4)&15); - opc = *((__u32 *) opcode); - dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if copy_from_user fails ? */ - copy_from_user(&temp, dxb, 8); - /* call the emulation function */ - ((void (*)(int, __u32, int))jump_table[opcode[5]]) - (opcode[1]>>4,temp,opcode[4]>>4); - emu_load_regd((opcode[1]>>4)&15); - return 0; - } - case 4: /* RXF format, __u32 constant */ { - __u32 *dxb, temp; - __u32 opc; - - emu_store_rege((opcode[1]>>4)&15); - opc = *((__u32 *) opcode); - dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if get_user fails ? */ - get_user(temp, dxb); - /* call the emulation function */ - ((void (*)(int, __u32, int))jump_table[opcode[5]]) - (opcode[1]>>4,temp,opcode[4]>>4); - emu_load_rege((opcode[1]>>4)&15); - return 0; - } - case 5: /* RXE format, __u32 constant */ - /* store_rege and load_regd */ - { - __u32 *dxb, temp; - __u32 opc; - emu_store_rege((opcode[1]>>4)&15); - opc = *((__u32 *) opcode); - dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if get_user fails ? */ - get_user(temp, dxb); - /* call the emulation function */ - ((void (*)(int, __u32))jump_table[opcode[5]]) - (opcode[1]>>4,temp); - emu_load_regd((opcode[1]>>4)&15); - return 0; - } - default: - return 1; - } -} - -/* - * Emulate LDR Rx,Ry with Rx or Ry not in {0, 2, 4, 6} - */ -void math_emu_ldr(__u8 *opcode) { - __u16 opc = *((__u16 *) opcode); - - if ((opc & 0x0090) == 0) { /* test if rx in {0,2,4,6} */ - /* we got an exception therfore ry can't be in {0,2,4,6} */ - __asm__ __volatile ( /* load rx from fp_regs.fprs[ry] */ - " bras 1,0f\n" - " ld 0,0(%1)\n" - "0: ex %0,0(1)" - : /* no output */ - : "a" (opc&0x00f0), - "a" (¤t->thread.fp_regs.fprs[opc&0x000f].d) - : "1" ); - } else if ((opc & 0x0009) == 0) { /* test if ry in {0,2,4,6} */ - __asm__ __volatile ( /* store ry to fp_regs.fprs[rx] */ - " bras 1,0f\n" - " std 0,0(%1)\n" - "0: ex %0,0(1)" - : /* no output */ - : "a" ((opc&0x000f)<<4), - "a" (¤t->thread.fp_regs.fprs[(opc&0x00f0)>>4].d) - : "1" ); - } else { /* move fp_regs.fprs[ry] to fp_regs.fprs[rx] */ - current->thread.fp_regs.fprs[(opc&0x00f0)>>4] = - current->thread.fp_regs.fprs[opc&0x000f]; - } -} - -/* - * Emulate LER Rx,Ry with Rx or Ry not in {0, 2, 4, 6} - */ -void math_emu_ler(__u8 *opcode) { - __u16 opc = *((__u16 *) opcode); - - if ((opc & 0x0090) == 0) { /* test if rx in {0,2,4,6} */ - /* we got an exception therfore ry can't be in {0,2,4,6} */ - __asm__ __volatile ( /* load rx from fp_regs.fprs[ry] */ - " bras 1,0f\n" - " le 0,0(%1)\n" - "0: ex %0,0(1)" - : /* no output */ - : "a" (opc&0x00f0), - "a" (¤t->thread.fp_regs.fprs[opc&0x000f].f) - : "1" ); - } else if ((opc & 0x0009) == 0) { /* test if ry in {0,2,4,6} */ - __asm__ __volatile ( /* store ry to fp_regs.fprs[rx] */ - " bras 1,0f\n" - " ste 0,0(%1)\n" - "0: ex %0,0(1)" - : /* no output */ - : "a" ((opc&0x000f)<<4), - "a" (¤t->thread.fp_regs.fprs[(opc&0x00f0)>>4].f) - : "1" ); - } else { /* move fp_regs.fprs[ry] to fp_regs.fprs[rx] */ - current->thread.fp_regs.fprs[(opc&0x00f0)>>4] = - current->thread.fp_regs.fprs[opc&0x000f]; - } -} - -/* - * Emulate LD R,D(X,B) with R not in {0, 2, 4, 6} - */ -void math_emu_ld(__u8 *opcode, struct pt_regs * regs) { - __u32 opc = *((__u32 *) opcode); - __u64 *dxb; - - dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if copy_from_user fails ? */ - copy_from_user(¤t->thread.fp_regs.fprs[(opc>>20)&15].d, dxb, 8); -} - -/* - * Emulate LE R,D(X,B) with R not in {0, 2, 4, 6} - */ -void math_emu_le(__u8 *opcode, struct pt_regs * regs) { - __u32 opc = *((__u32 *) opcode); - __u32 *mem, *dxb; - - dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if get_user fails ? */ - mem = (__u32 *) (¤t->thread.fp_regs.fprs[(opc>>20)&15].f); - get_user(mem[0], dxb); -} - -/* - * Emulate STD R,D(X,B) with R not in {0, 2, 4, 6} - */ -void math_emu_std(__u8 *opcode, struct pt_regs * regs) { - __u32 opc = *((__u32 *) opcode); - __u64 *dxb; - dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if copy_to_user fails ? */ - copy_to_user(dxb, ¤t->thread.fp_regs.fprs[(opc>>20)&15].d, 8); -} - -/* - * Emulate STE R,D(X,B) with R not in {0, 2, 4, 6} - */ -void math_emu_ste(__u8 *opcode, struct pt_regs * regs) { - __u32 opc = *((__u32 *) opcode); - __u32 *mem, *dxb; - dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if put_user fails ? */ - mem = (__u32 *) (¤t->thread.fp_regs.fprs[(opc>>20)&15].f); - put_user(mem[0], dxb); -} - -/* - * Emulate LFPC D(B) - */ -int math_emu_lfpc(__u8 *opcode, struct pt_regs *regs) { - /* FIXME: how to do that ?!? */ - return 0; -} - -/* - * Emulate STFPC D(B) - */ -int math_emu_stfpc(__u8 *opcode, struct pt_regs *regs) { - /* FIXME: how to do that ?!? */ - return 0; -} - -/* - * Emulate SRNM D(B) - */ -int math_emu_srnm(__u8 *opcode, struct pt_regs *regs) { - /* FIXME: how to do that ?!? */ - return 0; -} - - - - - - - - - - - - - - - - diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/process.c linux.ac/arch/s390x/kernel/process.c --- linux.vanilla/arch/s390x/kernel/process.c Thu Oct 11 13:52:08 2001 +++ linux.ac/arch/s390x/kernel/process.c Wed Oct 10 01:47:35 2001 @@ -44,8 +44,6 @@ #include #include -spinlock_t semaphore_wake_lock = SPIN_LOCK_UNLOCKED; - asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); /* @@ -210,7 +208,7 @@ void show_regs(struct pt_regs *regs) { char buff[80]; - int line; + int i, line; printk("CPU: %d\n",smp_processor_id()); printk("Process %s (pid: %d, stackpage=%016lX)\n", @@ -218,6 +216,17 @@ for (line = 0; sprintf_regs(line, buff, current, regs); line++) printk(buff); + + if (regs->psw.mask & PSW_PROBLEM_STATE) + { + printk("User Code:\n"); + memset(buff, 0, 20); + copy_from_user(buff, + (char *) (regs->psw.addr & PSW_ADDR_MASK), 20); + for (i = 0; i < 20; i++) + printk("%02x ", buff[i]); + printk("\n"); + } } char *task_show_regs(struct task_struct *task, char *buffer) @@ -323,28 +332,19 @@ asmlinkage int sys_fork(struct pt_regs regs) { - int ret; - - lock_kernel(); - ret = do_fork(SIGCHLD, regs.gprs[15], ®s, 0); - unlock_kernel(); - return ret; + return do_fork(SIGCHLD, regs.gprs[15], ®s, 0); } asmlinkage int sys_clone(struct pt_regs regs) { unsigned long clone_flags; unsigned long newsp; - int ret; - lock_kernel(); clone_flags = regs.gprs[3]; newsp = regs.orig_gpr2; if (!newsp) newsp = regs.gprs[15]; - ret = do_fork(clone_flags, newsp, ®s, 0); - unlock_kernel(); - return ret; + return do_fork(clone_flags, newsp, ®s, 0); } /* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/s390_ksyms.c linux.ac/arch/s390x/kernel/s390_ksyms.c --- linux.vanilla/arch/s390x/kernel/s390_ksyms.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390x/kernel/s390_ksyms.c Wed Oct 10 01:47:35 2001 @@ -18,11 +18,11 @@ /* * memory management */ -EXPORT_SYMBOL(_oi_bitmap); -EXPORT_SYMBOL(_ni_bitmap); -EXPORT_SYMBOL(_zb_findmap); -EXPORT_SYMBOL(__copy_from_user_fixup); -EXPORT_SYMBOL(__copy_to_user_fixup); +EXPORT_SYMBOL_NOVERS(_oi_bitmap); +EXPORT_SYMBOL_NOVERS(_ni_bitmap); +EXPORT_SYMBOL_NOVERS(_zb_findmap); +EXPORT_SYMBOL_NOVERS(__copy_from_user_fixup); +EXPORT_SYMBOL_NOVERS(__copy_to_user_fixup); /* * semaphore ops @@ -41,12 +41,12 @@ EXPORT_SYMBOL_NOVERS(strlen); EXPORT_SYMBOL_NOVERS(strchr); EXPORT_SYMBOL_NOVERS(strcmp); -EXPORT_SYMBOL_NOVERS(strcat); EXPORT_SYMBOL_NOVERS(strncat); EXPORT_SYMBOL_NOVERS(strncmp); EXPORT_SYMBOL_NOVERS(strncpy); EXPORT_SYMBOL_NOVERS(strnlen); EXPORT_SYMBOL_NOVERS(strrchr); +EXPORT_SYMBOL_NOVERS(strstr); EXPORT_SYMBOL_NOVERS(strtok); EXPORT_SYMBOL_NOVERS(strpbrk); @@ -66,8 +66,5 @@ EXPORT_SYMBOL(kernel_thread); EXPORT_SYMBOL(console_mode); EXPORT_SYMBOL(console_device); +EXPORT_SYMBOL_NOVERS(do_call_softirq); -#if CONFIG_IP_MULTICAST -/* Required for lcs gigibit ethernet multicast support */ -EXPORT_SYMBOL(arp_mc_map); -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/setup.c linux.ac/arch/s390x/kernel/setup.c --- linux.vanilla/arch/s390x/kernel/setup.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390x/kernel/setup.c Wed Oct 10 01:47:35 2001 @@ -47,6 +47,9 @@ unsigned int console_device = -1; unsigned long memory_size = 0; unsigned long machine_flags = 0; +struct { unsigned long addr, size, type; } memory_chunk[16]; +#define CHUNK_READ_WRITE 0 +#define CHUNK_READ_ONLY 1 __u16 boot_cpu_addr; int cpus_initialized = 0; unsigned long cpu_initialized = 0; @@ -257,6 +260,8 @@ * Setup function called from init/main.c just after the banner * was printed. */ +extern char _pstart, _pend, _stext; + void __init setup_arch(char **cmdline_p) { unsigned long bootmap_size; @@ -266,19 +271,14 @@ unsigned long start_pfn, end_pfn; static unsigned int smptrap=0; unsigned long delay = 0; + struct _lowcore *lowcore; + int i; if (smptrap) return; smptrap=1; /* - * Setup lowcore information for boot cpu - */ - cpu_init(); - boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; - __cpu_logical_map[0] = boot_cpu_addr; - - /* * print what head.S has found out about the machine */ printk((MACHINE_IS_VM) ? @@ -287,7 +287,7 @@ ROOT_DEV = to_kdev_t(0x0100); memory_start = (unsigned long) &_end; /* fixit if use $CODELO etc*/ - memory_end = memory_size; /* detected in head.s */ + memory_end = memory_size & ~0x200000UL; /* detected in head.s */ init_mm.start_code = PAGE_OFFSET; init_mm.end_code = (unsigned long) &_etext; init_mm.end_data = (unsigned long) &_edata; @@ -362,11 +362,26 @@ bootmap_size = init_bootmem(start_pfn, end_pfn); /* - * Register RAM pages with the bootmem allocator. + * Register RAM areas with the bootmem allocator. */ - free_bootmem(start_pfn << PAGE_SHIFT, - (end_pfn - start_pfn) << PAGE_SHIFT); + for (i = 0; i < 16 && memory_chunk[i].size > 0; i++) { + unsigned long start_chunk, end_chunk; + if (memory_chunk[i].type != CHUNK_READ_WRITE) + continue; + start_chunk = (memory_chunk[i].addr + PAGE_SIZE - 1); + start_chunk >>= PAGE_SHIFT; + end_chunk = (memory_chunk[i].addr + memory_chunk[i].size); + end_chunk >>= PAGE_SHIFT; + if (start_chunk < start_pfn) + start_chunk = start_pfn; + if (end_chunk > end_pfn) + end_chunk = end_pfn; + if (start_chunk < end_chunk) + free_bootmem(start_chunk << PAGE_SHIFT, + (end_chunk - start_chunk) << PAGE_SHIFT); + } + /* * Reserve the bootmem bitmap itself as well. We do this in two * steps (first step was init_bootmem()) because this catches @@ -390,6 +405,36 @@ } #endif + /* + * Setup lowcore for boot cpu + */ + lowcore = (struct _lowcore *) + __alloc_bootmem(2*PAGE_SIZE, 2*PAGE_SIZE, 0); + memset(lowcore, 0, 2*PAGE_SIZE); + lowcore->restart_psw.mask = _RESTART_PSW_MASK; + lowcore->restart_psw.addr = (addr_t) &restart_int_handler; + lowcore->external_new_psw.mask = _EXT_PSW_MASK; + lowcore->external_new_psw.addr = (addr_t) &ext_int_handler; + lowcore->svc_new_psw.mask = _SVC_PSW_MASK; + lowcore->svc_new_psw.addr = (addr_t) &system_call; + lowcore->program_new_psw.mask = _PGM_PSW_MASK; + lowcore->program_new_psw.addr = (addr_t) &pgm_check_handler; + lowcore->mcck_new_psw.mask = _MCCK_PSW_MASK; + lowcore->mcck_new_psw.addr = (addr_t) &mcck_int_handler; + lowcore->io_new_psw.mask = _IO_PSW_MASK; + lowcore->io_new_psw.addr = (addr_t) &io_int_handler; + lowcore->ipl_device = S390_lowcore.ipl_device; + lowcore->kernel_stack = ((__u32) &init_task_union) + 16384; + lowcore->async_stack = (__u64) + __alloc_bootmem(4*PAGE_SIZE, 4*PAGE_SIZE, 0) + 16384; + set_prefix((__u32)(__u64) lowcore); + cpu_init(); + boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; + __cpu_logical_map[0] = boot_cpu_addr; + + /* + * Create kernel page tables and switch to virtual addressing. + */ paging_init(); res = alloc_bootmem_low(sizeof(struct resource)); @@ -422,30 +467,49 @@ } /* - * Get CPU information for use by the procfs. + * get_cpuinfo - Get information on one CPU for use by procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. */ -int get_cpuinfo(char * buffer) +int get_cpuinfo(char *buffer, unsigned *cpu_np) { struct cpuinfo_S390 *cpuinfo; char *p = buffer; - int i; + unsigned n; - p += sprintf(p,"vendor_id : IBM/S390\n" - "# processors : %i\n" - "bogomips per cpu: %lu.%02lu\n", - smp_num_cpus, loops_per_jiffy/(500000/HZ), - (loops_per_jiffy/(5000/HZ))%100); - for (i = 0; i < smp_num_cpus; i++) { - cpuinfo = &safe_get_cpu_lowcore(i).cpu_data; - p += sprintf(p,"processor %i: " - "version = %02X, " - "identification = %06X, " - "machine = %04X\n", - i, cpuinfo->cpu_id.version, - cpuinfo->cpu_id.ident, - cpuinfo->cpu_id.machine); - } + n = *cpu_np; + while (n < NR_CPUS && (cpu_online_map & (1 << n)) == 0) + n++; + if (n >= NR_CPUS) { + *cpu_np = NR_CPUS; + return (0); + } + *cpu_np = n + 1; + + if (n == 0) { + p += sprintf(p, "vendor_id : IBM/S390\n" + "# processors : %i\n" + "bogomips per cpu: %lu.%02lu\n", + smp_num_cpus, loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ))%100); + } + cpuinfo = &safe_get_cpu_lowcore(n).cpu_data; + p += sprintf(p, "processor %i: " + "version = %02X, " + "identification = %06X, " + "machine = %04X\n", + n, cpuinfo->cpu_id.version, + cpuinfo->cpu_id.ident, + cpuinfo->cpu_id.machine); return p - buffer; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/signal.c linux.ac/arch/s390x/kernel/signal.c --- linux.vanilla/arch/s390x/kernel/signal.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390x/kernel/signal.c Wed Oct 10 01:47:35 2001 @@ -24,31 +24,25 @@ #include #include #include +#include #include #include -#define DEBUG_SIG 0 - #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -/* pretcode & sig are used to store the return addr on Intel - & the signal no as the first parameter we do this differently - using gpr14 & gpr2. */ - -#define SIGFRAME_COMMON \ -__u8 callee_used_stack[__SIGNAL_FRAMESIZE]; \ -struct sigcontext sc; \ -_sigregs sregs; \ -__u8 retcode[S390_SYSCALL_SIZE]; typedef struct { - SIGFRAME_COMMON + __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; + struct sigcontext sc; + _sigregs sregs; + __u8 retcode[S390_SYSCALL_SIZE]; } sigframe; typedef struct { - SIGFRAME_COMMON + __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; + __u8 retcode[S390_SYSCALL_SIZE]; struct siginfo info; struct ucontext uc; } rt_sigframe; @@ -206,7 +200,7 @@ err=__copy_from_user(regs,&sregs->regs,sizeof(_s390_regs_common)); if(!err) { - regs->orig_gpr2 = -1; /* disable syscall checks */ + regs->trap = -1; /* disable syscall checks */ regs->psw.mask=(saved_psw.mask&~PSW_MASK_DEBUGCHANGE)| (regs->psw.mask&PSW_MASK_DEBUGCHANGE); regs->psw.addr=(saved_psw.addr&~PSW_ADDR_DEBUGCHANGE)| @@ -218,53 +212,51 @@ return(err); } -static int -restore_sigcontext(struct sigcontext *sc, struct pt_regs *regs, - _sigregs *sregs,sigset_t *set) -{ - unsigned int err; - - err=restore_sigregs(regs,sregs); - if(!err) - err=__copy_from_user(&set->sig,&sc->oldmask,_SIGMASK_COPY_SIZE); - return(err); -} - -int sigreturn_common(struct pt_regs *regs,int framesize) +asmlinkage long sys_sigreturn(struct pt_regs *regs) { sigframe *frame = (sigframe *)regs->gprs[15]; sigset_t set; if (verify_area(VERIFY_READ, frame, sizeof(*frame))) - return -1; - if (restore_sigcontext(&frame->sc,regs,&frame->sregs,&set)) - return -1; + goto badframe; + if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE)) + goto badframe; + sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); - return 0; -} -asmlinkage long sys_sigreturn(struct pt_regs *regs) -{ - - if (sigreturn_common(regs,sizeof(sigframe))) + if (restore_sigregs(regs, &frame->sregs)) goto badframe; + return regs->gprs[2]; badframe: force_sig(SIGSEGV, current); return 0; -} +} asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) { rt_sigframe *frame = (rt_sigframe *)regs->gprs[15]; + sigset_t set; - if (sigreturn_common(regs,sizeof(rt_sigframe))) + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigregs(regs, &frame->uc.uc_mcontext)) + goto badframe; + /* It is more difficult to avoid calling this function than to call it and ignore errors. */ do_sigaltstack(&frame->uc.uc_stack, NULL, regs->gprs[15]); @@ -273,7 +265,7 @@ badframe: force_sig(SIGSEGV, current); return 0; -} +} /* * Set up a signal frame. @@ -307,58 +299,48 @@ return (void *)((sp - frame_size) & -8ul); } -static void *setup_frame_common(int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs * regs, - int frame_size,u16 retcode) +static inline int map_signal(int sig) { - sigframe *frame; - int err; + if (current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32) + return current->exec_domain->signal_invmap[sig]; + else + return sig; +} - frame = get_sigframe(ka, regs,frame_size); - if (!access_ok(VERIFY_WRITE, frame,frame_size)) - return 0; - err = save_sigregs(regs,&frame->sregs); - if(!err) - err=__put_user(&frame->sregs,&frame->sc.sregs); - if(!err) +static void setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) +{ + sigframe *frame = get_sigframe(ka, regs, sizeof(sigframe)); + if (!access_ok(VERIFY_WRITE, frame, sizeof(sigframe))) + goto give_sigsegv; + + if (__copy_to_user(&frame->sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE)) + goto give_sigsegv; + + if (save_sigregs(regs, &frame->sregs)) + goto give_sigsegv; + if (__put_user(&frame->sregs, &frame->sc.sregs)) + goto give_sigsegv; - err=__copy_to_user(&frame->sc.oldmask,&set->sig,_SIGMASK_COPY_SIZE); - if(!err) - { - regs->gprs[2]=(current->exec_domain - && current->exec_domain->signal_invmap - && sig < 32 - ? current->exec_domain->signal_invmap[sig] - : sig); - /* Set up registers for signal handler */ - regs->gprs[15] = (addr_t)frame; - regs->psw.addr = FIX_PSW(ka->sa.sa_handler); - regs->psw.mask = _USER_PSW_MASK; - } /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa.sa_flags & SA_RESTORER) { regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); } else { regs->gprs[14] = FIX_PSW(frame->retcode); - err |= __put_user(retcode, (u16 *)(frame->retcode)); + if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, + (u16 *)(frame->retcode))) + goto give_sigsegv; } - return(err ? 0:frame); -} -static void setup_frame(int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs * regs) -{ - sigframe *frame; + /* Set up registers for signal handler */ + regs->gprs[15] = (addr_t)frame; + regs->psw.addr = FIX_PSW(ka->sa.sa_handler); + regs->psw.mask = _USER_PSW_MASK; - if((frame=setup_frame_common(sig,ka,set,regs,sizeof(sigframe), - (S390_SYSCALL_OPCODE|__NR_sigreturn)))==0) - goto give_sigsegv; -#if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", - current->comm, current->pid, frame, regs->eip, frame->pretcode); -#endif - /* Martin wants this for pthreads */ + regs->gprs[2] = map_signal(sig); regs->gprs[3] = (addr_t)&frame->sc; /* We forgot to include these in the sigcontext. @@ -376,34 +358,44 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs * regs) { - rt_sigframe *frame; - addr_t orig_sp=regs->gprs[15]; - int err; + int err = 0; + rt_sigframe *frame = get_sigframe(ka, regs, sizeof(rt_sigframe)); + if (!access_ok(VERIFY_WRITE, frame, sizeof(rt_sigframe))) + goto give_sigsegv; - if((frame=setup_frame_common(sig,ka,set,regs,sizeof(rt_sigframe), - (S390_SYSCALL_OPCODE|__NR_rt_sigreturn)))==0) + if (copy_siginfo_to_user(&frame->info, info)) goto give_sigsegv; - - err = copy_siginfo_to_user(&frame->info, info); /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); err |= __put_user(0, &frame->uc.uc_link); err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); - err |= __put_user(sas_ss_flags(orig_sp), + err |= __put_user(sas_ss_flags(regs->gprs[15]), &frame->uc.uc_stack.ss_flags); err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); - err |= __put_user(&frame->sc,&frame->uc.sc); - regs->gprs[3] = (addr_t)&frame->info; - regs->gprs[4] = (addr_t)&frame->uc; - + err |= save_sigregs(regs, &frame->uc.uc_mcontext); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); if (err) goto give_sigsegv; -#if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", - current->comm, current->pid, frame, regs->eip, frame->pretcode); -#endif + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); + } else { + regs->gprs[14] = FIX_PSW(frame->retcode); + err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, + (u16 *)(frame->retcode)); + } + + /* Set up registers for signal handler */ + regs->gprs[15] = (addr_t)frame; + regs->psw.addr = FIX_PSW(ka->sa.sa_handler); + regs->psw.mask = _USER_PSW_MASK; + + regs->gprs[2] = map_signal(sig); + regs->gprs[3] = (addr_t)&frame->info; + regs->gprs[4] = (addr_t)&frame->uc; return; give_sigsegv: @@ -558,13 +550,16 @@ continue; /* FALLTHRU */ - case SIGSTOP: + case SIGSTOP: { + struct signal_struct *sig; set_current_state(TASK_STOPPED); current->exit_code = signr; - if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + sig = current->p_pptr->sig; + if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) notify_parent(current, SIGCHLD); schedule(); continue; + } case SIGQUIT: case SIGILL: case SIGTRAP: case SIGABRT: case SIGFPE: case SIGSEGV: diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/signal32.c linux.ac/arch/s390x/kernel/signal32.c --- linux.vanilla/arch/s390x/kernel/signal32.c Thu Apr 12 03:02:29 2001 +++ linux.ac/arch/s390x/kernel/signal32.c Wed Oct 10 01:47:35 2001 @@ -11,6 +11,7 @@ * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson */ +#include #include #include #include @@ -22,32 +23,27 @@ #include #include #include +#include #include #include #include "linux32.h" -#define DEBUG_SIG 0 - #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -/* pretcode & sig are used to store the return addr on Intel - & the signal no as the first parameter we do this differently - using gpr14 & gpr2. */ - -#define SIGFRAME_COMMON32 \ -__u8 callee_used_stack[__SIGNAL_FRAMESIZE32]; \ -struct sigcontext32 sc; \ -_sigregs32 sregs; \ -__u8 retcode[S390_SYSCALL_SIZE]; +#define _USER_PSW_MASK32 0x0701C00080000000 typedef struct { - SIGFRAME_COMMON32 + __u8 callee_used_stack[__SIGNAL_FRAMESIZE32]; + struct sigcontext32 sc; + _sigregs32 sregs; + __u8 retcode[S390_SYSCALL_SIZE]; } sigframe32; typedef struct { - SIGFRAME_COMMON32 + __u8 callee_used_stack[__SIGNAL_FRAMESIZE32]; + __u8 retcode[S390_SYSCALL_SIZE]; struct siginfo32 info; struct ucontext32 uc; } rt_sigframe32; @@ -328,7 +324,7 @@ if(!err) { - regs->orig_gpr2 = -1; /* disable syscall checks */ + regs->trap = -1; /* disable syscall checks */ regs->psw.mask=(saved_psw.mask&~PSW_MASK_DEBUGCHANGE)| (regs->psw.mask&PSW_MASK_DEBUGCHANGE); regs->psw.addr=(saved_psw.addr&~PSW_ADDR_DEBUGCHANGE)| @@ -342,40 +338,25 @@ return(err); } -static int -restore_sigcontext32(struct sigcontext32 *sc, struct pt_regs *regs, - _sigregs32 *sregs,sigset_t *set) -{ - unsigned int err; - - err=restore_sigregs32(regs,sregs); - if(!err) - err=__copy_from_user(&set->sig,&sc->oldmask,_SIGMASK_COPY_SIZE32); - return(err); -} - -int sigreturn_common32(struct pt_regs *regs) +asmlinkage long sys32_sigreturn(struct pt_regs *regs) { sigframe32 *frame = (sigframe32 *)regs->gprs[15]; sigset_t set; if (verify_area(VERIFY_READ, frame, sizeof(*frame))) - return -1; - if (restore_sigcontext32(&frame->sc,regs,&frame->sregs,&set)) - return -1; + goto badframe; + if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32)) + goto badframe; + sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); - return 0; -} -asmlinkage long sys32_sigreturn(struct pt_regs *regs) -{ - - if (sigreturn_common32(regs)) + if (restore_sigregs32(regs, &frame->sregs)) goto badframe; + return regs->gprs[2]; badframe: @@ -386,11 +367,23 @@ asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs) { rt_sigframe32 *frame = (rt_sigframe32 *)regs->gprs[15]; + sigset_t set; stack_t st; int err; mm_segment_t old_fs = get_fs(); - if (sigreturn_common32(regs)) + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigregs32(regs, &frame->uc.uc_mcontext)) goto badframe; err = __get_user(st.ss_sp, &frame->uc.uc_stack.ss_sp); @@ -399,17 +392,18 @@ err |= __get_user(st.ss_flags, &frame->uc.uc_stack.ss_flags); if (err) goto badframe; - set_fs (KERNEL_DS); + /* It is more difficult to avoid calling this function than to call it and ignore errors. */ + set_fs (KERNEL_DS); do_sigaltstack(&st, NULL, regs->gprs[15]); set_fs (old_fs); return regs->gprs[2]; badframe: - force_sig(SIGSEGV, current); - return 0; + force_sig(SIGSEGV, current); + return 0; } /* @@ -444,58 +438,54 @@ return (void *)((sp - frame_size) & -8ul); } -static void *setup_frame_common32(int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs * regs, - int frame_size,u16 retcode) +static inline int map_signal(int sig) { - sigframe32 *frame; - int err; + if (current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32) + return current->exec_domain->signal_invmap[sig]; + else + return sig; +} - frame = get_sigframe(ka, regs,frame_size); - if (!access_ok(VERIFY_WRITE, frame,frame_size)) - return 0; - err = save_sigregs32(regs,&frame->sregs); - if(!err) - err=__put_user(&frame->sregs,&frame->sc.sregs); - if(!err) +static void setup_frame32(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) +{ + sigframe32 *frame = get_sigframe(ka, regs, sizeof(sigframe32)); + if (!access_ok(VERIFY_WRITE, frame, sizeof(sigframe32))) + goto give_sigsegv; + + if (__copy_to_user(&frame->sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE32)) + goto give_sigsegv; + + if (save_sigregs32(regs, &frame->sregs)) + goto give_sigsegv; + if (__put_user(&frame->sregs, &frame->sc.sregs)) + goto give_sigsegv; - err=__copy_to_user(&frame->sc.oldmask,&set->sig,_SIGMASK_COPY_SIZE32); - if(!err) - { - regs->gprs[2]=(current->exec_domain - && current->exec_domain->signal_invmap - && sig < 32 - ? current->exec_domain->signal_invmap[sig] - : sig); - /* Set up registers for signal handler */ - regs->gprs[15] = (addr_t)frame; - regs->psw.addr = FIX_PSW(ka->sa.sa_handler); - } /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa.sa_flags & SA_RESTORER) { - regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); + regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); } else { - regs->gprs[14] = FIX_PSW(frame->retcode); - err |= __put_user(retcode, (u16 *)(frame->retcode)); - } - return(err ? 0:frame); -} + regs->gprs[14] = FIX_PSW(frame->retcode); + if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, + (u16 *)(frame->retcode))) + goto give_sigsegv; + } -static void setup_frame32(int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs * regs) -{ - sigframe32 *frame; - - if((frame=setup_frame_common32(sig,ka,set,regs,sizeof(sigframe32), - (S390_SYSCALL_OPCODE|__NR_sigreturn)))==0) - goto give_sigsegv; -#if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", - current->comm, current->pid, frame, regs->eip, frame->pretcode); -#endif - /* Martin wants this for pthreads */ - regs->gprs[3] = (addr_t)&frame->sc; + /* Set up registers for signal handler */ + regs->gprs[15] = (addr_t)frame; + regs->psw.addr = FIX_PSW(ka->sa.sa_handler); + regs->psw.mask = _USER_PSW_MASK32; + + regs->gprs[2] = map_signal(sig); + regs->gprs[3] = (addr_t)&frame->sc; + + /* We forgot to include these in the sigcontext. + To avoid breaking binary compatibility, they are passed as args. */ + regs->gprs[4] = current->thread.trap_no; + regs->gprs[5] = current->thread.prot_addr; return; give_sigsegv: @@ -507,33 +497,44 @@ static void setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs * regs) { - rt_sigframe32 *frame; - addr_t orig_sp=regs->gprs[15]; - int err; + int err = 0; + rt_sigframe32 *frame = get_sigframe(ka, regs, sizeof(rt_sigframe32)); + if (!access_ok(VERIFY_WRITE, frame, sizeof(rt_sigframe32))) + goto give_sigsegv; - if((frame=setup_frame_common32(sig,ka,set,regs,sizeof(rt_sigframe32), - (S390_SYSCALL_OPCODE|__NR_rt_sigreturn)))==0) + if (copy_siginfo_to_user32(&frame->info, info)) goto give_sigsegv; - - err = copy_siginfo_to_user32(&frame->info, info); /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); err |= __put_user(0, &frame->uc.uc_link); err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); - err |= __put_user(sas_ss_flags(orig_sp), - &frame->uc.uc_stack.ss_flags); + err |= __put_user(sas_ss_flags(regs->gprs[15]), + &frame->uc.uc_stack.ss_flags); err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); - regs->gprs[3] = (addr_t)&frame->info; - regs->gprs[4] = (addr_t)&frame->uc; - + err |= save_sigregs32(regs, &frame->uc.uc_mcontext); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); if (err) goto give_sigsegv; -#if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", - current->comm, current->pid, frame, regs->eip, frame->pretcode); -#endif + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); + } else { + regs->gprs[14] = FIX_PSW(frame->retcode); + err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, + (u16 *)(frame->retcode)); + } + + /* Set up registers for signal handler */ + regs->gprs[15] = (addr_t)frame; + regs->psw.addr = FIX_PSW(ka->sa.sa_handler); + regs->psw.mask = _USER_PSW_MASK32; + + regs->gprs[2] = map_signal(sig); + regs->gprs[3] = (addr_t)&frame->info; + regs->gprs[4] = (addr_t)&frame->uc; return; give_sigsegv: @@ -551,7 +552,7 @@ siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) { /* Are we from a system call? */ - if (regs->orig_gpr2 >= 0) { + if (regs->trap == __LC_SVC_OLD_PSW) { /* If so, check system call restarting.. */ switch (regs->gprs[2]) { case -ERESTARTNOHAND: @@ -692,12 +693,12 @@ case SIGQUIT: case SIGILL: case SIGTRAP: case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: if (do_coredump(signr, regs)) exit_code |= 0x80; /* FALLTHRU */ default: - lock_kernel(); sigaddset(¤t->pending.signal, signr); recalc_sigpending(current); current->flags |= PF_SIGNALED; @@ -712,7 +713,7 @@ } /* Did we come from a system call? */ - if ( regs->trap == __LC_SVC_OLD_PSW /* System Call! */ ) { + if ( regs->trap == __LC_SVC_OLD_PSW /* System Call! */ ) { /* Restart the system call - no handlers present */ if (regs->gprs[2] == -ERESTARTNOHAND || regs->gprs[2] == -ERESTARTSYS || diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/smp.c linux.ac/arch/s390x/kernel/smp.c --- linux.vanilla/arch/s390x/kernel/smp.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390x/kernel/smp.c Wed Oct 10 01:47:35 2001 @@ -57,6 +57,8 @@ spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; +unsigned long cpu_online_map; + /* * Setup routine for controlling SMP activation * @@ -92,6 +94,95 @@ extern void reipl(unsigned long devno); +static sigp_ccode smp_ext_bitcall(int, ec_bit_sig); +static void smp_ext_bitcall_others(ec_bit_sig); + +/* + * Structure and data for smp_call_function(). This is designed to minimise + * static memory requirements. It also looks cleaner. + */ +static spinlock_t call_lock = SPIN_LOCK_UNLOCKED; + +struct call_data_struct { + void (*func) (void *info); + void *info; + atomic_t started; + atomic_t finished; + int wait; +}; + +static struct call_data_struct * call_data; + +/* + * 'Call function' interrupt callback + */ +static void do_call_function(void) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + atomic_inc(&call_data->started); + (*func)(info); + if (wait) + atomic_inc(&call_data->finished); +} + +/* + * this function sends a 'generic call function' IPI to all other CPUs + * in the system. + */ + +int smp_call_function (void (*func) (void *info), void *info, int nonatomic, + int wait) +/* + * [SUMMARY] Run a function on all other CPUs. + * The function to run. This must be fast and non-blocking. + * An arbitrary pointer to pass to the function. + * currently unused. + * If true, wait (atomically) until function has completed on other CPUs. + * [RETURNS] 0 on success, else a negative status code. Does not return until + * remote CPUs are nearly ready to execute <> or are or have executed. + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler, you may call it from a bottom half handler. + */ +{ + struct call_data_struct data; + int cpus = smp_num_cpus-1; + + if (!cpus || !atomic_read(&smp_commenced)) + return 0; + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + spin_lock_bh(&call_lock); + call_data = &data; + /* Send a message to all other CPUs and wait for them to respond */ + smp_ext_bitcall_others(ec_call_function); + + /* Wait for response */ + while (atomic_read(&data.started) != cpus) + barrier(); + + if (wait) + while (atomic_read(&data.finished) != cpus) + barrier(); + spin_unlock_bh(&call_lock); + + return 0; +} + + +/* + * Various special callbacks + */ + void do_machine_restart(void) { smp_send_stop(); @@ -148,7 +239,6 @@ void do_ext_call_interrupt(struct pt_regs *regs, __u16 code) { - ec_ext_call *ec, *next; unsigned long bits; /* @@ -167,138 +257,15 @@ do_machine_halt(); if (test_bit(ec_power_off, &bits)) do_machine_power_off(); - - /* - * Handle external call commands with a parameter area - */ - ec = (ec_ext_call *) xchg(&S390_lowcore.ext_call_queue, 0); - if (ec == NULL) - return; /* no command signals */ - - /* Make a fifo out of the lifo */ - next = ec->next; - ec->next = NULL; - while (next != NULL) { - ec_ext_call *tmp = next->next; - next->next = ec; - ec = next; - next = tmp; - } - - /* Execute every sigp command on the queue */ - while (ec != NULL) { - switch (ec->cmd) { - case ec_callback_async: { - void (*func)(void *info); - void *info; - - func = ec->func; - info = ec->info; - atomic_set(&ec->status,ec_executing); - (func)(info); - return; - } - case ec_callback_sync: - atomic_set(&ec->status,ec_executing); - (ec->func)(ec->info); - atomic_set(&ec->status,ec_done); - return; - default: - } - ec = ec->next; - } -} - -/* - * Swap in a new request to external call queue - */ -static inline void smp_add_ext_call(ec_ext_call *ec, struct _lowcore *lowcore) -{ - int success; - - while (1) { - ec->next = (ec_ext_call*) lowcore->ext_call_queue; - __asm__ __volatile__ ( - " lgr 0,%2\n" - " csg 0,%3,%1\n" - " ipm %0\n" - " srl %0,28\n" - : "=d" (success), "+m" (lowcore->ext_call_queue) - : "d" (ec->next), "d" (ec) - : "cc", "0" ); - if (success == 0) break; - } -} - -/* - * Send an external call sigp to another cpu and wait for its completion. - */ -sigp_ccode -smp_ext_call(int cpu, void (*func)(void *info), void *info, int wait) -{ - sigp_ccode ccode; - ec_ext_call ec; - - ec.cmd = wait ? ec_callback_sync:ec_callback_async; - atomic_set(&ec.status, ec_pending); - ec.func = func; - ec.info = info; - /* swap in new request to external call queue */ - smp_add_ext_call(&ec, &get_cpu_lowcore(cpu)); - /* - * We try once to deliver the signal. There are four possible - * return codes: - * 0) Order code accepted - can't show up on an external call - * 1) Status stored - fine, wait for completion. - * 2) Busy - there is another signal pending. Thats fine too, because - * do_ext_call from the pending signal will execute all signals on - * the queue. We wait for completion. - * 3) Not operational - something very bad has happened to the cpu. - * do not wait for completion. - */ - ccode = signal_processor(cpu, sigp_external_call); - - if (ccode != sigp_not_operational) - /* wait for completion, FIXME: possible seed of a deadlock */ - while (atomic_read(&ec.status) != (wait?ec_done:ec_executing)); - - return ccode; -} - -/* - * Send a callback sigp to every other cpu in the system. - */ -void smp_ext_call_others(void (*func)(void *info), void *info, int wait) -{ - ec_ext_call ec[NR_CPUS]; - sigp_ccode ccode; - int i; - - for (i = 0; i < smp_num_cpus; i++) { - if (smp_processor_id() == i) - continue; - ec[i].cmd = wait ? ec_callback_sync : ec_callback_async; - atomic_set(&ec[i].status, ec_pending); - ec[i].func = func; - ec[i].info = info; - smp_add_ext_call(ec+i, &get_cpu_lowcore(i)); - ccode = signal_processor(i, sigp_external_call); - } - - /* wait for completion, FIXME: possible seed of a deadlock */ - for (i = 0; i < smp_num_cpus; i++) { - if (smp_processor_id() == i) - continue; - while (atomic_read(&ec[i].status) != - (wait ? ec_done:ec_executing)); - } + if (test_bit(ec_call_function, &bits)) + do_call_function(); } /* * Send an external call sigp to another cpu and return without waiting * for its completion. */ -sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig) +static sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig) { sigp_ccode ccode; @@ -314,7 +281,7 @@ * Send an external call sigp to every other cpu in the system and * return without waiting for its completion. */ -void smp_ext_bitcall_others(ec_bit_sig sig) +static void smp_ext_bitcall_others(ec_bit_sig sig) { sigp_ccode ccode; int i; @@ -331,51 +298,6 @@ } /* - * cycles through all the cpus, - * returns early if info is not NULL & the processor has something - * of intrest to report in the info structure. - * it returns the next cpu to check if it returns early. - * i.e. it should be used as follows if you wish to receive info. - * next_cpu=0; - * do - * { - * info->cpu=next_cpu; - * next_cpu=smp_signal_others(order_code,parameter,1,info); - * ... check info here - * } while(next_cpu<=smp_num_cpus) - * - * if you are lazy just use it like - * smp_signal_others(order_code,parameter,0,1,NULL); - */ -int smp_signal_others(sigp_order_code order_code, u32 parameter, - int spin, sigp_info *info) -{ - sigp_ccode ccode; - u32 dummy; - u16 i; - - if (info) - info->intresting = 0; - for (i = (info ? info->cpu : 0); i < smp_num_cpus; i++) { - if (smp_processor_id() != i) { - do { - ccode = signal_processor_ps( - (info ? &info->status : &dummy), - parameter, i, order_code); - } while(spin && ccode == sigp_busy); - if (info && ccode != sigp_order_code_accepted) { - info->intresting = 1; - info->cpu = i; - info->ccode = ccode; - i++; - break; - } - } - } - return i; -} - -/* * this function sends a 'stop' sigp to all other CPUs in the system. * it goes straight through. */ @@ -392,7 +314,18 @@ /* stop all processors */ - smp_signal_others(sigp_stop, 0, 1, NULL); + for (i = 0; i < smp_num_cpus; i++) { + if (smp_processor_id() != i) { + int ccode; + do { + ccode = signal_processor_ps( + &dummy, + 0, + i, + sigp_stop); + } while(ccode == sigp_busy); + } + } /* store status of all processors in their lowcores (real 0) */ @@ -469,7 +402,7 @@ parms.end_ctl = cr; parms.orvals[cr] = 1 << bit; parms.andvals[cr] = -1L; - smp_ext_call_others(smp_ctl_bit_callback, &parms, 1); + smp_call_function(smp_ctl_bit_callback, &parms, 0, 1); } __ctl_set_bit(cr, bit); } @@ -485,34 +418,11 @@ parms.end_ctl = cr; parms.orvals[cr] = 0; parms.andvals[cr] = ~(1L << bit); - smp_ext_call_others(smp_ctl_bit_callback, &parms, 1); + smp_call_function(smp_ctl_bit_callback, &parms, 0, 1); } __ctl_clear_bit(cr, bit); } -/* - * Call a function on all other processors - */ - -int -smp_call_function(void (*func)(void *info), void *info, int retry, int wait) -/* - * [SUMMARY] Run a function on all other CPUs. - * The function to run. This must be fast and non-blocking. - * An arbitrary pointer to pass to the function. - * currently unused. - * If true, wait (atomically) until function has completed on other CPUs. - * [RETURNS] 0 on success, else a negative status code. Does not return until - * remote CPUs are nearly ready to execute <> or are or have executed. - * - * You must not call this function with disabled interrupts or from a - * hardware interrupt handler, you may call it from a bottom half handler. - */ -{ - if (atomic_read(&smp_commenced) != 0) - smp_ext_call_others(func, info, wait); - return 0; -} /* * Lets check how many CPUs we have. @@ -607,15 +517,20 @@ init_tasks[cpu] = idle; cpu_lowcore=&get_cpu_lowcore(cpu); - cpu_lowcore->kernel_stack=idle->thread.ksp; - __asm__ __volatile__("stctg 0,15,%0\n\t" - "stam 0,15,%1" + cpu_lowcore->save_area[15] = idle->thread.ksp; + cpu_lowcore->kernel_stack = (idle->thread.ksp | 16383) + 1; + __asm__ __volatile__("la 1,%0\n\t" + "stctg 0,15,0(1)\n\t" + "la 1,%1\n\t" + "stam 0,15,0(1)" : "=m" (cpu_lowcore->cregs_save_area[0]), "=m" (cpu_lowcore->access_regs_save_area[0]) - : : "memory"); + : : "1", "memory"); eieio(); signal_processor(cpu,sigp_restart); + /* Mark this cpu as online. */ + set_bit(cpu, &cpu_online_map); } /* @@ -643,6 +558,7 @@ void __init smp_boot_cpus(void) { struct _lowcore *curr_lowcore; + unsigned long async_stack; sigp_ccode ccode; int i; @@ -673,8 +589,16 @@ printk("smp_boot_cpus failed to allocate prefix memory\n"); break; } + async_stack = __get_free_pages(GFP_KERNEL,2); + if (async_stack == 0) { + printk("smp_boot_cpus failed to allocate asyncronous" + " interrupt stack\n"); + free_page((unsigned long) curr_lowcore); + break; + } lowcore_ptr[i] = curr_lowcore; memcpy(curr_lowcore, &S390_lowcore, sizeof(struct _lowcore)); + curr_lowcore->async_stack = async_stack + (4 * PAGE_SIZE); /* * Most of the parameters are set up when the cpu is * started up. @@ -733,8 +657,6 @@ s390_do_profile(regs->psw.addr); if (!--prof_counter[cpu]) { - int system = 1-user; - struct task_struct * p = current; /* * The multiplier may have changed since the last time we got @@ -756,9 +678,7 @@ * WrongThing (tm) to do. */ - irq_enter(cpu, 0); update_process_times(user); - irq_exit(cpu, 0); } } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/time.c linux.ac/arch/s390x/kernel/time.c --- linux.vanilla/arch/s390x/kernel/time.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390x/kernel/time.c Wed Oct 10 01:47:35 2001 @@ -155,17 +155,16 @@ extern __u16 boot_cpu_addr; #endif -void do_timer_interrupt(struct pt_regs *regs,int error_code) +void do_timer_interrupt(struct pt_regs *regs, __u16 error_code) { - unsigned long flags; + int cpu = smp_processor_id(); + + irq_enter(cpu, 0); /* * reset timer to 10ms minus time already elapsed * since timer-interrupt pending */ - - save_flags(flags); - cli(); #ifdef CONFIG_SMP if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr) { write_lock(&xtime_lock); @@ -201,8 +200,8 @@ write_unlock(&xtime_lock); #endif } - restore_flags(flags); + irq_exit(cpu, 0); } /* @@ -256,4 +255,7 @@ init_timer_cc -= 0x8126d60e46000000LL - (0x3c26700LL*1000000*4096); tod_to_timeval(init_timer_cc, &xtime); + + /* Set do_get_fast_time function pointer. */ + do_get_fast_time = do_gettimeofday; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/traps.c linux.ac/arch/s390x/kernel/traps.c --- linux.vanilla/arch/s390x/kernel/traps.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390x/kernel/traps.c Wed Oct 10 01:47:35 2001 @@ -31,7 +31,6 @@ #include #include #include -#include #if CONFIG_REMOTE_DEBUG #include #endif @@ -59,14 +58,16 @@ extern void pfault_interrupt(struct pt_regs *regs, __u16 error_code); #endif -spinlock_t die_lock; +spinlock_t die_lock = SPIN_LOCK_UNLOCKED; void die(const char * str, struct pt_regs * regs, long err) { console_verbose(); spin_lock_irq(&die_lock); + bust_spinlocks(1); printk("%s: %04lx\n", str, err & 0xffff); show_regs(regs); + bust_spinlocks(0); spin_unlock_irq(&die_lock); do_exit(SIGSEGV); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/wrapper32.S linux.ac/arch/s390x/kernel/wrapper32.S --- linux.vanilla/arch/s390x/kernel/wrapper32.S Tue Feb 13 22:13:44 2001 +++ linux.ac/arch/s390x/kernel/wrapper32.S Wed Oct 10 01:47:35 2001 @@ -1069,3 +1069,23 @@ llgfr %r4,%r4 # unsigned long jg sys32_fcntl64 # branch to system call + .globl sys32_stat64_wrapper +sys32_stat64_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # struct stat64 * + llgfr %r4,%r4 # long + jg sys32_stat64 # branch to system call + + .globl sys32_lstat64_wrapper +sys32_lstat64_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # struct stat64 * + llgfr %r4,%r4 # long + jg sys32_lstat64 # branch to system call + + .globl sys32_fstat64_wrapper +sys32_fstat64_wrapper: + llgfr %r2,%r2 # unsigned long + llgtr %r3,%r3 # struct stat64 * + llgfr %r4,%r4 # long + jg sys32_fstat64 # branch to system call diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/mm/extable.c linux.ac/arch/s390x/mm/extable.c --- linux.vanilla/arch/s390x/mm/extable.c Tue Feb 13 22:13:44 2001 +++ linux.ac/arch/s390x/mm/extable.c Wed Oct 10 01:47:35 2001 @@ -10,6 +10,7 @@ #include #include +#include #include extern const struct exception_table_entry __start___ex_table[]; @@ -36,26 +37,32 @@ return 0; } +extern spinlock_t modlist_lock; + unsigned long search_exception_table(unsigned long addr) { - unsigned long ret; + unsigned long ret = 0; + unsigned long flags; #ifndef CONFIG_MODULES /* There is only the kernel to search. */ ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); - if (ret) return FIX_PSW(ret); + return ret; #else /* The kernel is the last "module" -- no need to treat it special. */ struct module *mp; + + spin_lock_irqsave(&modlist_lock, flags); for (mp = module_list; mp != NULL; mp = mp->next) { - if (mp->ex_table_start == NULL) + if (mp->ex_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING))) continue; ret = search_one_table(mp->ex_table_start, mp->ex_table_end - 1, addr); - if (ret) return FIX_PSW(ret); + if (ret) + break; } + spin_unlock_irqrestore(&modlist_lock, flags); + return ret; #endif - - return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/mm/fault.c linux.ac/arch/s390x/mm/fault.c --- linux.vanilla/arch/s390x/mm/fault.c Wed Jul 25 22:12:01 2001 +++ linux.ac/arch/s390x/mm/fault.c Wed Oct 10 01:47:35 2001 @@ -4,6 +4,7 @@ * S390 version * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Hartmut Penner (hp@de.ibm.com) + * Ulrich Weigand (uweigand@de.ibm.com) * * Derived from "arch/i386/mm/fault.c" * Copyright (C) 1995 Linus Torvalds @@ -22,6 +23,7 @@ #include #include #include +#include #include #include @@ -33,33 +35,33 @@ #endif extern void die(const char *,struct pt_regs *,long); +static void force_sigsegv(struct task_struct *tsk, int code, void *address); extern spinlock_t timerlist_lock; /* * Unlock any spinlocks which will prevent us from getting the - * message out + * message out (timerlist_lock is acquired through the + * console unblank code) */ void bust_spinlocks(int yes) { - spin_lock_init(&timerlist_lock); - if (yes) { - oops_in_progress = 1; -#ifdef CONFIG_SMP - atomic_set(&global_irq_lock,0); -#endif - } else { - int loglevel_save = console_loglevel; - oops_in_progress = 0; - /* - * OK, the message is on the console. Now we call printk() - * without oops_in_progress set so that printk will give klogd - * a poke. Hold onto your hats... - */ - console_loglevel = 15; /* NMI oopser may have shut the console up */ - printk(" "); - console_loglevel = loglevel_save; - } + spin_lock_init(&timerlist_lock); + if (yes) { + oops_in_progress = 1; + } else { + int loglevel_save = console_loglevel; + oops_in_progress = 0; + console_unblank(); + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; + printk(" "); + console_loglevel = loglevel_save; + } } /* @@ -84,6 +86,29 @@ int si_code = SEGV_MAPERR; int kernel_address = 0; + tsk = current; + mm = tsk->mm; + + /* + * Check for low-address protection. This needs to be treated + * as a special case because the translation exception code + * field is not guaranteed to contain valid data in this case. + */ + if ((error_code & 0xff) == 4 && !(S390_lowcore.trans_exc_code & 4)) { + + /* Low-address protection hit in kernel mode means + NULL pointer write access in kernel mode. */ + if (!(regs->psw.mask & PSW_PROBLEM_STATE)) { + address = 0; + kernel_address = 1; + goto no_context; + } + + /* Low-address protection hit in user mode 'cannot happen'. */ + die ("Low-address protection", regs, error_code); + do_exit(SIGKILL); + } + /* * get the failing address * more specific the segment and page table portion of @@ -92,11 +117,6 @@ address = S390_lowcore.trans_exc_code&-4096L; - tsk = current; - mm = tsk->mm; - - if (in_interrupt() || !mm) - goto no_context; /* * Check which address space the address belongs to @@ -127,6 +147,7 @@ } } die("page fault via unknown access register", regs, error_code); + do_exit(SIGKILL); break; case 2: /* Secondary Segment Table Descriptor */ @@ -135,6 +156,11 @@ break; } + /* + * Check whether we have a user MM in the first place. + */ + if (in_interrupt() || !mm) + goto no_context; /* * When we get here, the fault happened in the current @@ -144,10 +170,8 @@ down_read(&mm->mmap_sem); vma = find_vma(mm, address); - if (!vma) { - printk("no vma for address %lX\n",address); + if (!vma) goto bad_area; - } if (vma->vm_start <= address) goto good_area; if (!(vma->vm_flags & VM_GROWSDOWN)) @@ -177,6 +201,7 @@ goto bad_area; } + survive: /* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo @@ -207,7 +232,6 @@ /* User mode accesses just cause a SIGSEGV */ if (regs->psw.mask & PSW_PROBLEM_STATE) { - struct siginfo si; tsk->thread.prot_addr = address; tsk->thread.trap_no = error_code; #ifndef CONFIG_SYSCTL @@ -224,10 +248,8 @@ show_regs(regs); } #endif - si.si_signo = SIGSEGV; - si.si_code = si_code; - si.si_addr = (void*) address; - force_sig_info(SIGSEGV, &si, tsk); + + force_sigsegv(tsk, si_code, (void *)address); return; } @@ -242,6 +264,7 @@ * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. */ + if (kernel_address) printk(KERN_ALERT "Unable to handle kernel pointer dereference" " at virtual kernel address %016lx\n", address); @@ -249,10 +272,6 @@ printk(KERN_ALERT "Unable to handle kernel paging request" " at virtual user address %016lx\n", address); -/* - * need to define, which information is useful here - */ - die("Oops", regs, error_code); do_exit(SIGKILL); @@ -263,6 +282,12 @@ */ out_of_memory: up_read(&mm->mmap_sem); + if (tsk->pid == 1) { + tsk->policy |= SCHED_YIELD; + schedule(); + down_read(&mm->mmap_sem); + goto survive; + } printk("VM: killing process %s\n", tsk->comm); if (regs->psw.mask & PSW_PROBLEM_STATE) do_exit(SIGKILL); @@ -284,6 +309,20 @@ goto no_context; } +/* + * Send SIGSEGV to task. This is an external routine + * to keep the stack usage of do_page_fault small. + */ +static void force_sigsegv(struct task_struct *tsk, int code, void *address) +{ + struct siginfo si; + si.si_signo = SIGSEGV; + si.si_code = code; + si.si_addr = address; + force_sig_info(SIGSEGV, &si, tsk); +} + + #ifdef CONFIG_PFAULT /* * 'pfault' pseudo page faults routines. @@ -316,13 +355,11 @@ int resolved; } pseudo_wait_t; -static pseudo_wait_t *pseudo_lock_queue = NULL; -static spinlock_t pseudo_wait_spinlock; /* spinlock to protect lock queue */ - int pfault_init(void) { pfault_refbk_t refbk = - { 0x258, 0, 5, 2, __LC_KERNEL_STACK, 1ULL << 48, 1ULL << 48, 0ULL }; + { 0x258, 0, 5, 2, __LC_KERNEL_STACK, 1ULL << 48, 1ULL << 48, + 0x8000000000000000ULL }; int rc; if (pfault_disable) @@ -362,7 +399,6 @@ asmlinkage void pfault_interrupt(struct pt_regs *regs, __u16 error_code) { - DECLARE_WAITQUEUE(wait, current); struct task_struct *tsk; wait_queue_head_t queue; wait_queue_head_t *qp; @@ -375,7 +411,7 @@ * external interrupt. */ subcode = S390_lowcore.cpu_addr; - if ((subcode & 0xff00) != 0x06) + if ((subcode & 0xff00) != 0x0600) return; /* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/mm/init.c linux.ac/arch/s390x/mm/init.c --- linux.vanilla/arch/s390x/mm/init.c Fri Sep 21 04:02:03 2001 +++ linux.ac/arch/s390x/mm/init.c Wed Oct 10 01:47:35 2001 @@ -36,7 +36,7 @@ #include #include #include - + mmu_gather_t mmu_gathers[NR_CPUS]; static unsigned long totalram_pages; @@ -44,44 +44,23 @@ pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((__aligned__(PAGE_SIZE))); char empty_zero_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); -static int test_access(unsigned long loc) -{ - static const int ssm_mask = 0x07000000L; - int rc, i; - - rc = 0; - for (i=0; i<2; i++) { - __asm__ __volatile__( - " slgr %0,%0\n" - " ssm %1\n" - " tprot 0(%2),0\n" - "0: jne 1f\n" - " lghi %0,1\n" - "1: ssm %3\n" - ".section __ex_table,\"a\"\n" - " .align 8\n" - " .quad 0b,1b\n" - ".previous" - : "+&d" (rc) : "i" (0), "a" (loc), "m" (ssm_mask) - : "cc"); - if (rc == 0) - break; - loc += 0x100000; - } - return rc; -} - int do_check_pgt_cache(int low, int high) { int freed = 0; if(pgtable_cache_size > high) { do { - if(pgd_quicklist) - free_pgd_slow(get_pgd_fast()), freed += 4; - if(pmd_quicklist) - pmd_free_slow(pmd_alloc_one_fast(NULL, 0)), freed += 4; - if(pte_quicklist) - pte_free_slow(pte_alloc_one_fast(NULL, 0)), freed++; + if(pgd_quicklist) { + free_pgd_slow(get_pgd_fast()); + freed += 4; + } + if(pmd_quicklist) { + pmd_free_slow(pmd_alloc_one_fast(NULL, 0)); + freed += 4; + } + if(pte_quicklist) { + pte_free_slow(pte_alloc_one_fast(NULL, 0)); + freed += 4; + } } while(pgtable_cache_size > low); } return freed; @@ -139,7 +118,7 @@ int i,j,k; unsigned long address=0; unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | - _REGION_TABLE; + _KERN_REGION_TABLE; unsigned long end_mem = (unsigned long) __va(max_low_pfn*PAGE_SIZE); static const int ssm_mask = 0x04000000L; @@ -212,7 +191,6 @@ void __init mem_init(void) { unsigned long codesize, reservedpages, datasize, initsize; - unsigned long tmp; max_mapnr = num_physpages = max_low_pfn; high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); @@ -223,25 +201,7 @@ /* this will put all low memory onto the freelists */ totalram_pages += free_all_bootmem(); - /* mark usable pages in the mem_map[] and count reserved pages */ reservedpages = 0; - tmp = 0; - do { - if (tmp && (tmp & 0x1ff) == 0 && - test_access(tmp * PAGE_SIZE) == 0) { - printk("2M Segment 0x%016lX not available\n", - tmp * PAGE_SIZE); - do { - set_bit(PG_reserved, &mem_map[tmp].flags); - reservedpages++; - tmp++; - } while (tmp < max_low_pfn && (tmp & 0x1ff)); - } else { - if (PageReserved(mem_map+tmp)) - reservedpages++; - tmp++; - } - } while (tmp < max_low_pfn); codesize = (unsigned long) &_etext - (unsigned long) &_text; datasize = (unsigned long) &_edata - (unsigned long) &_etext; @@ -287,7 +247,7 @@ void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sh/config.in linux.ac/arch/sh/config.in --- linux.vanilla/arch/sh/config.in Sat Sep 8 20:29:09 2001 +++ linux.ac/arch/sh/config.in Wed Oct 10 01:47:35 2001 @@ -9,6 +9,7 @@ define_bool CONFIG_UID16 y define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n +define_bool CONFIG_GENERIC_BUST_SPINLOCK n mainmenu_option next_comment comment 'Code maturity level options' diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sh/kernel/init_task.c linux.ac/arch/sh/kernel/init_task.c --- linux.vanilla/arch/sh/kernel/init_task.c Mon Sep 17 23:29:09 2001 +++ linux.ac/arch/sh/kernel/init_task.c Wed Oct 10 01:47:35 2001 @@ -5,6 +5,7 @@ #include #include +static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sh/kernel/setup.c linux.ac/arch/sh/kernel/setup.c --- linux.vanilla/arch/sh/kernel/setup.c Sat Sep 8 20:29:09 2001 +++ linux.ac/arch/sh/kernel/setup.c Wed Oct 10 01:47:35 2001 @@ -514,12 +514,30 @@ } /* - * Get CPU information for use by the procfs. + * get_cpuinfo - Get information on one CPU for use by procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. */ #ifdef CONFIG_PROC_FS -int get_cpuinfo(char *buffer) +int get_cpuinfo(char *buffer, unsigned *cpu_np) { char *p = buffer; + unsigned n; + + /* No SMP at the moment, so just toggle 0/1 */ + n = *cpu_np; + *cpu_np = 1; + if (n != 0) { + return (0); + } #if defined(__sh3__) p += sprintf(p,"cpu family\t: SH-3\n" diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sh/mm/init.c linux.ac/arch/sh/mm/init.c --- linux.vanilla/arch/sh/mm/init.c Fri Sep 21 04:02:03 2001 +++ linux.ac/arch/sh/mm/init.c Wed Oct 10 01:47:35 2001 @@ -207,7 +207,7 @@ void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = totalhigh_pages; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/config.in linux.ac/arch/sparc/config.in --- linux.vanilla/arch/sparc/config.in Tue Jun 12 03:15:27 2001 +++ linux.ac/arch/sparc/config.in Wed Oct 10 01:47:35 2001 @@ -50,6 +50,7 @@ define_bool CONFIG_SUN_IO y define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n +define_bool CONFIG_GENERIC_BUST_SPINLOCK n bool 'Support for SUN4 machines (disables SUN4[CDM] support)' CONFIG_SUN4 if [ "$CONFIG_SUN4" != "y" ]; then diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/kernel/init_task.c linux.ac/arch/sparc/kernel/init_task.c --- linux.vanilla/arch/sparc/kernel/init_task.c Mon Sep 17 23:29:09 2001 +++ linux.ac/arch/sparc/kernel/init_task.c Wed Oct 10 01:47:35 2001 @@ -4,6 +4,7 @@ #include #include +static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/kernel/setup.c linux.ac/arch/sparc/kernel/setup.c --- linux.vanilla/arch/sparc/kernel/setup.c Thu Sep 20 22:11:57 2001 +++ linux.ac/arch/sparc/kernel/setup.c Wed Oct 10 01:47:35 2001 @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.125 2001/09/20 00:35:30 davem Exp $ +/* $Id: setup.c,v 1.124 2001/04/14 21:13:46 davem Exp $ * linux/arch/sparc/kernel/setup.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -441,6 +441,10 @@ breakpoint(); } + /* Due to stack alignment restrictions and assumptions... */ + init_mm.mmap->vm_page_prot = PAGE_SHARED; + init_mm.mmap->vm_start = PAGE_OFFSET; + init_mm.mmap->vm_end = PAGE_OFFSET + highest_paddr; init_mm.context = (unsigned long) NO_CONTEXT; init_task.thread.kregs = &fake_swapper_regs; @@ -455,15 +459,37 @@ return -EIO; } -/* BUFFER is PAGE_SIZE bytes long. */ +/* + * get_cpuinfo - Get information on one CPU for use by procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * BUFFER is PAGE_SIZE - 1K bytes long. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. + */ extern char *sparc_cpu_type[]; extern char *sparc_fpu_type[]; -int get_cpuinfo(char *buffer) +int get_cpuinfo(char *buffer, unsigned *cpu_np) { int cpuid=hard_smp_processor_id(); int len; + unsigned n; + + /* Don't have much per-CPU info, so just toggle 0/1 */ + n = *cpu_np; + *cpu_np = 1; + if (n != 0) { + return (0); + } len = sprintf(buffer, "cpu\t\t: %s\n" "fpu\t\t: %s\n" diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/kernel/sys_sparc.c linux.ac/arch/sparc/kernel/sys_sparc.c --- linux.vanilla/arch/sparc/kernel/sys_sparc.c Sat Apr 14 04:15:55 2001 +++ linux.ac/arch/sparc/kernel/sys_sparc.c Wed Oct 10 01:47:35 2001 @@ -54,17 +54,35 @@ return -ENOMEM; if (ARCH_SUN4C_SUN4 && len > 0x20000000) return -ENOMEM; - if (!addr) - addr = TASK_UNMAPPED_BASE; - if (flags & MAP_SHARED) - addr = COLOUR_ALIGN(addr); - else - addr = PAGE_ALIGN(addr); + if (addr) { + if (flags & MAP_SHARED) + addr = COLOUR_ALIGN(addr); + else + addr = PAGE_ALIGN(addr); + vmm = find_vma(current->mm, addr); + if (ARCH_SUN4C_SUN4 && addr < 0xe0000000 && + 0x20000000 - len < addr) { + addr = PAGE_OFFSET; + vmm = find_vma(current->mm, PAGE_OFFSET); + } + if (TASK_SIZE - PAGE_SIZE - len >= addr && + (!vmm || addr + len <= vmm->vm_start)) + return addr; + if (addr > TASK_UNMAPPED_BASE) + addr = 0; + } + if (!addr) { + if (flags & MAP_SHARED) + addr = COLOUR_ALIGN(TASK_UNMAPPED_BASE); + else + addr = PAGE_ALIGN(TASK_UNMAPPED_BASE); + } for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { /* At this point: (!vmm || addr < vmm->vm_end). */ - if (ARCH_SUN4C_SUN4 && addr < 0xe0000000 && 0x20000000 - len < addr) { + if (ARCH_SUN4C_SUN4 && addr < 0xe0000000 && + 0x20000000 - len < addr) { addr = PAGE_OFFSET; vmm = find_vma(current->mm, PAGE_OFFSET); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/kernel/sys_sunos.c linux.ac/arch/sparc/kernel/sys_sunos.c --- linux.vanilla/arch/sparc/kernel/sys_sunos.c Wed Aug 15 03:57:28 2001 +++ linux.ac/arch/sparc/kernel/sys_sunos.c Wed Oct 10 01:47:35 2001 @@ -605,7 +605,7 @@ struct sunos_nfs_mount_args { struct sockaddr_in *addr; /* file server address */ - struct nfs_fh *fh; /* File handle to be mounted */ + struct nfs3_fh *fh; /* File handle to be mounted */ int flags; /* flags */ int wsize; /* write size in bytes */ int rsize; /* read size in bytes */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/mm/init.c linux.ac/arch/sparc/mm/init.c --- linux.vanilla/arch/sparc/mm/init.c Thu Oct 11 13:52:08 2001 +++ linux.ac/arch/sparc/mm/init.c Thu Oct 11 14:03:30 2001 @@ -511,7 +511,7 @@ void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = totalhigh_pages; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/config.in linux.ac/arch/sparc64/config.in --- linux.vanilla/arch/sparc64/config.in Thu Sep 20 22:11:57 2001 +++ linux.ac/arch/sparc64/config.in Wed Oct 10 01:47:36 2001 @@ -35,6 +35,7 @@ define_bool CONFIG_HAVE_DEC_LOCK y define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y +define_bool CONFIG_GENERIC_BUST_SPINLOCK n define_bool CONFIG_ISA n define_bool CONFIG_ISAPNP n define_bool CONFIG_EISA n @@ -211,7 +212,9 @@ source drivers/fc4/Config.in -source drivers/message/fusion/Config.in +if [ "$CONFIG_PCI" = "y" ]; then + source drivers/message/fusion/Config.in +fi source drivers/ieee1394/Config.in diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/init_task.c linux.ac/arch/sparc64/kernel/init_task.c --- linux.vanilla/arch/sparc64/kernel/init_task.c Thu Sep 20 22:11:57 2001 +++ linux.ac/arch/sparc64/kernel/init_task.c Wed Oct 10 01:47:36 2001 @@ -3,8 +3,8 @@ #include #include -#include +static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/setup.c linux.ac/arch/sparc64/kernel/setup.c --- linux.vanilla/arch/sparc64/kernel/setup.c Thu Oct 11 13:52:08 2001 +++ linux.ac/arch/sparc64/kernel/setup.c Thu Oct 11 17:00:56 2001 @@ -592,10 +592,32 @@ unsigned long up_clock_tick; #endif -int get_cpuinfo(char *buffer) +/* + * get_cpuinfo - Get information on one CPU for use by procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. + */ + +int get_cpuinfo(char *buffer, unsigned *cpu_np) { int cpuid=smp_processor_id(); int len; + unsigned n; + + /* Don't have much per-CPU info, so just toggle 0/1 */ + n = *cpu_np; + *cpu_np = 1; + if (n != 0) { + return (0); + } len = sprintf(buffer, "cpu\t\t: %s\n" diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/sys_sparc.c linux.ac/arch/sparc64/kernel/sys_sparc.c --- linux.vanilla/arch/sparc64/kernel/sys_sparc.c Sat Apr 14 04:15:55 2001 +++ linux.ac/arch/sparc64/kernel/sys_sparc.c Wed Oct 10 01:47:36 2001 @@ -60,15 +60,31 @@ task_size = 0xf0000000UL; if (len > task_size || len > -PAGE_OFFSET) return -ENOMEM; - if (!addr) - addr = TASK_UNMAPPED_BASE; - - if (flags & MAP_SHARED) - addr = COLOUR_ALIGN(addr); - else - addr = PAGE_ALIGN(addr); task_size -= len; + + if (addr) { + if (flags & MAP_SHARED) + addr = COLOUR_ALIGN(addr); + else + addr = PAGE_ALIGN(addr); + vmm = find_vma(current->mm, addr); + if (addr < PAGE_OFFSET && -PAGE_OFFSET - len < addr) { + addr = PAGE_OFFSET; + vmm = find_vma(current->mm, PAGE_OFFSET); + } + if (task_size >= addr && + (!vmm || addr + len <= vmm->vm_start)) + return addr; + if (addr > TASK_UNMAPPED_BASE) + addr = 0; + } + if (!addr) { + if (flags & MAP_SHARED) + addr = COLOUR_ALIGN(TASK_UNMAPPED_BASE); + else + addr = PAGE_ALIGN(TASK_UNMAPPED_BASE); + } for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { /* At this point: (!vmm || addr < vmm->vm_end). */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/sys_sparc32.c linux.ac/arch/sparc64/kernel/sys_sparc32.c --- linux.vanilla/arch/sparc64/kernel/sys_sparc32.c Thu Oct 11 13:52:08 2001 +++ linux.ac/arch/sparc64/kernel/sys_sparc32.c Thu Oct 11 14:28:06 2001 @@ -887,24 +887,24 @@ return sys32_fcntl(fd, cmd, arg); } -struct dqblk32 { - __u32 dqb_bhardlimit; - __u32 dqb_bsoftlimit; - __u32 dqb_curblocks; +struct mem_dqblk32 { __u32 dqb_ihardlimit; __u32 dqb_isoftlimit; __u32 dqb_curinodes; + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u64 dqb_curspace; __kernel_time_t32 dqb_btime; __kernel_time_t32 dqb_itime; }; -extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr); +extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, __kernel_caddr_t addr); asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned long addr) { int cmds = cmd >> SUBCMDSHIFT; int err; - struct dqblk d; + struct mem_dqblk d; mm_segment_t old_fs; char *spec; @@ -914,33 +914,35 @@ case Q_SETQUOTA: case Q_SETUSE: case Q_SETQLIM: - if (copy_from_user (&d, (struct dqblk32 *)addr, - sizeof (struct dqblk32))) + if (copy_from_user (&d, (struct mem_dqblk32 *)addr, + sizeof (struct mem_dqblk32))) return -EFAULT; - d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; - d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; + d.dqb_itime = ((struct mem_dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct mem_dqblk32 *)&d)->dqb_btime; break; default: return sys_quotactl(cmd, special, - id, (caddr_t)addr); + id, (__kernel_caddr_t)addr); } spec = getname (special); err = PTR_ERR(spec); if (IS_ERR(spec)) return err; old_fs = get_fs (); set_fs (KERNEL_DS); - err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d); set_fs (old_fs); putname (spec); + if (err) + return err; if (cmds == Q_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; - ((struct dqblk32 *)&d)->dqb_itime = i; - ((struct dqblk32 *)&d)->dqb_btime = b; - if (copy_to_user ((struct dqblk32 *)addr, &d, - sizeof (struct dqblk32))) + ((struct mem_dqblk32 *)&d)->dqb_itime = i; + ((struct mem_dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct mem_dqblk32 *)addr, &d, + sizeof (struct mem_dqblk32))) return -EFAULT; } - return err; + return 0; } static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/sys_sunos32.c linux.ac/arch/sparc64/kernel/sys_sunos32.c --- linux.vanilla/arch/sparc64/kernel/sys_sunos32.c Wed Aug 15 03:57:28 2001 +++ linux.ac/arch/sparc64/kernel/sys_sunos32.c Wed Oct 10 01:47:36 2001 @@ -570,7 +570,7 @@ struct sunos_nfs_mount_args { struct sockaddr_in *addr; /* file server address */ - struct nfs_fh *fh; /* File handle to be mounted */ + struct nfs3_fh *fh; /* File handle to be mounted */ int flags; /* flags */ int wsize; /* write size in bytes */ int rsize; /* read size in bytes */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/mm/init.c linux.ac/arch/sparc64/mm/init.c --- linux.vanilla/arch/sparc64/mm/init.c Thu Oct 11 13:52:08 2001 +++ linux.ac/arch/sparc64/mm/init.c Thu Oct 11 14:28:12 2001 @@ -1533,7 +1533,7 @@ void si_meminfo(struct sysinfo *val) { val->totalram = num_physpages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/Makefile linux.ac/arch/um/Makefile --- linux.vanilla/arch/um/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/Makefile Wed Oct 10 01:47:36 2001 @@ -0,0 +1,95 @@ +include arch/$(ARCH)/Makefile-$(SUBARCH) + +include/linux/version.h: arch/$(ARCH)/Makefile + +ARCH_DIR = $(TOPDIR)/arch/um + +MAKEBOOT = $(MAKE) -C $(ARCH_DIR)/boot + +ifeq ($(CONFIG_DEBUGSYM),y) +DEBUG = -g +CFLAGS := $(subst -fomit-frame-pointer,,$(CFLAGS)) +endif + +ifeq ($(CONFIG_GCOV),y) +CFLAGS += -fprofile-arcs -ftest-coverage +endif + +ifeq ($(CONFIG_GPROF), y) +PROFILE += -pg -DPROFILING +LINK_PROFILE = $(PROFILE) -Wl,--wrap,__monstartup +endif + +SUBDIRS += $(ARCH_DIR)/fs $(ARCH_DIR)/drivers $(ARCH_DIR)/kernel \ + $(ARCH_DIR)/sys-$(SUBARCH) + +LIBS += $(shell [ -e $(ARCH_DIR)/fs/fs.o ] && echo $(ARCH_DIR)/fs/fs.o) \ + $(ARCH_DIR)/kernel/um.o $(ARCH_DIR)/drivers/um_drivers.o \ + $(ARCH_DIR)/sys-$(SUBARCH)/sys.o + +ifeq ($(CONFIG_PT_PROXY), y) +SUBDIRS += $(ARCH_DIR)/ptproxy +LIBS += $(ARCH_DIR)/ptproxy/ptproxy.a +endif + +NESTING = 0 + +ARCH_INCLUDE = $(ARCH_DIR)/include + +CFLAGS += $(DEBUG) $(PROFILE) $(ARCH_CFLAGS) -D__arch_um__ \ + -DSUBARCH=\"$(SUBARCH)\" -DNESTING=$(NESTING) -D_LARGEFILE64_SOURCE \ + -I$(ARCH_INCLUDE) -Derrno=kernel_errno + +LINKFLAGS += -r + +LINK_WRAPS = -Wl,--wrap,malloc -Wl,--wrap,free -Wl,--wrap,calloc + +$(ARCH_DIR)/link.ld: + m4 -DSTART=$(START_ADDR) -DSUBARCH=$(SUBARCH) \ + -DELF_SUBARCH=$(ELF_SUBARCH) \ + $(ARCH_DIR)/link.ld.in > $(ARCH_DIR)/link.ld + +SYMLINK_HEADERS = include/asm-um/archparam.h include/asm-um/system.h \ + include/asm-um/sigcontext.h include/asm-um/processor.h + +ARCH_SYMLINKS = include/asm-um/arch arch/um/include/sysdep $(SYMLINK_HEADERS) + +linux: $(ARCH_SYMLINKS) $(ARCH_DIR)/main.o vmlinux $(ARCH_DIR)/link.ld + mv vmlinux vmlinux.o + $(CC) -Wl,-T,$(ARCH_DIR)/link.ld $(LINK_PROFILE) $(LINK_WRAPS) \ + -o linux -static $(ARCH_DIR)/main.o vmlinux.o -L/usr/lib + rm -f $(ARCH_DIR)/link.ld + +USER_CFLAGS := $(patsubst -I%,,$(CFLAGS)) +USER_CFLAGS := $(patsubst -Derrno=kernel_errno,,$(USER_CFLAGS)) +USER_CFLAGS := $(patsubst -D__KERNEL__,,$(USER_CFLAGS)) -I$(ARCH_INCLUDE) + +$(ARCH_DIR)/main.o: $(ARCH_DIR)/main.c + $(CC) -D__KERNEL__ $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +archmrproper: + $(MAKE) -C $(ARCH_DIR)/sys-$(SUBARCH) archmrproper + rm -f $(SYMLINK_HEADERS) $(ARCH_SYMLINKS) include/asm \ + $(addprefix $(ARCH_DIR)/kernel/,$(KERN_SYMLINKS)) + +archclean: + find . \( -name '*.bb' -o -name '*.bbg' -o -name '*.da' \ + -o -name '*.gcov' \) -type f -print | xargs rm -f + rm -f linux x.i gmon.out link.ld + @$(MAKEBOOT) clean + +archdep: $(ARCH_SYMLINKS) + @$(MAKEBOOT) dep + +$(SYMLINK_HEADERS): + echo $@ + cd $(TOPDIR)/$(dir $@) ; \ + ln -sf $(basename $(notdir $@))-$(SUBARCH)$(suffix $@) $(notdir $@) + +include/asm-um/arch: + cd $(TOPDIR)/include/asm-um && ln -sf ../asm-$(SUBARCH) arch + +arch/um/include/sysdep: + cd $(TOPDIR)/arch/um/include && ln -sf sysdep-$(SUBARCH) sysdep + +export SUBARCH USER_CFLAGS diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/Makefile-i386 linux.ac/arch/um/Makefile-i386 --- linux.vanilla/arch/um/Makefile-i386 Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/Makefile-i386 Wed Oct 10 01:47:36 2001 @@ -0,0 +1,8 @@ +ifeq ($(CONFIG_HOST_2G_2G), y) +START_ADDR = 0x60000000 +else +START_ADDR = 0xa0000000 +endif + +ARCH_CFLAGS = -U__$(SUBARCH)__ -U$(SUBARCH) -DUM_FASTCALL +ELF_SUBARCH = $(SUBARCH) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/Makefile-ia64 linux.ac/arch/um/Makefile-ia64 --- linux.vanilla/arch/um/Makefile-ia64 Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/Makefile-ia64 Wed Oct 10 01:47:36 2001 @@ -0,0 +1 @@ +START_ADDR = 0x1000000000000000 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/Makefile-ppc linux.ac/arch/um/Makefile-ppc --- linux.vanilla/arch/um/Makefile-ppc Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/Makefile-ppc Wed Oct 10 01:47:36 2001 @@ -0,0 +1,5 @@ +START_ADDR = 0x20000000 +ARCH_CFLAGS = -U__powerpc__ -D__UM_PPC__ + +# The arch is ppc, but the elf32 name is powerpc +ELF_SUBARCH = powerpc diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/boot/Makefile linux.ac/arch/um/boot/Makefile --- linux.vanilla/arch/um/boot/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/boot/Makefile Wed Oct 10 01:47:36 2001 @@ -0,0 +1,3 @@ +dep: + +clean: diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/config.in linux.ac/arch/um/config.in --- linux.vanilla/arch/um/config.in Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/config.in Wed Oct 10 01:47:36 2001 @@ -0,0 +1,104 @@ +define_bool CONFIG_USERMODE y + +mainmenu_name "Linux/Usermode Kernel Configuration" + +define_bool CONFIG_ISA y +define_bool CONFIG_SBUS n + +define_bool CONFIG_UID16 y + +define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y +define_bool CONFIG_GENERIC_BUST_SPINLOCK n + +mainmenu_option next_comment +comment 'Code maturity level options' +bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL +endmenu + +mainmenu_option next_comment +comment 'Processor features' +comment 'General Setup' +define_bool CONFIG_STDIO_CONSOLE y +bool 'Networking support' CONFIG_NET +bool 'System V IPC' CONFIG_SYSVIPC +bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT +bool 'Sysctl support' CONFIG_SYSCTL +tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT +tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC +bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS +if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then + int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 +fi +bool 'Virtual serial line' CONFIG_SSL +tristate 'Host filesystem' CONFIG_HOSTFS +bool 'Management console' CONFIG_MCONSOLE +dep_bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ $CONFIG_MCONSOLE +bool '2G/2G host address space split' CONFIG_HOST_2G_2G +endmenu + +mainmenu_option next_comment +comment 'Loadable module support' +bool 'Enable loadable module support' CONFIG_MODULES +if [ "$CONFIG_MODULES" = "y" ]; then +# MODVERSIONS does not yet work in this architecture +# bool ' Set version information on all module symbols' CONFIG_MODVERSIONS + bool ' Kernel module loader' CONFIG_KMOD +fi +endmenu + +# SMP doesn't work in UML yet +#mainmenu_option next_comment +#comment 'Processor features' +# bool 'Symmetric multi-processing support' CONFIG_SMP +#endmenu + +mainmenu_option next_comment +comment 'Devices' +define_bool CONFIG_BLK_DEV_UBD y +bool 'Always do synchronous disk IO for UBD' CONFIG_BLK_DEV_UBD_SYNC +tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP +dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET +tristate 'RAM disk support' CONFIG_BLK_DEV_RAM +if [ "$CONFIG_BLK_DEV_RAM" = "y" -o "$CONFIG_BLK_DEV_RAM" = "m" ]; then + int ' Default RAM disk size' CONFIG_BLK_DEV_RAM_SIZE 4096 +fi +dep_bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM +tristate 'Example IO memory driver' CONFIG_MMAPPER +endmenu + +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in +fi + +mainmenu_option next_comment +comment 'Network drivers' +if [ "$CONFIG_NET" = "y" ]; then + source drivers/net/Config.in +fi +endmenu + +if [ "$CONFIG_NET" = "y" ]; then + mainmenu_option next_comment + comment 'Network device support' + + bool ' Virtual network device support' CONFIG_UML_NET + if [ "$CONFIG_UML_NET" != "n" ]; then + bool ' Ethertap transport' CONFIG_UML_NET_ETHERTAP + bool ' TUN/TAP transport' CONFIG_UML_NET_TUNTAP + bool ' SLIP transport' CONFIG_UML_NET_SLIP + bool ' Daemon transport' CONFIG_UML_NET_DAEMON + bool ' Multicast transport' CONFIG_UML_NET_MCAST + fi + endmenu +fi + +source fs/Config.in + +mainmenu_option next_comment +comment 'Kernel hacking' +bool 'Enable kernel debugging symbols' CONFIG_DEBUGSYM +dep_bool 'Enable ptrace proxy' CONFIG_PT_PROXY $CONFIG_DEBUGSYM +dep_bool 'Enable gprof support' CONFIG_GPROF $CONFIG_DEBUGSYM +dep_bool 'Enable gcov support' CONFIG_GCOV $CONFIG_DEBUGSYM +endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/config.release linux.ac/arch/um/config.release --- linux.vanilla/arch/um/config.release Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/config.release Wed Oct 10 01:47:36 2001 @@ -0,0 +1,192 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_USERMODE=y +CONFIG_ISA=y +# CONFIG_SBUS is not set +CONFIG_UID16=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Processor features +# +# CONFIG_SMP is not set + +# +# General Setup +# +CONFIG_STDIO_CONSOLE=y +CONFIG_MODULES=y +CONFIG_NET=y +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_SYSCTL=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +CONFIG_SSL=y +CONFIG_HOSTFS=y + +# +# Block devices +# +CONFIG_BLK_DEV_UBD=y +# CONFIG_BLK_DEV_UBD_SYNC is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# 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 +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y +CONFIG_NET_UM_ETH=y +CONFIG_NET_UMN=y + +# +# File systems +# +CONFIG_QUOTA=y +CONFIG_AUTOFS_FS=m +CONFIG_AUTOFS4_FS=m +CONFIG_REISERFS_FS=y +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_UMSDOS_FS=y +CONFIG_VFAT_FS=y +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_RAMFS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +CONFIG_MINIX_FS=m +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +CONFIG_NLS=y + +# +# Native Language Support +# +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_UTF8 is not set + +# +# Kernel hacking +# +# CONFIG_DEBUGSYM is not set diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/defconfig linux.ac/arch/um/defconfig --- linux.vanilla/arch/um/defconfig Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/defconfig Wed Oct 10 01:47:36 2001 @@ -0,0 +1,293 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_USERMODE=y +# CONFIG_ISA is not set +# CONFIG_SBUS is not set +# CONFIG_PCI is not set +CONFIG_UID16=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# General Setup +# +CONFIG_STDIO_CONSOLE=y +CONFIG_NET=y +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_SYSCTL=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +CONFIG_SSL=y +CONFIG_HOSTFS=m +CONFIG_MCONSOLE=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_HOST_2G_2G is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_KMOD is not set + +# +# Devices +# +CONFIG_BLK_DEV_UBD=y +# CONFIG_BLK_DEV_UBD_SYNC is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y +# CONFIG_MMAPPER is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# 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 +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network drivers +# + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +CONFIG_ETHERTAP=y + +# +# Ethernet (10 or 100Mbit) +# +# CONFIG_NET_ETHERNET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_ACENIC_OMIT_TIGON_I is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPP_DEFLATE is not set +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPPOE is not set +CONFIG_SLIP=y +# CONFIG_SLIP_COMPRESSED is not set +# CONFIG_SLIP_SMART is not set +# CONFIG_SLIP_MODE_SLIP6 is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y +CONFIG_UML_NET=y +CONFIG_UML_NET_ETHERTAP=y +CONFIG_UML_NET_TUNTAP=y +CONFIG_UML_NET_SLIP=y +CONFIG_UML_NET_DAEMON=y +CONFIG_UML_NET_MCAST=y +CONFIG_ETHERTAP=y +CONFIG_TUN=y + +# +# File systems +# +CONFIG_QUOTA=y +CONFIG_AUTOFS_FS=m +CONFIG_AUTOFS4_FS=m +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_UMSDOS_FS=m +CONFIG_VFAT_FS=m +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_TMPFS is not set +# CONFIG_RAMFS is not set +CONFIG_ISO9660_FS=m +# CONFIG_JOLIET is not set +CONFIG_MINIX_FS=m +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFS_V3 is not set +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +CONFIG_NLS=y + +# +# Native Language Support +# +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Kernel hacking +# +CONFIG_DEBUGSYM=y +CONFIG_PT_PROXY=y +# CONFIG_GPROF is not set +# CONFIG_GCOV is not set diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/Makefile linux.ac/arch/um/drivers/Makefile --- linux.vanilla/arch/um/drivers/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/Makefile Wed Oct 10 01:47:36 2001 @@ -0,0 +1,50 @@ +# +# Copyright (C) 2000 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +O_TARGET := um_drivers.o + +obj-y = +obj-m = + +CFLAGS_stdio_console.o := $(CFLAGS) +CFLAGS_stdio_console_user.o := $(USER_CFLAGS) +CFLAGS_ssl.o := $(CFLAGS) +CFLAGS_chan_kern.o := $(CFLAGS) +CFLAGS_chan_user.o := $(USER_CFLAGS) +CFLAGS_ubd.o := $(CFLAGS) +CFLAGS_ubd_user.o := $(USER_CFLAGS) +CFLAGS_slip_kern.o := $(CFLAGS) +CFLAGS_slip_user.o := $(USER_CFLAGS) +CFLAGS_ethertap_kern.o := $(CFLAGS) +CFLAGS_ethertap_user.o := $(USER_CFLAGS) +CFLAGS_tuntap_kern.o := $(CFLAGS) +CFLAGS_tuntap_user.o := $(USER_CFLAGS) +CFLAGS_daemon_kern.o := $(CFLAGS) +CFLAGS_daemon_user.o := $(USER_CFLAGS) +CFLAGS_net_kern.o := $(CFLAGS) +CFLAGS_net_user.o := $(USER_CFLAGS) +CFLAGS_mcast_user.o := $(USER_CFLAGS) +CFLAGS_mcast_kern.o := $(CFLAGS) +CFLAGS_mconsole_kern.o := $(CFLAGS) +CFLAGS_mconsole_user.o := $(USER_CFLAGS) +CFLAGS_mmapper_kern.o := $(CFLAGS) + +obj-$(CONFIG_STDIO_CONSOLE) += stdio_console.o stdio_console_user.o +obj-$(CONFIG_SSL) += ssl.o +obj-$(CONFIG_SSL) += chan_user.o chan_kern.o +obj-$(CONFIG_STDIO_CONSOLE) += chan_user.o chan_kern.o +obj-$(CONFIG_BLK_DEV_UBD) += ubd.o ubd_user.o +obj-$(CONFIG_UML_NET) += net_kern.o net_user.o +obj-$(CONFIG_UML_NET_SLIP) += slip_user.o slip_kern.o +obj-$(CONFIG_UML_NET_ETHERTAP) += ethertap_user.o ethertap_kern.o +obj-$(CONFIG_UML_NET_TUNTAP) += tuntap_user.o tuntap_kern.o +obj-$(CONFIG_UML_NET_DAEMON) += daemon_kern.o daemon_user.o +obj-$(CONFIG_UML_NET_MCAST) += mcast_user.o mcast_kern.o +obj-$(CONFIG_MCONSOLE) += mconsole_kern.o mconsole_user.o +obj-$(CONFIG_MMAPPER) += mmapper_kern.o + +override CFLAGS = + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/chan_kern.c linux.ac/arch/um/drivers/chan_kern.c --- linux.vanilla/arch/um/drivers/chan_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/chan_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include "chan.h" +#include "user_util.h" +#include "kern.h" + +void tty_interrupt(struct io_chan *chan, void *tty_ptr, int count) +{ + struct tty_struct *tty = tty_ptr; + int n; + + if(count == 0) return; + n = tty_read(&chan->in, tty); + if(tty) tty_flip_buffer_push(tty); + if(n > 0){ + chan->in.hung_up = 0; + reactivate_fd(chan->in.fd); + } +} + +void tty_eof(void *tty_ptr, int *hung_up) +{ + struct tty_struct *tty = tty_ptr; + + if(tty == NULL) return; + if(!*hung_up) tty_hangup(tty); + *hung_up = 1; +} + +void tty_receive_char(void *tty_ptr, char ch) +{ + struct tty_struct *tty = tty_ptr; + + if(tty == NULL) return; + + if (I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) { + if (ch == STOP_CHAR(tty)) + stop_tty(tty); + else if (ch == START_CHAR(tty)) + start_tty(tty); + } + + if((tty->flip.flag_buf_ptr == NULL) || + (tty->flip.char_buf_ptr == NULL)) + return; + tty_insert_flip_char(tty, ch, TTY_NORMAL); +} + +static void accept_interrupt(int irq, void *dev, struct pt_regs *unused) +{ + accept_connection(dev); +} + +static int setup_accept_irq(int fd, void *data) +{ + return(um_request_irq(ACCEPT_IRQ, fd, accept_interrupt, + SA_INTERRUPT | SA_SHIRQ, "socket", data)); +} + +static int open_chan(struct chan *chan, int (*irq_setup)(int fd, void *data), + int input, void *data) +{ + struct io_chan subchan; + int err; + + if(chan->opened) return(0); + switch(chan->type){ + case XTERM: + err = open_xterm(chan); + break; + case PTY: + err = open_pty(chan); + break; + case TTY: + err = open_tty(chan); + break; + case PTS: + err = open_pts(chan); + break; + case FD: + err = open_fd(chan); + break; + case SOCKET: + subchan = ((struct io_chan) + PTS_IO_CHAN_INIT(NULL, -1, 1, INIT_STATIC)); + err = open_chan_pair(&subchan, irq_setup, data); + if(err) return(err); + chan->data.sock.pty = subchan.in.fd; + err = open_socket(chan); + irq_setup = setup_accept_irq; + data = chan; + break; + default: + printk("open_chan : Unknown channel type - %d\n", chan->type); + err = -ENODEV; + break; + } + if(err) return(err); + if(input && irq_setup) err = (*irq_setup)(chan->fd, data); + if(err) return(err); + chan->opened = 1; + return(0); +} + +int open_chan_pair(struct io_chan *chan, int (*irq_setup)(int fd, void *data), + void *data) +{ + int err; + + err = open_chan(&chan->in, irq_setup, 1, data); + if(err) return(err); + if(chan->out.type != COPY) + err = open_chan(&chan->out, irq_setup, 0, data); + return(err); +} + +static void close_chan(struct chan *chan) +{ + switch(chan->type){ + case XTERM: + if(chan->data.xterm.pid != -1) + kill(chan->data.xterm.pid, SIGKILL); + break; + default: + break; + } +} + +void close_chan_pair(struct io_chan *chan) +{ + close_chan(&chan->in); + if(chan->out.type != COPY) + close_chan(&chan->out); +} + +int write_chan(struct io_chan *chan, const char *buf, int len) +{ + struct chan *c; + int n; + + if(chan->out.type == COPY) c = &chan->in; + else c = &chan->out; + if(c->fd == -1) return(-ENODEV); + n = write(c->fd, buf, len); + if(errno == EAGAIN) n = 0; + if(n < 0) return(-errno); + return(n); +} + +static int parse_chan(char *str, int device, struct chan *chan, int pri, + struct chan_opts *opts) +{ + if(pri <= chan->init_pri) return(0); + if(!strncmp(str, "pty", strlen("pty"))){ + *chan = ((struct chan) PTY_CHAN_INIT(opts->announce, + opts->dev, opts->raw_pty, + pri)); + } + else if(!strncmp(str, "tty", strlen("tty"))){ + char *tty_name; + + tty_name = &str[strlen("tty")]; + if(*tty_name != ':'){ + printk("parse_chan : channel type 'tty' must " + "specify a device\n"); + return(-1); + } + tty_name++; + *chan = ((struct chan) TTY_CHAN_INIT(tty_name, opts->raw_pty, + pri)); + } + else if(!strncmp(str, "pts", strlen("pts"))){ + *chan = ((struct chan) PTS_CHAN_INIT(opts->announce, + opts->dev, opts->raw_pty, + pri)); + } + else if(!strncmp(str, "xterm", strlen("xterm"))){ + *chan = ((struct chan) + XTERM_CHAN_INIT(device, opts->xterm_title, + opts->raw_pty, pri)); + } + else if(!strncmp(str, "socket", strlen("socket"))){ + char *sock; + int n; + + sock = &str[strlen("socket")]; + if(*sock != ':'){ + printk("parse_chan : channel type 'socket' must " + "specify a socket\n"); + return(-1); + } + sock++; + n = simple_strtoul(sock, NULL, 0); + if(n == 0){ + printk("parse_chan : invalid socket number - '%s'\n", + sock); + return(-1); + } + *chan = ((struct chan) SOCK_CHAN_INIT(n, pri)); + } + else if(!strncmp(str, "fd", strlen("fd"))){ + char *fd, *end; + int n; + + fd = &str[strlen("fd")]; + if(*fd != ':'){ + printk("parse_chan : channel type 'fd' must " + "specify a file descriptor\n"); + return(-1); + } + fd++; + n = simple_strtoul(fd, &end, 0); + if(*end != '\0'){ + printk("parse_chan : couldn't parse file descriptor " + "'%s'", fd); + return(-1); + } + *chan = ((struct chan) FD_CHAN_INIT(n, pri)); + return(0); + } + else { + printk("parse_chan couldn't parse \"%s\"\n", str); + return(-1); + } + chan->init_pri = pri; + return(0); +} + +int parse_chan_pair(char *str, int device, struct io_chan *chan, int pri, + struct chan_opts *opts) +{ + char *in, *out; + int err; + + if((out = strchr(str, ',')) != NULL){ + in = str; + *out = '\0'; + out++; + err = parse_chan(in, device, &chan->in, pri, opts); + if(err) return(err); + err = parse_chan(out, device, &chan->out, pri, opts); + return(err); + } + else { + err = parse_chan(str, device, &chan->in, pri, opts); + if(err) return(err); + chan->out.type = COPY; + return(0); + } +} + + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/chan_user.c linux.ac/arch/um/drivers/chan_user.c --- linux.vanilla/arch/um/drivers/chan_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/chan_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ssl.h" +#include "chan.h" +#include "user.h" +#include "user_util.h" +#include "kern_util.h" + +int tty_read(struct chan *chan, void *tty) +{ + int n; + char ch; + + while((n = read(chan->fd, &ch, sizeof(ch))) == sizeof(ch)) + tty_receive_char(tty, ch); + if(n < 0){ + if(errno == EIO){ + tty_eof(tty, &chan->hung_up); + return(0); + } + else if(errno != EAGAIN){ + printk("tty_read read failed, errno = %d\n", errno); + return(-1); + } + } + else if(n == 0) return(0); + return(1); +} + +int open_fd(struct chan *chan) +{ + raw(chan->fd, 0); + return(0); +} + +struct xterm_info { + char tty[2]; + int fd; + int console_num; + int *pid_out; + char *title; +}; + +static void xterm_tramp(void *arg) +{ + struct xterm_info *info; + int pid; + char title[256], flag[sizeof("Sxxnn\0")]; + + info = arg; + sprintf(flag, "-S%c%c%d", info->tty[0], info->tty[1], info->fd); + sprintf(title, info->title, info->console_num); + if((pid = fork()) != 0) *info->pid_out = pid; + else { + execlp("xterm", "xterm", flag, "-T", title, NULL); + printk("execlp of xterm failed - errno = %d\n", errno); + close(info->fd); + exit(1); + } +} + +static int getmaster(char *line, int raw) +{ + struct termios tt; + struct stat stb; + char *pty, *bank, *cp; + int master; + + pty = &line[strlen("/dev/ptyp")]; + for (bank = "pqrs"; *bank; bank++) { + line[strlen("/dev/pty")] = *bank; + *pty = '0'; + if (stat(line, &stb) < 0) + break; + for (cp = "0123456789abcdef"; *cp; cp++) { + *pty = *cp; + master = open(line, O_RDWR); + if (master >= 0) { + char *tp = &line[strlen("/dev/")]; + int ok; + + /* verify slave side is usable */ + *tp = 't'; + ok = access(line, R_OK|W_OK) == 0; + *tp = 'p'; + if (ok) { + if(raw){ + tcgetattr(master, &tt); + cfmakeraw(&tt); + tcsetattr(master, TCSADRAIN, + &tt); + } + return(master); + } + (void) close(master); + } + } + } + return(-1); +} + +int open_xterm(struct chan *chan) +{ + struct xterm_info info; + int master, slave; + char dev[] = "/dev/ptyXX", c; + + master = getmaster(dev, chan->data.xterm.raw); + if(master == -1){ + printk("No unused host ptys found\n"); + return(-ENODEV); + } + dev[strlen("/dev/")] = 't'; + slave = open(dev, O_RDWR); + if(slave == -1) return(-errno); + info.tty[0] = dev[strlen("/dev/pty")]; + info.tty[1] = dev[strlen("/dev/ptyX")]; + info.fd = master; + info.console_num = chan->data.xterm.line; + info.pid_out = &chan->data.xterm.pid; + info.title = chan->data.xterm.title; + tracing_cb(xterm_tramp, &info); + close(master); + while((read(slave, &c, sizeof(c)) == sizeof(c)) && (c != '\n')) ; + if(chan->data.xterm.raw) raw(slave, 1); + chan->fd = slave; + return(0); +} + +int open_pty(struct chan *chan) +{ + char dev[sizeof("/dev/ptyxx\0")] = "/dev/ptyxx"; + + chan->fd = getmaster(dev, chan->data.pty.raw); + if(chan->fd < 0) return(-errno); + if(chan->data.pty.announce) + (*chan->data.pty.announce)(dev, chan->data.pty.dev); + return(0); +} + +int open_tty(struct chan *chan) +{ + int fd; + + fd = open(chan->data.tty.dev, O_RDWR); + if(fd < 0) return(-errno); + chan->fd = fd; + if(chan->data.tty.raw) raw(fd, 1); + return(0); +} + +int open_pts(struct chan *chan) +{ + int fd; + + if((fd = get_pty()) < 0){ + printk("open_pts : Failed to open pts\n"); + return(-errno); + } + chan->fd = fd; + if(chan->data.tty.raw) raw(fd, 1); + if(chan->data.pts.announce) + (*chan->data.pts.announce)(ptsname(fd), chan->data.pts.dev); + return(0); +} + +int open_socket(struct chan *chan) +{ + struct sockaddr_in addr; + int sock; + + sock = socket(PF_INET, SOCK_STREAM, 0); + if(sock < 0) return(-errno); + addr.sin_family = AF_INET; + addr.sin_port = htons(chan->data.sock.port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + if(bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) + return(-errno); + if(listen(sock, 1) < 0) return(-errno); + chan->data.sock.connected = 0; + chan->fd = sock; + return(0); +} + +struct telnetd_args { + int fd; + char helper[sizeof("/tmp/uml_dev/nnnn\0")]; +}; + +static void telnetd_tramp(void *arg) +{ + struct telnetd_args info = *((struct telnetd_args *) arg); + + if(fork() == 0){ + dup2(info.fd, 0); + dup2(info.fd, 1); + dup2(info.fd, 2); + close(info.fd); + execlp("in.telnetd", "in.telnetd", "-L", info.helper, NULL); + printk("execlp of telnetd failed - errno = %d\n", errno); + exit(1); + } +} + +void accept_connection(struct chan *chan) +{ + struct telnetd_args info; + char *name; + + if((info.fd = accept(chan->fd, NULL, 0)) < 0){ + printk("tty_read: accept failed - errno = %d\n", + errno); + return; + } + mkdir("/tmp/uml_dev", 0777); + name = ptsname(chan->data.sock.pty); + name = rindex(name, '/'); + sprintf(info.helper, "/tmp/uml_dev%s", name); + if(symlink("/home/jdike/bin/telnet_helper", info.helper)){ + printk("symlink from '%s' failed, errno = %d\n", info.helper, + errno); + return; + } + tracing_cb(telnetd_tramp, &info); + chan->data.sock.connected = 1; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/daemon.h linux.ac/arch/um/drivers/daemon.h --- linux.vanilla/arch/um/drivers/daemon.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/daemon.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "net_user.h" + +struct daemon_data { + char *sock_type; + char *ctl_sock; + char *data_sock; + void *ctl_addr; + void *data_addr; + void *local_addr; + unsigned char hwaddr[ETH_ADDR_LEN]; + int hw_setup; + int control; + void *dev; +}; + +extern struct net_user_info daemon_user_info; + +extern int daemon_user_set_mac(struct daemon_data *pri, unsigned char *hwaddr, + int len); +extern int daemon_user_read(int fd, void *buf, int len, + struct daemon_data *data); +extern int daemon_user_write(int fd, void *buf, int len, + struct daemon_data *pri); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/daemon_kern.c linux.ac/arch/um/drivers/daemon_kern.c --- linux.vanilla/arch/um/drivers/daemon_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/daemon_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include "linux/init.h" +#include "linux/netdevice.h" +#include "linux/etherdevice.h" +#include "net_kern.h" +#include "net_user.h" +#include "daemon.h" +#include "daemon_kern.h" + +struct daemon_data daemon_priv[MAX_UML_NETDEV] = { + [ 0 ... MAX_UML_NETDEV - 1 ] = + { + sock_type: "unix", + ctl_sock: "/tmp/uml.ctl", + data_sock: "/tmp/uml.data", + hwaddr: { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, + hw_setup: 0, + control: -1, + } +}; + +struct net_device *daemon_init(int private_size, int index) +{ + struct net_device *dev; + struct uml_net_private *pri; + struct daemon_data *dpri; + + dev = init_etherdev(NULL, private_size); + if(dev == NULL) return(NULL); + pri = dev->priv; + dpri = (struct daemon_data *) pri->user; + *dpri = daemon_priv[index]; + memcpy(dev->dev_addr, dpri->hwaddr, ETH_ALEN); + printk("daemon backend"); + if(dpri->hw_setup) + printk("- ethernet address = %x:%x:%x:%x:%x:%x\n", + dpri->hwaddr[0], dpri->hwaddr[1], dpri->hwaddr[2], + dpri->hwaddr[3], dpri->hwaddr[4], dpri->hwaddr[5]); + printk("\n"); + return(dev); +} + +static unsigned short daemon_protocol(struct sk_buff *skb) +{ + return(eth_type_trans(skb, skb->dev)); +} + +static int daemon_set_mac(struct sockaddr *addr, void *data) +{ + struct daemon_data *pri = data; + struct net_device *dev = pri->dev; + struct sockaddr *hwaddr = addr; + + memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN); + return(daemon_user_set_mac(pri, hwaddr->sa_data, ETH_ALEN)); +} + +static int daemon_read(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); + if(*skb == NULL) return(-ENOMEM); + return(daemon_user_read(fd, (*skb)->mac.raw, + (*skb)->dev->mtu + ETH_HEADER_OTHER, + (struct daemon_data *) &lp->user)); +} + +static int daemon_write(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return(daemon_user_write(fd, (*skb)->data, (*skb)->len, + (struct daemon_data *) &lp->user)); +} + +static struct net_kern_info daemon_kern_info = { + init: daemon_init, + protocol: daemon_protocol, + set_mac: daemon_set_mac, + read: daemon_read, + write: daemon_write, +}; + +static int daemon_count = 0; + +void daemon_setup(char *str, struct uml_net *dev) +{ + int err, n = daemon_count; + + dev->user = &daemon_user_info; + dev->kern = &daemon_kern_info; + dev->private_size = sizeof(struct daemon_data); + dev->transport_index = daemon_count++; + if(*str != ',') return; + str++; + if(*str != ','){ + err = setup_etheraddr(str, daemon_priv[n].hwaddr); + if(!err) daemon_priv[n].hw_setup = 1; + } + str = strchr(str, ','); + if(str == NULL) return; + *str++ = '\0'; + if(*str != ',') daemon_priv[n].sock_type = str; + str = strchr(str, ','); + if(str == NULL) return; + *str++ = '\0'; + if(*str != ',') daemon_priv[n].ctl_sock = str; + str = strchr(str, ','); + if(str == NULL) return; + *str++ = '\0'; + if(*str != '\0') daemon_priv[n].data_sock = str; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/daemon_kern.h linux.ac/arch/um/drivers/daemon_kern.h --- linux.vanilla/arch/um/drivers/daemon_kern.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/daemon_kern.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,8 @@ +#ifndef __UM_DAEMON_KERN_H +#define __UM_DAEMON_KERN_H + +#include "net_kern.h" + +extern void daemon_setup(char *arg, struct uml_net *dev); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/daemon_user.c linux.ac/arch/um/drivers/daemon_user.c --- linux.vanilla/arch/um/drivers/daemon_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/daemon_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include +#include +#include +#include +#include +#include "net_user.h" +#include "daemon.h" +#include "kern_util.h" +#include "user_util.h" +#include "user.h" + +#define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER) + +enum request_type { REQ_NEW_CONTROL }; + +struct request { + enum request_type type; + union { + struct { + unsigned char addr[ETH_ADDR_LEN]; + struct sockaddr_un name; + } new_control; + struct { + unsigned long cookie; + } new_data; + } u; +}; + +static struct sockaddr_un *new_addr(void *name, int len) +{ + struct sockaddr_un *sun; + + sun = um_kmalloc(sizeof(struct sockaddr_un)); + if(sun == NULL){ + printk("new_addr: allocation of sockaddr_un failed\n"); + return(NULL); + } + sun->sun_family = AF_UNIX; + memcpy(sun->sun_path, name, len); + return(sun); +} + +static void daemon_user_init(void *data, void *dev) +{ + struct daemon_data *pri = data; + struct timeval tv; + struct { + char zero; + int pid; + int usecs; + } name; + + if(!strcmp(pri->sock_type, "unix")){ + pri->ctl_addr = new_addr(pri->ctl_sock, + strlen(pri->ctl_sock) + 1); + pri->data_addr = new_addr(pri->data_sock, + strlen(pri->data_sock) + 1); + } + name.zero = 0; + name.pid = getpid(); + gettimeofday(&tv, NULL); + name.usecs = tv.tv_usec; + pri->local_addr = new_addr(&name, sizeof(name)); + pri->dev = dev; +} + +static int daemon_open(void *data) +{ + struct daemon_data *pri = data; + struct sockaddr_un *ctl_addr = pri->ctl_addr; + struct sockaddr_un *local_addr = pri->local_addr; + struct request req; + char addr[sizeof("255.255.255.255\0")]; + int fd, n, err; + + if(!pri->hw_setup){ + pri->hwaddr[0] = 0xfe; + pri->hwaddr[1] = 0xfd; + pri->hwaddr[2] = 0x0; + pri->hwaddr[3] = 0x0; + pri->hwaddr[4] = 0x0; + pri->hwaddr[5] = 0x0; + dev_ip_addr(pri->dev, addr, &pri->hwaddr[2]); + set_ether_mac(pri->dev, pri->hwaddr); + } + if((ctl_addr == NULL) || (pri->data_addr == NULL) || + (pri->local_addr == NULL)) + return(-EINVAL); + + if((pri->control = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){ + printk("daemon_open : control socket failed, errno = %d\n", + errno); + return(-ENOMEM); + } + + if(connect(pri->control, (struct sockaddr *) ctl_addr, + sizeof(*ctl_addr)) < 0){ + printk("daemon_open : control connect failed, errno = %d\n", + errno); + err = -ENOTCONN; + goto out; + } + + req.type = REQ_NEW_CONTROL; + memcpy(req.u.new_control.addr, pri->hwaddr, + sizeof(req.u.new_control.addr)); + req.u.new_control.name = *local_addr; + n = write(pri->control, &req, sizeof(req)); + if(n != sizeof(req)){ + printk("daemon_open : control setup request returned %d, " + "errno = %d\n", n, errno); + err = -ENOTCONN; + goto out; + } + + if((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0){ + printk("daemon_open : data socket failed, errno = %d\n", + errno); + err = -ENOMEM; + goto out; + } + if(bind(fd, (struct sockaddr *) local_addr, sizeof(*local_addr)) < 0){ + printk("daemon_open : data bind failed, errno = %d\n", + errno); + close(fd); + err = -EINVAL; + goto out; + } + + return(fd); + out: + close(pri->control); + return(err); +} + +static void daemon_close(int fd, void *data) +{ + struct daemon_data *pri = data; + + close(fd); + close(pri->control); +} + +int daemon_user_read(int fd, void *buf, int len, struct daemon_data *data) +{ + int n; + + while(((n = recvfrom(fd, buf, len, 0, NULL, NULL)) < 0) && + (errno == EINTR)) ; + if(n < 0){ + if(errno == EAGAIN) return(0); + return(-errno); + } + else if(n == 0) return(-ENOTCONN); + return(n); +} + +int daemon_user_write(int fd, void *buf, int len, struct daemon_data *pri) +{ + int n; + struct sockaddr_un *data_addr = pri->data_addr; + + n = sendto(fd, buf, len, 0, (struct sockaddr *) data_addr, + sizeof(*data_addr)); + if(n < 0){ + if(errno == EAGAIN) return(0); + return(-errno); + } + return(n); +} + +static int daemon_set_mtu(int mtu, void *data) +{ + return(mtu); +} + +int daemon_user_set_mac(struct daemon_data *pri, unsigned char *hwaddr, + int len) +{ + memcpy(pri->hwaddr, hwaddr, len); + return(0); +} + +struct net_user_info daemon_user_info = { + init: daemon_user_init, + open: daemon_open, + close: daemon_close, + set_mtu: daemon_set_mtu, + add_address: NULL, + delete_address: NULL, + max_packet: MAX_PACKET - ETH_HEADER_OTHER +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/etap.h linux.ac/arch/um/drivers/etap.h --- linux.vanilla/arch/um/drivers/etap.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/etap.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "net_user.h" + +struct ethertap_data { + char *dev_name; + char *gate_addr; + int data_fd; + int control_fd; + void *dev; + unsigned char hw_addr[ETH_ADDR_LEN]; + int hw_setup; +}; + +extern struct net_user_info ethertap_user_info; +extern int etap_user_write(int fd, void *buf, int len, + struct ethertap_data *pri); +extern int etap_user_read(int fd, void *buf, int len, + struct ethertap_data *pri); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/etap_kern.h linux.ac/arch/um/drivers/etap_kern.h --- linux.vanilla/arch/um/drivers/etap_kern.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/etap_kern.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UM_ETHERTAP_KERN_H +#define __UM_ETHERTAP_KERN_H + +#include "net_kern.h" + +extern void ethertap_setup(char *arg, struct uml_net *dev); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/ethertap_kern.c linux.ac/arch/um/drivers/ethertap_kern.c --- linux.vanilla/arch/um/drivers/ethertap_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/ethertap_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include "linux/init.h" +#include "linux/netdevice.h" +#include "linux/etherdevice.h" +#include "net_kern.h" +#include "net_user.h" +#include "etap.h" +#include "etap_kern.h" + +struct ethertap_setup { + char *dev_name; + unsigned char hw_addr[ETH_ALEN]; + int hw_setup; + char *gate_addr; +}; + +struct ethertap_setup ethertap_priv[MAX_UML_NETDEV] = { + [ 0 ... MAX_UML_NETDEV - 1 ] = + { + dev_name: NULL, + hw_addr: { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, + hw_setup: 0, + gate_addr: NULL, + } +}; + +struct net_device *etap_init(int private_size, int index) +{ + struct net_device *dev; + struct uml_net_private *pri; + struct ethertap_data *epri; + + dev = init_etherdev(NULL, private_size); + if(dev == NULL) return(NULL); + pri = dev->priv; + epri = (struct ethertap_data *) pri->user; + epri->dev_name = ethertap_priv[index].dev_name; + epri->gate_addr = ethertap_priv[index].gate_addr; + memcpy(dev->dev_addr, ethertap_priv[index].hw_addr, ETH_ALEN); + memcpy(epri->hw_addr, ethertap_priv[index].hw_addr, + sizeof(epri->hw_addr)); + printk("ethertap backend - %s", epri->dev_name); + if(epri->gate_addr != NULL) printk(", IP = %s", epri->gate_addr); + epri->hw_setup = ethertap_priv[index].hw_setup; + if(epri->hw_setup) + printk(", ether = %x:%x:%x:%x:%x:%x", + epri->hw_addr[0], epri->hw_addr[1], epri->hw_addr[2], + epri->hw_addr[3], epri->hw_addr[4], epri->hw_addr[5]); + printk("\n"); + epri->data_fd = -1; + epri->control_fd = -1; + return(dev); +} + +static unsigned short etap_protocol(struct sk_buff *skb) +{ + return(eth_type_trans(skb, skb->dev)); +} + +static int etap_set_mac(struct sockaddr *addr, void *data) +{ + struct ethertap_data *pri = data; + struct sockaddr *hwaddr = addr; + + memcpy(pri->hw_addr, hwaddr->sa_data, ETH_ALEN); + + return 0; +} + +static int etap_read(int fd, struct sk_buff **skb, struct uml_net_private *lp) +{ + int len; + + *skb = ether_adjust_skb(*skb, ETH_HEADER_ETHERTAP); + if(*skb == NULL) return(-ENOMEM); + len = etap_user_read(fd, (*skb)->mac.raw, + (*skb)->dev->mtu + 2 * ETH_HEADER_ETHERTAP, + (struct ethertap_data *) &lp->user); + if(len <= 0) return(len); + skb_pull(*skb, 2); + len -= 2; + return(len); +} + +static int etap_write(int fd, struct sk_buff **skb, struct uml_net_private *lp) +{ + if(skb_headroom(*skb) < 2){ + struct sk_buff *skb2; + + skb2 = skb_realloc_headroom(*skb, 2); + dev_kfree_skb(*skb); + if (skb2 == NULL) return(-ENOMEM); + *skb = skb2; + } + skb_push(*skb, 2); + return(etap_user_write(fd, (*skb)->data, (*skb)->len, + (struct ethertap_data *) &lp->user)); +} + +struct net_kern_info ethertap_kern_info = { + init: etap_init, + protocol: etap_protocol, + set_mac: etap_set_mac, + read: etap_read, + write: etap_write, +}; + +static int ethertap_count = 0; + +void ethertap_setup(char *str, struct uml_net *dev) +{ + struct ethertap_setup *pri; + + dev->user = ðertap_user_info; + dev->kern = ðertap_kern_info; + dev->private_size = sizeof(struct ethertap_data); + pri = ðertap_priv[ethertap_count]; + dev->transport_index = ethertap_count++; + tap_setup_common(str, "ethertap", &pri->dev_name, pri->hw_addr, + &pri->hw_setup, &pri->gate_addr); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/ethertap_user.c linux.ac/arch/um/drivers/ethertap_user.c --- linux.vanilla/arch/um/drivers/ethertap_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/ethertap_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user.h" +#include "kern_util.h" +#include "net_user.h" +#include "etap.h" + +#define MAX_PACKET ETH_MAX_PACKET + +void etap_user_init(void *data, void *dev) +{ + struct ethertap_data *pri = data; + + pri->dev = dev; +} + +struct etap_open_data { + char *name; + char *gate; + int data_remote; + int data_me; + int control_remote; + int control_me; + int err; +}; + +struct addr_change { + enum { ADD_ADDR, DEL_ADDR } what; + unsigned char addr[4]; +}; + +static void etap_open_addr(unsigned char *addr, void *arg) +{ + int fd = *((int *) arg); + struct addr_change change; + + change.what = ADD_ADDR; + memcpy(change.addr, addr, sizeof(change.addr)); + if(write(fd, &change, sizeof(change)) != sizeof(change)) + printk("etap_add_addr - request failed, errno = %d\n", + errno); +} + +static void etap_close_addr(unsigned char *addr, void *arg) +{ + int fd = *((int *) arg); + struct addr_change change; + + change.what = DEL_ADDR; + memcpy(change.addr, addr, sizeof(change.addr)); + if(write(fd, &change, sizeof(change)) != sizeof(change)) + printk("etap_close_addr - request failed, errno = %d\n", + errno); +} + +static void etap_tramp(void *arg) +{ + struct etap_open_data *data = arg; + int pid, status, n; + char version_buf[sizeof("nnnnn\0")]; + char data_fd_buf[sizeof("nnnnnn\0")]; + char control_fd_buf[sizeof("nnnnnn\0")]; + char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")], c; + char *setup_args[] = { "uml_net", version_buf, "ethertap", data->name, + data_fd_buf, control_fd_buf, gate_buf, NULL }; + char *nosetup_args[] = { "uml_net", version_buf, "ethertap", + data->name, data_fd_buf, control_fd_buf, + NULL }; + char **args; + + sprintf(data_fd_buf, "%d", data->data_remote); + sprintf(control_fd_buf, "%d", data->control_remote); + sprintf(version_buf, "%d", UML_NET_VERSION); + if(data->gate != NULL){ + strcpy(gate_buf, data->gate); + args = setup_args; + } + else args = nosetup_args; + data->err = 0; + if((pid = fork()) == 0){ + char zero = 0; /* XXX Not necessary */ + + close(data->data_me); + close(data->control_me); + execvp(args[0], args); + printk("Exec of '%s' failed - errno = %d\n", args[0], errno); + write(data->control_remote, &zero, sizeof(zero)); + exit(errno); + } + else if(pid < 0) data->err = errno; + close(data->data_remote); + close(data->control_remote); + n = read(data->control_me, &c, sizeof(c)); + if(n != sizeof(c)){ + printk("etap_open - failed to read response from helper : " + "return = %d, errno = %d\n", n, errno); + if(waitpid(pid, &status, 0) < 0) data->err = errno; + else if(!WIFEXITED(status) || (WEXITSTATUS(status) != 1)){ + printk("uml_net didn't exit with status 1\n"); + data->err = EINVAL; + } + else data->err = EINVAL; + } + else if(c != 1) data->err = EINVAL; +} + +static int etap_open(void *data) +{ + struct ethertap_data *pri = data; + struct etap_open_data tap_data; + int data_fds[2], control_fds[2], err; + + err = tap_open_common(pri->dev, pri->hw_setup, pri->gate_addr); + if(err) return(err); + + tap_data.name = pri->dev_name; + + if(socketpair(PF_UNIX, SOCK_DGRAM, 0, data_fds) < 0){ + printk("data socketpair failed - errno = %d\n", errno); + return(-errno); + } + tap_data.data_remote = data_fds[1]; + tap_data.data_me = data_fds[0]; + + if(socketpair(PF_UNIX, SOCK_STREAM, 0, control_fds) < 0){ + printk("data socketpair failed - errno = %d\n", errno); + return(-errno); + } + tap_data.control_remote = control_fds[1]; + tap_data.control_me = control_fds[0]; + + tap_data.gate = pri->gate_addr; + tracing_cb(etap_tramp, &tap_data); + if(tap_data.err != 0){ + printk("etap_tramp failed - errno = %d\n", tap_data.err); + return(-tap_data.err); + } + pri->data_fd = data_fds[0]; + pri->control_fd = control_fds[0]; + iter_addresses(pri->dev, etap_open_addr, &pri->control_fd); + return(data_fds[0]); +} + +static void etap_close(int fd, void *data) +{ + struct ethertap_data *pri = data; + + iter_addresses(pri->dev, etap_close_addr, &pri->control_fd); + close(fd); + shutdown(pri->data_fd, SHUT_RDWR); + close(pri->data_fd); + close(pri->control_fd); +} + +int etap_user_read(int fd, void *buf, int len, struct ethertap_data *pri) +{ + int n; + + while(((n = recvfrom(fd, buf, len, 0, NULL, NULL)) < 0) && + (errno == EINTR)) ; + if(errno == EAGAIN) return(0); + if(n < 0) return(-errno); + return(n); +} + +int etap_user_write(int fd, void *buf, int len, struct ethertap_data *pri) +{ + int n; + + while(((n = send(fd, buf, len, 0)) < 0) && (errno == EINTR)) ; + if(errno == EAGAIN) n = 0; + if(n < 0) return(-errno); + return(n); +} + +static int etap_set_mtu(int mtu, void *data) +{ + return(mtu); +} + +static void etap_add_addr(unsigned char *addr, void *data) +{ + struct ethertap_data *pri = data; + + if(pri->control_fd == -1) return; + etap_open_addr(addr, &pri->control_fd); +} + +static void etap_del_addr(unsigned char *addr, void *data) +{ + struct ethertap_data *pri = data; + + if(pri->control_fd == -1) return; + etap_close_addr(addr, &pri->control_fd); +} + +struct net_user_info ethertap_user_info = { + init: etap_user_init, + open: etap_open, + close: etap_close, + set_mtu: etap_set_mtu, + add_address: etap_add_addr, + delete_address: etap_del_addr, + max_packet: MAX_PACKET - ETH_HEADER_ETHERTAP +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/mcast.h linux.ac/arch/um/drivers/mcast.h --- linux.vanilla/arch/um/drivers/mcast.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/mcast.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "net_user.h" + +struct mcast_data { + char *addr; + unsigned short port; + void *mcast_addr; + int ttl; + unsigned char hwaddr[ETH_ADDR_LEN]; + int hw_setup; + void *dev; +}; + +extern struct net_user_info mcast_user_info; + +extern int mcast_user_set_mac(struct mcast_data *pri, unsigned char *hwaddr, + int len); +extern int mcast_user_read(int fd, void *buf, int len, + struct mcast_data *data); +extern int mcast_user_write(int fd, void *buf, int len, + struct mcast_data *pri); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/mcast_kern.c linux.ac/arch/um/drivers/mcast_kern.c --- linux.vanilla/arch/um/drivers/mcast_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/mcast_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,168 @@ +/* + * user-mode-linux networking multicast transport + * Copyright (C) 2001 by Harald Welte + * + * based on the existing uml-networking code, which is + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * + * Licensed under the GPL. + */ + +#include "linux/kernel.h" +#include "linux/init.h" +#include "linux/netdevice.h" +#include "linux/etherdevice.h" +#include "linux/in.h" +#include "linux/inet.h" +#include "net_kern.h" +#include "net_user.h" +#include "mcast.h" +#include "mcast_kern.h" + +struct mcast_data mcast_priv[MAX_UML_NETDEV] = { + [ 0 ... MAX_UML_NETDEV - 1 ] = + { + addr: "239.192.168.1", + port: 1102, + ttl: 1, + hwaddr: { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, + hw_setup: 0, + } +}; + +struct net_device *mcast_init(int private_size, int index) +{ + struct net_device *dev; + struct uml_net_private *pri; + struct mcast_data *dpri; + + dev = init_etherdev(NULL, private_size); + if (!dev) + return NULL; + + pri = dev->priv; + dpri = (struct mcast_data *) pri->user; + *dpri = mcast_priv[index]; + memcpy(dev->dev_addr, dpri->hwaddr, ETH_ALEN); + printk("mcast backend "); + if(dpri->hw_setup) + printk("ethernet address=%x:%x:%x:%x:%x:%x ", + dpri->hwaddr[0], dpri->hwaddr[1], dpri->hwaddr[2], + dpri->hwaddr[3], dpri->hwaddr[4], dpri->hwaddr[5]); + + printk("multicast adddress: %s:%u, TTL:%u ", + dpri->addr, dpri->port, dpri->ttl); + + printk("\n"); + return(dev); +} + +static unsigned short mcast_protocol(struct sk_buff *skb) +{ + return eth_type_trans(skb, skb->dev); +} + +static int mcast_set_mac(struct sockaddr *addr, void *data) +{ + struct mcast_data *pri = data; + struct net_device *dev = pri->dev; + struct sockaddr *hwaddr = addr; + + memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN); + return mcast_user_set_mac(pri, hwaddr->sa_data, ETH_ALEN); +} + +static int mcast_read(int fd, struct sk_buff **skb, struct uml_net_private *lp) +{ + *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); + if(*skb == NULL) return(-ENOMEM); + return mcast_user_read(fd, (*skb)->mac.raw, + (*skb)->dev->mtu + ETH_HEADER_OTHER, + (struct mcast_data *) &lp->user); +} + +static int mcast_write(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return mcast_user_write(fd, (*skb)->data, (*skb)->len, + (struct mcast_data *) &lp->user); +} + +static struct net_kern_info mcast_kern_info = { + init: mcast_init, + protocol: mcast_protocol, + set_mac: mcast_set_mac, + read: mcast_read, + write: mcast_write, +}; + +static int mcast_count = 0; + +void mcast_setup(char *str, struct uml_net *dev) +{ + int err, n = mcast_count; + int num = 0; + char *p1, *p2; + + dev->user = &mcast_user_info; + dev->kern = &mcast_kern_info; + dev->private_size = sizeof(struct mcast_data); + dev->transport_index = mcast_count++; + + + /* somewhat more sophisticated parser, needed for in_aton */ + + p1 = str; + if (*str == ',') + p1++; + while (p1 && *p1) { + if ((p2 = strchr(p1, ','))) + *p2++ = '\0'; + if (strlen(p1) > 0) { + switch (num) { + case 0: + /* First argument: Ethernet address */ + err = setup_etheraddr(p1, + mcast_priv[n].hwaddr); + if (!err) + mcast_priv[n].hw_setup = 1; + break; + case 1: + /* Second argument: Multicast group */ + mcast_priv[n].addr = p1; + break; + case 2: + /* Third argument: Port number */ + mcast_priv[n].port = + htons(simple_strtoul(p1, NULL, 10)); + break; + case 3: + /* Fourth argument: TTL */ + mcast_priv[n].ttl = + simple_strtoul(p1, NULL, 10); + break; + } + } + p1 = p2; + num++; + } + + printk(KERN_INFO "Configured mcast device: %s:%u-%u\n", + mcast_priv[n].addr, mcast_priv[n].port, + mcast_priv[n].ttl); + + return; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/mcast_kern.h linux.ac/arch/um/drivers/mcast_kern.h --- linux.vanilla/arch/um/drivers/mcast_kern.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/mcast_kern.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,8 @@ +#ifndef __UM_MCAST_KERN_H +#define __UM_MCAST_KERN_H + +#include "net_kern.h" + +extern void mcast_setup(char *arg, struct uml_net *dev); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/mcast_user.c linux.ac/arch/um/drivers/mcast_user.c --- linux.vanilla/arch/um/drivers/mcast_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/mcast_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,214 @@ +/* + * user-mode-linux networking multicast transport + * Copyright (C) 2001 by Harald Welte + * + * based on the existing uml-networking code, which is + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * + * Licensed under the GPL. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "net_user.h" +#include "mcast.h" +#include "kern_util.h" +#include "user_util.h" +#include "user.h" + +#define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER) + +static struct sockaddr_in *new_addr(char *addr, unsigned short port) +{ + struct sockaddr_in *sin; + + sin = um_kmalloc(sizeof(struct sockaddr_in)); + if(sin == NULL){ + printk("new_addr: allocation of sockaddr_in failed\n"); + return(NULL); + } + sin->sin_addr.s_addr = in_aton(addr); + sin->sin_port = port; + return(sin); +} + +static void mcast_user_init(void *data, void *dev) +{ + struct mcast_data *pri = data; + + pri->mcast_addr = new_addr(pri->addr, pri->port); + pri->dev = dev; +} + +static int mcast_open(void *data) +{ + struct mcast_data *pri = data; + struct sockaddr_in *sin = pri->mcast_addr; + struct ip_mreq mreq; + char addr[sizeof("255.255.255.255\0")]; + int fd, err, yes = 1; + + + if(!pri->hw_setup){ + pri->hwaddr[0] = 0xfe; + pri->hwaddr[1] = 0xfd; + pri->hwaddr[2] = 0x0; + pri->hwaddr[3] = 0x0; + pri->hwaddr[4] = 0x0; + pri->hwaddr[5] = 0x0; + dev_ip_addr(pri->dev, addr, &pri->hwaddr[2]); + set_ether_mac(pri->dev, pri->hwaddr); + } + + if ((sin->sin_addr.s_addr == 0) || (sin->sin_port == 0)) { + err = -EINVAL; + goto out; + } + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){ + printk("mcast_open : data socket failed, errno = %d\n", + errno); + err = -ENOMEM; + goto out; + } + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { + printk("mcast_open: SO_REUSEADDR failed, errno = %d\n", + errno); + close(fd); + err = -EINVAL; + goto out; + } + + /* set ttl according to config */ + if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl, + sizeof(pri->ttl)) < 0) { + printk("mcast_open: IP_MULTICAST_TTL failed, error = %d\n", + errno); + close(fd); + err = -EINVAL; + goto out; + } + + /* set LOOP, so data does get fed back to local sockets */ + if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { + printk("mcast_open: IP_MULTICAST_LOOP failed, error = %d\n", + errno); + close(fd); + err = -EINVAL; + goto out; + } + + /* bind socket to mcast address */ + if (bind(fd, (struct sockaddr *) sin, sizeof(*sin)) < 0) { + printk("mcast_open : data bind failed, errno = %d\n", errno); + close(fd); + err = -EINVAL; + goto out; + } + + /* subscribe to the multicast group */ + mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr; + mreq.imr_interface.s_addr = 0; + if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + printk("mcast_open: IP_ADD_MEMBERSHIP failed, error = %d\n", + errno); + close(fd); + err = -EINVAL; + goto out; + } + + return(fd); + out: + return(err); +} + +static void mcast_close(int fd, void *data) +{ + struct ip_mreq mreq; + struct mcast_data *pri = data; + struct sockaddr_in *sin = pri->mcast_addr; + + mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr; + mreq.imr_interface.s_addr = 0; + if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + printk("mcast_open: IP_DROP_MEMBERSHIP failed, error = %d\n", + errno); + } + + close(fd); +} + +int mcast_user_read(int fd, void *buf, int len, struct mcast_data *data) +{ + int n; + + while (((n = recvfrom(fd, buf, len, 0, NULL, NULL)) < 0) && + (errno == EINTR)) ; + + if (n < 0) { + if (errno == EAGAIN) + return 0; + return -errno; + } else if (n == 0) + return -ENOTCONN; + + return n; +} + +int mcast_user_write(int fd, void *buf, int len, struct mcast_data *pri) +{ + int n; + struct sockaddr_in *data_addr = pri->mcast_addr; + + n = sendto(fd, buf, len, 0, (struct sockaddr *) data_addr, + sizeof(*data_addr)); + + if (n < 0) + return(-errno); + + return(n); +} + +static int mcast_set_mtu(int mtu, void *data) +{ + return(mtu); +} + +int mcast_user_set_mac(struct mcast_data *pri, unsigned char *hwaddr, + int len) +{ + memcpy(pri->hwaddr, hwaddr, len); + return 0; +} + +struct net_user_info mcast_user_info = { + init: mcast_user_init, + open: mcast_open, + close: mcast_close, + set_mtu: mcast_set_mtu, + add_address: NULL, + delete_address: NULL, + max_packet: MAX_PACKET - ETH_HEADER_OTHER +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/mconsole_kern.c linux.ac/arch/um/drivers/mconsole_kern.c --- linux.vanilla/arch/um/drivers/mconsole_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/mconsole_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) + * Licensed under the GPL + */ + +#include "linux/kernel.h" +#include "linux/slab.h" +#include "linux/init.h" +#include "linux/notifier.h" +#include "linux/reboot.h" +#include "linux/utsname.h" +#include "linux/ctype.h" +#include "linux/interrupt.h" +#include "linux/sysrq.h" +#include "asm/irq.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "mconsole.h" +#include "mconsole_kern.h" + +extern int create_listening_socket(void); +extern int get_request(int fd, struct mconsole_request *req); +extern void handle_request(struct mconsole_request *req); + +static int do_unlink_socket(struct notifier_block *, unsigned long, void *); + +static struct notifier_block reboot_notifier = { + notifier_call: do_unlink_socket, + priority: 0, +}; + +int do_unlink_socket(struct notifier_block *notifier, unsigned long what, + void *data) +{ + return(unlink_socket()); +} + +LIST_HEAD(mc_requests); + +void mc_task_proc(void *unused) +{ + struct mconsole_entry *req; + unsigned long flags; + int done; + + do { + save_flags(flags); + req = list_entry(mc_requests.next, struct mconsole_entry, + list); + list_del(&req->list); + done = list_empty(&mc_requests); + restore_flags(flags); + handle_request(&req->request); + kfree(req); + } while(!done); +} + +struct tq_struct mconsole_task = { + routine: mc_task_proc, + data: NULL +}; + +void mconsole_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int fd; + struct mconsole_entry *new; + struct mconsole_request req; + + fd = (int) dev_id; + while (get_request(fd, &req)) { + new = kmalloc(sizeof(req), GFP_ATOMIC); + if(new == NULL) + mconsole_reply(&req, "ERR Out of memory"); + else { + new->request = req; + list_add(&new->list, &mc_requests); + } + } + schedule_task(&mconsole_task); + reactivate_fd(fd); +} + +void mconsole_version(struct mconsole_request *req) +{ + char version[256]; + + sprintf(version, "OK %s %s %s %s %s", system_utsname.sysname, + system_utsname.nodename, system_utsname.release, + system_utsname.version, system_utsname.machine); + mconsole_reply(req, version); +} + +#define UML_MCONSOLE_HELPTEXT "OK +Commands: + version - Get kernel version + help - Print this message + halt - Halt UML + reboot - Reboot UML + config = - Add a new device to UML; + same syntax as command line + remove - Remove a device from the client +" + +void mconsole_help(struct mconsole_request *req) +{ + mconsole_reply(req, UML_MCONSOLE_HELPTEXT) ; +} + +void mconsole_halt(struct mconsole_request *req) +{ + mconsole_reply(req, "OK"); + machine_halt(); +} + +void mconsole_reboot(struct mconsole_request *req) +{ + mconsole_reply(req, "OK"); + machine_restart(NULL); +} + +LIST_HEAD(mconsole_devices); + +void mconsole_register_dev(struct mc_device *new) +{ + list_add(&new->list, &mconsole_devices); +} + +static struct mc_device *mconsole_find_dev(char *name) +{ + struct list_head *ele; + struct mc_device *dev; + + list_for_each(ele, &mconsole_devices){ + dev = list_entry(ele, struct mc_device, list); + if(!strncmp(name, dev->name, strlen(dev->name))) + return(dev); + } + return(NULL); +} + +void mconsole_config(struct mconsole_request *req) +{ + struct mc_device *dev; + char *ptr = req->buf, *ok; + int err; + + ptr += strlen("config"); + while(isspace(*ptr)) ptr++; + dev = mconsole_find_dev(ptr); + if(dev == NULL){ + mconsole_reply(req, "ERR Bad configuration option"); + return; + } + err = (*dev->config)(&ptr[strlen(dev->name)]); + if(err) ok = "ERR"; + else ok = "OK"; + mconsole_reply(req, ok); +} + +void mconsole_remove(struct mconsole_request *req) +{ + struct mc_device *dev; + char *ptr = req->buf, *ok; + int err; + + ptr += strlen("remove"); + while(isspace(*ptr)) ptr++; + dev = mconsole_find_dev(ptr); + if(dev == NULL){ + mconsole_reply(req, "ERR Bad remove option"); + return; + } + err = (*dev->remove)(&ptr[strlen(dev->name)]); + if(err) ok = "ERR"; + else ok = "OK"; + mconsole_reply(req, ok); +} + +#ifdef CONFIG_MAGIC_SYSRQ +void mconsole_sysrq(struct mconsole_request *req) +{ + char *ptr = req->buf; + + ptr += strlen("sysrq"); + while(isspace(*ptr)) ptr++; + + handle_sysrq(*ptr, (struct pt_regs *) ¤t->thread.process_regs, + NULL, NULL); + mconsole_reply(req, "OK"); +} +#else +void mconsole_sysrq(struct mconsole_request *req) +{ + mconsole_reply(req, "ERR sysrq not compiled in"); +} +#endif + +int mconsole_init(void) +{ + int err; + int sock; + + sock = create_listening_socket(); + if (sock < 0) { + printk("Failed to initialize management console\n"); + return 1; + } + + register_reboot_notifier(&reboot_notifier); + + err = um_request_irq(MCONSOLE_IRQ, sock, mconsole_interrupt, + SA_INTERRUPT | SA_SHIRQ, "mconsole", + (void *)sock); + if (err) { + printk("Failed to get IRQ for management console\n"); + return 1; + } + + printk("mconsole initialized on %s\n", socket_name); + return 0; +} + +__initcall(mconsole_init); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/mconsole_user.c linux.ac/arch/um/drivers/mconsole_user.c --- linux.vanilla/arch/um/drivers/mconsole_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/mconsole_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user.h" +#include "mconsole.h" +#include "umid.h" + +static struct mconsole_command commands[] = { + { "version", mconsole_version }, + { "halt", mconsole_halt }, + { "reboot", mconsole_reboot }, + { "config", mconsole_config }, + { "remove", mconsole_remove }, + { "sysrq", mconsole_sysrq }, + { "help", mconsole_help }, +}; + +char socket_name[256]; + +static int has_correct_credentials(struct msghdr *msg) +{ + struct cmsghdr *cmsg; + + cmsg = CMSG_FIRSTHDR(msg); + while (cmsg != NULL) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS) { + struct ucred *cred; + + cred = (struct ucred *)CMSG_DATA(cmsg); + if (cred->uid == getuid()) + return 1; + } + cmsg = CMSG_NXTHDR(msg, cmsg); + } + + return 0; +} + +int get_request(int fd, struct mconsole_request *req) +{ + char anc[64]; + struct iovec iov; + struct msghdr msg; + + iov.iov_base = req->buf; + iov.iov_len = sizeof(req->buf) - 1; + + msg.msg_name = &(req->origin); + msg.msg_namelen = sizeof(req->origin); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = anc; + msg.msg_controllen = sizeof(anc); + msg.msg_flags = 0; + + req->len = recvmsg(fd, &msg, 0); + if (req->len < 0) + return 0; + + if (req->len < sizeof(req->buf)) + req->buf[req->len] = 0; + + req->originlen = msg.msg_namelen; + req->originating_fd = fd; + req->has_correct_credentials = has_correct_credentials(&msg); + + return 1; +} + +int mconsole_reply(struct mconsole_request *req, char *reply) +{ + struct iovec iov; + struct msghdr msg; + + iov.iov_base = reply; + iov.iov_len = strlen(reply); + + msg.msg_name = &(req->origin); + msg.msg_namelen = req->originlen; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + return sendmsg(req->originating_fd, &msg, 0); +} + +int unlink_socket(void) +{ + unlink(socket_name); + return 0; +} + +int create_listening_socket(void) +{ + struct sockaddr_un addr; + char file[256]; + int sock, err, yes = 1; + + sock = socket(PF_UNIX, SOCK_DGRAM, 0); + if (sock < 0) { + printk("create_listening_socket - socket failed, errno = %d\n", + errno); + return(-1); + } + + addr.sun_family = AF_UNIX; + + if(umid_file_name("mconsole", file, sizeof(file))) return(-1); + + strcpy(socket_name, file); + strcpy(addr.sun_path, file); + + err = bind(sock, (struct sockaddr *) &addr, sizeof(addr)); + if (err < 0) { + if (errno != EADDRINUSE) { + printk("create_listening_socket - bind failed, " + "errno = %d\n", errno); + return(-1); + } + } + + setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &yes, sizeof(yes)); + + return sock; +} + +void handle_request(struct mconsole_request *req) +{ + struct mconsole_command *cmd; + int i; + + if (!req->has_correct_credentials) + return; + + for(i=0;ibuf, cmd->command, strlen(cmd->command))) { + cmd->handler(req); + return; + } + } + mconsole_reply(req, "ERR unknown command"); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/mmapper_kern.c linux.ac/arch/um/drivers/mmapper_kern.c --- linux.vanilla/arch/um/drivers/mmapper_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/mmapper_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,146 @@ +/* + * arch/um/drivers/mmapper_kern.c + * + * BRIEF MODULE DESCRIPTION + * + * Copyright (C) 2000 RidgeRun, Inc. + * Author: RidgeRun, Inc. + * Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mem_user.h" +#include "user_util.h" + +static unsigned long mmapper_size; +static char *p_buf = NULL; +static char *v_buf = NULL; + +static ssize_t +mmapper_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + if(*ppos > mmapper_size) + return -EINVAL; + + if(count + *ppos > mmapper_size) + count = count + *ppos - mmapper_size; + + if(count < 0) + return -EINVAL; + + copy_to_user(buf,&v_buf[*ppos],count); + + return count; +} + +static ssize_t +mmapper_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + if(*ppos > mmapper_size) + return -EINVAL; + + if(count + *ppos > mmapper_size) + count = count + *ppos - mmapper_size; + + if(count < 0) + return -EINVAL; + + copy_from_user(&v_buf[*ppos],buf,count); + + return count; +} + +static int +mmapper_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + return(-ENOIOCTLCMD); +} + +static int +mmapper_mmap(struct file *file, struct vm_area_struct * vma) +{ + int ret = -EINVAL; + int size; + + lock_kernel(); + if (vma->vm_pgoff != 0) + goto out; + + size = vma->vm_end - vma->vm_start; + + /* XXX A comment above remap_page_range says it should only be + * called when the mm semaphore is held + */ + if (remap_page_range(vma->vm_start, (unsigned long) p_buf, + size, vma->vm_page_prot)) + goto out; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int +mmapper_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int +mmapper_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static struct file_operations mmapper_fops = { + owner: THIS_MODULE, + read: mmapper_read, + write: mmapper_write, + ioctl: mmapper_ioctl, + mmap: mmapper_mmap, + open: mmapper_open, + release: mmapper_release, +}; + +static int __init mmapper_init(void) +{ + printk(KERN_INFO "Mapper v0.1\n"); + + p_buf = find_iomem("mmapper", &mmapper_size); + + v_buf = p_buf; + + devfs_register (NULL, "mmapper", DEVFS_FL_DEFAULT, + 30, 0, S_IFCHR | S_IRUGO | S_IWUGO, + &mmapper_fops, NULL); + devfs_mk_symlink(NULL, "mmapper", DEVFS_FL_DEFAULT, "mmapper0", + NULL, NULL); + return(0); +} + +static void mmapper_exit(void) +{ +} + +module_init(mmapper_init); +module_exit(mmapper_exit); + +MODULE_AUTHOR("Greg Lonnon "); +MODULE_DESCRIPTION("DSPLinux simulator mmapper driver"); +/* + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/net_kern.c linux.ac/arch/um/drivers/net_kern.c --- linux.vanilla/arch/um/drivers/net_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/net_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,627 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include "linux/config.h" +#include "linux/netdevice.h" +#include "linux/skbuff.h" +#include "linux/socket.h" +#include "linux/spinlock.h" +#include "linux/module.h" +#include "linux/init.h" +#include "linux/etherdevice.h" +#include "linux/list.h" +#include "linux/inetdevice.h" +#include "linux/ctype.h" +#include "user_util.h" +#include "kern_util.h" +#include "net_kern.h" +#include "net_user.h" +#include "slip.h" +#include "slip_kern.h" +#include "etap.h" +#include "etap_kern.h" +#include "tuntap.h" +#include "tuntap_kern.h" +#include "daemon.h" +#include "daemon_kern.h" +#include "mcast.h" +#include "mcast_kern.h" +#include "mconsole_kern.h" + +LIST_HEAD(opened); + +struct uml_net devices[MAX_UML_NETDEV] = { + [ 0 ... MAX_UML_NETDEV - 1 ] = + { + dev: NULL, + user: NULL, + kern: NULL, + private_size: 0, + } +}; + +static int eth_setup_common(char *str, int *index_out) +{ + char *end; + int n; + + n = simple_strtoul(str, &end, 0); + if(end == str){ + printk("eth_setup: Failed to parse '%s'\n", str); + return(1); + } + if((n < 0) || (n > sizeof(devices)/sizeof(devices[0]))){ + printk("eth_setup: device %d out of range\n", n); + return(1); + } + str = end; + if(*str != '='){ + printk("eth_setup: expected '=' after device number\n"); + return(1); + } + str++; + if(devices[n].dev != NULL){ + printk(KERN_ERR "eth_setup: Device %d already configured\n", + n); + return(1); + } + if(index_out) *index_out = n; +#ifdef CONFIG_UML_NET_ETHERTAP + if(!strncmp(str, "ethertap", strlen("ethertap"))){ + ethertap_setup(&str[strlen("ethertap")], &devices[n]); + return(0); + } +#endif +#ifdef CONFIG_UML_NET_TUNTAP + if(!strncmp(str, "tuntap", strlen("tuntap"))){ + tuntap_setup(&str[strlen("tuntap")], &devices[n]); + return(0); + } +#endif +#ifdef CONFIG_UML_NET_DAEMON + if(!strncmp(str, "daemon", strlen("daemon"))){ + daemon_setup(&str[strlen("daemon")], &devices[n]); + return(0); + } +#endif +#ifdef CONFIG_UML_NET_SLIP + if(!strncmp(str, "slip", strlen("slip"))){ + slip_setup(&str[strlen("slip")], &devices[n]); + return(0); + } +#endif +#ifdef CONFIG_UML_NET_MCAST + if(!strncmp(str, "mcast", strlen("mcast"))){ + mcast_setup(&str[strlen("mcast")], &devices[n]); + return(0); + } +#endif + printk(KERN_ERR "Unknown transport in eth_setup : %s\n", str); + return(1); +} + +static int eth_setup(char *str) +{ + eth_setup_common(str, NULL); + return(1); +} + +__setup("eth", eth_setup); + +int ndev = 0; + +static int uml_net_rx(struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + int pkt_len; + struct sk_buff *skb; + + /* If we can't allocate memory, try again next round. */ + if ((skb = dev_alloc_skb(dev->mtu)) == NULL) { + lp->stats.rx_dropped++; + return 0; + } + + skb->dev = dev; + skb_put(skb, dev->mtu); + skb->mac.raw = skb->data; + pkt_len = (*lp->read)(lp->fd, &skb, lp); + + reactivate_fd(lp->fd); + if (pkt_len > 0) { + skb_trim(skb, pkt_len); + skb->protocol = (*lp->protocol)(skb); + netif_rx(skb); + + lp->stats.rx_bytes += skb->len; + lp->stats.rx_packets++; + return pkt_len; + } + + kfree_skb(skb); + return pkt_len; +} + +void uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct uml_net_private *lp = dev->priv; + int err; + + if (netif_running(dev)) { + spin_lock(&lp->lock); + while((err = uml_net_rx(dev)) > 0) ; + if(err < 0) { + printk("Device '%s' read returned %d, shutting it " + "down\n", dev->name, err); + dev->flags &= ~IFF_UP; + dev_close(dev); + } + spin_unlock(&lp->lock); + } +} + +static int uml_net_open(struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + int err; + + spin_lock(&lp->lock); + + if(lp->fd >= 0){ + err = -ENXIO; + goto out; + } + + lp->fd = (*lp->open)(&lp->user); + if(lp->fd < 0){ + err = lp->fd; + goto out; + } + + err = um_request_irq(dev->irq, lp->fd, uml_net_interrupt, + SA_INTERRUPT | SA_SHIRQ, dev->name, dev); + if(err != 0){ + printk("uml_net_open: failed to get irq(%d)\n", err); + (*lp->close)(lp->fd, &lp->user); + lp->fd = -1; + err = -ENETUNREACH; + } + + lp->tl.data = (unsigned long) &lp->user; + netif_start_queue(dev); + + list_add(&lp->list, &opened); + MOD_INC_USE_COUNT; + out: + spin_unlock(&lp->lock); + return(err); +} + +static int uml_net_close(struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + + netif_stop_queue(dev); + spin_lock(&lp->lock); + + free_irq(dev->irq, dev); + (*lp->close)(lp->fd, &lp->user); + lp->fd = -1; + list_del(&lp->list); + + MOD_DEC_USE_COUNT; + spin_unlock(&lp->lock); + return 0; +} + +static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + unsigned long flags; + int len; + + netif_stop_queue(dev); + + spin_lock_irqsave(&lp->lock, flags); + + len = (*lp->write)(lp->fd, &skb, lp); + + if(len == skb->len) { + lp->stats.tx_packets++; + lp->stats.tx_bytes += skb->len; + dev->trans_start = jiffies; + netif_start_queue(dev); + + /* this is normally done in the interrupt when tx finishes */ + netif_wake_queue(dev); + } + else if(len == 0){ + netif_start_queue(dev); + lp->stats.tx_dropped++; + } + else { + netif_start_queue(dev); + printk("uml_net_start_xmit: failed(%d)\n", len); + } + + spin_unlock_irqrestore(&lp->lock, flags); + + dev_kfree_skb(skb); + + return 0; +} + +static struct net_device_stats *uml_net_get_stats(struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + return &lp->stats; +} + +static void uml_net_set_multicast_list(struct net_device *dev) +{ + if (dev->flags & IFF_PROMISC) return; + else if (dev->mc_count) dev->flags |= IFF_ALLMULTI; + else dev->flags &= ~IFF_ALLMULTI; +} + +static void uml_net_tx_timeout(struct net_device *dev) +{ + dev->trans_start = jiffies; + netif_wake_queue(dev); +} + +static int uml_net_set_mac(struct net_device *dev, void *addr) +{ + struct uml_net_private *lp = dev->priv; + struct sockaddr *hwaddr = addr; + int err; + + spin_lock(&lp->lock); + + err = (*lp->set_mac)(hwaddr, &lp->user); + + spin_unlock(&lp->lock); + + return err; +} + +static int uml_net_change_mtu(struct net_device *dev, int new_mtu) +{ + struct uml_net_private *lp = dev->priv; + int err = 0; + + spin_lock(&lp->lock); + + new_mtu = (*lp->set_mtu)(new_mtu, &lp->user); + if(new_mtu < 0){ + err = new_mtu; + goto out; + } + + dev->mtu = new_mtu; + + out: + spin_unlock(&lp->lock); + return err; +} + +static int uml_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + return(-EINVAL); +} + +void uml_net_user_timer_expire(unsigned long _conn) +{ +#ifdef undef + struct connection *conn = (struct connection *)_conn; + + dprintk("uml_net_user_timer_expire [%p]\n", conn); + do_connect(conn); +#endif +} + +static int eth_configure(struct uml_net *device, int n) +{ + struct net_device *dev; + struct uml_net_private *lp; + + device->private_size += sizeof(struct uml_net_private) + + sizeof(((struct uml_net_private *) 0)->user); + printk(KERN_INFO "Netdevice %d : ", n); + dev = (*device->kern->init)(device->private_size, + device->transport_index); + device->dev = dev; + + if (dev == NULL){ + printk(KERN_ERR "eth_configure: Out of memory on device %d\n", + n); + return(1); + } + + dev->mtu = device->user->max_packet; + dev->open = uml_net_open; + dev->hard_start_xmit = uml_net_start_xmit; + dev->stop = uml_net_close; + dev->get_stats = uml_net_get_stats; + dev->set_multicast_list = uml_net_set_multicast_list; + dev->tx_timeout = uml_net_tx_timeout; + dev->set_mac_address = uml_net_set_mac; + dev->change_mtu = uml_net_change_mtu; + dev->do_ioctl = uml_net_ioctl; + dev->watchdog_timeo = (HZ >> 1); + dev->irq = UM_ETH_IRQ; + + lp = dev->priv; + spin_lock_init(&lp->lock); + init_timer(&lp->tl); + lp->tl.function = uml_net_user_timer_expire; + lp->list = ((struct list_head) LIST_HEAD_INIT(lp->list)); + memset(&lp->stats, 0, sizeof(lp->stats)); + lp->fd = -1; + lp->protocol = device->kern->protocol; + lp->set_mac = device->kern->set_mac; + lp->open = device->user->open; + lp->close = device->user->close; + lp->read = device->kern->read; + lp->write = device->kern->write; + lp->add_address = device->user->add_address; + lp->delete_address = device->user->delete_address; + lp->set_mtu = device->user->set_mtu; + + if(device->user->init) + (*device->user->init)(&lp->user, dev); + return(0); +} + +int __init uml_net_probe(void) +{ + int i; + + for(i = 0; i < sizeof(devices)/sizeof(devices[0]); i++){ + if(devices[i].user == NULL) continue; + eth_configure(&devices[i], i); + } + return(0); +} + +static int net_config(char *str) +{ + int err, n; + + str = uml_strdup(str); + if(str == NULL){ + printk(KERN_ERR "net_config failed to strdup string\n"); + return(1); + } + err = eth_setup_common(str, &n); + if(err){ + kfree(str); + return(err); + } + err = eth_configure(&devices[n], n); + return(err); +} + +static int net_remove(char *str) +{ + struct net_device *dev; + struct uml_net_private *lp; + int n; + + if(!isdigit(*str)) return(-1); + n = *str - '0'; + if(devices[n].dev == NULL) return(0); + dev = devices[n].dev; + lp = dev->priv; + if(lp->fd > 0) return(-1); + unregister_netdev(dev); + devices[n].dev = NULL; + return(0); +} + +static struct mc_device net_mc = { + name: "eth", + config: net_config, + remove: net_remove, +}; + +static int uml_inetaddr_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct in_ifaddr *ifa = ptr; + u32 addr = ifa->ifa_address; + struct net_device *dev = ifa->ifa_dev->dev; + struct uml_net_private *lp; + void (*proc)(unsigned char *addr, void *); + unsigned char addr_buf[4]; + + if(dev->open != uml_net_open) return NOTIFY_DONE; + + lp = dev->priv; + proc = NULL; + switch (event){ + case NETDEV_UP: + proc = lp->add_address; + break; + case NETDEV_DOWN: + proc = lp->delete_address; + break; + } + if(proc != NULL){ + addr_buf[0] = addr & 0xff; + addr_buf[1] = (addr >> 8) & 0xff; + addr_buf[2] = (addr >> 16) & 0xff; + addr_buf[3] = addr >> 24; + (*proc)(addr_buf, &lp->user); + } + return NOTIFY_DONE; +} + +struct notifier_block uml_inetaddr_notifier = { + notifier_call: uml_inetaddr_event, +}; + +static int uml_net_init(void) +{ + mconsole_register_dev(&net_mc); + register_inetaddr_notifier(¨_inetaddr_notifier); + return(0); +} + +__initcall(uml_net_init); + +static void close_devices(void) +{ + struct list_head *ele; + struct uml_net_private *lp; + + list_for_each(ele, &opened){ + lp = list_entry(ele, struct uml_net_private, list); + (*lp->close)(lp->fd, &lp->user); + } +} + +__exitcall(close_devices); + +int setup_etheraddr(char *str, unsigned char *addr) +{ + char *end; + int i; + + for(i=0;i<6;i++){ + addr[i] = simple_strtoul(str, &end, 16); + if((end == str) || + ((*end != ':') && (*end != ',') && (*end != '\0'))){ + printk("setup_etheraddr: failed to parse '%s' " + "as an ethernet address\n", str); + return(-1); + } + str = end + 1; + } + if(addr[0] & 1){ + printk(KERN_ERR + "Attempt to assign a broadcast ethernet address to a " + "device disallowed\n"); + return(-1); + } + return(0); +} + +void dev_ip_addr(void *d, char *buf, char *bin_buf) +{ + struct net_device *dev = d; + struct in_device *ip = dev->ip_ptr; + struct in_ifaddr *in; + u32 addr; + + if(ip == NULL){ + printk(KERN_WARNING "dev_ip_addr - device not assigned an " + "IP address\n"); + return; + } + in = ip->ifa_list; + addr = in->ifa_address; + sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff, + (addr >> 16) & 0xff, addr >> 24); + if(bin_buf){ + bin_buf[0] = addr & 0xff; + bin_buf[1] = (addr >> 8) & 0xff; + bin_buf[2] = (addr >> 16) & 0xff; + bin_buf[3] = addr >> 24; + } +} + +void set_ether_mac(void *d, unsigned char *addr) +{ + struct net_device *dev = d; + + memcpy(dev->dev_addr, addr, ETH_ALEN); +} + +struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra) +{ + if((skb != NULL) && (skb_tailroom(skb) < extra)){ + struct sk_buff *skb2; + + skb2 = skb_copy_expand(skb, 0, extra, GFP_ATOMIC); + dev_kfree_skb(skb); + skb = skb2; + } + if(skb != NULL) skb_put(skb, extra); + return(skb); +} + +void iter_addresses(void *d, void (*cb)(unsigned char *, void *), void *arg) +{ + struct net_device *dev = d; + struct in_device *ip = dev->ip_ptr; + struct in_ifaddr *in; + unsigned char address[4]; + + if(ip == NULL) return; + in = ip->ifa_list; + while(in != NULL){ + address[0] = in->ifa_address & 0xff; + address[1] = (in->ifa_address >> 8) & 0xff; + address[2] = (in->ifa_address >> 16) & 0xff; + address[3] = in->ifa_address >> 24; + (*cb)(address, arg); + in = in->ifa_next; + } +} + +void *get_output_buffer(int *len_out) +{ + void *ret; + + ret = (void *) __get_free_pages(GFP_KERNEL, 0); + if(ret) *len_out = PAGE_SIZE; + else *len_out = 0; + return(ret); +} + +void free_output_buffer(void *buffer) +{ + free_pages((unsigned long) buffer, 0); +} + +void tap_setup_common(char *str, char *type, char **dev_name, char *hw_addr, + int *hw_setup, char **gate_addr) +{ + int err; + + if(*str != ','){ + printk(KERN_ERR + "ethertap_setup: expected ',' after '%s'\n", type); + return; + } + str++; + if(*str != ',') *dev_name = str; + str = strchr(str, ','); + if(str == NULL) return; + *str++ = '\0'; + if(*str != ','){ + err = setup_etheraddr(str, hw_addr); + if(!err) *hw_setup = 1; + } + str = strchr(str, ','); + if(str == NULL) return; + *str++ = '\0'; + if(*str != '\0') *gate_addr = str; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/net_kern.h linux.ac/arch/um/drivers/net_kern.h --- linux.vanilla/arch/um/drivers/net_kern.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/net_kern.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,66 @@ +#ifndef __UM_NET_KERN_H +#define __UM_NET_KERN_H + +#include "linux/netdevice.h" +#include "linux/skbuff.h" +#include "linux/socket.h" +#include "linux/list.h" + +#define MAX_UML_NETDEV (16) + +struct uml_net { + struct net_device *dev; + struct net_user_info *user; + struct net_kern_info *kern; + int private_size; + int transport_index; +}; + +struct uml_net_private { + spinlock_t lock; + + struct timer_list tl; + struct list_head list; + struct net_device_stats stats; + int fd; + unsigned short (*protocol)(struct sk_buff *); + int (*set_mac)(struct sockaddr *hwaddr, void *); + int (*open)(void *); + void (*close)(int, void *); + int (*read)(int, struct sk_buff **skb, struct uml_net_private *); + int (*write)(int, struct sk_buff **skb, struct uml_net_private *); + + void (*add_address)(unsigned char *, void *); + void (*delete_address)(unsigned char *, void *); + int (*set_mtu)(int mtu, void *); + int user[1]; +}; + +struct net_kern_info { + struct net_device *(*init)(int, int); + unsigned short (*protocol)(struct sk_buff *); + int (*set_mac)(struct sockaddr *hwaddr, void *); + int (*read)(int, struct sk_buff **skb, struct uml_net_private *); + int (*write)(int, struct sk_buff **skb, struct uml_net_private *); +}; + +extern struct net_device *ether_init(int); +extern unsigned short ether_protocol(struct sk_buff *); +extern int ether_set_mac(struct sockaddr *hwaddr, void *); +extern int setup_etheraddr(char *str, unsigned char *addr); +extern struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra); +extern void tap_setup_common(char *str, char *type, char **dev_name, + char *hw_addr, int *hw_setup, char **gate_addr); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/net_user.c linux.ac/arch/um/drivers/net_user.c --- linux.vanilla/arch/um/drivers/net_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/net_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include "user.h" +#include "net_user.h" + +int tap_open_common(void *dev, int hw_setup, char *gate_addr) +{ + char addr[sizeof("255.255.255.255\0")]; + char ether[ETH_ADDR_LEN]; + + if((gate_addr != NULL) || !hw_setup){ + ether[0] = 0xfe; + ether[1] = 0xfd; + ether[2] = 0x0; + ether[3] = 0x0; + ether[4] = 0x0; + ether[5] = 0x0; + dev_ip_addr(dev, addr, ðer[2]); + } + if(gate_addr != NULL){ + int uml_addr[4], tap_addr[4]; + if(sscanf(addr, "%d.%d.%d.%d", ¨_addr[0], ¨_addr[1], + ¨_addr[2], ¨_addr[3]) != 4){ + printk("Invalid UML IP address - '%s'\n", addr); + return(-EINVAL); + } + if(sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], + &tap_addr[1], &tap_addr[2], &tap_addr[3]) != 4){ + printk("Invalid tap IP address - '%s'\n", + gate_addr); + return(-EINVAL); + } + if((uml_addr[0] == tap_addr[0]) && + (uml_addr[1] == tap_addr[1]) && + (uml_addr[2] == tap_addr[2]) && + (uml_addr[3] == tap_addr[3])){ + printk("The tap IP address and the UML eth IP address" + " must be different\n"); + return(-EINVAL); + } + } + if(!hw_setup){ + ether[0] = 0xfe; + ether[1] = 0xfd; + set_ether_mac(dev, ether); + } + return(0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/net_user.h linux.ac/arch/um/drivers/net_user.h --- linux.vanilla/arch/um/drivers/net_user.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/net_user.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,43 @@ +#ifndef __UM_NET_USER_H__ +#define __UM_NET_USER_H__ + +#define ETH_ADDR_LEN (6) +#define ETH_HEADER_ETHERTAP (16) +#define ETH_HEADER_OTHER (14) +#define ETH_MAX_PACKET (1500) + +#define UML_NET_VERSION (2) + +struct net_user_info { + void (*init)(void *, void *); + int (*open)(void *); + void (*close)(int, void *); + int (*set_mtu)(int mtu, void *); + void (*add_address)(unsigned char *, void *); + void (*delete_address)(unsigned char *, void *); + int max_packet; +}; + +extern void ether_user_init(void *data, void *dev); +extern void dev_ip_addr(void *d, char *buf, char *bin_buf); +extern void set_ether_mac(void *d, unsigned char *addr); +extern void iter_addresses(void *d, void (*cb)(unsigned char *, void *), + void *arg); + +extern void *get_output_buffer(int *len_out); +extern void free_output_buffer(void *buffer); + +extern int tap_open_common(void *dev, int hw_setup, char *gate_addr); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/radix.h linux.ac/arch/um/drivers/radix.h --- linux.vanilla/arch/um/drivers/radix.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/radix.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,143 @@ +#ifndef _RADIX_H_ +#define _RADIX_H_ + +#include + +#define RADIX_INIT(head) \ +{ \ + (head)->RdX_root = NULL; \ +} + +#define RADIX_ENTRY(type,bits) \ +struct { \ + struct type *RdX_next[(0x01 << bits)]; \ +} + +#define RADIX_HEAD(name,type) \ +struct name { \ + struct type *RdX_root; \ +}; + +#define RADIX_INSERT(head,type,field,bits,tdef,key,klen,result) \ +{ \ + struct type **RdX_node = &((head)->RdX_root); \ + tdef RdX_mask; \ + int RdX_index; \ + int RdX_size = sizeof(tdef)*8; \ + char RdX_status = 0; \ + int RdX_count = 0; \ + \ + if(klen % bits) result = -1; \ + else { \ + RdX_mask = (0x01 << bits)-1; \ + while((RdX_count*bits) < klen) { \ + RdX_status = 1; \ + if(!(*RdX_node)) { \ + RdX_status = 2; \ + (*RdX_node) = (struct type *)kmalloc(sizeof(struct type), \ + GFP_KERNEL); \ + if(!(*RdX_node)) { \ + result = -2; \ + break; \ + } \ + RdX_status = 3; \ + memset((*RdX_node),0,sizeof(struct type)); \ + RdX_status = 4; \ + } \ + RdX_index = (key >> (RdX_size - bits - (RdX_count * bits))) & RdX_mask; \ + RdX_status = 5; \ + RdX_node = &((*RdX_node)->field.RdX_next[RdX_index]); \ + RdX_status = 6; \ + RdX_count++; \ + result = 0; \ + } \ + if(result != -2 && (!(*RdX_node))) { \ + (*RdX_node) = (struct type *)kmalloc(sizeof(struct type), \ + GFP_KERNEL); \ + if(!(*RdX_node)) { \ + result = -2; \ + } else { \ + memset((*RdX_node),0,sizeof(struct type)); \ + } \ + } \ + } \ +} + +#define RADIX_DO(head,type,field,bits,tdef,key,klen,flm,elem,result,RxD_set) \ +{ \ + struct type *RdX_node = (head)->RdX_root; \ + tdef RdX_mask; \ + int RdX_index; \ + int RdX_size = sizeof(tdef)*8; \ + int RdX_count = 0; \ + \ + if(klen % bits) result = -1; \ + else { \ + RdX_mask = (0x01 << bits)-1; \ + while((RdX_count*bits) < klen) { \ + if(!RdX_node) { \ + result = -2; \ + break; \ + } else { \ + RdX_index = (key >> (RdX_size - bits - (RdX_count * bits))) & RdX_mask;\ + RdX_node = RdX_node->field.RdX_next[RdX_index]; \ + } \ + RdX_count++; \ + } \ + if(!RdX_node) { \ + result = -3; \ + } else { \ + if(RxD_set) { \ + RdX_node->flm = elem; \ + } else { \ + elem = RdX_node->flm; \ + } \ + result = 0; \ + } \ + } \ +} + +#define RADIX_SET(head,type,field,bits,tdef,key,klen,flm,elem,result) \ + RADIX_DO(head,type,field,bits,tdef,key,klen,flm,elem,result,1) +#define RADIX_GET(head,type,field,bits,tdef,key,klen,flm,elem,result) \ + RADIX_DO(head,type,field,bits,tdef,key,klen,flm,elem,result,0) + +#define RADIX_VISIT_ALL(head,type,field,bits,visit,extra) \ +{ \ + int RdX_node_count = (0x1 << bits); \ + struct type *RdX_stack[RdX_node_count]; \ + int RdX_stack_count[RdX_node_count]; \ + int RdX_stack_depth = 0; \ + struct type *RdX_node; \ + int RdX_done = 0; \ + int R = 0; \ + \ + if((RdX_node = (head)->RdX_root)) { \ + while(!RdX_done) { \ + if(RdX_node->field.RdX_next[R]) { \ + RdX_stack[RdX_stack_depth] = RdX_node; \ + RdX_stack_count[RdX_stack_depth] = R + 1; \ + RdX_stack_depth++; \ + RdX_node = RdX_node->field.RdX_next[R]; \ + R = 0; \ + } else { \ + R++; \ + if(R >= RdX_node_count) { \ + if(visit(RdX_node,extra)) { \ + RdX_done = 1; \ + break; \ + } \ + RdX_stack_depth--; \ + if(RdX_stack_depth >= 0) { \ + RdX_node = RdX_stack[RdX_stack_depth]; \ + R = RdX_stack_count[RdX_stack_depth]; \ + } else { \ + RdX_done = 1; \ + } \ + } \ + } \ + } \ + } \ +} + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/slip.h linux.ac/arch/um/drivers/slip.h --- linux.vanilla/arch/um/drivers/slip.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/slip.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,33 @@ +#ifndef __UM_SLIP_H +#define __UM_SLIP_H + +#define BUF_SIZE 1500 + +struct slip_data { + void *dev; + char *addr; + char *gate_addr; + int slave; + char buf[2 * BUF_SIZE]; + int pos; + int esc; +}; + +extern struct net_user_info slip_user_info; + +extern int set_umn_addr(int fd, char *addr, char *ptp_addr); +extern int slip_user_read(int fd, void *buf, int len, struct slip_data *pri); +extern int slip_user_write(int fd, void *buf, int len, struct slip_data *pri); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/slip_kern.c linux.ac/arch/um/drivers/slip_kern.c --- linux.vanilla/arch/um/drivers/slip_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/slip_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,105 @@ +#include "linux/stddef.h" +#include "linux/init.h" +#include "linux/netdevice.h" +#include "linux/if_arp.h" +#include "net_kern.h" +#include "net_user.h" +#include "kern.h" +#include "slip.h" +#include "slip_kern.h" + +struct slip_data slip_priv[MAX_UML_NETDEV] = { + [ 0 ... MAX_UML_NETDEV - 1 ] = + { + addr: NULL, + gate_addr: NULL, + slave: -1, + buf: { 0 }, + pos: 0, + esc: 0, + } +}; + +struct net_device umn_dev; + +struct net_device *slip_init(int private_size, int index) +{ + struct uml_net_private *private; + struct slip_data *spri; + + private = kmalloc(private_size, GFP_KERNEL); + if(private == NULL) return(NULL); + umn_dev.priv = private; + spri = (struct slip_data *) private->user; + *spri = slip_priv[index]; + strncpy(umn_dev.name, "umn", IFNAMSIZ); + umn_dev.init = NULL; + umn_dev.hard_header_len = 0; + umn_dev.addr_len = 4; + umn_dev.type = ARPHRD_ETHER; + umn_dev.tx_queue_len = 256; + umn_dev.flags = IFF_NOARP; + if(register_netdev(&umn_dev)) + printk("Couldn't initialize umn\n"); + printk("SLIP backend - SLIP IP = %s\n", spri->gate_addr); + + return(&umn_dev); +} + +static int slip_set_mac(struct sockaddr *hwaddr, void *data) +{ + return(0); +} + +static unsigned short slip_protocol(struct sk_buff *skbuff) +{ + return(htons(ETH_P_IP)); +} + +static int slip_read(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return(slip_user_read(fd, (*skb)->mac.raw, (*skb)->dev->mtu, + (struct slip_data *) &lp->user)); +} + +static int slip_write(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return(slip_user_write(fd, (*skb)->data, (*skb)->len, + (struct slip_data *) &lp->user)); +} + +struct net_kern_info slip_kern_info = { + init: slip_init, + protocol: slip_protocol, + set_mac: slip_set_mac, + read: slip_read, + write: slip_write, +}; + +static int slip_count = 0; + +void slip_setup(char *str, struct uml_net *dev) +{ + int n = slip_count; + + dev->user = &slip_user_info; + dev->kern = &slip_kern_info; + dev->private_size = sizeof(struct slip_data); + dev->transport_index = slip_count++; + if(*str != ',') return; + str++; + if(str[0] != '\0') slip_priv[n].gate_addr = str; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/slip_kern.h linux.ac/arch/um/drivers/slip_kern.h --- linux.vanilla/arch/um/drivers/slip_kern.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/slip_kern.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,8 @@ +#ifndef __UM_SLIP_KERN_H +#define __UM_SLIP_KERN_H + +#include "net_kern.h" + +extern void slip_setup(char *arg, struct uml_net *dev); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/slip_user.c linux.ac/arch/um/drivers/slip_user.c --- linux.vanilla/arch/um/drivers/slip_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/slip_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,284 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "net_user.h" +#include "slip.h" + +void slip_user_init(void *data, void *dev) +{ + struct slip_data *pri = data; + + pri->dev = dev; +} + +static int set_up_tty(int fd) +{ + int i; + struct termios tios; + + if (tcgetattr(fd, &tios) < 0) { + printk("could not get initial terminal attributes\n"); + return(-1); + } + + tios.c_cflag = CS8 | CREAD | HUPCL | CLOCAL; + tios.c_iflag = IGNBRK | IGNPAR; + tios.c_oflag = 0; + tios.c_lflag = 0; + for (i = 0; i < NCCS; i++) + tios.c_cc[i] = 0; + tios.c_cc[VMIN] = 1; + tios.c_cc[VTIME] = 0; + + cfsetospeed(&tios, B38400); + cfsetispeed(&tios, B38400); + + if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) { + printk("failed to set terminal attributes\n"); + return(-1); + } + return(0); +} + +struct slip_tramp_data { + char **args; + int err; +}; + +void slip_tramp(void *arg) +{ + struct slip_tramp_data *data = arg; + char **argv = data->args; + int status, pid; + + data->err = 0; + if((pid = fork()) == 0){ + execvp(argv[0], argv); + exit(errno); + } + else if(pid < 0) data->err = errno; + else { + if(waitpid(pid, &status, 0) < 0) data->err = errno; + else if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0)){ + printk("'%s' didn't exit with status 0\n", argv[0]); + data->err = EINVAL; + } + } +} + +static int slip_open(void *data) +{ + struct slip_data *pri = data; + struct slip_tramp_data slip_data; + char version_buf[sizeof("nnnnn\0")]; + char fd_buf[sizeof("nnnnnn\0")], addr_buf[sizeof("nnn.nnn.nnn.nnn\0")]; + char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")]; + char *argv[] = { "uml_net", version_buf, "slip", "up", fd_buf, + gate_buf, addr_buf, NULL }; + int sfd, mfd, disc, sencap; + + if((mfd = get_pty()) < 0){ + printk("umn : Failed to open pty\n"); + return(-1); + } + if((sfd = open(ptsname(mfd), O_RDWR)) < 0){ + printk("Couldn't open tty for slip line\n"); + return(-1); + } + if(set_up_tty(sfd)) return(-1); + pri->slave = sfd; + pri->pos = 0; + pri->esc = 0; + if(pri->gate_addr != NULL){ + sprintf(version_buf, "%d", UML_NET_VERSION); + sprintf(fd_buf, "%d", sfd); + dev_ip_addr(pri->dev, addr_buf, NULL); + strcpy(gate_buf, pri->gate_addr); + slip_data.args = argv; + tracing_cb(slip_tramp, &slip_data); + if(slip_data.err != 0){ + printk("slip_tramp failed - errno = %d\n", + slip_data.err); + return(-slip_data.err); + } + } + else { + disc = N_SLIP; + if(ioctl(sfd, TIOCSETD, &disc) < 0){ + printk("Failed to set slip line discipline - " + "errno = %d\n", errno); + return(-errno); + } + sencap = 0; + if(ioctl(sfd, SIOCSIFENCAP, &sencap) < 0){ + printk("Failed to sett slip encapsulation - " + "errno = %d\n", errno); + return(-errno); + } + } + return(mfd); +} + +static void slip_close(int fd, void *data) +{ + struct slip_data *pri = data; + struct slip_tramp_data slip_data; + char fd_buf[sizeof("nnnnnn\0")], addr_buf[sizeof("nnn.nnn.nnn.nnn\0")]; + char version_buf[sizeof("nnnnn\0")]; + char *argv[] = { "uml_net", "slip", version_buf, "down", fd_buf, + addr_buf, NULL }; + + sprintf(version_buf, "%d", UML_NET_VERSION); + sprintf(fd_buf, "%d", pri->slave); + dev_ip_addr(pri->dev, addr_buf, NULL); + slip_data.args = argv; + tracing_cb(slip_tramp, &slip_data); + if(slip_data.err != 0) + printk("slip_tramp failed - errno = %d\n", slip_data.err); + close(fd); + close(pri->slave); +} + +/* SLIP protocol characters. */ +#define END 0300 /* indicates end of frame */ +#define ESC 0333 /* indicates byte stuffing */ +#define ESC_END 0334 /* ESC ESC_END means END 'data' */ +#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ + +static int slip_unesc(struct slip_data *sl, unsigned char c) +{ + int ret; + + switch(c){ + case END: + sl->esc = 0; + ret = sl->pos; + sl->pos = 0; + return(ret); + case ESC: + sl->esc = 1; + return(0); + case ESC_ESC: + if(sl->esc){ + sl->esc = 0; + c = ESC; + } + break; + case ESC_END: + if(sl->esc){ + sl->esc = 0; + c = END; + } + break; + } + sl->buf[sl->pos++] = c; + return(0); +} + +int slip_user_read(int fd, void *buf, int len, struct slip_data *pri) +{ + int i, n, size, start; + + n = read(fd, &pri->buf[pri->pos], sizeof(pri->buf) - pri->pos); + if(n < 0){ + if(errno == EAGAIN) return(0); + return(-errno); + } + else { + start = pri->pos; + for(i = 0; i < n; i++){ + size = slip_unesc(pri, pri->buf[start + i]); + if(size){ + memcpy(buf, pri->buf, size); + return(size); + } + } + } + return(0); +} + +static int slip_esc(unsigned char *s, unsigned char *d, int len) +{ + unsigned char *ptr = d; + unsigned char c; + + /* + * Send an initial END character to flush out any + * data that may have accumulated in the receiver + * due to line noise. + */ + + *ptr++ = END; + + /* + * For each byte in the packet, send the appropriate + * character sequence, according to the SLIP protocol. + */ + + while (len-- > 0) { + switch(c = *s++) { + case END: + *ptr++ = ESC; + *ptr++ = ESC_END; + break; + case ESC: + *ptr++ = ESC; + *ptr++ = ESC_ESC; + break; + default: + *ptr++ = c; + break; + } + } + *ptr++ = END; + return (ptr - d); +} + +int slip_user_write(int fd, void *buf, int len, struct slip_data *pri) +{ + int actual, n; + + actual = slip_esc(buf, pri->buf, len); + while(((n = write(fd, pri->buf, actual)) < 0) && (errno == EINTR)) ; + if(n < 0) return(-errno); + else if(n == actual) return(len); + else return(len - 1); +} + +static int slip_set_mtu(int mtu, void *data) +{ + return(mtu); +} + +struct net_user_info slip_user_info = { + init: slip_user_init, + open: slip_open, + close: slip_close, + set_mtu: slip_set_mtu, + add_address: NULL, + delete_address: NULL, + max_packet: BUF_SIZE +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/ssl.c linux.ac/arch/um/drivers/ssl.c --- linux.vanilla/arch/um/drivers/ssl.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/ssl.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/fs.h" +#include "linux/tty.h" +#include "linux/tty_driver.h" +#include "linux/major.h" +#include "linux/mm.h" +#include "linux/init.h" +#include "asm/termbits.h" +#include "asm/irq.h" +#include "ssl.h" +#include "chan.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" + +static int ssl_version = 1; + +static struct tty_driver ssl_driver; + +static int ssl_refcount = 0; + +#define NR_PORTS 64 + +static struct tty_struct *ssl_table[NR_PORTS]; +static struct termios *ssl_termios[NR_PORTS]; +static struct termios *ssl_termios_locked[NR_PORTS]; + +static struct ssl { + struct io_chan chan; + int count; + struct tty_struct *tty; +} private[NR_PORTS] = { [0 ... NR_PORTS - 1] = + { PTY_IO_CHAN_INIT(NULL, 0, 1, INIT_STATIC), + 0, NULL } }; + +static void ssl_interrupt(int irq, void *dev, struct pt_regs *unused) +{ + struct ssl *line = dev; + + tty_interrupt(&line->chan, line->tty, line->count); +} + +static int setup_ssl_irq(int fd, void *data) +{ + return(um_request_irq(SSL_IRQ, fd, ssl_interrupt, + SA_INTERRUPT | SA_SHIRQ, "ssl", data)); +} + +void ssl_announce(char *dev_name, int dev) +{ + printk("Serial line %d assigned device '%s'\n", dev, dev_name); +} + +DECLARE_MUTEX(ssl_sem); + +int ssl_open(struct tty_struct *tty, struct file *filp) +{ + int line, err; + + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) + return -ENODEV; + down(&ssl_sem); + if(tty == NULL) panic("NULL tty in ssl_open"); + private[line].tty = tty; + tty->driver_data = &private[line]; + err = open_chan_pair(&private[line].chan, setup_ssl_irq, + &private[line]); + up(&ssl_sem); + if(err){ + printk("Couldn't open serial line %d - errno = %d\n", line, + -err); + up(&ssl_sem); + return(err); + } + private[line].count++; + up(&ssl_sem); + return(0); +} + +static void ssl_close(struct tty_struct *tty, struct file * filp) +{ + int line; + + line = MINOR(tty->device) - tty->driver.minor_start; + private[line].count--; + if(private[line].count == 0) private[line].tty = NULL; +} + +static int ssl_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int line; + + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) + panic("Bad tty in ssl_put_char"); + return(write_chan(&private[line].chan, buf, count)); +} + +static void ssl_put_char(struct tty_struct *tty, unsigned char ch) +{ + int line; + + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) + panic("Bad tty in ssl_put_char"); + write_chan(&private[line].chan, &ch, sizeof(ch)); +} + +static void ssl_flush_chars(struct tty_struct *tty) +{ + return; +} + +static int ssl_write_room(struct tty_struct *tty) +{ + return(16384); +} + +static int ssl_chars_in_buffer(struct tty_struct *tty) +{ + return(0); +} + +static void ssl_flush_buffer(struct tty_struct *tty) +{ + return; +} + +static int ssl_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int ret; + + ret = 0; + switch(cmd){ + case TCGETS: + case TCSETS: + case TCFLSH: + case TCSETSF: + case TCSETSW: + case TCGETA: + ret = -ENOIOCTLCMD; + break; + default: + printk("Unimplemented ioctl in ssl_ioctl : 0x%x\n", cmd); + ret = -ENOIOCTLCMD; + break; + } + return(ret); +} + +static void ssl_throttle(struct tty_struct * tty) +{ + printk("Someone should implement ssl_throttle\n"); +} + +static void ssl_unthrottle(struct tty_struct * tty) +{ + printk("Someone should implement ssl_unthrottle\n"); +} + +static void ssl_set_termios(struct tty_struct *tty, + struct termios *old_termios) +{ +} + +static void ssl_stop(struct tty_struct *tty) +{ + printk("Someone should implement ssl_stop\n"); +} + +static void ssl_start(struct tty_struct *tty) +{ + printk("Someone should implement ssl_start\n"); +} + +void ssl_hangup(struct tty_struct *tty) +{ +} + +extern void tty_register_devfs (struct tty_driver *driver, unsigned int flags, + unsigned int minor); +extern void tty_unregister_devfs (struct tty_driver *driver, unsigned minor); + +int ssl_init(void) +{ + int i; + + for(i=0;ichan, term->tty, term->count); +} + +DECLARE_MUTEX(stdio_sem); + +static int setup_console_irq(int fd, void *data) +{ + return(um_request_irq(CONSOLE_IRQ, fd, console_interrupt, + SA_INTERRUPT | SA_SHIRQ, "console", data)); +} + +static int open_console(int line, struct tty_struct *tty) +{ + int err; + + down(&stdio_sem); + err = open_chan_pair(&vts[line].chan, setup_console_irq, &vts[line]); + if(err < 0){ + printk("Failed to open virtual console %d, errno = %d\n", + line, err); + up(&stdio_sem); + return(err); + } + vts[line].count++; + vts[line].tty = tty; + up(&stdio_sem); + return(0); +} + +static int con_open(struct tty_struct * tty, struct file * filp) +{ + int line, ret; + + line = MINOR(tty->device) - tty->driver.minor_start; + ret = open_console(line, tty); + update_console_size(CHAN_IN_FD(vts[line].chan), &tty->winsize.ws_row, + &tty->winsize.ws_col); + return(ret); +} + +static void con_close(struct tty_struct * tty, struct file * filp) +{ + int line; + + line = MINOR(tty->device) - tty->driver.minor_start; + vts[line].count--; +} + +static int con_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int line; + + if (in_interrupt() && tty->stopped) + return 0; + while (tty->stopped) { + schedule(); + } + line = MINOR(tty->device) - tty->driver.minor_start; + return(write_chan(&vts[line].chan, buf, count)); +} + +static int write_room(struct tty_struct * tty) +{ + return(1024); +} + +static void set_termios(struct tty_struct *tty, struct termios * old) +{ +} + +static int chars_in_buffer(struct tty_struct *tty) +{ + return(0); +} + +extern void tty_register_devfs(struct tty_driver *driver, unsigned int flags, + unsigned int minor); +extern void tty_unregister_devfs (struct tty_driver *driver, unsigned minor); + +int stdio_init(void) +{ + int i; + + for(i=0;iindex].chan)); + write_chan(&vts[console->index].chan, string, len); + raw(CHAN_OUT_FD(vts[console->index].chan), 0); +} + +static kdev_t console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, c->index); +} + +static int console_setup(struct console *co, char *options) +{ + return(0); +} + +static struct console stdiocons = { + "tty", + console_write, + NULL, + console_device, + NULL, + NULL, + console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + +void stdio_console_init(void) +{ + save_console_flags(); + register_console(&stdiocons); +} + +void stdio_announce(char *dev_name, int dev) +{ + printk("Virtual console %d assigned device '%s'\n", dev, dev_name); +} + +static struct chan_opts opts = { + announce: stdio_announce, + dev: -1, + xterm_title: "Virtual Console #%d", + raw_pty: 1 +}; + +static int console_chan_setup(char *str) +{ + int i, n; + char *end; + + if(*str == '=') n = -1; + else { + n = simple_strtoul(str, &end, 0); + if(*end != '='){ + printk("console_chan_setup failed to parse \"%s\"\n", + str); + return(1); + } + str = end; + } + str++; + if(n == -1){ + for(i=0;i +#include +#include +#include +#include +#include +#include +#include +#include "stdio_console.h" +#include "user_util.h" +#include "kern_util.h" +#include "user.h" + +struct termios cooked_tt; + +void save_console_flags(void) +{ + if(isatty(0)) tcgetattr(0, &cooked_tt); + else if(isatty(1)) tcgetattr(1, &cooked_tt); + else tcgetattr(2, &cooked_tt); +} + +void cooked(int fd) +{ + tcsetattr(fd, TCSADRAIN, &cooked_tt); +} + +void update_console_size(int fd, unsigned short *rows_out, + unsigned short *cols_out) +{ + struct winsize size; + + if(ioctl(fd, TIOCGWINSZ, &size) == 0){ + *rows_out = size.ws_row; + *cols_out = size.ws_col; + } +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/tuntap.h linux.ac/arch/um/drivers/tuntap.h --- linux.vanilla/arch/um/drivers/tuntap.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/tuntap.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UM_TUNTAP_H +#define __UM_TUNTAP_H + +#include "net_user.h" + +struct tuntap_data { + char *dev_name; + char *gate_addr; + int fd; + void *dev; + unsigned char hw_addr[ETH_ADDR_LEN]; + int hw_setup; +}; + +extern struct net_user_info tuntap_user_info; + +extern int tuntap_user_write(int fd, void *buf, int len, + struct tuntap_data *pri); +extern int tuntap_user_read(int fd, void *buf, int len, + struct tuntap_data *pri); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/tuntap_kern.c linux.ac/arch/um/drivers/tuntap_kern.c --- linux.vanilla/arch/um/drivers/tuntap_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/tuntap_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/stddef.h" +#include "linux/netdevice.h" +#include "linux/etherdevice.h" +#include "linux/skbuff.h" +#include "asm/errno.h" +#include "net_kern.h" +#include "net_user.h" +#include "tuntap.h" + +struct tuntap_setup { + char *dev_name; + unsigned char hw_addr[ETH_ALEN]; + int hw_setup; + char *gate_addr; +}; + +struct tuntap_setup tuntap_priv[MAX_UML_NETDEV] = { + [ 0 ... MAX_UML_NETDEV - 1 ] = + { + dev_name: NULL, + hw_addr: { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, + hw_setup: 0, + gate_addr: NULL, + } +}; + +struct net_device *tuntap_init(int private_size, int index) +{ + struct net_device *dev; + struct uml_net_private *pri; + struct tuntap_data *tpri; + + dev = init_etherdev(NULL, private_size); + if(dev == NULL) return(NULL); + pri = dev->priv; + tpri = (struct tuntap_data *) pri->user; + tpri->dev_name = tuntap_priv[index].dev_name; + tpri->gate_addr = tuntap_priv[index].gate_addr; + memcpy(dev->dev_addr, tuntap_priv[index].hw_addr, ETH_ALEN); + memcpy(tpri->hw_addr, tuntap_priv[index].hw_addr, + sizeof(tpri->hw_addr)); + printk("TUN/TAP backend - "); + if(tpri->gate_addr != NULL) + printk("IP = %s", tpri->gate_addr); + tpri->hw_setup = tuntap_priv[index].hw_setup; + if(tpri->hw_setup) + printk(" ether = %x:%x:%x:%x:%x:%x", + tpri->hw_addr[0], tpri->hw_addr[1], tpri->hw_addr[2], + tpri->hw_addr[3], tpri->hw_addr[4], tpri->hw_addr[5]); + printk("\n"); + tpri->fd = -1; + return(dev); +} + +static unsigned short tuntap_protocol(struct sk_buff *skb) +{ + return(eth_type_trans(skb, skb->dev)); +} + +static int tuntap_set_mac(struct sockaddr *addr, void *data) +{ + struct tuntap_data *pri = data; + struct sockaddr *hwaddr = addr; + + memcpy(pri->hw_addr, hwaddr->sa_data, ETH_ALEN); + + return 0; +} + +static int tuntap_read(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + int len; + + *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); + if(*skb == NULL) return(-ENOMEM); + len = tuntap_user_read(fd, (*skb)->mac.raw, + (*skb)->dev->mtu + ETH_HEADER_OTHER, + (struct tuntap_data *) &lp->user); + if(len <= 0) return(len); + return(len); +} + +static int tuntap_write(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return(tuntap_user_write(fd, (*skb)->data, (*skb)->len, + (struct tuntap_data *) &lp->user)); +} + +struct net_kern_info tuntap_kern_info = { + init: tuntap_init, + protocol: tuntap_protocol, + set_mac: tuntap_set_mac, + read: tuntap_read, + write: tuntap_write, +}; + +static int tuntap_count = 0; + +void tuntap_setup(char *str, struct uml_net *dev) +{ + struct tuntap_setup *pri; + + dev->user = &tuntap_user_info; + dev->kern = &tuntap_kern_info; + dev->user = &tuntap_user_info; + dev->kern = &tuntap_kern_info; + dev->private_size = sizeof(struct tuntap_data); + pri = &tuntap_priv[tuntap_count]; + dev->transport_index = tuntap_count++; + tap_setup_common(str, "tuntap", &pri->dev_name, pri->hw_addr, + &pri->hw_setup, &pri->gate_addr); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/tuntap_kern.h linux.ac/arch/um/drivers/tuntap_kern.h --- linux.vanilla/arch/um/drivers/tuntap_kern.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/tuntap_kern.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UM_TUNTAP_KERN_H +#define __UM_TUNTAP_KERN_H + +#include "net_kern.h" + +extern void tuntap_setup(char *arg, struct uml_net *dev); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/tuntap_user.c linux.ac/arch/um/drivers/tuntap_user.c --- linux.vanilla/arch/um/drivers/tuntap_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/tuntap_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "net_user.h" +#include "tuntap.h" +#include "user.h" +#include "kern_util.h" + +#define MAX_PACKET ETH_MAX_PACKET + +void tuntap_user_init(void *data, void *dev) +{ + struct tuntap_data *pri = data; + + pri->dev = dev; +} + +struct tuntap_open_data { + char *name; + char *gate; + int data_fd; + int remote; + int me; + int err; + char *buffer; + int len; + int used; +}; + +static void tuntap_open_tramp(void *arg) +{ + struct tuntap_open_data *data = arg; + char version_buf[sizeof("nnnnn\0")]; + char fd_buf[sizeof("nnnnnn\0")]; + char *args[] = { "uml_net", version_buf, "tuntap", "up", "", fd_buf, + data->gate, NULL }; + char buf[CMSG_SPACE(sizeof(data->data_fd))]; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + int pid, n; + + sprintf(version_buf, "%d", UML_NET_VERSION); + sprintf(fd_buf, "%d", data->remote); + data->err = 0; + if((pid = fork()) == 0){ + close(data->me); + execvp(args[0], args); + printk("Exec of '%s' failed - errno = %d\n", args[0], errno); + exit(1); + } + else if(pid < 0) data->err = errno; + close(data->remote); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + if(data->buffer != NULL){ + iov = ((struct iovec) { data->buffer, data->len }); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + } + else { + msg.msg_iov = NULL; + msg.msg_iovlen = 0; + } + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + msg.msg_flags = 0; + n = recvmsg(data->me, &msg, 0); + data->used = n; + if(n < 0){ + printk("tuntap_open_tramp : recvmsg failed - errno = %d\n", + errno); + data->err = errno; + return; + } + waitpid(pid, NULL, 0); + + cmsg = CMSG_FIRSTHDR(&msg); + if(cmsg == NULL){ + printk("tuntap_open_tramp : didn't receive a message\n"); + data->err = EINVAL; + return; + } + if((cmsg->cmsg_level != SOL_SOCKET) || + (cmsg->cmsg_type != SCM_RIGHTS)){ + printk("tuntap_open_tramp : didn't receive a descriptor\n"); + data->err = EINVAL; + return; + } + data->data_fd = ((int *) CMSG_DATA(cmsg))[0]; +} + +struct tuntap_change_data { + char *dev; + char *what; + char *address; +}; + +static void tuntap_change_tramp(void *arg) +{ + int pid; + struct tuntap_change_data *data = arg; + char version[sizeof("nnnnn\0")]; + char *argv[] = { "uml_net", version, "tuntap", data->what, data->dev, + data->address, NULL }; + + sprintf(version, "%d", UML_NET_VERSION); + if((pid = fork()) == 0){ + execvp(argv[0], argv); + printk("Exec of '%s' failed - errno = %d\n", argv[0], errno); + exit(1); + } + waitpid(pid, NULL, 0); +} + +static void tuntap_change(char *dev, char *what, unsigned char *addr) +{ + char addr_buf[sizeof("255.255.255.255\0")]; + struct tuntap_change_data data; + + data.dev = dev; + data.what = what; + sprintf(addr_buf, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); + data.address = addr_buf; + tracing_cb(tuntap_change_tramp, &data); +} + +static void tuntap_open_addr(unsigned char *addr, void *arg) +{ + char *dev = arg; + + tuntap_change(dev, "add", addr); +} + +static void tuntap_close_addr(unsigned char *addr, void *arg) +{ + char *dev = arg; + + tuntap_change(dev, "del", addr); +} + +static void tuntap_add_addr(unsigned char *addr, void *data) +{ + struct tuntap_data *pri = data; + + if(pri->fd == -1) return; + tuntap_open_addr(addr, pri->dev_name); +} + +static void tuntap_del_addr(unsigned char *addr, void *data) +{ + struct tuntap_data *pri = data; + + if(pri->fd == -1) return; + tuntap_close_addr(addr, pri->dev_name); +} + +static int tuntap_open(void *data) +{ + struct tuntap_data *pri = data; + struct tuntap_open_data tap_data; + int err, fds[2]; + + err = tap_open_common(pri->dev, pri->hw_setup, pri->gate_addr); + if(err) return(err); + + if(socketpair(PF_UNIX, SOCK_DGRAM, 0, fds) < 0){ + printk("data socketpair failed - errno = %d\n", errno); + return(-errno); + } + + tap_data.me = fds[0]; + tap_data.remote = fds[1]; + tap_data.data_fd = -1; + tap_data.gate = pri->gate_addr; + tap_data.buffer = get_output_buffer(&tap_data.len); + if(tap_data.buffer != NULL) tap_data.len--; + tap_data.used = 0; + + tracing_cb(tuntap_open_tramp, &tap_data); + if(tap_data.buffer != NULL){ + pri->dev_name = uml_strdup(tap_data.buffer); + tap_data.buffer[tap_data.used] = '\0'; + printk(tap_data.buffer + IFNAMSIZ); + free_output_buffer(tap_data.buffer); + } + if(tap_data.err != 0){ + printk("tuntap_open_tramp failed - errno = %d\n", + tap_data.err); + return(-tap_data.err); + } + close(fds[0]); + + pri->fd = tap_data.data_fd; + iter_addresses(pri->dev, tuntap_open_addr, pri->dev_name); + return(tap_data.data_fd); +} + +static void tuntap_close(int fd, void *data) +{ + struct tuntap_data *pri = data; + + iter_addresses(pri->dev, tuntap_close_addr, pri->dev_name); + close(fd); +} + +int tuntap_user_read(int fd, void *buf, int len, struct tuntap_data *pri) +{ + int n; + + while(((n = read(fd, buf, len)) < 0) && (errno == EINTR)) ; + if(errno == EAGAIN) return(0); + if(n < 0) return(-errno); + return(n); +} + +int tuntap_user_write(int fd, void *buf, int len, struct tuntap_data *pri) +{ + int n; + + while(((n = write(fd, buf, len)) < 0) && (errno == EINTR)) ; + if(errno == EAGAIN) n = 0; + if(n < 0) return(-errno); + return(n); +} + +static int tuntap_set_mtu(int mtu, void *data) +{ + return(mtu); +} + +struct net_user_info tuntap_user_info = { + init: tuntap_user_init, + open: tuntap_open, + close: tuntap_close, + set_mtu: tuntap_set_mtu, + add_address: tuntap_add_addr, + delete_address: tuntap_del_addr, + max_packet: MAX_PACKET +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/ubd.c linux.ac/arch/um/drivers/ubd.c --- linux.vanilla/arch/um/drivers/ubd.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/ubd.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,794 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "ubd_user.h" +#define MAJOR_NR UBD_MAJOR +#include "linux/config.h" +#include "linux/blk.h" +#include "linux/blkdev.h" +#include "linux/hdreg.h" +#include "linux/init.h" +#include "linux/devfs_fs_kernel.h" +#include "linux/cdrom.h" +#include "linux/proc_fs.h" +#include "linux/ctype.h" +#include "linux/capability.h" +#include "linux/mm.h" +#include "linux/vmalloc.h" +#include "asm/segment.h" +#include "asm/uaccess.h" +#include "asm/irq.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "mconsole_kern.h" + +static int ubd_open(struct inode * inode, struct file * filp); +static int ubd_release(struct inode * inode, struct file * file); +static int ubd_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg); + +#define MAX_DEV (8) + +static int blk_sizes[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = BLOCK_SIZE }; + +static int hardsect_sizes[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = BLOCK_SIZE }; + +static int sizes[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = 0 }; + +static struct block_device_operations ubd_blops = { + open: ubd_open, + release: ubd_release, + ioctl: ubd_ioctl, +}; + +static struct hd_struct ubd_part[MAX_DEV] = +{ [ 0 ... MAX_DEV - 1 ] = { 0, 0, 0 } }; + +static int fake_major = 0; + +static struct gendisk ubd_gendisk = { + MAJOR_NR, /* Major number */ + "ubd", /* Major name */ + 0, /* Bits to shift to get real from partition */ + 1, /* Number of partitions per real */ + ubd_part, /* hd struct */ + sizes, /* block sizes */ + MAX_DEV, /* number */ + NULL, /* internal */ + NULL, /* next */ + &ubd_blops, /* file operations */ +}; + +static struct gendisk fake_gendisk = { + 0, /* Major number */ + "ubd", /* Major name */ + 0, /* Bits to shift to get real from partition */ + 1, /* Number of partitions per real */ + ubd_part, /* hd struct */ + sizes, /* block sizes */ + MAX_DEV, /* number */ + NULL, /* internal */ + NULL, /* next */ + &ubd_blops, /* file operations */ +}; + +#ifdef CONFIG_BLK_DEV_UBD_SYNC +#define OPEN_FLAGS O_RDWR | O_SYNC +#else +#define OPEN_FLAGS O_RDWR +#endif + +struct cow { + char *file; + int fd; + unsigned long *bitmap; + int bitmap_offset; + int data_offset; +}; + +struct ubd { + char *file; + int is_dir; + int count; + int fd; + __u64 size; + int boot_openflags; + int openflags; + devfs_handle_t real; + devfs_handle_t fake; + struct cow cow; +}; + +#define DEFAULT_COW { \ + file: NULL, \ + fd: -1, \ + bitmap: NULL, \ + bitmap_offset: 0, \ + data_offset: 0, \ +} + +#define DEFAULT_UBD { \ + file: NULL, \ + is_dir: 0, \ + count: 0, \ + fd: -1, \ + size: -1, \ + boot_openflags: OPEN_FLAGS, \ + openflags: OPEN_FLAGS, \ + real: NULL, \ + fake: NULL, \ + cow: DEFAULT_COW, \ +} + +struct ubd ubd_dev[MAX_DEV] = { +{ + file: "root_fs", + is_dir: 0, + count: 0, + fd: -1, + size: 0, + boot_openflags: OPEN_FLAGS, + openflags: OPEN_FLAGS, + real: NULL, + fake: NULL, + cow: DEFAULT_COW, +}, +[ 1 ... MAX_DEV - 1 ] = DEFAULT_UBD +}; + +static struct hd_driveid ubd_id = { + cyls: 0, + heads: 128, + sectors: 32, +}; + +static int fake_ide = 0; +static struct proc_dir_entry *proc_ide_root = NULL; +static struct proc_dir_entry *proc_ide = NULL; + +static void make_proc_ide(void) +{ + proc_ide_root = proc_mkdir("ide", 0); + proc_ide = proc_mkdir("ide0", proc_ide_root); +} + +static int proc_ide_read_media(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int len; + + strcpy(page, "disk\n"); + len = strlen("disk\n"); + len -= off; + if (len < count){ + *eof = 1; + if (len <= 0) return 0; + } + else len = count; + *start = page + off; + return len; + +} + +static void make_ide_entries(char *dev_name) +{ + struct proc_dir_entry *dir, *ent; + char name[64]; + + if(!fake_ide) return; + if(proc_ide_root == NULL) make_proc_ide(); + dir = proc_mkdir(dev_name, proc_ide); + ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir); + if(!ent) return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_ide_read_media; + ent->write_proc = NULL; + sprintf(name,"ide0/%s", dev_name); + proc_symlink(dev_name, proc_ide_root, name); +} + +static int fake_ide_setup(char *str) +{ + fake_ide = 1; + return(1); +} + +__setup("fake_ide", fake_ide_setup); + +static int ubd_setup_common(char *str, int *index_out) +{ + char *backing_file; + int n, sync, perm = O_RDWR; + + if(index_out) *index_out = -1; + n = *str++; + if(n == '='){ + char *end; + int major; + + if(!strcmp(str, "sync")){ + sync = 1; + return(0); + } + major = simple_strtoul(str, &end, 0); + if(*end != '\0'){ + printk("ubd_setup : didn't parse major number\n"); + return(1); + } + fake_gendisk.major = major; + fake_major = major; + printk("Setting extra ubd major number to %d\n", major); + return(0); + } + if(n < '0'){ + printk("ubd_setup : index out of range\n"); + return(1); + } + n -= '0'; + if(n >= MAX_DEV){ + printk("ubd_setup : index out of range\n"); + return(1); + } + if(index_out) *index_out = n; + sync = ubd_dev[n].boot_openflags & O_SYNC; + if (*str == 'r') { + perm = O_RDONLY; + str++; + } + if (*str == 's') { + sync = O_SYNC; + str++; + } + if(*str++ != '='){ + printk("ubd_setup : Expected '='\n"); + return(1); + } + backing_file = strchr(str, ','); + if(backing_file){ + *backing_file = '\0'; + backing_file++; + } + ubd_dev[n].file = str; + ubd_dev[n].cow.file = backing_file; + ubd_dev[n].boot_openflags = perm | sync; + return(0); +} + +static int ubd_setup(char *str) +{ + ubd_setup_common(str, NULL); + return(1); +} + +__setup("ubd", ubd_setup); + +static int fakehd(char *str) +{ + printk("fakehd : Changing ubd_gendisk.major_name to \"hd\".\n"); + ubd_gendisk.major_name = "hd"; + return(1); +} + +__setup("fakehd", fakehd); + +static void do_ubd_request(request_queue_t * q); + +int thread_fds[2] = { -1, -1 }; + +int intr_count = 0; + +extern int errno; + +#ifdef CONFIG_SMP +#error end_request needs some locking +#endif + +static void ubd_finish(int error) +{ + int nsect; + + if(error){ + end_request(0); + return; + } + nsect = CURRENT->current_nr_sectors; + CURRENT->sector += nsect; + CURRENT->buffer += nsect << 9; + CURRENT->errors = 0; + CURRENT->nr_sectors -= nsect; + CURRENT->current_nr_sectors = 0; + end_request(1); +} + +static void ubd_handler(void) +{ + struct io_thread_req req; + + DEVICE_INTR = NULL; + intr_count++; + if(read_ubd_fs(thread_fds[0], &req, sizeof(req)) != sizeof(req)){ + printk("Pid %d - spurious interrupt in ubd_handler, " + "errno = %d\n", getpid(), errno); + end_request(0); + return; + } + + if((req.offset != ((__u64) (CURRENT->sector)) << 9) || + (req.length != (CURRENT->current_nr_sectors) << 9)) + panic("I/O op mismatch"); + + ubd_finish(req.error); + reactivate_fd(thread_fds[0]); + if (!QUEUE_EMPTY) do_ubd_request(NULL); +} + +static void ubd_intr(int irq, void *dev, struct pt_regs *unused) +{ + ubd_handler(); +} + +static int io_pid = -1; + +void kill_io_thread(void) +{ + if(io_pid != -1) kill(io_pid, SIGKILL); +} + +__exitcall(kill_io_thread); + +int sync = 0; + +devfs_handle_t ubd_dir_handle; + +static int ubd_add(int n) +{ + char name[sizeof("nnnnnn\0")], dev_name[sizeof("ubd0x")]; + + if(ubd_dev[n].file == NULL) return(-1); + sprintf(name, "%d", n); + ubd_dev[n].real = devfs_register (ubd_dir_handle, name, + DEVFS_FL_DEFAULT, MAJOR_NR, n, + S_IFBLK | S_IRUSR | S_IWUSR | + S_IRGRP |S_IWGRP, + &ubd_blops, NULL); + if(fake_major != 0){ + ubd_dev[n].fake = devfs_register (ubd_dir_handle, name, + DEVFS_FL_DEFAULT, fake_major, + n, S_IFBLK | S_IRUSR | + S_IWUSR | S_IRGRP | S_IWGRP, + &ubd_blops, NULL); + } + if(!strcmp(ubd_gendisk.major_name, "ubd")){ + sprintf(dev_name, "%s%d", ubd_gendisk.major_name, n); + } + else sprintf(dev_name, "%s%c", ubd_gendisk.major_name, + n + 'a'); + make_ide_entries(dev_name); + return(0); +} + +static int ubd_config(char *str) +{ + int n, err; + + str = uml_strdup(str); + if(str == NULL){ + printk(KERN_ERR "ubd_config failed to strdup string\n"); + return(1); + } + err = ubd_setup_common(str, &n); + if(err){ + kfree(str); + return(-1); + } + if(n != -1) ubd_add(n); + return(0); +} + +static int ubd_remove(char *str) +{ + int n; + + if(!isdigit(*str)) return(-1); + n = *str - '0'; + if(ubd_dev[n].file == NULL) return(0); + if(ubd_dev[n].count > 0) return(-1); + if(ubd_dev[n].real != NULL) devfs_unregister(ubd_dev[n].real); + if(ubd_dev[n].fake != NULL) devfs_unregister(ubd_dev[n].fake); + ubd_dev[n] = ((struct ubd) DEFAULT_UBD); + return(0); +} + +static struct mc_device ubd_mc = { + name: "ubd", + config: ubd_config, + remove: ubd_remove, +}; + +int ubd_mc_init(void) +{ + mconsole_register_dev(&ubd_mc); + return(0); +} + +__initcall(ubd_mc_init); + +int ubd_init(void) +{ + unsigned long stack; + int i, err; + request_queue_t *q; + + ubd_dir_handle = devfs_mk_dir (NULL, "ubd", NULL); + if (devfs_register_blkdev(MAJOR_NR, "ubd", &ubd_blops)) { + printk("ubd: unable to get major %d\n", MAJOR_NR); + return -1; + } + q = BLK_DEFAULT_QUEUE(MAJOR_NR); + blk_init_queue(q, DEVICE_REQUEST); + elevator_init(&q->elevator, ELEVATOR_NOOP); + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ + blksize_size[MAJOR_NR] = blk_sizes; + blk_size[MAJOR_NR] = sizes; + hardsect_size[MAJOR_NR] = hardsect_sizes; + + add_gendisk(&ubd_gendisk); + + if (fake_major != 0){ + if(devfs_register_blkdev(fake_major, "ubd", &ubd_blops)) { + printk("ubd: unable to get major %d\n", fake_major); + return -1; + } + blk_init_queue(BLK_DEFAULT_QUEUE(fake_major), DEVICE_REQUEST); + read_ahead[fake_major] = 8; /* 8 sector (4kB) read-ahead */ + blksize_size[fake_major] = blk_sizes; + hardsect_size[fake_major] = hardsect_sizes; + + add_gendisk(&fake_gendisk); + } + for(i=0;ifd); + if(dev->cow.file != NULL) { + close_fd(dev->cow.fd); + vfree(dev->cow.bitmap); + dev->cow.bitmap = NULL; + } +} + +static int ubd_open_dev(struct ubd *dev) +{ + char *backing_file; + int err, flags, n, bitmap_len; + + dev->fd = open_ubd_file(dev->file, &dev->openflags, &backing_file, + &dev->cow.bitmap_offset, &bitmap_len, + &dev->cow.data_offset); + + if((dev->fd == -ENOENT) && (dev->cow.file != NULL)){ + printk(KERN_INFO "Creating \"%s\" as COW file for \"%s\"\n", + dev->file, dev->cow.file); + n = dev - ubd_dev; + dev->fd = create_cow_file(dev->file, dev->cow.file, 1 << 9, + &dev->cow.bitmap_offset, &bitmap_len, + &dev->cow.data_offset); + if(dev->fd < 0){ + printk(KERN_ERR "Creation of COW file \"%s\" failed, " + "errno = %d\n", dev->file, -dev->fd); + } + } + + if(dev->fd < 0) return(dev->fd); + + if(dev->cow.file == NULL) dev->cow.file = backing_file; + if((backing_file != NULL) && strcmp(dev->cow.file, backing_file)){ + printk(KERN_WARNING "Backing file mismatch - \"%s\" " + "requested, \"%s\" specified in COW header of \"%s\"\n", + dev->cow.file, backing_file, dev->file); + printk(KERN_WARNING "Using \"%s\"\n", backing_file); + dev->cow.file = backing_file; + } + + if(dev->cow.file != NULL){ + err = -ENOMEM; + dev->cow.bitmap = (void *) vmalloc(bitmap_len); + flush_tlb_kernel_vm(); + if(dev->cow.bitmap == NULL) goto error; + + err = read_cow_bitmap(dev->fd, dev->cow.bitmap, + dev->cow.bitmap_offset, bitmap_len); + if(err) goto error; + + flags = O_RDONLY; + err = open_ubd_file(dev->cow.file, &flags, NULL, NULL, NULL, + NULL); + if(err < 0) goto error; + dev->cow.fd = err; + } + return(0); + error: + close_fd(dev->fd); + return(err); +} + +static int ubd_open(struct inode * inode, struct file * filp) +{ + char *file; + int n; + + n = DEVICE_NR(inode->i_rdev); + if(n > MAX_DEV) + return -ENODEV; + if(ubd_is_dir(ubd_dev[n].file)){ + ubd_dev[n].is_dir = 1; + return(0); + } + if(ubd_dev[n].count == 0){ + ubd_dev[n].openflags = ubd_dev[n].boot_openflags; + /* XXX This error is wrong when errno isn't stored in + * ubd_dev[n].fd + */ + if(ubd_open_dev(&ubd_dev[n]) < 0){ + printk(KERN_ERR "ubd%d: Can't open \"%s\": " + "errno = %d\n", n, ubd_dev[n].file, + -ubd_dev[n].fd); + } + if(ubd_dev[n].fd < 0) + return -ENODEV; + file = ubd_dev[n].cow.file ? ubd_dev[n].cow.file : + ubd_dev[n].file; + ubd_dev[n].size = file_size(file); + if(ubd_dev[n].size < 0) return(ubd_dev[n].size); + ubd_part[n].start_sect = 0; + ubd_part[n].nr_sects = ubd_dev[n].size / blk_sizes[n]; + sizes[n] = ubd_dev[n].size / BLOCK_SIZE; + } + ubd_dev[n].count++; + if ((filp->f_mode & FMODE_WRITE) && + ((ubd_dev[n].openflags & ~O_SYNC) == O_RDONLY)){ + if(--ubd_dev[n].count == 0) ubd_close(&ubd_dev[n]); + return -EROFS; + } + return(0); +} + +static int ubd_release(struct inode * inode, struct file * file) +{ + int n; + + n = DEVICE_NR(inode->i_rdev); + if(n > MAX_DEV) + return -ENODEV; + if(--ubd_dev[n].count == 0) ubd_close(&ubd_dev[n]); + return(0); +} + +int cow_read = 0; +int cow_write = 0; + +void cowify_req(struct io_thread_req *req, struct ubd *dev) +{ + int i, update_bitmap, sector = req->offset >> 9; + + if(req->length > (sizeof(req->sector_mask) * 8) << 9) + panic("Operation too long"); + if(req->read) { + for(i = 0; i < req->length >> 9; i++){ + if(ubd_test_bit(sector + i, dev->cow.bitmap)){ + ubd_set_bit(i, &req->sector_mask); + cow_read++; + } + } + } + else { + update_bitmap = 0; + for(i = 0; i < req->length >> 9; i++){ + cow_write++; + ubd_set_bit(i, &req->sector_mask); + if(!ubd_test_bit(sector + i, dev->cow.bitmap)) + update_bitmap = 1; + ubd_set_bit(sector + i, dev->cow.bitmap); + } + if(update_bitmap){ + req->bitmap_start = sector / (sizeof(long) * 8); + req->bitmap_end = (sector + i) / (sizeof(long) * 8); + req->bitmap_end++; + req->bitmap_offset = dev->cow.bitmap_offset; + } + } +} + +static void do_one_request(void) +{ + struct io_thread_req req; + int block, nsect, dev, n, err; + + if(CURRENT->rq_status == RQ_INACTIVE) return; + if (DEVICE_INTR) return; + INIT_REQUEST; + block = CURRENT->sector; + nsect = CURRENT->current_nr_sectors; + dev = MINOR(CURRENT->rq_dev); + if(ubd_dev[dev].is_dir){ + strcpy(CURRENT->buffer, "HOSTFS:"); + strcat(CURRENT->buffer, ubd_dev[dev].file); + end_request(1); + return; + } + if((CURRENT->cmd == WRITE) && + ((ubd_dev[dev].openflags & O_ACCMODE) == O_RDONLY)){ + printk("Write attempted on readonly ubd device %d\n", + dev); + end_request(0); + return; + } + + req.read = (CURRENT->cmd == READ); + req.fds[0] = (ubd_dev[dev].cow.file != NULL) ? + ubd_dev[dev].cow.fd : ubd_dev[dev].fd; + req.fds[1] = ubd_dev[dev].fd; + req.offsets[0] = 0; + req.offsets[1] = ubd_dev[dev].cow.data_offset; + req.offset = ((__u64) block) << 9; + req.length = nsect << 9; + req.buffer = CURRENT->buffer; + req.sectorsize = 1 << 9; + req.sector_mask = 0; + req.bitmap = ubd_dev[dev].cow.bitmap; + req.bitmap_offset = -1; + req.bitmap_start = -1; + req.bitmap_end = -1; + req.error = 0; + + if(ubd_dev[dev].cow.file != NULL) cowify_req(&req, &ubd_dev[dev]); + + if(thread_fds[0] == -1){ + err = do_io(&req); + ubd_finish(err); + } + else { + SET_INTR(ubd_handler); + n = write_ubd_fs(thread_fds[1], (char *) &req, sizeof(req)); + if(n != sizeof(req)) + printk("write to io thread failed - returned %d, " + "errno %d\n", n, errno); + } +} + +static void do_ubd_request(request_queue_t * q) +{ + if(thread_fds[0] == -1){ + while(!QUEUE_EMPTY) do_one_request(); + } + else do_one_request(); +} + +static int ubd_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct hd_geometry *loc = (struct hd_geometry *) arg; + int dev, err; + + if ((!inode) || !(inode->i_rdev)) + return -EINVAL; + dev = DEVICE_NR(inode->i_rdev); + if (dev > MAX_DEV) + return -EINVAL; + switch (cmd) { + struct hd_geometry g; + struct cdrom_volctrl volume; + case HDIO_GETGEO: + if (!loc) return -EINVAL; + g.heads = 128; + g.sectors = 32; + g.cylinders = ubd_dev[dev].size / (128 * 32); + g.start = 2; + return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0; + case BLKRASET: + if(!capable(CAP_SYS_ADMIN)) return -EACCES; + if(arg > 0xff) return -EINVAL; + read_ahead[MAJOR(inode->i_rdev)] = arg; + return 0; + case BLKRAGET: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + return 0; + case BLKGETSIZE: /* Return device size */ + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + put_user(ubd_dev[dev].size >> 9, (long *) arg); + return 0; + case BLKFLSBUF: + if(!capable(CAP_SYS_ADMIN)) return -EACCES; + return 0; + + case BLKRRPART: /* Re-read partition tables */ + return 0; /* revalidate_hddisk(inode->i_rdev, 1); */ + + case HDIO_SET_UNMASKINTR: + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if ((arg > 1) || (MINOR(inode->i_rdev) & 0x3F)) + return -EINVAL; + return 0; + + case HDIO_GET_UNMASKINTR: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + return 0; + + case HDIO_GET_MULTCOUNT: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + return 0; + + case HDIO_SET_MULTCOUNT: + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL; + return 0; + + case HDIO_GET_IDENTITY: + ubd_id.cyls = ubd_dev[dev].size / (128 * 32); + if (copy_to_user((char *) arg, (char *) &ubd_id, + sizeof(ubd_id))) + return -EFAULT; + return 0; + + case CDROMVOLREAD: + if(copy_from_user(&volume, (char *) arg, sizeof(volume))) + return -EFAULT; + volume.channel0 = 255; + volume.channel1 = 255; + volume.channel2 = 255; + volume.channel3 = 255; + if(copy_from_user((char *) arg, &volume, sizeof(volume))) + return -EFAULT; + return 0; + + default: + return -EINVAL; + } +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/ubd_user.c linux.ac/arch/um/drivers/ubd_user.c --- linux.vanilla/arch/um/drivers/ubd_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/ubd_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,475 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2001 Ridgerun,Inc (glonnon@ridgerun.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "ubd_user.h" + +#include +#include +#if __BYTE_ORDER == __BIG_ENDIAN +# define ntohll(x) (x) +# define htonll(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +# define ntohll(x) bswap_64(x) +# define htonll(x) bswap_64(x) +#else +#error "__BYTE_ORDER not defined" +#endif + +extern void panic(char *fmt, ...); + +#define PATH_LEN_V1 256 + +struct cow_header_v1 { + int magic; + int version; + char backing_file[PATH_LEN_V1]; + time_t mtime; + __u64 size; + int sectorsize; +}; + +#define PATH_LEN_V2 MAXPATHLEN + +struct cow_header_v2 { + unsigned long magic; + unsigned long version; + char backing_file[PATH_LEN_V2]; + time_t mtime; + __u64 size; + int sectorsize; +}; + +union cow_header { + struct cow_header_v1 v1; + struct cow_header_v2 v2; +}; + +#define COW_MAGIC 0x4f4f4f4d /* MOOO */ +#define COW_VERSION 2 + +static void sizes(__u64 size, int sectorsize, int bitmap_offset, + int *bitmap_len_out, int *data_offset_out) +{ + *bitmap_len_out = (size + sectorsize - 1) / (8 * sectorsize); + + *data_offset_out = bitmap_offset + *bitmap_len_out; + *data_offset_out = (*data_offset_out + sectorsize - 1) / sectorsize; + *data_offset_out *= sectorsize; +} + +static int read_cow_header(int fd, int *magic_out, char **backing_file_out, + time_t *mtime_out, __u64 *size_out, + int *sectorsize_out, int *bitmap_offset_out) +{ + union cow_header *header; + char *file; + int err, n; + unsigned long version, magic; + + header = um_kmalloc(sizeof(*header)); + if(header == NULL){ + printk("read_cow_header - Failed to allocate header\n"); + return(-ENOMEM); + } + err = -EINVAL; + n = read(fd, header, sizeof(*header)); + if(n < offsetof(typeof(header->v1), backing_file)){ + printk("read_cow_header - short header\n"); + goto out; + } + + err = 0; + magic = header->v1.magic; + if(magic == COW_MAGIC) { + version = header->v1.version; + } + else if(magic == ntohl(COW_MAGIC)){ + version = ntohl(header->v1.version); + } + else goto out; + + *magic_out = COW_MAGIC; + + err = -EINVAL; + if(version == 1){ + if(n < sizeof(header->v1)){ + printk("read_cow_header - failed to read V1 header\n"); + goto out; + } + *mtime_out = header->v1.mtime; + *size_out = header->v1.size; + *sectorsize_out = header->v1.sectorsize; + *bitmap_offset_out = sizeof(header->v1); + file = header->v1.backing_file; + } + else if(version == 2){ + if(n < sizeof(header->v2)){ + printk("read_cow_header - failed to read V2 header\n"); + goto out; + } + *mtime_out = ntohl(header->v2.mtime); + *size_out = ntohll(header->v2.size); + *sectorsize_out = ntohl(header->v2.sectorsize); + *bitmap_offset_out = sizeof(header->v2); + file = header->v2.backing_file; + } + else { + printk("read_cow_header - invalid COW version\n"); + goto out; + } + err = -ENOMEM; + *backing_file_out = uml_strdup(file); + if(*backing_file_out == NULL){ + printk("read_cow_header - failed to allocate backing file\n"); + goto out; + } + err = 0; + out: + kfree(header); + return(err); +} + +int open_ubd_file(char *file, int *openflags, char **backing_file_out, + int *bitmap_offset_out, int *bitmap_len_out, + int *data_offset_out) +{ + struct stat64 buf; + time_t mtime; + __u64 size; + int fd, err, sectorsize, magic, mode = 0644; + + if(backing_file_out != NULL) *backing_file_out = NULL; + if((fd = open64(file, *openflags, mode)) < 0){ + if(((*openflags & O_ACCMODE) != O_RDWR) || + ((errno != EROFS) && (errno != EACCES))) return(-errno); + *openflags &= ~O_ACCMODE; + *openflags |= O_RDONLY; + if((fd = open64(file, *openflags, mode)) < 0) return(-errno); + } + if(backing_file_out == NULL) return(fd); + + err = read_cow_header(fd, &magic, backing_file_out, &mtime, &size, + §orsize, bitmap_offset_out); + if(err) goto error; + + if(magic != COW_MAGIC){ + *backing_file_out = NULL; + return(fd); + } + + err = stat64(*backing_file_out, &buf); + if(err) goto error; + + err = -EINVAL; + if(buf.st_size != size){ + printk("Size mismatch (%ld vs %ld) of COW header vs backing " + "file\n", buf.st_size, size); + goto error; + } + if(buf.st_mtime != mtime){ + printk("mtime mismatch (%ld vs %ld) of COW header vs backing " + "file\n", buf.st_mtime, mtime); + goto error; + } + + sizes(size, sectorsize, *bitmap_offset_out, bitmap_len_out, + data_offset_out); + + return(fd); + error: + close(fd); + return(err); +} + +int read_cow_bitmap(int fd, void *buf, int offset, int len) +{ + int err; + + err = lseek64(fd, offset, SEEK_SET); + if(err != offset) return(-errno); + err = read(fd, buf, len); + if(err < 0) return(-errno); + return(0); +} + +static int absolutize(char *to, int size, char *from) +{ + char save_cwd[256], *slash; + int remaining; + + if(getcwd(save_cwd, sizeof(save_cwd)) == NULL) { + printk("absolutize : unable to get cwd - errno = %d\n", errno); + return(-1); + } + slash = strrchr(from, '/'); + if(slash != NULL){ + *slash = '\0'; + if(chdir(from)){ + *slash = '/'; + printk("absolutize : Can't cd to '%s' - errno = %d\n", + from, errno); + return(-1); + } + *slash = '/'; + if(getcwd(to, size) == NULL){ + printk("absolutize : unable to get cwd of '%s' - " + "errno = %d\n", from, errno); + return(-1); + } + remaining = size - strlen(to); + if(strlen(slash) + 1 > remaining){ + printk("absolutize : unable to fit '%s' into %d " + "chars\n", from, size); + return(-1); + } + strcat(to, slash); + } + else { + if(strlen(save_cwd) + 1 + strlen(from) + 1 > size){ + printk("absolutize : unable to fit '%s' into %d " + "chars\n", from, size); + return(-1); + } + strcpy(to, save_cwd); + strcat(to, "/"); + strcat(to, from); + } + chdir(save_cwd); + return(0); +} + +int create_cow_file(char *cow_file, char *backing_file, int sectorsize, + int *bitmap_offset_out, int *bitmap_len_out, + int *data_offset_out) +{ + struct cow_header_v2 *header; + struct stat64 buf; + __u64 blocks; + long zero; + int err, fd, i, flags; + __u64 size; + + flags = O_RDWR | O_CREAT; + fd = open_ubd_file(cow_file, &flags, NULL, NULL, NULL, NULL); + if(fd < 0) return(fd); + + err = -ENOMEM; + header = um_kmalloc(sizeof(*header)); + if(header == NULL){ + printk("Failed to allocate COW V2 header\n"); + goto out_close; + } + header->magic = htonl(COW_MAGIC); + header->version = htonl(COW_VERSION); + + err = -EINVAL; + if(strlen(backing_file) > sizeof(header->backing_file) - 1){ + printk("Backing file name \"%s\" is too long - names are " + "limited to %d characters\n", backing_file, + sizeof(header->backing_file) - 1); + goto out_free; + } + + if(absolutize(header->backing_file, sizeof(header->backing_file), + backing_file)) + goto out_free; + + err = stat64(header->backing_file, &buf); + if(err < 0) goto out_free; + + header->mtime = htonl(buf.st_mtime); + header->size = htonll(buf.st_size); + header->sectorsize = htonl(sectorsize); + size = buf.st_size; + + err = write(fd, header, sizeof(*header)); + if(err != sizeof(*header)) goto out_free; + + blocks = (size + sectorsize - 1) / sectorsize; + blocks = (blocks + sizeof(long) * 8 - 1) / (sizeof(long) * 8); + zero = 0; + for(i = 0; i < blocks; i++){ + err = write(fd, &zero, sizeof(zero)); + if(err != sizeof(zero)) goto out_free; + } + + sizes(size, sectorsize, sizeof(struct cow_header_v2), + bitmap_len_out, data_offset_out); + *bitmap_offset_out = sizeof(struct cow_header_v2); + + kfree(header); + return(fd); + + out_free: + kfree(header); + out_close: + close(fd); + return(err); +} + +int read_ubd_fs(int fd, void *buffer, int len) +{ + return(read(fd, buffer, len)); +} + +int write_ubd_fs(int fd, char *buffer, int len) +{ + return(write(fd, buffer, len)); +} + +int ubd_is_dir(char *file) +{ + struct stat64 buf; + + if(stat64(file, &buf) < 0) return(0); + return(S_ISDIR(buf.st_mode)); +} + +__u64 last_offset = -1; + +int do_io(struct io_thread_req *req) +{ + char *buf; + unsigned long len; + int n, nsectors, start, end, bit; + __u64 off; + + nsectors = req->length / req->sectorsize; + start = 0; + do { + bit = ubd_test_bit(start, &req->sector_mask); + end = start; + while((end < nsectors) && + (ubd_test_bit(end, &req->sector_mask) == bit)) + end++; + + if(end != nsectors) + printk("end != nsectors\n"); + off = req->offset + req->offsets[bit]; + len = (end - start) * req->sectorsize; + buf = &req->buffer[start * req->sectorsize]; + last_offset = off; + + if(lseek64(req->fds[bit], off, SEEK_SET) != off){ + printk("do_io - lseek failed : errno = %d\n", + errno); + return(-1); + } + if(req->read){ + n = read(req->fds[bit], buf, len); + if(n <= 0){ + printk("do_io - read returned %d : " + "errno = %d fd = %d\n", n, + errno, req->fds[bit]); + return(-1); + } + else if(n != len) memset(&buf[n], 0, len - n); + } + else { + n = write(req->fds[bit], buf, len); + if(n != len){ + printk("do_io - write returned %d : " + "errno = %d fd = %d\n", n, + errno, req->fds[bit]); + return(-1); + } + } + + start = end; + } while(start < nsectors); + + if(req->bitmap_start != -1){ + off = req->bitmap_start * sizeof(req->bitmap[0]); + off += req->bitmap_offset; + if(lseek64(req->fds[1], off, SEEK_SET) != off){ + printk("do_io - bitmap lseek failed : errno = %d\n", + errno); + return(-1); + } + len = (req->bitmap_end - req->bitmap_start) * + sizeof(req->bitmap[0]); + n = write(req->fds[1], &req->bitmap[req->bitmap_start], len); + if(n != len){ + printk("do_io - bitmap update returned %d : " + "errno = %d fd = %d\n", n, errno, req->fds[1]); + return(-1); + } + } + return(0); +} + +int kernel_fds[2] = { -1, -1 }; + +int io_count = 0; + +int io_thread(void *arg) +{ + struct io_thread_req req; + int n; + + while(1){ + n = read(kernel_fds[0], &req, sizeof(req)); + if(n < 0) printk("io_thread - read returned %d, errno = %d\n", + n, errno); + else if(n < sizeof(req)){ + printk("io_thread - short read : length = %d\n", n); + continue; + } + io_count++; + if(do_io(&req)) req.error = 1; + if(write(kernel_fds[1], &req, sizeof(req)) != sizeof(req)) + printk("io_thread - write failed, errno = %d\n", + errno); + } +} + +int start_io_thread(unsigned long sp, int *fds_out) +{ + int pid; + + if((kernel_fds[0] = get_pty()) < 0) return(-1); + raw(kernel_fds[0], 1); + if((fds_out[0] = open(ptsname(kernel_fds[0]), O_RDWR)) < 0){ + printk("Couldn't open tty for IO\n"); + return(-1); + } + fds_out[1] = fds_out[0]; + kernel_fds[1] = kernel_fds[0]; + pid = clone(io_thread, (void *) sp, CLONE_VM | CLONE_FILES | SIGCHLD, + NULL); + if(pid < 0){ + printk("start_io_thread - clone failed : errno = %d\n", errno); + return(-errno); + } + return(pid); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/fs/Makefile linux.ac/arch/um/fs/Makefile --- linux.vanilla/arch/um/fs/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/fs/Makefile Wed Oct 10 01:47:36 2001 @@ -0,0 +1,16 @@ +# +# Copyright (C) 2000 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +O_TARGET := fs.o + +subdir-$(CONFIG_HOSTFS) = hostfs + +MOD_SUB_DIRS := $(subdir-m) +SUB_DIRS := $(subdir-y) + +obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o)) +obj-m += $(join $(subdir-m),$(subdir-m:%=/%.o)) + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/fs/hostfs/Makefile linux.ac/arch/um/fs/hostfs/Makefile --- linux.vanilla/arch/um/fs/hostfs/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/fs/hostfs/Makefile Wed Oct 10 01:47:36 2001 @@ -0,0 +1,31 @@ +# +# Copyright (C) 2000 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +# struct stat64 changed the inode field name between 2.2 and 2.4 from st_ino +# to __st_ino. It stayed in the same place, so as long as the correct name +# is used, hostfs compiled on 2.2 should work on 2.4 and vice versa. + +STAT64_INO_FIELD := $(shell grep -q __st_ino /usr/include/bits/stat.h && \ + echo __)st_ino + +USER_CFLAGS := $(USER_CFLAGS) -DSTAT64_INO_FIELD=$(STAT64_INO_FIELD) + +O_TARGET := +obj-y = +obj-m = + +CFLAGS_hostfs_kern.o := $(CFLAGS) +CFLAGS_hostfs_user.o := $(USER_CFLAGS) + +ifneq ($(CONFIG_HOSTFS), n) + O_TARGET := hostfs.o +endif + +obj-y += hostfs_kern.o hostfs_user.o +obj-m += $(O_TARGET) + +override CFLAGS = + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/fs/hostfs/hostfs.h linux.ac/arch/um/fs/hostfs/hostfs.h --- linux.vanilla/arch/um/fs/hostfs/hostfs.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/fs/hostfs/hostfs.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,74 @@ +#ifndef __UM_FS_HOSTFS +#define __UM_FS_HOSTFS + +#define HOSTFS_FILE 1 +#define HOSTFS_DIR 2 +#define HOSTFS_SYMLINK 3 +#define HOSTFS_CHARDEV 4 +#define HOSTFS_BLOCDEV 5 +#define HOSTFS_FIFO 6 +#define HOSTFS_SOCK 7 + +/* These are exactly the same definitions as in fs.h, but the names are + * changed so that this file can be included in both kernel and user files. + */ + +#define HOSTFS_ATTR_MODE 1 +#define HOSTFS_ATTR_UID 2 +#define HOSTFS_ATTR_GID 4 +#define HOSTFS_ATTR_SIZE 8 +#define HOSTFS_ATTR_ATIME 16 +#define HOSTFS_ATTR_MTIME 32 +#define HOSTFS_ATTR_CTIME 64 +#define HOSTFS_ATTR_ATIME_SET 128 +#define HOSTFS_ATTR_MTIME_SET 256 +#define HOSTFS_ATTR_FORCE 512 /* Not a change, but a change it */ +#define HOSTFS_ATTR_ATTR_FLAG 1024 + +struct hostfs_iattr { + unsigned int ia_valid; + mode_t ia_mode; + uid_t ia_uid; + gid_t ia_gid; + loff_t ia_size; + time_t ia_atime; + time_t ia_mtime; + time_t ia_ctime; + unsigned int ia_attr_flags; +}; + +extern int stat_file(const char *path, int *dev_out, unsigned long long *inode_out, + int *mode_out, int *nlink_out, int *uid_out, + int *gid_out, unsigned long long *size_out, + unsigned long *atime_out, unsigned long *mtime_out, + unsigned long *ctime_out, int *blksize_out, + unsigned long long *blocks_out); +extern int access_file(char *path, int r, int w, int x); +extern int open_file(char *path, int r, int w); +extern int file_type(const char *path, int *rdev); +extern void *open_dir(char *path, int *err_out); +extern char *read_dir(void *stream, unsigned long long *pos, + unsigned long long *ino_out, int *len_out); +extern void close_file(void *stream); +extern void close_dir(void *stream); +extern int read_file(int fd, unsigned long long *offset, char *buf, int len); +extern int write_file(int fd, unsigned long long *offset, const char *buf, + int len); +extern int lseek_file(int fd, long long offset, int whence); +extern int file_create(char *name, int ur, int uw, int ux, int gr, + int gw, int gx, int or, int ow, int ox); +extern int set_attr(const char *file, struct hostfs_iattr *attrs); +extern int make_symlink(const char *from, const char *to); +extern int unlink_file(const char *file); +extern int do_mkdir(const char *file, int mode); +extern int do_rmdir(const char *file); +extern int do_mknod(const char *file, int mode, int dev); +extern int link_file(const char *from, const char *to); +extern int do_readlink(char *file, char *buf, int size); +extern int rename_file(char *from, char *to); +extern int do_statfs(char *root, long *bsize_out, long long *blocks_out, + long long *bfree_out, long long *bavail_out, long long *files_out, + long long *ffree_out, void *fsid_out, int fsid_size, + long *namelen_out, long *spare_out); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/fs/hostfs/hostfs_kern.c linux.ac/arch/um/fs/hostfs/hostfs_kern.c --- linux.vanilla/arch/um/fs/hostfs/hostfs_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/fs/hostfs/hostfs_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,782 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hostfs.h" +#include "kern_util.h" +#include "kern.h" +#include "user_util.h" + +#define file_hostfs_i(file) (&(file)->f_dentry->d_inode->u.hostfs_i) + +int hostfs_d_delete(struct dentry *dentry) +{ + return(1); +} + +struct dentry_operations hostfs_dentry_ops = { + d_delete: hostfs_d_delete, +}; + +static char *root_ino = "/"; + +#define HOSTFS_SUPER_MAGIC 0x00c0ffee + +static struct inode_operations hostfs_iops; +static struct address_space_operations hostfs_link_aops; + +static char *dentry_name(struct dentry *dentry, int extra) +{ + struct dentry *parent; + char *root, *name; + int len; + + len = 0; + parent = dentry; + while(parent->d_parent != parent){ + len += parent->d_name.len + 1; + parent = parent->d_parent; + } + + root = parent->d_inode->u.hostfs_i.host_filename; + len += strlen(root); + name = kmalloc(len + extra + 1, GFP_KERNEL); + if(name == NULL) return(NULL); + + name[len] = '\0'; + parent = dentry; + while(parent->d_parent != parent){ + len -= parent->d_name.len + 1; + name[len] = '/'; + strncpy(&name[len + 1], parent->d_name.name, + parent->d_name.len); + parent = parent->d_parent; + } + strncpy(name, root, strlen(root)); + return(name); +} + +static char *inode_name(struct inode *ino, int extra) +{ + struct dentry *dentry; + + dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias); + return(dentry_name(dentry, extra)); +} + +static int read_name(struct inode *ino, char *name) +{ + /* The non-int inode fields are copied into ints by stat_file and + * then copied into the inode because passing the actual pointers + * in and having them treated as int * breaks on big-endian machines + */ + int err; + int i_dev, i_mode, i_nlink, i_blksize; + unsigned long long i_size; + unsigned long long i_ino; + unsigned long long i_blocks; + err = stat_file(name, &i_dev, &i_ino, &i_mode, &i_nlink, + &ino->i_uid, &ino->i_gid, &i_size, &ino->i_atime, + &ino->i_mtime, &ino->i_ctime, &i_blksize, &i_blocks); + if(err) return(err); + ino->i_ino = i_ino; + ino->i_dev = i_dev; + ino->i_mode = i_mode; + ino->i_nlink = i_nlink; + ino->i_size = i_size; + ino->i_blksize = i_blksize; + ino->i_blocks = i_blocks; + if((ino->i_sb->s_dev == ROOT_DEV) && (ino->i_uid == getuid())) + ino->i_uid = 0; + return(0); +} + +static int read_inode(struct inode *ino) +{ + char *name; + int err; + + name = inode_name(ino, 0); + if(name == NULL) return(-ENOMEM); + err = read_name(ino, name); + kfree(name); + return(err); +} + +void hostfs_delete_inode(struct inode *ino) +{ + if(ino->u.hostfs_i.host_filename) kfree(ino->u.hostfs_i.host_filename); + ino->u.hostfs_i.host_filename = NULL; + if(ino->u.hostfs_i.fd != -1) close_file(&ino->u.hostfs_i.fd); + ino->u.hostfs_i.mode = 0; + clear_inode(ino); +} + +int hostfs_statfs(struct super_block *sb, struct statfs *sf) +{ + /* do_statfs uses struct statfs64 internally, but the linux kernel + * struct statfs still has 32-bit versions for most of these fields, + * so we convert them here + */ + int err; + long long f_blocks; + long long f_bfree; + long long f_bavail; + long long f_files; + long long f_ffree; + + err = do_statfs(sb->s_root->d_inode->u.hostfs_i.host_filename, + &sf->f_bsize, &f_blocks, &f_bfree, &f_bavail, &f_files, + &f_ffree, &sf->f_fsid, sizeof(sf->f_fsid), + &sf->f_namelen, sf->f_spare); + if(err) return(err); + sf->f_blocks = f_blocks; + sf->f_bfree = f_bfree; + sf->f_bavail = f_bavail; + sf->f_files = f_files; + sf->f_ffree = f_ffree; + sf->f_type = HOSTFS_SUPER_MAGIC; + return(0); +} + +static struct super_operations hostfs_sbops = { + put_inode: force_delete, + delete_inode: hostfs_delete_inode, + statfs: hostfs_statfs, +}; + +int hostfs_readdir(struct file *file, void *ent, filldir_t filldir) +{ + void *dir; + char *name; + unsigned long long next, ino; + int error, len; + + name = dentry_name(file->f_dentry, 0); + if(name == NULL) return(-ENOMEM); + dir = open_dir(name, &error); + kfree(name); + if(dir == NULL) return(-error); + next = file->f_pos; + while((name = read_dir(dir, &next, &ino, &len)) != NULL){ + error = (*filldir)(ent, name, len, file->f_pos, + ino, DT_UNKNOWN); + if(error) break; + file->f_pos = next; + } + close_dir(dir); + return(0); +} + +unsigned int hostfs_poll(struct file *file, struct poll_table_struct *table) +{ + not_implemented(); + return(-EINVAL); +} + +int hostfs_ioctl(struct inode *ino, struct file *file, unsigned int code, + unsigned long data) +{ + not_implemented(); + return(-EINVAL); +} + +int hostfs_file_open(struct inode *ino, struct file *file) +{ + char *name; + int mode = 0, r = 0, w = 0, fd; + + mode = file->f_mode & (FMODE_READ | FMODE_WRITE); + if((mode & ino->u.hostfs_i.mode) == mode) return(0); + + if(ino->u.hostfs_i.fd != -1){ + close_file(&ino->u.hostfs_i.fd); + ino->u.hostfs_i.fd = -1; + } + ino->u.hostfs_i.mode |= mode; + if(ino->u.hostfs_i.mode & FMODE_READ) r = 1; + if(ino->u.hostfs_i.mode & FMODE_WRITE) w = 1; + if(w) r = 1; + name = dentry_name(file->f_dentry, 0); + if(name == NULL) return(-ENOMEM); + fd = open_file(name, r, w); + kfree(name); + if(fd < 0) return(fd); + file_hostfs_i(file)->fd = fd; + return(0); +} + +int hostfs_dir_open(struct inode *ino, struct file *file) +{ + return(0); +} + +int hostfs_dir_release(struct inode *ino, struct file *file) +{ + return(0); +} + +int hostfs_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + return(0); +} + +int hostfs_fasync(int fd, struct file *file, int on) +{ + not_implemented(); + return(-EINVAL); +} + +static struct file_operations hostfs_file_fops = { + owner: NULL, + read: generic_file_read, + write: generic_file_write, + poll: hostfs_poll, + mmap: generic_file_mmap, + open: hostfs_file_open, + release: NULL, + fsync: hostfs_fsync, + fasync: hostfs_fasync +}; + +static struct file_operations hostfs_dir_fops = { + owner: NULL, + readdir: hostfs_readdir, + poll: hostfs_poll, + ioctl: hostfs_ioctl, + open: hostfs_dir_open, + release: hostfs_dir_release, + fsync: hostfs_fsync, + fasync: hostfs_fasync +}; + +int hostfs_writepage(struct page *page) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = mapping->host; + char *buffer; + unsigned long long base; + int count = PAGE_CACHE_SIZE; + int end_index = inode->i_size >> PAGE_CACHE_SHIFT; + int err; + + if (page->index >= end_index) + count = inode->i_size & (PAGE_CACHE_SIZE-1); + + buffer = kmap(page); + base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT; + + err = write_file(inode->u.hostfs_i.fd, &base, buffer, count); + if(err != count){ + ClearPageUptodate(page); + goto out; + } + + if (base > inode->i_size) + inode->i_size = base; + + if (PageError(page)) + ClearPageError(page); + + out: + kunmap(page); + + UnlockPage(page); + return err; +} + +int hostfs_readpage(struct file *file, struct page *page) +{ + char *buffer; + long long start; + int err = 0; + + start = (long long) page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + err = read_file(file_hostfs_i(file)->fd, &start, buffer, + PAGE_CACHE_SIZE); + if(err < 0) goto out; + + flush_dcache_page(page); + SetPageUptodate(page); + if (PageError(page)) ClearPageError(page); + err = 0; + out: + kunmap(page); + UnlockPage(page); + return(err); +} + +int hostfs_prepare_write(struct file *file, struct page *page, unsigned from, + unsigned to) +{ + char *buffer; + long long start; + int err; + + start = (long long) page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + if(from != 0){ + err = read_file(file_hostfs_i(file)->fd, &start, buffer, + from); + if(err < 0) goto out; + } + if(to != PAGE_CACHE_SIZE){ + start += to; + err = read_file(file_hostfs_i(file)->fd, &start, buffer + to, + PAGE_CACHE_SIZE - to); + if(err < 0) goto out; + } + err = 0; + out: + kunmap(page); + return(err); +} + +int hostfs_commit_write(struct file *file, struct page *page, unsigned from, + unsigned to) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = mapping->host; + char *buffer; + long long start; + int err = 0; + + start = (long long) (page->index << PAGE_CACHE_SHIFT) + from; + buffer = kmap(page); + err = write_file(file_hostfs_i(file)->fd, &start, buffer + from, + to - from); + if(err > 0) err = 0; + if(!err && (start > inode->i_size)) + inode->i_size = start; + + kunmap(page); + return(err); +} + +static struct address_space_operations hostfs_aops = { + writepage: hostfs_writepage, + readpage: hostfs_readpage, + prepare_write: hostfs_prepare_write, + commit_write: hostfs_commit_write +}; + +static struct inode *get_inode(struct super_block *sb, struct dentry *dentry, + int *error) +{ + struct inode *inode; + char *name; + int type, err = 0, rdev; + + inode = get_empty_inode(); + if(inode == NULL) return(NULL); + inode->u.hostfs_i.host_filename = NULL; + inode->u.hostfs_i.fd = -1; + inode->u.hostfs_i.mode = 0; + if(error) *error = 0; + insert_inode_hash(inode); + if(dentry){ + name = dentry_name(dentry, 0); + if(name == NULL){ + err = -ENOMEM; + goto out; + } + type = file_type(name, &rdev); + kfree(name); + } + else type = HOSTFS_DIR; + inode->i_sb = sb; + + if(type == HOSTFS_SYMLINK) + inode->i_op = &page_symlink_inode_operations; + else inode->i_op = &hostfs_iops; + + if(type == HOSTFS_DIR) inode->i_fop = &hostfs_dir_fops; + else inode->i_fop = &hostfs_file_fops; + + if(type == HOSTFS_SYMLINK) inode->i_mapping->a_ops = &hostfs_link_aops; + else inode->i_mapping->a_ops = &hostfs_aops; + + switch (type) { + case HOSTFS_CHARDEV: + init_special_inode(inode, S_IFCHR, rdev); + break; + case HOSTFS_BLOCDEV: + init_special_inode(inode, S_IFBLK, rdev); + break; + case HOSTFS_FIFO: + init_special_inode(inode, S_IFIFO, 0); + break; + case HOSTFS_SOCK: + init_special_inode(inode, S_IFSOCK, 0); + break; + } + + return(inode); + out: + iput(inode); + if(error) *error = err; + return(NULL); +} + +int hostfs_create(struct inode *dir, struct dentry *dentry, int mode) +{ + struct inode *inode; + char *name; + int error; + + inode = get_inode(dir->i_sb, dentry, &error); + if(error) return(error); + name = dentry_name(dentry, 0); + if(name == NULL){ + iput(inode); + return(-ENOMEM); + } + error = file_create(name, + mode | S_IRUSR, mode | S_IWUSR, mode | S_IXUSR, + mode | S_IRGRP, mode | S_IWGRP, mode | S_IXGRP, + mode | S_IROTH, mode | S_IWOTH, mode | S_IXOTH); + if(!error) error = read_name(inode, name); + kfree(name); + if(error){ + iput(inode); + return(error); + } + d_instantiate(dentry, inode); + return(0); +} + +struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry) +{ + struct inode *inode; + char *name; + int error; + + inode = get_inode(ino->i_sb, dentry, &error); + if(error != 0) return(ERR_PTR(error)); + name = dentry_name(dentry, 0); + if(name == NULL) return(ERR_PTR(-ENOMEM)); + error = read_name(inode, name); + kfree(name); + if(error){ + iput(inode); + if(error == -ENOENT) inode = NULL; + else return(ERR_PTR(error)); + } + d_add(dentry, inode); + dentry->d_op = &hostfs_dentry_ops; + return(NULL); +} + +static char *inode_dentry_name(struct inode *ino, struct dentry *dentry) +{ + char *file; + int len; + + file = inode_name(ino, dentry->d_name.len + 1); + if(file == NULL) return(NULL); + strcat(file, "/"); + len = strlen(file); + strncat(file, dentry->d_name.name, dentry->d_name.len); + file[len + dentry->d_name.len] = '\0'; + return(file); +} + +int hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from) +{ + char *from_name, *to_name; + int err; + + if((from_name = inode_dentry_name(ino, from)) == NULL) + return(-ENOMEM); + to_name = dentry_name(to, 0); + if(to_name == NULL){ + kfree(from_name); + return(-ENOMEM); + } + err = link_file(to_name, from_name); + kfree(from_name); + kfree(to_name); + return(err); +} + +int hostfs_unlink(struct inode *ino, struct dentry *dentry) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = unlink_file(file); + kfree(file); + return(err); +} + +int hostfs_symlink(struct inode *ino, struct dentry *dentry, const char *to) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = make_symlink(file, to); + kfree(file); + return(err); +} + +int hostfs_mkdir(struct inode *ino, struct dentry *dentry, int mode) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = do_mkdir(file, mode); + kfree(file); + return(err); +} + +int hostfs_rmdir(struct inode *ino, struct dentry *dentry) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = do_rmdir(file); + kfree(file); + return(err); +} + +int hostfs_mknod(struct inode *ino, struct dentry *dentry, int mode, int dev) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = do_mknod(file, mode, dev); + kfree(file); + return(err); +} + +int hostfs_rename(struct inode *from_ino, struct dentry *from, + struct inode *to_ino, struct dentry *to) +{ + char *from_name, *to_name; + int err; + + if((from_name = inode_dentry_name(from_ino, from)) == NULL) + return(-ENOMEM); + if((to_name = inode_dentry_name(to_ino, to)) == NULL){ + kfree(from_name); + return(-ENOMEM); + } + err = rename_file(from_name, to_name); + kfree(from_name); + kfree(to_name); + return(err); +} + +void hostfs_truncate(struct inode *ino) +{ + not_implemented(); +} + +int hostfs_permission(struct inode *ino, int desired) +{ + char *name; + int r = 0, w = 0, x = 0, err; + + if(desired & MAY_READ) r = 1; + if(desired & MAY_WRITE) w = 1; + if(desired & MAY_EXEC) x = 1; + name = inode_name(ino, 0); + if(name == NULL) return(-ENOMEM); + err = access_file(name, r, w, x); + kfree(name); + if(!err) err = vfs_permission(ino, desired); + return(err); +} + +int hostfs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct hostfs_iattr attrs; + char *name; + int err; + + attrs.ia_valid = 0; + if(attr->ia_valid & ATTR_MODE){ + attrs.ia_valid |= HOSTFS_ATTR_MODE; + attrs.ia_mode = attr->ia_mode; + } + if(attr->ia_valid & ATTR_UID){ + attrs.ia_valid |= HOSTFS_ATTR_UID; + attrs.ia_uid = attr->ia_uid; + } + if(attr->ia_valid & ATTR_GID){ + attrs.ia_valid |= HOSTFS_ATTR_GID; + attrs.ia_gid = attr->ia_gid; + } + if(attr->ia_valid & ATTR_SIZE){ + attrs.ia_valid |= HOSTFS_ATTR_SIZE; + attrs.ia_size = attr->ia_size; + } + if(attr->ia_valid & ATTR_ATIME){ + attrs.ia_valid |= HOSTFS_ATTR_ATIME; + attrs.ia_atime = attr->ia_atime; + } + if(attr->ia_valid & ATTR_MTIME){ + attrs.ia_valid |= HOSTFS_ATTR_MTIME; + attrs.ia_mtime = attr->ia_mtime; + } + if(attr->ia_valid & ATTR_CTIME){ + attrs.ia_valid |= HOSTFS_ATTR_CTIME; + attrs.ia_ctime = attr->ia_ctime; + } + if(attr->ia_valid & ATTR_ATIME_SET){ + attrs.ia_valid |= HOSTFS_ATTR_ATIME_SET; + } + if(attr->ia_valid & ATTR_MTIME_SET){ + attrs.ia_valid |= HOSTFS_ATTR_MTIME_SET; + } + name = dentry_name(dentry, 0); + if(name == NULL) return(-ENOMEM); + err = set_attr(name, &attrs); + kfree(name); + return(err); +} + +int hostfs_getattr(struct dentry *dentry, struct iattr *attr) +{ + not_implemented(); + return(-EINVAL); +} + +static struct inode_operations hostfs_iops = { + create: hostfs_create, + lookup: hostfs_lookup, + link: hostfs_link, + unlink: hostfs_unlink, + symlink: hostfs_symlink, + mkdir: hostfs_mkdir, + rmdir: hostfs_rmdir, + mknod: hostfs_mknod, + rename: hostfs_rename, + truncate: hostfs_truncate, + permission: hostfs_permission, + setattr: hostfs_setattr, + getattr: hostfs_getattr, +}; + +int hostfs_link_readpage(struct file *file, struct page *page) +{ + char *buffer, *name; + long long start; + int err; + + start = page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + name = inode_name(page->mapping->host, 0); + if(name == NULL) return(-ENOMEM); + err = do_readlink(name, buffer, PAGE_CACHE_SIZE); + kfree(name); + if(err == 0){ + flush_dcache_page(page); + SetPageUptodate(page); + if (PageError(page)) ClearPageError(page); + } + kunmap(page); + UnlockPage(page); + return(err); +} + +static struct address_space_operations hostfs_link_aops = { + readpage: hostfs_link_readpage, +}; + +static struct super_block *hostfs_read_super_common(struct super_block *sb, + char *data) +{ + struct inode * root_inode; + char *name; + + sb->s_blocksize = 1024; + sb->s_blocksize_bits = 10; + sb->s_magic = HOSTFS_SUPER_MAGIC; + sb->s_op = &hostfs_sbops; + if((data == NULL) || (*((char *) data) == '\0')) data = root_ino; + name = kmalloc(strlen(data) + 1, GFP_KERNEL); + if(name == NULL) return(NULL); + strcpy(name, data); + root_inode = get_inode(sb, NULL, NULL); + if(root_inode == NULL){ + kfree(name); + return(NULL); + } + root_inode->u.hostfs_i.host_filename = name; + sb->s_root = d_alloc_root(root_inode); + if(read_inode(root_inode)){ + iput(root_inode); + return(NULL); + } + return(sb); +} + +struct super_block *hostfs_read_super(struct super_block *sb, void *data, + int silent) +{ + return(hostfs_read_super_common(sb, data)); +} + +struct super_block *hostfs_root_read_super(struct super_block *sb, void *data, + int silent) +{ + struct buffer_head * bh; + struct super_block *ret = NULL; + kdev_t dev = sb->s_dev; + int blocksize = get_hardsect_size(dev); + + if(blocksize == 0) blocksize = BLOCK_SIZE; + set_blocksize (dev, blocksize); + if(!(bh = bread (dev, 0, blocksize))) return NULL; + if(strncmp(bh->b_data, "HOSTFS:", strlen("HOSTFS:"))) goto out; + ret = hostfs_read_super_common(sb, bh->b_data + strlen("HOSTFS:")); + out: + brelse (bh); + return(ret); +} + +DECLARE_FSTYPE(hostfs_type, "hostfs", hostfs_read_super, 0); +DECLARE_FSTYPE_DEV(hostfs_root_type, "root hostfs", hostfs_root_read_super); + +static int __init init_hostfs(void) +{ + return(register_filesystem(&hostfs_type) || + register_filesystem(&hostfs_root_type)); +} + +static void __exit exit_hostfs(void) +{ + unregister_filesystem(&hostfs_type); + unregister_filesystem(&hostfs_root_type); +} + +module_init(init_hostfs) +module_exit(exit_hostfs) + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/fs/hostfs/hostfs_user.c linux.ac/arch/um/fs/hostfs/hostfs_user.c --- linux.vanilla/arch/um/fs/hostfs/hostfs_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/fs/hostfs/hostfs_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hostfs.h" +#include "kern_util.h" +#include "user.h" + +int stat_file(const char *path, int *dev_out, unsigned long long *inode_out, + int *mode_out, int *nlink_out, int *uid_out, int *gid_out, + unsigned long long *size_out, unsigned long *atime_out, + unsigned long *mtime_out, unsigned long *ctime_out, + int *blksize_out, unsigned long long *blocks_out) +{ + struct stat64 buf; + + if(lstat64(path, &buf) < 0) + return(-errno); + if(dev_out != NULL) *dev_out = buf.st_dev; + + /* See the Makefile for why STAT64_INO_FIELD is passed in + * by the build + */ + if(inode_out != NULL) *inode_out = buf.STAT64_INO_FIELD; + if(mode_out != NULL) *mode_out = buf.st_mode; + if(nlink_out != NULL) *nlink_out = buf.st_nlink; + if(uid_out != NULL) *uid_out = buf.st_uid; + if(gid_out != NULL) *gid_out = buf.st_gid; + if(size_out != NULL) *size_out = buf.st_size; + if(atime_out != NULL) *atime_out = buf.st_atime; + if(mtime_out != NULL) *mtime_out = buf.st_mtime; + if(ctime_out != NULL) *ctime_out = buf.st_ctime; + if(blksize_out != NULL) *blksize_out = buf.st_blksize; + if(blocks_out != NULL) *blocks_out = buf.st_blocks; + return(0); +} + +int file_type(const char *path, int *rdev) +{ + struct stat64 buf; + + if(lstat64(path, &buf) < 0) return(-errno); + *rdev = buf.st_rdev; + if(S_ISDIR(buf.st_mode)) return(HOSTFS_DIR); + else if(S_ISLNK(buf.st_mode)) return(HOSTFS_SYMLINK); + else if(S_ISCHR(buf.st_mode)) return(HOSTFS_CHARDEV); + else if(S_ISBLK(buf.st_mode)) return(HOSTFS_BLOCDEV); + else if(S_ISFIFO(buf.st_mode))return(HOSTFS_FIFO); + else if(S_ISSOCK(buf.st_mode))return(HOSTFS_SOCK); + + else return(HOSTFS_FILE); +} + +int access_file(char *path, int r, int w, int x) +{ + int mode = 0; + + if(r) mode = R_OK; + if(w) mode |= W_OK; + if(x) mode |= X_OK; + if(access(path, mode) != 0) return(-errno); + else return(0); +} + +int open_file(char *path, int r, int w) +{ + int mode = 0, fd; + + if(r && !w) mode = O_RDONLY; + else if(!r && w) mode = O_WRONLY; + else if(r && w) mode = O_RDWR; + else panic("Impossible mode in open_file"); + fd = open64(path, mode); + if(fd < 0) return(-errno); + else return(fd); +} + +void *open_dir(char *path, int *err_out) +{ + DIR *dir; + + dir = opendir(path); + *err_out = errno; + if(dir == NULL) return(NULL); + return(dir); +} + +char *read_dir(void *stream, unsigned long long *pos, + unsigned long long *ino_out, int *len_out) +{ + DIR *dir = stream; + struct dirent *ent; + + seekdir(dir, *pos); + ent = readdir(dir); + if(ent == NULL) return(NULL); + *len_out = strlen(ent->d_name); + *ino_out = ent->d_ino; + *pos = telldir(dir); + return(ent->d_name); +} + +int read_file(int fd, unsigned long long *offset, char *buf, int len) +{ + int n; + + n = pread64(fd, buf, len, *offset); + if(n < 0) return(-errno); + *offset += n; + return(n); +} + +int write_file(int fd, unsigned long long *offset, const char *buf, int len) +{ + int n; + + n = pwrite64(fd, buf, len, *offset); + if(n < 0) return(-errno); + *offset += n; + return(n); +} + +int lseek_file(int fd, long long offset, int whence) +{ + int ret; + + ret = lseek64(fd, offset, whence); + if(ret < 0) return(-errno); + return(0); +} + +void close_file(void *stream) +{ + close(*((int *) stream)); +} + +void close_dir(void *stream) +{ + closedir(stream); +} + +int file_create(char *name, int ur, int uw, int ux, int gr, + int gw, int gx, int or, int ow, int ox) +{ + int mode, fd; + + mode = 0; + mode |= ur ? S_IRUSR : 0; + mode |= uw ? S_IWUSR : 0; + mode |= ux ? S_IXUSR : 0; + mode |= gr ? S_IRGRP : 0; + mode |= gw ? S_IWGRP : 0; + mode |= gx ? S_IXGRP : 0; + mode |= or ? S_IROTH : 0; + mode |= ow ? S_IWOTH : 0; + mode |= ox ? S_IXOTH : 0; + fd = open64(name, O_CREAT, mode); + if(fd < 0) return(-errno); + close(fd); + return(0); +} + +int set_attr(const char *file, struct hostfs_iattr *attrs) +{ + struct utimbuf buf; + int err, ma; + + if(attrs->ia_valid & HOSTFS_ATTR_MODE){ + if(chmod(file, attrs->ia_mode) != 0) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_UID){ + if(chown(file, attrs->ia_uid, -1)) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_GID){ + if(chown(file, -1, attrs->ia_gid)) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_SIZE){ + if(truncate(file, attrs->ia_size)) return(-errno); + } + ma = HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET; + if((attrs->ia_valid & ma) == ma){ + buf.actime = attrs->ia_atime; + buf.modtime = attrs->ia_mtime; + if(utime(file, &buf) != 0) return(-errno); + } + else { + if(attrs->ia_valid & HOSTFS_ATTR_ATIME_SET){ + err = stat_file(file, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, &buf.modtime, NULL, + NULL, NULL); + if(err != 0) return(err); + buf.actime = attrs->ia_atime; + if(utime(file, &buf) != 0) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_MTIME_SET){ + err = stat_file(file, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &buf.actime, NULL, NULL, + NULL, NULL); + if(err != 0) return(err); + buf.modtime = attrs->ia_mtime; + if(utime(file, &buf) != 0) return(-errno); + } + } + if(attrs->ia_valid & HOSTFS_ATTR_CTIME) ; + if(attrs->ia_valid & (HOSTFS_ATTR_ATIME | HOSTFS_ATTR_MTIME)){ + err = stat_file(file, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &attrs->ia_atime, &attrs->ia_mtime, + NULL, NULL, NULL); + if(err != 0) return(err); + } + return(0); +} + +int make_symlink(const char *from, const char *to) +{ + int err; + + err = symlink(to, from); + if(err) return(-errno); + return(0); +} + +int unlink_file(const char *file) +{ + int err; + + err = unlink(file); + if(err) return(-errno); + return(0); +} + +int do_mkdir(const char *file, int mode) +{ + int err; + + err = mkdir(file, mode); + if(err) return(-errno); + return(0); +} + +int do_rmdir(const char *file) +{ + int err; + + err = rmdir(file); + if(err) return(-errno); + return(0); +} + +int do_mknod(const char *file, int mode, int dev) +{ + int err; + + err = mknod(file, mode, dev); + if(err) return(-errno); + return(0); +} + +int link_file(const char *to, const char *from) +{ + int err; + + err = link(to, from); + if(err) return(-errno); + return(0); +} + +int do_readlink(char *file, char *buf, int size) +{ + int err; + + err = readlink(file, buf, size); + if(err < 0) return(-errno); + if(err < size) buf[err] = '\0'; + return(0); +} + +int rename_file(char *from, char *to) +{ + int err; + + err = rename(from, to); + if(err < 0) return(-errno); + return(0); +} + +int do_statfs(char *root, long *bsize_out, long long *blocks_out, + long long *bfree_out, long long *bavail_out, + long long *files_out, long long *ffree_out, + void *fsid_out, int fsid_size, long *namelen_out, + long *spare_out) +{ + struct statfs64 buf; + int err; + + err = statfs64(root, &buf); + if(err < 0) return(-errno); + *bsize_out = buf.f_bsize; + *blocks_out = buf.f_blocks; + *bfree_out = buf.f_bfree; + *bavail_out = buf.f_bavail; + *files_out = buf.f_files; + *ffree_out = buf.f_ffree; + memcpy(fsid_out, &buf.f_fsid, + sizeof(buf.f_fsid) > fsid_size ? fsid_size : + sizeof(buf.f_fsid)); + *namelen_out = buf.f_namelen; + spare_out[0] = buf.f_spare[0]; + spare_out[1] = buf.f_spare[1]; + spare_out[2] = buf.f_spare[2]; + spare_out[3] = buf.f_spare[3]; + spare_out[4] = buf.f_spare[4]; + spare_out[5] = buf.f_spare[5]; + return(0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/chan.h linux.ac/arch/um/include/chan.h --- linux.vanilla/arch/um/include/chan.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/chan.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __TTY_H__ +#define __TTY_H__ + +struct chan { + enum { XTERM, PTY, SOCKET, FD, TTY, PTS, FILE_CHAN, COPY } type; + int fd; + int hung_up; + int opened; + int init_pri; + union { + struct { + int pid; + int line; + char *title; + int raw; + } xterm; + struct { + void (*announce)(char *dev_name, int dev); + int dev; + int raw; + } pty, pts; + struct { + char *dev; + int raw; + void *tty_state; + } tty; + struct { + int port; + int connected; + int pty; + } sock; + } data; +}; + +struct io_chan { + struct chan in; + struct chan out; +}; + +#define INIT_STATIC (0) +#define INIT_ALL (1) +#define INIT_ONE (2) + +#define PTY_CHAN_INIT(announce, dev, raw, pri) \ + { PTY, -1, 1, 0, pri, { pty: { announce, dev, raw } } } + +#define PTS_CHAN_INIT(announce, dev, raw, pri) \ + { PTS, -1, 0, 0, pri, { pts: { announce, dev, raw } } } + +#define XTERM_CHAN_INIT(n, title, raw, pri) \ + { XTERM, -1, 0, 0, pri, { xterm: { -1, n, title, raw } } } + +#define TTY_CHAN_INIT(dev, raw, pri) \ + { TTY, -1, 0, 0, pri, { tty: { dev, raw } } } + +#define SOCK_CHAN_INIT(port, pri) \ + { SOCKET, -1, 0, 0, pri, { sock: { port, 0, -1 } } } + +#define FD_CHAN_INIT(fd, pri) { FD, fd, 0, 0, pri } + +#define COPY_CHAN_INIT() { COPY } + +#define PTY_IO_CHAN_INIT(announce, data, raw, pri) \ + { PTY_CHAN_INIT(announce, data, raw, pri), COPY_CHAN_INIT() } + +#define PTS_IO_CHAN_INIT(announce, data, raw, pri) \ + { PTS_CHAN_INIT(announce, data, raw, pri), COPY_CHAN_INIT() } + +#define XTERM_IO_CHAN_INIT(n, title, raw, pri) \ + { XTERM_CHAN_INIT(n, title, raw, pri), COPY_CHAN_INIT() } + +#define STDIO_IO_CHAN_INIT(pri) \ + { { FD, 0, 0, 0, pri, { } }, { FD, 1, 0, 0, pri, { } } } + +#define TTY_IO_CHAN_INIT(dev, raw, pri) \ + { TTY_CHAN_INIT(dev, raw, pri), COPY_CHAN_INIT() } + +#define SOCK_IO_CHAN_INIT(port, pri) \ + { SOCK_CHAN_INIT(port, pri), COPY_CHAN_INIT() } + +#define FD_IO_CHAN_INIT(in, out, pri) \ + { FD_CHAN_INIT(in, pri), FD_CHAN_INIT(out, pri) } + +#define CHAN_IN_FD(chan) ((chan).in.fd) +#define CHAN_OUT_FD(chan) (((chan).out.type == COPY) ? ((chan).in.fd) : \ + ((chan).out.fd)) + +struct chan_opts { + void (*announce)(char *dev_name, int dev); + int dev; + char *xterm_title; + int raw_pty; +}; + +extern int tty_read(struct chan *chan, void *tty); +extern void tty_eof(void *tty_ptr, int *hung_up); +extern void tty_interrupt(struct io_chan *chan, void *tty_ptr, int count); +extern void tty_receive_char(void *tty_ptr, char ch); +extern int open_chan_pair(struct io_chan *chan, + int (*irq_setup)(int fd, void *data), void *data); +extern int open_fd(struct chan *chan); +extern int open_xterm(struct chan *chan); +extern int open_pty(struct chan *chan); +extern int open_tty(struct chan *chan); +extern int open_pts(struct chan *chan); +extern int open_socket(struct chan *chan); +extern int write_chan(struct io_chan *chan, const char *buf, int len); +extern int parse_chan_pair(char *str, int device, struct io_chan *chan, + int pri, struct chan_opts *opts); +extern void accept_connection(struct chan *chan); +extern void close_chan_pair(struct io_chan *chan); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/debug.h linux.ac/arch/um/include/debug.h --- linux.vanilla/arch/um/include/debug.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/debug.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,9 @@ +#ifndef __DEBUG_H +#define __DEBUG_H + +extern void debugger_proxy(int status, pid_t pid); +extern void child_proxy(pid_t pid, int status); +extern void init_proxy (pid_t, int, int); +extern int start_debugger(char *prog, int startup, int stop, int *debugger_fd); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/kern.h linux.ac/arch/um/include/kern.h --- linux.vanilla/arch/um/include/kern.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/kern.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __KERN_H__ +#define __KERN_H__ + +/* These are all user-mode things which are convenient to call directly + * from kernel code and for which writing a wrapper is too much of a pain. + * The regular include files can't be included because this file is included + * only into kernel code, and user-space includes conflict with kernel + * includes. + */ + +extern int errno; + +extern int getpid(void); +extern int clone(int (*proc)(void *), void *sp, int flags, void *data); +extern int sleep(int); +extern int printf(char *fmt, ...); +extern char *strerror(int errnum); +extern char *ptsname(int __fd); +extern int munmap(void *, int); +extern void *sbrk(int increment); +extern void *malloc(int size); +extern void perror(char *err); +extern int kill(int pid, int sig); +extern int getuid(void); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/kern_util.h linux.ac/arch/um/include/kern_util.h --- linux.vanilla/arch/um/include/kern_util.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/kern_util.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __KERN_UTIL_H__ +#define __KERN_UTIL_H__ + +#include "sysdep/ptrace.h" + +#ifndef _UNISTD_H +extern int write(int, const char *, int); +#endif + +extern int ncpus; +extern char *linux_prog; +extern char *gdb_init; +extern int kmalloc_ok; + +#define ROUND_DOWN(addr) ((void *)(((unsigned long) addr) & PAGE_MASK)) +#define ROUND_UP(addr) ROUND_DOWN(((unsigned long) addr) + PAGE_SIZE - 1) + +extern int kernel_fork(unsigned long flags, int (*fn)(void *), void * arg); +extern unsigned long stack_sp(unsigned long page); +extern int kernel_thread_proc(void *data); +extern long execute_syscall(struct sys_pt_regs regs); +extern void syscall_segv(int sig); +extern int current_pid(void); +extern void set_init_pid(int pid); +extern unsigned long alloc_stack(void); +extern int do_signal(unsigned long *error, int *again_out); +extern int is_stack_fault(unsigned long sp); +extern unsigned long segv(unsigned long address, unsigned long ip, + int is_write, int is_user); +extern int set_user_thread(void *task, int on, int restore_regs, + int protect_mem); +extern void syscall_ready(void); +extern void set_tracing(void *t, int tracing); +extern int is_tracing(void *task); +extern int segv_syscall(void); +extern void ret_from_sys_call(void *t); +extern void kern_finish_exec(void *task, int new_pid, unsigned long stack); +extern int page_size(void); +extern int page_mask(void); +extern int need_finish_fork(void); +extern int do_proc_op(void *t, int proc_id); +extern void free_stack(unsigned long stack); +extern void add_input_request(int op, void (*proc)(int), void *arg); +extern int sys_execve(char *file, char **argv, char **env); +extern char *current_cmd(void); +extern void timer_handler(int sig, void *sc, int usermode); +extern int set_signals(int enable); +extern void force_sigbus(void); +extern int pid_to_processor_id(int pid); +extern void block_signals(void); +extern void unblock_signals(void); +extern void deliver_signals(void *t); +extern void lock_syscall(void); +extern void unlock_syscall(void); +extern void lock_trap(void); +extern void unlock_trap(void); +extern void lock_pid(void); +extern void unlock_pid(void); +extern int cpu_idle(void); +extern void finish_fork(void); +extern void *get_current_task(void); +extern void paging_init(void); +extern unsigned long um_virt_to_phys(void *t, unsigned long addr); +extern void init_flush_vm(void); +extern void *process_state(void *t, unsigned long *cr2_out, int *err_out); +extern struct sys_pt_regs *syscall_state(void *t, void **stack_out, + int *size_out); +extern void syscall_trace(void); +extern int hz(void); +extern int switching_modes(void *t); +extern void idle_timer(void); +extern unsigned int do_IRQ(int irq, int user_mode); +extern int external_pid(void *t); +extern void boot_timer_handler(int sig); +extern void interrupt_end(void); +extern void tracing_reboot(void); +extern void tracing_halt(void); +extern void tracing_cb(void (*proc)(void *), void *arg); +extern void debugger_signal(int status, int pid); +extern void child_signal(int pid, int status); +extern int init_ptrace_proxy(int idle_pid, int startup, int stop); +extern void check_stack_overflow(void *ptr); +extern void relay_signal(int sig, void *sc, int usermode); +extern int singlestepping(void *t); +extern void not_implemented(void); +extern void setup_kernel_stack(void); +extern void set_syscall_regs(void *t); +extern void finish_fork_handler(int sig); +extern int user_context(unsigned long sp); +extern void timer_irq(int user_mode); +extern void set_repeat_syscall(int again); +extern int get_repeat_syscall(void *t); +extern void set_sigreturn_syscall(int syscall); +extern void force_flush_all(void); +extern void unprotect_stack(unsigned long stack); +extern void kern_start_exec(int new_pid); +extern void do_exitcalls(void); +extern int get_restore_regs(void *t); +extern int attach_debugger(int idle_pid, int pid, int stop); +extern void *round_up(unsigned long addr); +extern void *round_down(unsigned long addr); +extern void bad_segv(unsigned long address, unsigned long ip, int is_write); +extern int config_gdb(char *str); +extern int remove_gdb(void); +extern char *uml_strdup(char *string); +extern void unprotect_kernel_mem(void); +extern void protect_kernel_mem(void); +extern unsigned long get_kmem_end(void); +extern void set_kmem_end(unsigned long); +extern void *diff_stacks(int size, char *stack1, struct sys_pt_regs *regs1, + char *stack2, struct sys_pt_regs *regs2, + char **mask_out, struct sys_pt_regs *regs_out, + struct sys_pt_regs *regs_out_mask); +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/mconsole.h linux.ac/arch/um/include/mconsole.h --- linux.vanilla/arch/um/include/mconsole.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/mconsole.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) + * Licensed under the GPL + */ + +#ifndef __MCONSOLE_H__ +#define __MCONSOLE_H__ + +struct mconsole_request +{ + int has_correct_credentials; + int len; + char buf[512]; + + int originating_fd; + int originlen; + unsigned char origin[128]; /* sockaddr_un */ +}; + +struct mconsole_command +{ + char *command; + void (*handler)(struct mconsole_request *req); +}; + +extern char socket_name[]; + +extern int unlink_socket(void); +extern int mconsole_reply(struct mconsole_request *req, char *reply); +extern void mconsole_version(struct mconsole_request *req); +extern void mconsole_help(struct mconsole_request *req); +extern void mconsole_halt(struct mconsole_request *req); +extern void mconsole_reboot(struct mconsole_request *req); +extern void mconsole_config(struct mconsole_request *req); +extern void mconsole_remove(struct mconsole_request *req); +extern void mconsole_sysrq(struct mconsole_request *req); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/mconsole_kern.h linux.ac/arch/um/include/mconsole_kern.h --- linux.vanilla/arch/um/include/mconsole_kern.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/mconsole_kern.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __MCONSOLE_KERN_H__ +#define __MCONSOLE_KERN_H__ + +#include "linux/list.h" +#include "mconsole.h" + +struct mconsole_entry { + struct list_head list; + struct mconsole_request request; +}; + +struct mc_device { + struct list_head list; + char *name; + int (*config)(char *); + int (*remove)(char *); +}; + +#ifdef CONFIG_MCONSOLE + +extern void mconsole_register_dev(struct mc_device *new); + +#else + +static inline void mconsole_register_dev(struct mc_device *new) +{ +} + +#endif + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/mem_user.h linux.ac/arch/um/include/mem_user.h --- linux.vanilla/arch/um/include/mem_user.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/mem_user.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,60 @@ +/* + * arch/um/include/mem_user.h + * + * BRIEF MODULE DESCRIPTION + * user side memory interface for support IO memory inside user mode linux + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: RidgeRun, Inc. + * Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MEM_USER_H +#define _MEM_USER_H + +#define ROUND_4M(n) ((((unsigned long) (n)) + (1 << 22)) & ~((1 << 22) - 1)) + +extern unsigned long host_task_size; +extern unsigned long task_size; + +extern int create_mem_file(unsigned long len); +extern void setup_range(int fd, char *driver, unsigned long start, + unsigned long usable, unsigned long total); +extern void map(unsigned long virt, void *p, unsigned long len, int r, + int w, int x); +extern void parse_iomem(char *str); +extern void setup_memory(void); +extern unsigned long find_iomem(char *driver, unsigned long *len_out); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/process.h linux.ac/arch/um/include/process.h --- linux.vanilla/arch/um/include/process.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/process.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __PROCESS_H__ +#define __PROCESS_H__ + +#include + +extern void sig_handler(int sig, struct sigcontext sc); +extern void irq_handler(int sig, struct sigcontext sc); +extern void alarm_handler(int sig, struct sigcontext sc); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/signal_kern.h linux.ac/arch/um/include/signal_kern.h --- linux.vanilla/arch/um/include/signal_kern.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/signal_kern.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SIGNAL_KERN_H__ +#define __SIGNAL_KERN_H__ + +#include "sysdep/ptrace.h" + +extern void signal_deliverer(int sig); +extern struct sys_pt_regs *signal_state(void *t); +extern int probe_stack(unsigned long sp, int delta); +extern int have_signals(void *t); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/signal_user.h linux.ac/arch/um/include/signal_user.h --- linux.vanilla/arch/um/include/signal_user.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/signal_user.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SIGNAL_USER_H__ +#define __SIGNAL_USER_H__ + +extern int signal_stack_size; + +extern void change_sig(int signal, int on); +extern void signal_handler(void *task, unsigned long handler, int sig); +extern void set_sigstack(void *stack, int size); +extern void set_handler(int sig, void (*handler)(int), int flags, ...); +extern void setup_stack(unsigned long stack_top, struct sys_pt_regs *regs_out); +extern void setup_signal_stack(void); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-i386/ptrace.h linux.ac/arch/um/include/sysdep-i386/ptrace.h --- linux.vanilla/arch/um/include/sysdep-i386/ptrace.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-i386/ptrace.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_I386_PTRACE_H +#define __SYSDEP_I386_PTRACE_H + +#define UM_MAX_REG (17) +#define UM_MAX_REG_OFFSET (UM_MAX_REG * sizeof(long)) + +struct sys_pt_regs { + unsigned long regs[UM_MAX_REG]; +}; + +#define EMPTY_REGS { { [ 0 ... UM_MAX_REG - 1 ] = 0 } } + +#define UM_REG(r, n) ((r)->regs[n]) + +#define UM_IP(r) UM_REG(r, EIP) +#define UM_SP(r) UM_REG(r, UESP) +#define UM_ELF_ZERO(r) UM_REG(r, EDX) + +#define UM_SYSCALL_RET(r) UM_REG(r, EAX) +#define UM_SYSCALL_NR(r) UM_REG(r, ORIG_EAX) + +#define UM_SYSCALL_ARG1(r) UM_REG(r, EBX) +#define UM_SYSCALL_ARG2(r) UM_REG(r, ECX) +#define UM_SYSCALL_ARG3(r) UM_REG(r, EDX) +#define UM_SYSCALL_ARG4(r) UM_REG(r, ESI) +#define UM_SYSCALL_ARG5(r) UM_REG(r, EDI) +#define UM_SYSCALL_ARG6(r) UM_REG(r, EBP) + +#define UM_IP_OFFSET (EIP * sizeof(long)) +#define UM_SP_OFFSET (UESP * sizeof(long)) +#define UM_ELF_ZERO_OFFSET (EDX * sizeof(long)) + +#define UM_SYSCALL_RET_OFFSET (EAX * sizeof(long)) +#define UM_SYSCALL_NR_OFFSET (ORIG_EAX * sizeof(long)) + +#define UM_SYSCALL_ARG1_OFFSET (EBX * sizeof(long)) +#define UM_SYSCALL_ARG2_OFFSET (ECX * sizeof(long)) +#define UM_SYSCALL_ARG3_OFFSET (EDX * sizeof(long)) +#define UM_SYSCALL_ARG4_OFFSET (ESI * sizeof(long)) +#define UM_SYSCALL_ARG5_OFFSET (EDI * sizeof(long)) +#define UM_SYSCALL_ARG6_OFFSET (EBP * sizeof(long)) + +#define UM_SET_SYSCALL_RETURN(r, result) UM_REG(r, EAX) = (result) + +#define UM_FIX_EXEC_STACK(sp) do ; while(0) + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-i386/sigcontext.h linux.ac/arch/um/include/sysdep-i386/sigcontext.h --- linux.vanilla/arch/um/include/sysdep-i386/sigcontext.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-i386/sigcontext.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYS_SIGCONTEXT_I386_H +#define __SYS_SIGCONTEXT_I386_H + +#define UM_ALLOCATE_SC(name) struct sigcontext name +#define SC_FAULT_ADDR(sc) ((sc)->cr2) +#define SC_FAULT_WRITE(sc) (((sc)->err) & 2) +#define SC_IP(sc) ((sc)->eip) +#define SC_SP(sc) ((sc)->esp_at_signal) +#define SEGV_IS_FIXABLE(sc) (((sc)->trapno == 14) || ((sc)->trapno == 13)) + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-i386/syscalls.h linux.ac/arch/um/include/sysdep-i386/syscalls.h --- linux.vanilla/arch/um/include/sysdep-i386/syscalls.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-i386/syscalls.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "asm/unistd.h" + +typedef long syscall_handler_t(struct sys_pt_regs regs); + +#define EXECUTE_SYSCALL(syscall, regs) (*sys_call_table[syscall])(regs) + +extern syscall_handler_t sys_modify_ldt; +extern syscall_handler_t old_mmap_i386; +extern syscall_handler_t old_select; + +#define ARCH_SYSCALLS \ + [ __NR_mmap ] = old_mmap_i386, \ + [ __NR_select ] = old_select, \ + [ __NR_vm86old ] = sys_ni_syscall, \ + [ __NR_modify_ldt ] = sys_modify_ldt, \ + [ __NR_lchown32 ] = sys_lchown, \ + [ __NR_getuid32 ] = sys_getuid, \ + [ __NR_getgid32 ] = sys_getgid, \ + [ __NR_geteuid32 ] = sys_geteuid, \ + [ __NR_getegid32 ] = sys_getegid, \ + [ __NR_setreuid32 ] = sys_setreuid, \ + [ __NR_setregid32 ] = sys_setregid, \ + [ __NR_getgroups32 ] = sys_getgroups, \ + [ __NR_setgroups32 ] = sys_setgroups, \ + [ __NR_fchown32 ] = sys_fchown, \ + [ __NR_setresuid32 ] = sys_setresuid, \ + [ __NR_getresuid32 ] = sys_getresuid, \ + [ __NR_setresgid32 ] = sys_setresgid, \ + [ __NR_getresgid32 ] = sys_getresgid, \ + [ __NR_chown32 ] = sys_chown, \ + [ __NR_setuid32 ] = sys_setuid, \ + [ __NR_setgid32 ] = sys_setgid, \ + [ __NR_setfsuid32 ] = sys_setfsuid, \ + [ __NR_setfsgid32 ] = sys_setfsgid, \ + [ __NR_pivot_root ] = sys_pivot_root, \ + [ __NR_mincore ] = sys_mincore, \ + [ __NR_madvise ] = sys_madvise, + +#define LAST_SYSCALL __NR_fcntl64 + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-ia64/ptrace.h linux.ac/arch/um/include/sysdep-ia64/ptrace.h --- linux.vanilla/arch/um/include/sysdep-ia64/ptrace.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-ia64/ptrace.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_IA64_PTRACE_H +#define __SYSDEP_IA64_PTRACE_H + +struct sys_pt_regs { + int foo; +}; + +#define EMPTY_REGS { 0 } + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-ia64/sigcontext.h linux.ac/arch/um/include/sysdep-ia64/sigcontext.h --- linux.vanilla/arch/um/include/sysdep-ia64/sigcontext.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-ia64/sigcontext.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_IA64_SIGCONTEXT_H +#define __SYSDEP_IA64_SIGCONTEXT_H + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-ia64/syscalls.h linux.ac/arch/um/include/sysdep-ia64/syscalls.h --- linux.vanilla/arch/um/include/sysdep-ia64/syscalls.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-ia64/syscalls.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_IA64_SYSCALLS_H +#define __SYSDEP_IA64_SYSCALLS_H + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-ppc/ptrace.h linux.ac/arch/um/include/sysdep-ppc/ptrace.h --- linux.vanilla/arch/um/include/sysdep-ppc/ptrace.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-ppc/ptrace.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,96 @@ +/* + * Licensed under the GPL + */ + +#ifndef __SYS_PTRACE_PPC_H +#define __SYS_PTRACE_PPC_H + +#include "linux/config.h" +#include "linux/types.h" + +/* the following taken from */ + +#ifdef CONFIG_PPC64 +#define PPC_REG unsigned long /*long*/ +#else +#define PPC_REG unsigned long +#endif +struct sys_pt_regs_s { + PPC_REG gpr[32]; + PPC_REG nip; + PPC_REG msr; + PPC_REG orig_gpr3; /* Used for restarting system calls */ + PPC_REG ctr; + PPC_REG link; + PPC_REG xer; + PPC_REG ccr; + PPC_REG mq; /* 601 only (not used at present) */ + /* Used on APUS to hold IPL value. */ + PPC_REG trap; /* Reason for being here */ + PPC_REG dar; /* Fault registers */ + PPC_REG dsisr; + PPC_REG result; /* Result of a system call */ +}; + +#define NUM_REGS (sizeof(struct sys_pt_regs_s) / sizeof(PPC_REG)) + +struct sys_pt_regs { + PPC_REG regs[sizeof(struct sys_pt_regs_s) / sizeof(PPC_REG)]; +}; + +#define UM_MAX_REG (PT_FPR0) +#define UM_MAX_REG_OFFSET (UM_MAX_REG * sizeof(PPC_REG)) + +#define EMPTY_REGS { { [ 0 ... NUM_REGS - 1] = 0 } } + +#define UM_REG(r, n) ((r)->regs[n]) + +#define UM_SYSCALL_RET(r) UM_REG(r, PT_R3) +#define UM_SP(r) UM_REG(r, PT_R1) +#define UM_IP(r) UM_REG(r, PT_NIP) +#define UM_ELF_ZERO(r) UM_REG(r, PT_FPSCR) +#define UM_SYSCALL_NR(r) UM_REG(r, PT_R0) +#define UM_SYSCALL_ARG1(r) UM_REG(r, PT_ORIG_R3) +#define UM_SYSCALL_ARG2(r) UM_REG(r, PT_R4) +#define UM_SYSCALL_ARG3(r) UM_REG(r, PT_R5) +#define UM_SYSCALL_ARG4(r) UM_REG(r, PT_R6) +#define UM_SYSCALL_ARG5(r) UM_REG(r, PT_R7) +#define UM_SYSCALL_ARG6(r) UM_REG(r, PT_R8) + +#define UM_SYSCALL_NR_OFFSET (PT_R0 * sizeof(PPC_REG)) +#define UM_SYSCALL_RET_OFFSET (PT_R3 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG1_OFFSET (PT_R3 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG2_OFFSET (PT_R4 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG3_OFFSET (PT_R5 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG4_OFFSET (PT_R6 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG5_OFFSET (PT_R7 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG6_OFFSET (PT_R8 * sizeof(PPC_REG)) +#define UM_SP_OFFSET (PT_R1 * sizeof(PPC_REG)) +#define UM_IP_OFFSET (PT_NIP * sizeof(PPC_REG)) +#define UM_ELF_ZERO_OFFSET (PT_R3 * sizeof(PPC_REG)) + +#define UM_SET_SYSCALL_RETURN(_regs, result) \ +do { \ + if (result < 0) { \ + (_regs)->regs[PT_CCR] |= 0x10000000; \ + UM_SYSCALL_RET((_regs)) = -result; \ + } else { \ + UM_SYSCALL_RET((_regs)) = result; \ + } \ +} while(0) + +extern void shove_aux_table(unsigned long sp); +#define UM_FIX_EXEC_STACK(sp) shove_aux_table(sp); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-ppc/sigcontext.h linux.ac/arch/um/include/sysdep-ppc/sigcontext.h --- linux.vanilla/arch/um/include/sysdep-ppc/sigcontext.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-ppc/sigcontext.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYS_SIGCONTEXT_PPC_H +#define __SYS_SIGCONTEXT_PPC_H + +#define UM_ALLOCATE_SC(name) \ + struct sys_pt_regs name##_regs; \ + struct sigcontext name; \ + name.regs = &name##_regs + +#define DSISR_WRITE 0x02000000 + +#define SC_FAULT_ADDR(sc) ({ \ + struct sigcontext_struct *_sc = (sc); \ + long retval = -1; \ + switch (_sc->regs->trap) { \ + case 0x300: \ + /* data exception */ \ + retval = _sc->regs->dar; \ + break; \ + case 0x400: \ + /* instruction exception */ \ + retval = _sc->regs->nip; \ + break; \ + default: \ + panic("SC_FAULT_ADDR: unhandled trap type\n"); \ + } \ + retval; \ + }) + +#define SC_FAULT_WRITE(sc) ({ \ + struct sigcontext_struct *_sc = (sc); \ + long retval = -1; \ + switch (_sc->regs->trap) { \ + case 0x300: \ + /* data exception */ \ + retval = !!(_sc->regs->dsisr & DSISR_WRITE); \ + break; \ + case 0x400: \ + /* instruction exception: not a write */ \ + retval = 0; \ + break; \ + default: \ + panic("SC_FAULT_ADDR: unhandled trap type\n"); \ + } \ + retval; \ + }) + +#define SC_IP(sc) ((sc)->regs->nip) +#define SC_SP(sc) ((sc)->regs->gpr[1]) +#define SEGV_IS_FIXABLE(sc) (1) + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-ppc/syscalls.h linux.ac/arch/um/include/sysdep-ppc/syscalls.h --- linux.vanilla/arch/um/include/sysdep-ppc/syscalls.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-ppc/syscalls.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +typedef long syscall_handler_t(unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, + unsigned long arg5, unsigned long arg6); + +#define EXECUTE_SYSCALL(syscall, regs) \ + (*sys_call_table[syscall])(UM_SYSCALL_ARG1(®s), \ + UM_SYSCALL_ARG2(®s), \ + UM_SYSCALL_ARG3(®s), \ + UM_SYSCALL_ARG4(®s), \ + UM_SYSCALL_ARG5(®s), \ + UM_SYSCALL_ARG6(®s)) + +extern syscall_handler_t sys_mincore; +extern syscall_handler_t sys_madvise; + +/* old_mmap needs the correct prototype since syscall_kern.c includes + * this file. + */ +int old_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long offset); + +#define ARCH_SYSCALLS \ + [ __NR_modify_ldt ] = sys_ni_syscall, \ + [ __NR_pciconfig_read ] = sys_ni_syscall, \ + [ __NR_pciconfig_write ] = sys_ni_syscall, \ + [ __NR_pciconfig_iobase ] = sys_ni_syscall, \ + [ __NR_pivot_root ] = sys_ni_syscall, \ + [ __NR_multiplexer ] = sys_ni_syscall, \ + [ __NR_mmap ] = old_mmap, \ + [ __NR_madvise ] = sys_madvise, \ + [ __NR_mincore ] = sys_mincore, + +#define LAST_SYSCALL __NR_mincore + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysrq.h linux.ac/arch/um/include/sysrq.h --- linux.vanilla/arch/um/include/sysrq.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysrq.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,6 @@ +#ifndef __UM_SYSRQ_H +#define __UM_SYSRQ_H + +extern void show_trace(unsigned long *stack); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/ubd_user.h linux.ac/arch/um/include/ubd_user.h --- linux.vanilla/arch/um/include/ubd_user.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/ubd_user.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2001 RidgeRun, Inc (glonnon@ridgerun.com) + * Licensed under the GPL + */ + +#ifndef __UM_UBD_USER_H +#define __UM_UBD_USER_H + + +struct io_thread_req { + int read; + int fds[2]; + unsigned long offsets[2]; + unsigned long long offset; + unsigned long length; + char *buffer; + int sectorsize; + unsigned long sector_mask; + unsigned long *bitmap; + unsigned long bitmap_offset; + unsigned long bitmap_start; + unsigned long bitmap_end; + int error; +}; + +extern int open_ubd_file(char *file, int *openflags, char **backing_file_out, + int *bitmap_offset_out, int *bitmap_len_out, + int *data_offset_out); +extern int create_cow_file(char *cow_file, char *backing_file, int sectorsize, + int *bitmap_offset_out, int *bitmap_len_out, + int *data_offset_out); +extern int read_cow_bitmap(int fd, void *buf, int offset, int len); +extern int read_ubd_fs(int fd, void *buffer, int len); +extern int write_ubd_fs(int fd, char *buffer, int len); +extern int start_io_thread(unsigned long sp, int *fds_out); +extern int do_io(struct io_thread_req *req); +extern int ubd_is_dir(char *file); + +static inline int ubd_test_bit(int bit, unsigned long *data) +{ + int bits, n, off; + + bits = sizeof(data[0]) * 8; + n = bit / bits; + off = bit % bits; + return((data[n] & (1 << off)) != 0); +} + +static inline void ubd_set_bit(int bit, unsigned long *data) +{ + int bits, n, off; + + bits = sizeof(data[0]) * 8; + n = bit / bits; + off = bit % bits; + data[n] |= (1 << off); +} + + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/umid.h linux.ac/arch/um/include/umid.h --- linux.vanilla/arch/um/include/umid.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/umid.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +extern int set_umid(char *name); +extern int umid_file_name(char *name, char *buf, int len); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/umn.h linux.ac/arch/um/include/umn.h --- linux.vanilla/arch/um/include/umn.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/umn.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UMN_H +#define __UMN_H + +extern int open_umn_tty(int *slave_out, int *slipno_out); +extern void close_umn_tty(int master, int slave); +extern int umn_send_packet(int fd, void *data, int len); +extern int set_umn_addr(int fd, char *addr, char *ptp_addr); +extern void slip_unesc(unsigned char s); +extern void umn_read(int fd); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/user.h linux.ac/arch/um/include/user.h --- linux.vanilla/arch/um/include/user.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/user.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __USER_H__ +#define __USER_H__ + +extern void panic(char *fmt, ...); +extern int printk(char *fmt, ...); +extern void schedule(void); +extern void *kmalloc(int size, int flags); +extern void kfree(void *ptr); +extern int in_aton(char *str); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/user_util.h linux.ac/arch/um/include/user_util.h --- linux.vanilla/arch/um/include/user_util.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/user_util.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __USER_UTIL_H__ +#define __USER_UTIL_H__ + +#include "asm/types.h" +#include "sysdep/ptrace.h" + +extern int grantpt(int __fd); +extern int unlockpt(int __fd); +extern char *ptsname(int __fd); + +#ifndef _UNISTD_H +extern int write(int, const char *, int); +#endif + +enum { OP_NONE, OP_EXEC, OP_SWITCH, OP_THREAD, OP_FORK, OP_FORK_FINISH, + OP_TRACE_ON, OP_TRACE_OFF, OP_REBOOT, OP_HALT, OP_CB }; + +struct cpu_task { + int pid; + void *task; +}; + +extern struct cpu_task cpu_tasks[]; + +extern unsigned long low_physmem; +extern unsigned long high_physmem; +extern unsigned long physmem; +extern unsigned long end_vm; +extern unsigned long start_vm; + +extern int tracing_pid; +extern int honeypot; + +extern char host_info[]; + +extern char saved_command_line[]; +extern char command_line[]; + +extern int gdb_pid; + +extern void *open_maps(void); +extern int read_map(void *fd, unsigned long *start_out, + unsigned long *end_out, char *r_out, char *w_out, + char *x_out, char *p_out); +extern void close_maps(void *fd); +extern unsigned long get_brk(void); +extern __u64 file_size(char *file); +extern void stop(void); +extern int proc_start_thread(unsigned long ip, unsigned long sp); +extern void stack_protections(unsigned long address); +extern void task_protections(unsigned long address); +extern void abandon_proc_space(int (*proc)(void *), unsigned long sp); +extern int signals(int (*init_proc)(void *), void *sp); +extern void map(unsigned long virt, void *p, unsigned long len, + int r, int w, int x); +extern int unmap(unsigned long address, unsigned long len); +extern void protect(unsigned long addr, unsigned long len, int r, int w, + int x); +extern void stop_pid(int pid); +extern void kill_pid(int pid); +extern void usr1_pid(int pid); +extern void cont_pid(int pid); +extern int __personality(int); +extern int syscall_handler(void *unused); +extern int do_syscall(void *task, int pid); +extern int wait_for_stop(int pid, int sig, int cont_type); +extern void *add_signal_handler(int sig, void (*handler)(int)); +extern void signal_init(void); +extern void finish_exec(int old_pid, int new_pid, struct sys_pt_regs *regs); +extern int start_fork_tramp(unsigned long sig_stack, unsigned long temp_stack, + int clone_vm, int (*tramp)(void *)); +extern void trace_myself(void); +extern void timer(void); +extern void get_profile_timer(void); +extern void disable_profile_timer(void); +extern void set_timers(int set_signal); +extern int clone_and_wait(int (*fn)(void *), void *arg, void *sp, int flags); +extern int input_loop(void); +extern void continue_execing_proc(int pid); +extern int linux_main(int argc, char **argv); +extern void remap_data(void *segment_start, void *segment_end); +extern void set_cmdline(char *cmd); +extern void continue_fork(void *task, int pid, struct sys_pt_regs *regs); +extern void input_cb(void (*proc)(void *), void *arg, int arg_len); +extern void setup_input(void); +extern int exit_kernel(int pid, void *task); +extern int get_pty(void); +extern void save_signal_state(int *sig_ptr); +extern void fill_in_sigcontext(void *sc, struct sys_pt_regs *regs, + unsigned long cr2, int err); +extern int activate_fd(int irq, int fd, void *dev_id); +extern void reactivate_fd(int fd); +extern void free_irq_fd(void *dev_id); +extern void forward_interrupts(int pid); +extern void init_irq_signals(int on_sigstack); +extern void *um_kmalloc(int size); +extern int raw(int fd, int complain); +extern void cooked(int fd); +extern int switcheroo(int fd, int prot, void *from, void *to, int size); +extern void idle_sleep(int secs); +extern int get_one_stack(int (*proc)(void *), void *arg, char **stack_out, + struct sys_pt_regs *regs_out); +extern void setup_machinename(char *machine_out); +extern void setup_hostinfo(void); +extern void add_arg(char *cmd_line, char *arg); +extern void sigio_handler(int sig, void *sc, int usermode); +extern void init_new_thread(void *sig_stack, void (*usr1_handler)(int)); +extern void start_exec(int old_pid, int new_pid, int *error, + struct sys_pt_regs *regs); +extern void attach_process(int pid); +extern void calc_sigframe_size(void); +extern int fork_tramp(void *sig_stack); +extern void do_exec(int old_pid, int new_pid); +extern void tracer_panic(char *msg, ...); +extern void close_fd(int); +extern char *get_umid(void); +extern void ptrace_cont_pid(int pid); +extern void create_pid_file(char *name); +extern int load_initrd(char *filename, void *buf, int size); +extern int ptrace_getregs(long pid, struct sys_pt_regs *regs_out); +extern int ptrace_setregs(long pid, struct sys_pt_regs *regs_in); +extern void do_longjmp(void *p); +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/Makefile linux.ac/arch/um/kernel/Makefile --- linux.vanilla/arch/um/kernel/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/Makefile Wed Oct 10 01:47:36 2001 @@ -0,0 +1,78 @@ +OBJ = um.o + +OBJS = process.o current.o exec_kern.o exec_user.o init_task.o irq.o \ + irq_user.o mem.o mem_user.o ptrace.o reboot.o resource.o \ + setup.o signal_user.o smp.o syscall_kern.o syscall_user.o \ + sysrq.o sys_call_table.o time.o time_kern.o tlb.o trap_kern.o \ + trap_user.o uaccess_user.o um_arch.o umid.o user_util.o + +OX_OBJS = ksyms.o process_kern.o signal_kern.o user_syms.o + +UNMAP_CFLAGS := $(patsubst -pg -DPROFILING,,$(USER_CFLAGS)) +UNMAP_CFLAGS := $(patsubst -fprofile-arcs -ftest-coverage,,$(UNMAP_CFLAGS)) + +ifeq ($(CONFIG_MODULES), y) + DMODULES = -D__CONFIG_MODULES__ +endif + +ifeq ($(CONFIG_MODVERSIONS), y) + DMODVERSIONS = -D__CONFIG_MODVERSIONS__ +endif + +CFLAGS_user_syms.o = -D__AUTOCONF_INCLUDED__ $(DMODULES) $(DMODVERSIONS) -I- \ + -I../include + +all: $(OBJ) unmap_fin.o + +exec_user.o: exec_user.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +irq_user.o: irq_user.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +mem_user.o: mem_user.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +process.o: process.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +signal_user.o: signal_user.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +syscall_user.o: syscall_user.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +time.o: time.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +trap_user.o: trap_user.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +uaccess_user.o: uaccess_user.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +umid.o: umid.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +user_util.o: user_util.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +unmap.o: unmap.c + echo $(UNMAP_CFLAGS) + $(CC) $(UNMAP_CFLAGS) -c -o $@ $< + +unmap_fin.o : unmap.o + ld -r -o $@ $< -lc -L/usr/lib + +$(OBJ): $(OBJS) $(OX_OBJS) + rm -f $@ + $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ + +clean: + rm -f $(OBJS) $(OX_OBJS) + +modules: + +fastdep: + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/current.c linux.ac/arch/um/kernel/current.c --- linux.vanilla/arch/um/kernel/current.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/current.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/sched.h" + +#ifndef CONFIG_SMP + +struct task_struct *current_task; + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/exec_kern.c linux.ac/arch/um/kernel/exec_kern.c --- linux.vanilla/arch/um/kernel/exec_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/exec_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/slab.h" +#include "linux/smp_lock.h" +#include "asm/ptrace.h" +#include "asm/pgtable.h" +#include "asm/pgalloc.h" +#include "asm/uaccess.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" + +static void check_vma(unsigned long addr) +{ + struct vm_area_struct *vma; + + vma = find_vma(current->mm, addr); + if((vma == NULL) || (vma->vm_start > addr)) force_sigbus(); +} + +/* See comment above fork_tramp for why sigstop is defined and used like + * this + */ + +static int sigstop = SIGSTOP; + +static int exec_tramp(void *sig_stack) +{ + int sig = sigstop; + + block_signals(); + init_new_thread(sig_stack, NULL); + kill(getpid(), sig); + return(0); +} + +void flush_thread(void) +{ + unsigned long stack; + int new_pid; + + stack = alloc_stack(); + new_pid = start_fork_tramp(current->thread.kernel_stack, + stack, 0, exec_tramp); + if(new_pid < 0){ + printk(KERN_ERR + "flush_thread : new thread failed, errno = %d\n", + errno); + do_exit(SIGKILL); + } + forward_interrupts(new_pid); + current->thread.request.op = OP_EXEC; + current->thread.request.u.exec.pid = new_pid; + unprotect_stack((unsigned long) current); + usr1_pid(getpid()); + + current->thread.extern_pid = new_pid; + free_page(stack); + protect(physmem, high_physmem - physmem, 1, 1, 0); + task_protections((unsigned long) current); + force_flush_all(); + unblock_signals(); +} + +void start_thread(struct pt_regs * regs, unsigned long eip, unsigned long esp) +{ + check_vma(current->mm->brk - 1); + check_vma(eip); + set_fs(USER_DS); + flush_tlb_mm(current->mm); + if(UM_SP(¤t->thread.process_regs) == 0) + current->thread.process_regs = current->thread.syscall_regs; + UM_IP(¤t->thread.process_regs) = eip; + UM_SP(¤t->thread.process_regs) = esp; + UM_ELF_ZERO(¤t->thread.process_regs) = 0; + UM_FIX_EXEC_STACK(esp); +} + +static int execve1(char *file, char **argv, char **env) +{ + int error; + + error = do_execve(file, argv, env, NULL); + if (error == 0){ + current->ptrace &= ~PT_DTRACE; + set_cmdline(current_cmd()); + } + return(error); +} + +int um_execve(char *file, char **argv, char **env) +{ + if(execve1(file, argv, env) == 0) set_user_thread(current, 1, 1, 1); + return(-1); +} + +int sys_execve(char *file, char **argv, char **env) +{ + int error; + char *filename; + + lock_kernel(); + filename = getname((char *) file); + error = PTR_ERR(filename); + if (IS_ERR(filename)) goto out; + error = execve1(filename, argv, env); + putname(filename); + out: + unlock_kernel(); + return(error); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/exec_user.c linux.ac/arch/um/kernel/exec_user.c --- linux.vanilla/arch/um/kernel/exec_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/exec_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" + +void do_exec(int old_pid, int new_pid) +{ + struct sys_pt_regs regs; + + if((ptrace(PTRACE_ATTACH, new_pid, 0, 0) < 0) || + (ptrace(PTRACE_CONT, new_pid, 0, 0) < 0) || + (waitpid(new_pid, 0, WUNTRACED) < 0)) + tracer_panic("do_exec failed to attach proc"); + + if(ptrace_getregs(old_pid, ®s) < 0) + tracer_panic("do_exec failed to get registers"); + + kill(old_pid, SIGKILL); + + if((ptrace_setregs(new_pid, ®s) < 0) || + (ptrace(PTRACE_CONT, new_pid, 0, 0) < 0)) + tracer_panic("do_exec failed to start new proc"); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/init_task.c linux.ac/arch/um/kernel/init_task.c --- linux.vanilla/arch/um/kernel/init_task.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/init_task.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/mm.h" +#include "linux/sched.h" +#include "asm/uaccess.h" +#include "asm/pgtable.h" +#include "user_util.h" + +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); + +/* + * Initial task structure. + * + * We need to make sure that this is 16384-byte aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ + +union task_union init_task_union +__attribute__((__section__(".data.init_task"))) = +{ INIT_TASK(init_task_union.task) }; + +struct task_struct *alloc_task_struct(void){ + struct task_struct *task; + + task = (struct task_struct *) __get_free_pages(GFP_KERNEL,2); + if(task == NULL) return(NULL); + return(task); +} + +void unprotect_stack(unsigned long stack) +{ + protect(stack, 4 * PAGE_SIZE, 1, 1, 0); +} + +void free_task_struct(struct task_struct *task) +{ + free_pages((unsigned long) task, 2); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/irq.c linux.ac/arch/um/kernel/irq.c --- linux.vanilla/arch/um/kernel/irq.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/irq.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,809 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + * Derived (i.e. mostly copied) from arch/i386/kernel/irq.c: + * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar + */ + +#include "linux/config.h" +#include "linux/kernel.h" +#include "linux/smp.h" +#include "linux/irq.h" +#include "linux/kernel_stat.h" +#include "linux/interrupt.h" +#include "linux/random.h" +#include "linux/slab.h" +#include "linux/file.h" +#include "linux/proc_fs.h" +#include "linux/init.h" +#include "asm/irq.h" +#include "asm/hw_irq.h" +#include "asm/hardirq.h" +#include "asm/atomic.h" +#include "asm/signal.h" +#include "asm/system.h" +#include "asm/errno.h" +#include "asm/uaccess.h" +#include "user_util.h" + +static void register_irq_proc (unsigned int irq); + +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = + { [0 ... NR_IRQS-1] = { 0, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED}}; + +/* + * Generic no controller code + */ + +static void enable_none(unsigned int irq) { } +static unsigned int startup_none(unsigned int irq) { return 0; } +static void disable_none(unsigned int irq) { } +static void ack_none(unsigned int irq) +{ +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves, it doesnt deserve + * a generic callback i think. + */ +#if CONFIG_X86 + printk("unexpected IRQ trap at vector %02x\n", irq); +#ifdef CONFIG_X86_LOCAL_APIC + /* + * Currently unexpected vectors happen only on SMP and APIC. + * We _must_ ack these because every local APIC has only N + * irq slots per priority level, and a 'hanging, unacked' IRQ + * holds up an irq slot - in excessive cases (when multiple + * unexpected vectors occur) that might lock up the APIC + * completely. + */ + ack_APIC_irq(); +#endif +#endif +} + +/* startup is the same as "enable", shutdown is same as "disable" */ +#define shutdown_none disable_none +#define end_none enable_none + +struct hw_interrupt_type no_irq_type = { + "none", + startup_none, + shutdown_none, + enable_none, + disable_none, + ack_none, + end_none +}; + +volatile unsigned long irq_err_count; + +/* + * Generic, controller-independent functions: + */ + +int get_irq_list(char *buf) +{ + int i, j; + struct irqaction * action; + char *p = buf; + + p += sprintf(p, " "); + for (j=0; jtypename); + p += sprintf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + p += sprintf(p, ", %s", action->name); + *p++ = '\n'; + } + p += sprintf(p, "\n"); +#if CONFIG_SMP + p += sprintf(p, "LOC: "); + for (j = 0; j < smp_num_cpus; j++) + p += sprintf(p, "%10u ", + apic_timer_irqs[cpu_logical_map(j)]); + p += sprintf(p, "\n"); +#endif + p += sprintf(p, "ERR: %10lu\n", irq_err_count); + return p - buf; +} + + +/* + * This should really return information about whether + * we should do bottom half handling etc. Right now we + * end up _always_ checking the bottom half, which is a + * waste of time and is not what some drivers would + * prefer. + */ +int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, + struct irqaction * action) +{ + int status; + int cpu = smp_processor_id(); + + irq_enter(cpu, irq); + + status = 1; /* Force the "do bottom halves" bit */ + + if (!(action->flags & SA_INTERRUPT)) + __sti(); + + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); + + irq_exit(cpu, irq); + + return status; +} + +/* + * Generic enable/disable code: this just calls + * down into the PIC-specific version for the actual + * hardware disable after having gotten the irq + * controller lock. + */ + +/** + * disable_irq_nosync - disable an irq without waiting + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables of an interrupt + * stack. Unlike disable_irq(), this function does not ensure existing + * instances of the IRQ handler have completed before returning. + * + * This function may be called from IRQ context. + */ + +void inline disable_irq_nosync(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->depth++) { + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/** + * disable_irq - disable an irq and wait for completion + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables of an interrupt + * stack. That is for two disables you need two enables. This + * function waits for any pending IRQ handlers for this interrupt + * to complete before returning. If you use this function while + * holding a resource the IRQ handler may need you will deadlock. + * + * This function may be called - with care - from IRQ context. + */ + +void disable_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + + if (!local_irq_count(smp_processor_id())) { + do { + barrier(); + } while (irq_desc[irq].status & IRQ_INPROGRESS); + } +} + +/** + * enable_irq - enable interrupt handling on an irq + * @irq: Interrupt to enable + * + * Re-enables the processing of interrupts on this IRQ line + * providing no disable_irq calls are now in effect. + * + * This function may be called from IRQ context. + */ + +void enable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + switch (desc->depth) { + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + desc->status = status; + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status = status | IRQ_REPLAY; + hw_resend_irq(desc->handler,irq); + } + desc->handler->enable(irq); + /* fall-through */ + } + default: + desc->depth--; + break; + case 0: + printk("enable_irq() unbalanced from %p\n", + __builtin_return_address(0)); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/* + * do_IRQ handles all normal device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + */ +unsigned int do_IRQ(int irq, int user_mode) +{ + /* + * 0 return value means that this irq is already being + * handled by some other CPU. (or is disabled) + */ + int cpu = smp_processor_id(); + irq_desc_t *desc = irq_desc + irq; + struct irqaction * action; + struct pt_regs regs; + unsigned int status; + + regs.user_mode = user_mode; + kstat.irqs[cpu][irq]++; + spin_lock(&desc->lock); + desc->handler->ack(irq); + /* + REPLAY is when Linux resends an IRQ that was dropped earlier + WAITING is used by probe to mark irqs that are being tested + */ + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + status |= IRQ_PENDING; /* we _want_ to handle it */ + + /* + * If the IRQ is disabled for whatever reason, we cannot + * use the action we have. + */ + action = NULL; + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + action = desc->action; + status &= ~IRQ_PENDING; /* we commit to handling */ + status |= IRQ_INPROGRESS; /* we are handling it */ + } + desc->status = status; + + /* + * If there is no IRQ handler or it was disabled, exit early. + Since we set PENDING, if another processor is handling + a different instance of this same irq, the other processor + will take care of it. + */ + if (!action) + goto out; + + /* + * Edge triggered interrupts need to remember + * pending events. + * This applies to any hw interrupts that allow a second + * instance of the same irq to arrive while we are in do_IRQ + * or in the handler. But the code here only handles the _second_ + * instance of the irq, not the third or fourth. So it is mostly + * useful for irq hardware that does not mask cleanly in an + * SMP environment. + */ + for (;;) { + spin_unlock(&desc->lock); + handle_IRQ_event(irq, ®s, action); + spin_lock(&desc->lock); + + if (!(desc->status & IRQ_PENDING)) + break; + desc->status &= ~IRQ_PENDING; + } + desc->status &= ~IRQ_INPROGRESS; +out: + /* + * The ->end() handler has to deal with interrupts which got + * disabled while the handler was running. + */ + desc->handler->end(irq); + spin_unlock(&desc->lock); + + if (softirq_pending(cpu)) + do_softirq(); + return 1; +} + +/** + * request_irq - allocate an interrupt line + * @irq: Interrupt line to allocate + * @handler: Function to be called when the IRQ occurs + * @irqflags: Interrupt type flags + * @devname: An ascii name for the claiming device + * @dev_id: A cookie passed back to the handler function + * + * This call allocates interrupt resources and enables the + * interrupt line and IRQ handling. From the point this + * call is made your handler function may be invoked. Since + * your handler function must clear any interrupt the board + * raises, you must take care both to initialise your hardware + * and to set up the interrupt handler in the right order. + * + * Dev_id must be globally unique. Normally the address of the + * device data structure is used as the cookie. Since the handler + * receives this value it makes sense to use it. + * + * If your interrupt is shared you must pass a non NULL dev_id + * as this is required when freeing the interrupt. + * + * Flags: + * + * SA_SHIRQ Interrupt is shared + * + * SA_INTERRUPT Disable local interrupts while processing + * + * SA_SAMPLE_RANDOM The interrupt can be used for entropy + * + */ + +int request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char * devname, + void *dev_id) +{ + int retval; + struct irqaction * action; + +#if 1 + /* + * Sanity-check: shared interrupts should REALLY pass in + * a real dev-ID, otherwise we'll have trouble later trying + * to figure out which interrupt is which (messes up the + * interrupt freeing logic etc). + */ + if (irqflags & SA_SHIRQ) { + if (!dev_id) + printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]); + } +#endif + + if (irq >= NR_IRQS) + return -EINVAL; + if (!handler) + return -EINVAL; + + action = (struct irqaction *) + kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + retval = setup_irq(irq, action); + if (retval) + kfree(action); + return retval; +} + +int um_request_irq(unsigned int irq, int fd, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, + void *dev_id) +{ + int retval; + + retval = request_irq(irq, handler, irqflags, devname, dev_id); + if(retval) return(retval); + return(activate_fd(irq, fd, dev_id)); +} + +/* this was setup_x86_irq but it seems pretty generic */ +int setup_irq(unsigned int irq, struct irqaction * new) +{ + int shared = 0; + unsigned long flags; + struct irqaction *old, **p; + irq_desc_t *desc = irq_desc + irq; + + /* + * Some drivers like serial.c use request_irq() heavily, + * so we have to be careful not to interfere with a + * running system. + */ + if (new->flags & SA_SAMPLE_RANDOM) { + /* + * This function might sleep, we want to call it first, + * outside of the atomic block. + * Yes, this might clear the entropy pool if the wrong + * driver is attempted to be loaded, without actually + * installing a new handler, but is this really a problem, + * only the sysadmin is able to do this. + */ + rand_initialize_irq(irq); + } + + /* + * The following block of code has to be executed atomically + */ + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) { + spin_unlock_irqrestore(&desc->lock,flags); + return -EBUSY; + } + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + *p = new; + + if (!shared) { + desc->depth = 0; + desc->status &= ~IRQ_DISABLED; + desc->handler->startup(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + + register_irq_proc(irq); + return 0; +} + +/** + * free_irq - free an interrupt + * @irq: Interrupt line to free + * @dev_id: Device identity to free + * + * Remove an interrupt handler. The handler is removed and if the + * interrupt line is no longer in use by any driver it is disabled. + * On a shared IRQ the caller must ensure the interrupt is disabled + * on the card it drives before calling this function. The function + * does not return until any executing interrupts for this IRQ + * have completed. + * + * This function may be called from interrupt context. + * + * Bugs: Attempting to free an irq in a handler for the same irq hangs + * the machine. + */ + +void free_irq(unsigned int irq, void *dev_id) +{ + irq_desc_t *desc; + struct irqaction **p; + unsigned long flags; + + if (irq >= NR_IRQS) + return; + + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + for (;;) { + struct irqaction * action = *p; + if (action) { + struct irqaction **pp = p; + p = &action->next; + if (action->dev_id != dev_id) + continue; + + /* Found it - now remove it from the list of entries */ + *pp = action->next; + if (!desc->action) { + desc->status |= IRQ_DISABLED; + desc->handler->shutdown(irq); + } + free_irq_fd(dev_id); + spin_unlock_irqrestore(&desc->lock,flags); + +#ifdef CONFIG_SMP + /* Wait to make sure it's not being used on another CPU */ + while (desc->status & IRQ_INPROGRESS) + barrier(); +#endif + kfree(action); + return; + } + printk("Trying to free free IRQ%d\n",irq); + spin_unlock_irqrestore(&desc->lock,flags); + return; + } +} + +static struct proc_dir_entry * root_irq_dir; +static struct proc_dir_entry * irq_dir [NR_IRQS]; +static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; + +static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; + +#define HEX_DIGITS 8 + +static int irq_affinity_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", irq_affinity[(long)data]); +} + +static unsigned int parse_hex_value (const char *buffer, + unsigned long count, unsigned long *ret) +{ + unsigned char hexnum [HEX_DIGITS]; + unsigned long value; + int i; + + if (!count) + return -EINVAL; + if (count > HEX_DIGITS) + count = HEX_DIGITS; + if (copy_from_user(hexnum, buffer, count)) + return -EFAULT; + + /* + * Parse the first 8 characters as a hex string, any non-hex char + * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same. + */ + value = 0; + + for (i = 0; i < count; i++) { + unsigned int c = hexnum[i]; + + switch (c) { + case '0' ... '9': c -= '0'; break; + case 'a' ... 'f': c -= 'a'-10; break; + case 'A' ... 'F': c -= 'A'-10; break; + default: + goto out; + } + value = (value << 4) | c; + } +out: + *ret = value; + return 0; +} + +static int irq_affinity_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int irq = (long) data, full_count = count, err; + unsigned long new_value; + + if (!irq_desc[irq].handler->set_affinity) + return -EIO; + + err = parse_hex_value(buffer, count, &new_value); + +#if CONFIG_SMP + /* + * Do not allow disabling IRQs completely - it's a too easy + * way to make the system unusable accidentally :-) At least + * one online CPU still has to be targeted. + */ + if (!(new_value & cpu_online_map)) + return -EINVAL; +#endif + + irq_affinity[irq] = new_value; + irq_desc[irq].handler->set_affinity(irq, new_value); + + return full_count; +} + +static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long *mask = (unsigned long *) data; + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", *mask); +} + +static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned long *mask = (unsigned long *) data, full_count = count, err; + unsigned long new_value; + + err = parse_hex_value(buffer, count, &new_value); + if (err) + return err; + + *mask = new_value; + return full_count; +} + +#define MAX_NAMELEN 10 + +static void register_irq_proc (unsigned int irq) +{ + struct proc_dir_entry *entry; + char name [MAX_NAMELEN]; + + if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) || + irq_dir[irq]) + return; + + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + irq_dir[irq] = proc_mkdir(name, root_irq_dir); + + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + + entry->nlink = 1; + entry->data = (void *)(long)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + + smp_affinity_entry[irq] = entry; +} + +unsigned long prof_cpu_mask = -1; + +void init_irq_proc (void) +{ + struct proc_dir_entry *entry; + int i; + + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", 0); + + /* create /proc/irq/prof_cpu_mask */ + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); + + entry->nlink = 1; + entry->data = (void *)&prof_cpu_mask; + entry->read_proc = prof_cpu_mask_read_proc; + entry->write_proc = prof_cpu_mask_write_proc; + + /* + * Create entries for all existing IRQs. + */ + for (i = 0; i < NR_IRQS; i++) + register_irq_proc(i); +} + +unsigned long probe_irq_on(void) +{ + return(0); +} + +int probe_irq_off(unsigned long val) +{ + return(0); +} + +static unsigned int startup_SIGIO_irq(unsigned int irq) +{ + return(0); +} + +static void shutdown_SIGIO_irq(unsigned int irq) +{ +} + +static void enable_SIGIO_irq(unsigned int irq) +{ +} + +static void disable_SIGIO_irq(unsigned int irq) +{ +} + +static void mask_and_ack_SIGIO(unsigned int irq) +{ +} + +static void end_SIGIO_irq(unsigned int irq) +{ +} + +static unsigned int startup_SIGVTALRM_irq(unsigned int irq) +{ + return(0); +} + +static void shutdown_SIGVTALRM_irq(unsigned int irq) +{ +} + +static void enable_SIGVTALRM_irq(unsigned int irq) +{ +} + +static void disable_SIGVTALRM_irq(unsigned int irq) +{ +} + +static void mask_and_ack_SIGVTALRM(unsigned int irq) +{ +} + +static void end_SIGVTALRM_irq(unsigned int irq) +{ +} + +static struct hw_interrupt_type SIGIO_irq_type = { + "SIGIO", + startup_SIGIO_irq, + shutdown_SIGIO_irq, + enable_SIGIO_irq, + disable_SIGIO_irq, + mask_and_ack_SIGIO, + end_SIGIO_irq, + NULL +}; + +static struct hw_interrupt_type SIGVTALRM_irq_type = { + "SIGVTALRM", + startup_SIGVTALRM_irq, + shutdown_SIGVTALRM_irq, + enable_SIGVTALRM_irq, + disable_SIGVTALRM_irq, + mask_and_ack_SIGVTALRM, + end_SIGVTALRM_irq, + NULL +}; + +void __init init_IRQ(void) +{ + int i; + + irq_desc[TIMER_IRQ].status = IRQ_DISABLED; + irq_desc[TIMER_IRQ].action = 0; + irq_desc[TIMER_IRQ].depth = 1; + irq_desc[TIMER_IRQ].handler = &SIGVTALRM_irq_type; + enable_irq(TIMER_IRQ); + for(i=1;i +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "process.h" +#include "signal_user.h" + +struct irq_fd { + struct irq_fd *next; + void *id; + int fd; + int irq; + int pid; +}; + +static struct irq_fd *active_fds = NULL; +static fd_set active_fd_mask; +static int max_fd = -1; + +extern int io_count, intr_count; + +void sigio_handler(int sig, void *sc, int usermode) +{ + struct irq_fd *irq_fd, *next; + struct timeval tv; + fd_set fds; + int i, n, fd; + + while(1){ + tv.tv_sec = 0; + tv.tv_usec = 0; + fds = active_fd_mask; + if((n = select(max_fd + 1, &fds, NULL, NULL, &tv)) < 0){ + if(errno == EINTR) continue; + printk("sigio_handler : select returned %d, " + "errno = %d\n", n, errno); + break; + } + if(n == 0) break; + for(i=0;i<=max_fd;i++){ + if(FD_ISSET(i, &fds)) FD_CLR(i, &active_fd_mask); + } + for(irq_fd=active_fds;irq_fd != NULL;irq_fd = next){ + /* These mysterious assignments protect us against + * the irq handler freeing the irq from under us. + */ + next = irq_fd->next; + fd = irq_fd->fd; + if(FD_ISSET(irq_fd->fd, &fds)) + do_IRQ(irq_fd->irq, usermode); + } + } +} + +int activate_fd(int irq, int fd, void *dev_id) +{ + struct irq_fd *new_fd; + int pid, retval; + + for(new_fd = active_fds;new_fd;new_fd = new_fd->next){ + if(new_fd->fd == fd){ + printk("Registering fd %d twice\n", fd); + printk("Irqs : %d, %d\n", new_fd->irq, irq); + printk("Ids : 0x%x, 0x%x\n", new_fd->id, dev_id); + return(-EIO); + } + } + if((retval = fcntl(fd, F_SETFL, O_ASYNC | O_NONBLOCK)) < 0){ + printk("Failed to set O_ASYNC and O_NONBLOCK on fd # %d, " + "errno = %d\n", fd, errno); + return(-retval); + } + pid = external_pid(NULL); + if((retval = fcntl(fd, F_SETOWN, pid)) < 0){ + printk("Failed to fcntl F_SETOWN fd %d to pid %d, " + "errno = %d\n", pid, errno); + return(-retval); + } + new_fd = um_kmalloc(sizeof(*new_fd)); + if(new_fd == NULL) return(-ENOMEM); + new_fd->next = active_fds; + new_fd->id = dev_id; + new_fd->irq = irq; + new_fd->fd = fd; + new_fd->pid = external_pid(NULL); + active_fds = new_fd; + if(fd > max_fd) max_fd = fd; + FD_SET(fd, &active_fd_mask); + return(0); +} + +void free_irq_fd(void *dev_id) +{ + struct irq_fd **prev; + + prev = &active_fds; + while(*prev != NULL){ + if((*prev)->id == dev_id){ + struct irq_fd *old_fd = *prev; + *prev = (*prev)->next; + FD_CLR(old_fd->fd, &active_fd_mask); + kfree(old_fd); + return; + } + prev = &(*prev)->next; + } +} + +void reactivate_fd(int fd) +{ + FD_SET(fd, &active_fd_mask); +} + +void forward_interrupts(int pid) +{ + struct irq_fd *irq; + + for(irq=active_fds;irq != NULL;irq = irq->next){ + if(fcntl(irq->fd, F_SETOWN, pid) < 0){ + int save_errno = errno; + if(fcntl(irq->fd, F_GETOWN, 0) != pid){ + /* XXX Just remove the irq rather than + * print out an infinite stream of these + */ + printk("Failed to forward %d to pid %d, " + "errno = %d\n", irq->fd, pid, + save_errno); + } + } + irq->pid = pid; + } +} + +void init_irq_signals(int on_sigstack) +{ + int flags; + + flags = on_sigstack ? SA_ONSTACK : 0; + set_handler(SIGVTALRM, (__sighandler_t) alarm_handler, + flags | SA_NODEFER | SA_RESTART, SIGUSR1, SIGIO, -1); + set_handler(SIGIO, (__sighandler_t) irq_handler, flags | SA_RESTART, + SIGUSR1, SIGIO, -1); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/ksyms.c linux.ac/arch/um/kernel/ksyms.c --- linux.vanilla/arch/um/kernel/ksyms.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/ksyms.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,23 @@ +#include "linux/module.h" +#include "linux/string.h" +#include "asm/current.h" +#include "asm/delay.h" +#include "asm/processor.h" +#include "asm/unistd.h" +#include "asm/pgalloc.h" +#include "kern_util.h" +#include "user_util.h" + +EXPORT_SYMBOL(stop); +EXPORT_SYMBOL(strtok); +EXPORT_SYMBOL(physmem); +EXPORT_SYMBOL(current_task); +EXPORT_SYMBOL(set_signals); +EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(__const_udelay); +EXPORT_SYMBOL(sys_waitpid); +EXPORT_SYMBOL(task_size); +EXPORT_SYMBOL(__do_copy_from_user); +EXPORT_SYMBOL(__do_strncpy_from_user); +EXPORT_SYMBOL(flush_tlb_range); +EXPORT_SYMBOL(__do_clear_user); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/mem.c linux.ac/arch/um/kernel/mem.c --- linux.vanilla/arch/um/kernel/mem.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/mem.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/types.h" +#include "linux/mm.h" +#include "linux/fs.h" +#include "linux/init.h" +#include "linux/bootmem.h" +#include "linux/swap.h" +#include "asm/page.h" +#include "asm/pgtable.h" +#include "asm/pgalloc.h" +#include "asm/bitops.h" +#include "asm/uaccess.h" +#include "user_util.h" +#include "kern_util.h" +#include "mem_user.h" +#include "kern.h" + +unsigned long high_physmem; + +unsigned long low_physmem; + +unsigned long vm_start; + +unsigned long vm_end; + +pgd_t swapper_pg_dir[1024]; + +unsigned long *empty_zero_page = NULL; + +unsigned long *empty_bad_page = NULL; + +const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n"; + +static unsigned long totalram_pages = 0; + +extern char __init_begin, __init_end; + +int kmalloc_ok = 0; + +void mem_init(void) +{ + max_mapnr = num_physpages = max_low_pfn; + + /* clear the zero-page */ + memset((void *) empty_zero_page, 0, PAGE_SIZE); + + /* this will put all low memory onto the freelists */ + totalram_pages += free_all_bootmem(); + printk("Memory: %luk available\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT-10)); + kmalloc_ok = 1; +} + +void paging_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES]; + int i; + + empty_zero_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE); + empty_bad_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE); + for(i=0;i> PAGE_SHIFT) - + (physmem >> PAGE_SHIFT) - zones_size[0]; + free_area_init(zones_size); +} + +static int meminfo_22 = 0; + +static int meminfo_compat(char *str) +{ + meminfo_22 = 1; + return(1); +} + +__setup("22_meminfo", meminfo_compat); + +void si_meminfo(struct sysinfo *val) +{ + val->totalram = totalram_pages; + val->sharedram = atomic_read(&shmem_nrpages); + val->freeram = nr_free_pages(); + val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = 0; + val->freehigh = 0; + val->mem_unit = PAGE_SIZE; + if(meminfo_22){ + val->freeram <<= PAGE_SHIFT; + val->bufferram <<= PAGE_SHIFT; + val->totalram <<= PAGE_SHIFT; + val->sharedram <<= PAGE_SHIFT; + } +} + +pte_t __bad_page(void) +{ + clear_page(empty_bad_page); + return pte_mkdirty(mk_pte((struct page *) empty_bad_page, + PAGE_SHARED)); +} + +/* This can't do anything because nothing in the kernel image can be freed + * since it's not in kernel physical memory. + */ + +void free_initmem(void) +{ +} + +#ifdef CONFIG_BLK_DEV_INITRD + +void free_initrd_mem(unsigned long start, unsigned long end) +{ + if (start < end) + printk ("Freeing initrd memory: %ldk freed\n", + (end - start) >> 10); + for (; start < end; start += PAGE_SIZE) { + ClearPageReserved(virt_to_page(start)); + set_page_count(virt_to_page(start), 1); + free_page(start); + totalram_pages++; + } +} + +#endif + +int do_check_pgt_cache(int low, int high) +{ + int freed = 0; + if(pgtable_cache_size > high) { + do { + if (pgd_quicklist) { + free_pgd_slow(get_pgd_fast()); + freed++; + } + if (pmd_quicklist) { + pmd_free_slow(pmd_alloc_one_fast(NULL, 0)); + freed++; + } + if (pte_quicklist) { + pte_free_slow(pte_alloc_one_fast(NULL, 0)); + freed++; + } + } while(pgtable_cache_size > low); + } + return freed; +} + +void show_mem(void) +{ + int i, total = 0, reserved = 0; + int shared = 0, cached = 0; + int highmem = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageHighMem(mem_map+i)) + highmem++; + if (PageReserved(mem_map+i)) + reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; + else if (page_count(mem_map+i)) + shared += page_count(mem_map+i) - 1; + } + printk("%d pages of RAM\n", total); + printk("%d pages of HIGHMEM\n",highmem); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%ld pages in page table cache\n",pgtable_cache_size); + show_buffers(); +} + +unsigned long kmem_top = 0; + +unsigned long get_kmem_end(void) +{ + if(kmem_top == 0) kmem_top = host_task_size - ABOVE_KMEM; + return(kmem_top); +} + +void set_kmem_end(unsigned long new) +{ + kmem_top = new; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/mem_user.c linux.ac/arch/um/kernel/mem_user.c --- linux.vanilla/arch/um/kernel/mem_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/mem_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,215 @@ +/* + * arch/um/kernel/mem_user.c + * + * BRIEF MODULE DESCRIPTION + * user side memory routines for supporting IO memory inside user mode linux + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: RidgeRun, Inc. + * Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kern_util.h" +#include "user.h" + +struct mem_region { + struct mem_region *next; + char *driver; + unsigned long start; + unsigned long usable; + unsigned long total; + int fd; +}; + +struct mem_region physmem_region; + +struct mem_region *mem_list = &physmem_region; + +int create_mem_file(unsigned long len) +{ + char tempname[sizeof("/tmp/vm_file-XXXXXX\0")]; + int fd; + char zero; + + strcpy(tempname, "/tmp/vm_file-XXXXXX"); + if((fd = mkstemp(tempname)) < 0){ + perror("open - cannot create /tmp/vm_file-XXXXXX"); + exit(1); + } + if(unlink(tempname) < 0){ + perror("unlink"); + exit(1); + } + if (fchmod(fd, 0777) < 0){ + perror("fchmod"); + exit(1); + } + if(lseek(fd, len, SEEK_SET) < 0){ + perror("lseek"); + exit(1); + } + zero = 0; + if(write(fd, &zero, 1) != 1){ + perror("write"); + exit(1); + } + if(fcntl(fd, F_SETFD, 1) != 0) + perror("Setting FD_CLOEXEC failed"); + return(fd); +} + +void setup_range(int fd, char *driver, unsigned long start, + unsigned long usable, unsigned long total) +{ + struct mem_region *region, *next; + + if(fd == -1){ + fd = create_mem_file(usable); + region = &physmem_region; + next = physmem_region.next; + } + else { + region = malloc(sizeof(*region)); + if(region == NULL){ + perror("Allocating iomem struct"); + exit(1); + } + next = physmem_region.next; + } + *region = ((struct mem_region) { next, driver, start, usable, + total, fd } ); + if(region != &physmem_region) physmem_region.next = region; +} + +void setup_memory(void) +{ + struct mem_region *region; + void *loc; + unsigned long start; + int page; + + start = -1; + region = mem_list; + page = page_size(); + while(region){ + if(region->start != -1) start = region->start; + else region->start = start; + loc = mmap((void *) region->start, region->usable, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, + region->fd, 0); + if(loc != (void *) region->start){ + perror("Mapping memory"); + exit(1); + } + start += region->total; + start = (start + page - 1) & ~(page - 1); + region = region->next; + } +} + +void parse_iomem(char *str) +{ + struct stat buf; + char *file, *driver; + int fd; + + driver = str; + file = strchr(str,','); + if(file == NULL){ + printk(__FUNCTION__ " failed to parse iomem\n"); + return; + } + *file = '\0'; + file++; + fd = open(file, O_RDWR); + if(fd < 0){ + perror("Couldn't open io file"); + return; + } + if(fstat(fd, &buf) < 0) { + perror(__FUNCTION__ "fstat - cannot fstat file"); + exit(1); + } + setup_range(fd, driver, -1, buf.st_size, buf.st_size); +} + +void map(unsigned long virt, void *p, unsigned long len, int r, + int w, int x) +{ + struct mem_region *region; + unsigned long phys = (unsigned long) p; + void *loc; + int prot; + + prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | + (x ? PROT_EXEC : 0); + for(region = mem_list; region ; region = region->next) { + if((phys < region->start) || + (phys >= region->start + region->usable)) + continue; + phys -= region->start; + loc = mmap((void *) virt, len, prot, MAP_SHARED | MAP_FIXED, + region->fd, phys); + if(loc != (void *) virt){ + panic("Error mapping a page - errno = %d", errno); + } + return; + } + panic("No physical or IO memory region for address 0x%x\n", phys); +} + +unsigned long find_iomem(char *driver, unsigned long *len_out) +{ + struct mem_region *region; + + for(region = mem_list; region ; region = region->next) { + if((region->driver != NULL) && + !strcmp(region->driver, driver)){ + *len_out = region->usable; + return(region->start); + } + } + *len_out = 0; + return 0; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/mprot.h linux.ac/arch/um/kernel/mprot.h --- linux.vanilla/arch/um/kernel/mprot.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/mprot.h Wed Oct 10 01:47:36 2001 @@ -0,0 +1,6 @@ +#ifndef __MPROT_H__ +#define __MPROT_H__ + +extern void no_access(unsigned long addr, unsigned int len); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/process.c linux.ac/arch/um/kernel/process.c --- linux.vanilla/arch/um/kernel/process.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/process.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef PROFILING +#include +#endif +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "process.h" +#include "signal_kern.h" +#include "signal_user.h" +#include "sysdep/ptrace.h" +#include "sysdep/sigcontext.h" + +void stop_pid(int pid) +{ + kill(pid, SIGSTOP); +} + +void kill_pid(int pid) +{ + kill(pid, SIGKILL); +} + +void usr1_pid(int pid) +{ + kill(pid, SIGUSR1); +} + +void cont_pid(int pid) +{ + kill(pid, SIGCONT); +} + +void init_new_thread(void *sig_stack, void (*usr1_handler)(int)) +{ + int flags = 0; + + if(sig_stack != NULL){ + set_sigstack(sig_stack, 2 * page_size()); + flags = SA_ONSTACK; + } + set_handler(SIGSEGV, (__sighandler_t) sig_handler, flags, + SIGUSR1, SIGIO, -1); + set_handler(SIGTRAP, (__sighandler_t) sig_handler, flags, + SIGUSR1, SIGIO, -1); + set_handler(SIGFPE, (__sighandler_t) sig_handler, flags, + SIGUSR1, SIGIO, -1); + set_handler(SIGILL, (__sighandler_t) sig_handler, flags, + SIGUSR1, SIGIO, -1); + set_handler(SIGBUS, (__sighandler_t) sig_handler, flags, + SIGUSR1, SIGIO, -1); + if(usr1_handler) set_handler(SIGUSR1, usr1_handler, flags, -1); + signal(SIGCHLD, SIG_IGN); + signal(SIGHUP, SIG_IGN); + set_timers(1); /* XXX A bit of a race here */ + init_irq_signals(sig_stack != NULL); +} + +/* This sigstop business works around a bug in gcc's -pg support. + * Normally a procedure's mcount call comes after esp has been copied to + * ebp and the new frame is constructed. With procedures with no locals, + * the mcount comes before, as the first thing that the procedure does. + * When that procedure is main for a thread, ebp comes in as NULL. So, + * when mcount dereferences it, it segfaults. So, UML works around this + * by adding a non-optimizable local to the various trampolines, fork_tramp + * and outer_tramp below, and exec_tramp. + */ + +static int sigstop = SIGSTOP; + +int fork_tramp(void *sig_stack) +{ + int sig = sigstop; + + block_signals(); + init_new_thread(sig_stack, finish_fork_handler); + kill(getpid(), sig); + return(0); +} + +struct tramp { + int (*tramp)(void *); + unsigned long temp_stack; + unsigned long stack; + int flags; + int pid; +}; + +/* See above for why sigkill is here */ + +int sigkill = SIGKILL; + +int outer_tramp(void *arg) +{ + struct tramp *t; + int sig = sigkill; + + t = arg; + t->pid = clone(t->tramp, (void *) t->temp_stack + page_size()/2, + t->flags, (void *) t->stack); + if(t->pid > 0) wait_for_stop(t->pid, SIGSTOP, PTRACE_CONT); + kill(getpid(), sig); + exit(0); +} + +int start_fork_tramp(unsigned long sig_stack, unsigned long temp_stack, + int clone_vm, int (*tramp)(void *)) +{ + struct tramp arg; + unsigned long sp; + int new_pid, flags, status, err; + + /* The trampoline will run on the temporary stack */ + sp = stack_sp(temp_stack); + + flags = CLONE_FILES | SIGCHLD; + if(clone_vm) flags |= CLONE_VM; + + arg.tramp = tramp; + arg.temp_stack = temp_stack; + arg.stack = sig_stack; + arg.flags = flags; + + /* Start the process and wait for it to stop itself */ + new_pid = clone(outer_tramp, (void *) sp, flags, &arg); + if(new_pid < 0) return(-errno); + while((err = waitpid(new_pid, &status, 0) < 0) && (errno == EINTR)) ; + if(err < 0) panic("Waiting for outer trampoline failed - errno = %d", + errno); + if(!WIFSIGNALED(status) || (WTERMSIG(status) != SIGKILL)) + panic("outer trampoline didn't exit with SIGKILL"); + + return(arg.pid); +} + +void trace_myself(void) +{ + if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) + panic("ptrace failed in trace_myself"); +} + +void continue_fork(void *task, int pid, struct sys_pt_regs *regs) +{ + if(ptrace(PTRACE_CONT, pid, 0, SIGUSR1) < 0) + tracer_panic("Couldn't continue forked process"); + wait_for_stop(pid, SIGSTOP, PTRACE_CONT); + if(ptrace_setregs(pid, regs) < 0) + tracer_panic("Couldn't restore registers"); +} + +int get_one_stack(int (*proc)(void *), void *arg, char **stack_out, + struct sys_pt_regs *regs_out) +{ + void *stack; + unsigned long sp; + int pid, n; + + if((stack = mmap(NULL, page_size(), + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED){ + perror("get_one_stack : couldn't mmap stack"); + exit(1); + } + sp = stack_sp((unsigned long) stack); + pid = clone(proc, (void *) sp, SIGCHLD | CLONE_VM, arg); + if(pid < 0){ + perror("get_one_stack : couldn't start thread"); + exit(1); + } + wait_for_stop(pid, SIGSTOP, PTRACE_CONT); + if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){ + perror("get_one_stack : couldn't attach to child"); + exit(1); + } + if(ptrace_getregs(pid, regs_out) < 0){ + perror("get_one_stack : couldn't get registers"); + exit(1); + } + kill(pid, SIGKILL); + kill(pid, SIGCONT); + waitpid(pid, NULL, 0); + n = page_size() - (UM_SP(regs_out) & ~page_mask()); + *stack_out = stack; + + return(n); +} + +void attach_process(int pid) +{ + if((ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) || + (ptrace(PTRACE_CONT, pid, 0, 0) < 0)) + tracer_panic("OP_FORK failed to attach pid"); + wait_for_stop(pid, SIGSTOP, PTRACE_CONT); +} + +int signal_frame_size; +static void *local_addr; + +static void frame_size(int sig) +{ + int n; + + signal_frame_size = (unsigned long) local_addr - (unsigned long) &n; +} + +void calc_sigframe_size(void) +{ + int n; + + signal(SIGUSR1, frame_size); + local_addr = &n; + usr1_pid(getpid()); +} + +void tracer_panic(char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vprintf(format, ap); + while(1) sleep(10); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/process_kern.c linux.ac/arch/um/kernel/process_kern.c --- linux.vanilla/arch/um/kernel/process_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/process_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,782 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/sched.h" +#include "linux/interrupt.h" +#include "linux/mm.h" +#include "linux/slab.h" +#include "linux/utsname.h" +#include "linux/fs.h" +#include "linux/utime.h" +#include "linux/smp_lock.h" +#include "linux/module.h" +#include "linux/init.h" +#include "asm/unistd.h" +#include "asm/mman.h" +#include "asm/segment.h" +#include "asm/stat.h" +#include "asm/pgtable.h" +#include "asm/processor.h" +#include "asm/pgalloc.h" +#include "asm/spinlock.h" +#include "asm/uaccess.h" +#include "asm/user.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "signal_kern.h" +#include "signal_user.h" + +struct cpu_task cpu_tasks[NR_CPUS] = { [0 ... NR_CPUS - 1] = { -1, NULL } }; + +static struct task_struct *get_task(int pid, int require) +{ + struct task_struct *task, *ret; + + ret = NULL; + read_lock(&tasklist_lock); + for_each_task(task){ + if(task->pid == pid){ + ret = task; + break; + } + } + read_unlock(&tasklist_lock); + if(require && (ret == NULL)) panic("get_task couldn't find a task\n"); + return(ret); +} + +int external_pid(void *t) +{ + struct task_struct *task = t; + if(task == NULL) task = current; + return(task->thread.extern_pid); +} + +void free_stack(unsigned long stack) +{ + free_page(stack); +} + +void set_init_pid(int pid) +{ + init_task.thread.extern_pid = pid; +} + +int set_user_thread(void *t, int on, int restore_regs, int protect_mem) +{ + struct task_struct *task; + int ret; + + if(t == NULL) task = current; + else task = t; + if(on == task->thread.tracing) return(on); + ret = task->thread.tracing; + task->thread.request.op = on ? OP_TRACE_ON : OP_TRACE_OFF; + task->thread.request.u.tracing.restore_regs = restore_regs; + if(on && protect_mem) protect_kernel_mem(); + usr1_pid(getpid()); + return(ret); +} + +void set_tracing(void *task, int tracing) +{ + ((struct task_struct *) task)->thread.tracing = tracing; +} + +int is_tracing(void *t) +{ + struct task_struct *task; + + if(t == NULL) task = current; + else task = t; + return(task->thread.tracing); +} + +extern void schedule_tail(struct task_struct *prev); + +static int new_thread_proc(void *t) +{ + struct task_struct *task; + int (*fn)(void *), pid; + void *arg; + + task = t; + trace_myself(); + init_new_thread(NULL, NULL); + pid = getpid(); + fn = task->thread.request.u.thread.proc; + arg = task->thread.request.u.thread.arg; + task->thread.extern_pid = pid; + stop_pid(pid); + set_cmdline("(kernel thread)"); + flush_tlb_all(); + if(current->thread.request.u.cswitch.from != NULL) + schedule_tail(current->thread.request.u.cswitch.from); + (*fn)(arg); + do_exit(0); + return(0); +} + +unsigned long alloc_stack(void) +{ + unsigned long page; + + if((page = __get_free_page(GFP_KERNEL)) == 0) + panic("Couldn't allocate new stack"); + stack_protections(page); + return(page); +} + +extern int inited_cpus; + +static int start_kernel_thread(struct task_struct *task, int (*fn)(void *), + void *arg, int cpu) +{ + int extern_pid; + unsigned long sp; + + sp = ((unsigned long) task) + 4 * PAGE_SIZE - sizeof(void *); + task->thread.request.u.thread.proc = fn; + task->thread.request.u.thread.arg = arg; + task->thread.extern_pid = -1; + extern_pid = clone_and_wait(new_thread_proc, task, (void *) sp, + CLONE_FILES | SIGCHLD); + if(task->thread.extern_pid == -1) + tracer_panic("task didn't set its pid"); + task->mm = NULL; + task->active_mm = NULL; +#ifdef CONFIG_SMP + if(cpu != -1){ + cpu_tasks[cpu].pid = extern_pid; + cpu_tasks[cpu].task = task; + inited_cpus++; + init_tasks[cpu] = task; + cpu_number_map[cpu] = cpu; + task->processor = cpu; + cont_pid(extern_pid); + } +#endif + return(extern_pid); +} + +static int kernel_thread1(int (*fn)(void *), void * arg, unsigned long flags, + int cpu, int *extern_pid_out) +{ + struct task_struct *new_task; + int pid, extern_pid; + + pid = do_fork(CLONE_VM | flags, 0, NULL, 0); + if(pid < 0) panic("do_fork failed in kernel_thread"); + new_task = get_task(pid, 1); + current->thread.request.op = OP_THREAD; + current->thread.request.u.thread.proc = fn; + current->thread.request.u.thread.arg = arg; + current->thread.request.u.thread.flags = flags; + current->thread.request.u.thread.new_task = new_task; + current->thread.request.u.thread.cpu = cpu; + usr1_pid(getpid()); + extern_pid = current->thread.request.u.thread.new_pid; + if(extern_pid < 0){ + printk("Kernel thread failed : errno = %d", -extern_pid); + return(extern_pid); + } + if(extern_pid_out != NULL) *extern_pid_out = extern_pid; + current->thread.request.u.cswitch.from = NULL; + return(pid); +} + +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + return(kernel_thread1(fn, arg, flags, -1, NULL)); +} + +void switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk, unsigned cpu) +{ + if (prev != next) + clear_bit(cpu, &prev->cpu_vm_mask); + set_bit(cpu, &next->cpu_vm_mask); +} + +void *_switch_to(void *prev, void *next) +{ + struct task_struct *from, *to; + + from = prev; + to = next; + current->thread.request.op = OP_SWITCH; + current->thread.request.u.cswitch.to = next; + forward_interrupts(to->thread.extern_pid); + block_signals(); + usr1_pid(getpid()); + flush_tlb_all(); + unblock_signals(); + return(current->thread.request.u.cswitch.from); +} + +void ret_from_sys_call(void *t) +{ + struct task_struct *task; + + task = t; + if(task == NULL) task = current; + if(task->need_resched) schedule(); + if(task->sigpending != 0) do_signal(NULL, NULL); +} + +void release_thread(struct task_struct *task) +{ +} + +void exit_thread(void) +{ + unprotect_stack((unsigned long) current); +} + +char *generic_stack = NULL; +char *generic_stack_mask = NULL; +int generic_stack_size = 0; +struct sys_pt_regs generic_regs; +struct sys_pt_regs generic_regs_mask; + +void set_syscall_regs(void *t) +{ + struct sys_pt_regs *regs; + int i; + struct task_struct *task = t; + + regs = &task->thread.syscall_regs; + for(i = 0; i < sizeof(regs->regs)/sizeof(regs->regs[0]); i++){ + task->thread.syscall_regs.regs[i] = generic_regs.regs[i]; + if(generic_regs_mask.regs[i]) + task->thread.syscall_regs.regs[i] += + task->thread.kernel_stack + 2 * PAGE_SIZE; + } +} + +int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, + unsigned long stack_top, struct task_struct * p, + struct pt_regs * regs) +{ + int new_pid, clone_vm; + unsigned long stack; + + p->thread = (struct thread_struct) INIT_THREAD; + p->thread.kernel_stack = (unsigned long) p + 2 * PAGE_SIZE; + p->thread.tracing = current->thread.forking; + set_syscall_regs(p); + if(current->thread.forking){ + p->thread.tracing = 0; + stack = alloc_stack(); + clone_vm = (p->mm == current->mm); + new_pid = start_fork_tramp(p->thread.kernel_stack, stack, + clone_vm, fork_tramp); + if(new_pid < 0){ + printk("copy_thread : clone failed with errno = %d", + -new_pid); + return(new_pid); + } + current->thread.request.op = OP_FORK; + current->thread.request.u.fork.pid = new_pid; + usr1_pid(getpid()); + p->thread.process_regs = current->thread.process_regs; + UM_SET_SYSCALL_RETURN(&p->thread.process_regs, 0); + if(sp != 0) UM_SP(&p->thread.process_regs) = sp; + p->thread.extern_pid = new_pid; + p->thread.request.op = OP_FORK_FINISH; + p->thread.request.u.fork_finish.stack = stack; + } + current->need_resched = 1; + return(0); +} + +void tracing_reboot(void) +{ + current->thread.request.op = OP_REBOOT; + usr1_pid(getpid()); +} + +void tracing_halt(void) +{ + current->thread.request.op = OP_HALT; + usr1_pid(getpid()); +} + +void tracing_cb(void (*proc)(void *), void *arg) +{ + if(getpid() == tracing_pid){ + (*proc)(arg); + } + else { + current->thread.request.op = OP_CB; + current->thread.request.u.cb.proc = proc; + current->thread.request.u.cb.arg = arg; + usr1_pid(getpid()); + } +} + +struct { + struct task_struct *from; + struct task_struct *to; + int processor; +} switch_record[1024]; + +int switch_index = 0; + +int do_proc_op(void *t, int proc_id) +{ + struct task_struct *task, *to; + struct thread_struct *thread; + int op, pid; + + task = t; + thread = &task->thread; + op = thread->request.op; + switch(op){ + case OP_NONE: + case OP_TRACE_ON: + case OP_TRACE_OFF: + break; + case OP_EXEC: + do_exec(thread->extern_pid, thread->request.u.exec.pid); + break; + case OP_SWITCH: + if((task->state == TASK_ZOMBIE) && + (thread->extern_pid != -1)) + kill_pid(thread->extern_pid); + to = thread->request.u.cswitch.to; + switch_record[switch_index].from = task; + switch_record[switch_index].to = to; + switch_record[switch_index++].processor = to->processor; + if(switch_index == 1024) switch_index = 0; +#ifdef CONFIG_SMP + cpu_tasks[proc_id].task = to; + cpu_tasks[proc_id].pid = to->thread.extern_pid; + if(cpu_tasks[0].pid == cpu_tasks[1].pid) + tracer_panic("Scheduled a process on two processors"); +#else + current = to; +#endif + if(to->thread.request.op == OP_FORK_FINISH){ + to->thread.request.u.fork_finish.from = task; + continue_fork(to, to->thread.extern_pid, + &to->thread.process_regs); + to->thread.request.op = OP_NONE; + set_tracing(to, 1); + } + else to->thread.request.u.cswitch.from = task; + cont_pid(to->thread.extern_pid); + break; + case OP_THREAD: + pid = start_kernel_thread(thread->request.u.thread.new_task, + thread->request.u.thread.proc, + thread->request.u.thread.arg, + thread->request.u.thread.cpu); + thread->request.u.thread.new_pid = pid; + break; + case OP_FORK: + attach_process(thread->request.u.fork.pid); + break; + case OP_CB: + (*thread->request.u.cb.proc)(thread->request.u.cb.arg); + break; + case OP_REBOOT: + case OP_HALT: + break; + default: + tracer_panic("Bad op in do_proc_op"); + break; + } + thread->request.op = OP_NONE; + return(op); +} + +unsigned long stack_sp(unsigned long page) +{ + return(page + PAGE_SIZE - sizeof(void *)); +} + +int current_pid(void) +{ + return(current->pid); +} + +static void do_idle(void) +{ + idle_timer(); + + while(1){ + /* endless idle loop with no priority at all */ + current->nice = 20; + current->counter = -100; + + /* + * although we are an idle CPU, we do not want to + * get into the scheduler unnecessarily. + */ + if (current->need_resched) { + schedule(); + check_pgt_cache(); + } + idle_sleep(10); + } +} + +static int idle_proc(void *unused) +{ + del_from_runqueue(current); + init_idle(); +#ifdef CONFIG_SMP + smp_num_cpus++; +#endif + do_idle(); + return(0); +} + +int cpu_idle(void) +{ + int i, pid; + + if(ncpus > 1){ + printk("Starting up other processors:\n"); + for(i=1;imm == NULL) return(0xffffffff); + pgd = pgd_offset(task->mm, addr); + pmd = pmd_offset(pgd, addr); + if(!pmd_present(*pmd)) return(0xffffffff); + pte = pte_offset(pmd, addr); + if(!pte_present(*pte)) return(0xffffffff); + return((pte_val(*pte) & PAGE_MASK) + (addr & ~PAGE_MASK)); +} + +char *current_cmd(void) +{ +#ifdef CONFIG_SMP + return("(Unknown)"); +#else + unsigned long addr; + + if((addr = um_virt_to_phys(current, + current->mm->arg_start)) == 0xffffffff) + return("(Unknown)"); + else return((char *) addr); +#endif +} + +void force_sigbus(void) +{ + printk("Killing pid %d because of a lack of memory\n", current->pid); + lock_kernel(); + sigaddset(¤t->pending.signal, SIGBUS); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(SIGBUS | 0x80); +} + +void finish_fork_handler(int sig) +{ + force_flush_all(); + if(current->mm != current->p_pptr->mm) + protect(physmem, high_physmem - physmem, 1, 1, 0); + task_protections((unsigned long) current); + if(current->thread.request.u.fork_finish.from) + schedule_tail(current->thread.request.u.fork_finish.from); + free_page(current->thread.request.u.fork_finish.stack); + change_sig(SIGUSR1, 1); + unblock_signals(); + kill(getpid(), SIGSTOP); +} + +void *get_current_task(void) +{ + return(current); +} + +void *process_state(void *t, unsigned long *cr2_out, int *err_out) +{ + struct task_struct *task; + + if(t == NULL) task = current; + else task = t; + if(cr2_out) *cr2_out = task->thread.cr2; + if(err_out) *err_out = task->thread.err; + return(&task->thread.process_regs); +} + +struct sys_pt_regs *syscall_state(void *t, void **stack_out, int *size_out) +{ + struct task_struct *task; + + task = t; + *stack_out = generic_stack; + *size_out = generic_stack_size; + return(&task->thread.syscall_regs); +} + +int get_repeat_syscall(void *t) +{ + struct task_struct *task; + + if(t == NULL) task = current; + else task = t; + return(task->thread.repeat_syscall); +} + +void set_repeat_syscall(int again) +{ + current->thread.repeat_syscall = again; +} + +void dump_thread(struct pt_regs *regs, struct user *u) +{ +} + +int switching_modes(void *t) +{ + struct task_struct *task; + + task = t; + return(task->thread.request.op == OP_TRACE_OFF); +} + +void enable_hlt(void) +{ + panic("enable_hlt"); +} + +void disable_hlt(void) +{ + panic("disable_hlt"); +} + +extern int signal_frame_size; + +void interrupt_end(void) +{ + if(current->need_resched) schedule(); + do_signal(NULL, NULL); +} + +void *um_kmalloc(int size) +{ + return(kmalloc(size, GFP_KERNEL)); +} + +unsigned long get_fault_addr(void) +{ + return((unsigned long) current->thread.fault_addr); +} + +EXPORT_SYMBOL(get_fault_addr); + +int singlestepping(void *t) +{ + struct task_struct *task; + int ret; + + task = (struct task_struct *) t; + ret = (task->ptrace & PT_DTRACE); + task->ptrace &= ~PT_DTRACE; + return(ret); +} + +void not_implemented(void) +{ + printk("Something isn't implemented in here\n"); +} + +EXPORT_SYMBOL(not_implemented); + +void *diff_stacks(int size, char *stack1, struct sys_pt_regs *regs1, + char *stack2, struct sys_pt_regs *regs2, char **mask_out, + struct sys_pt_regs *regs_out, + struct sys_pt_regs *regs_mask_out) +{ + char *mask; + void *new; + unsigned long s1, s2, n1, n2, off1, off2, i; + + s1 = (unsigned long) stack1; + s2 = (unsigned long) stack2; + new = malloc(size); + mask = malloc(size); + if((new == NULL) || (mask == NULL)){ + perror("diff_stacks : allocating new stack and mask"); + exit(1); + } + memset(mask, 0, size); + for(i = PAGE_SIZE - size; i < PAGE_SIZE - sizeof(void *); i++){ + /* This is horribly word-length- and byte-order-dependent */ + n1 = stack1[i] | (stack1[i + 1] << 8) | + (stack1[i + 2] << 16) | stack1[i + 3] << 24; + n2 = stack2[i] | (stack2[i + 1] << 8) | + (stack2[i + 2] << 16) | stack2[i + 3] << 24; + + /* Internal pointers have to be different on different + * stacks, and they have to point to the stack page. If not, + * then keep looking. + */ + if((n1 == n2) || ((n1 & PAGE_MASK) != s1) || + ((n2 & PAGE_MASK) != s2)) continue; + + /* The offsets have to be the same and they have to point + * within the used area of the stack. + */ + off1 = n1 & ~PAGE_MASK; + off2 = n2 & ~PAGE_MASK; + if((off1 == off2) && (off1 > PAGE_SIZE - size)){ + *((unsigned long *) &stack1[i]) -= s1 + PAGE_SIZE; + mask[i - (PAGE_SIZE - size)] = 1; + i += sizeof(unsigned long); + } + } + memcpy((char *) new, &stack1[PAGE_SIZE - size], size); + for(i = 0; i < sizeof(regs1->regs)/sizeof(regs1->regs[0]); i++){ + regs_out->regs[i] = regs1->regs[i]; + regs_mask_out->regs[i] = 0; + if((regs1->regs[i] == regs2->regs[i]) || + ((regs1->regs[i] & PAGE_MASK) != s1) || + ((regs2->regs[i] & PAGE_MASK) != s2)) continue; + off1 = regs1->regs[i] & ~PAGE_MASK; + off2 = regs2->regs[i] & ~PAGE_MASK; + if((off1 == off2) && (off1 >= PAGE_SIZE - size)){ + regs_out->regs[i] = regs1->regs[i] - s1 - PAGE_SIZE; + regs_mask_out->regs[i] = 1; + } + } + *mask_out = mask; + return(new); +} + +void setup_kernel_stack(void) +{ + struct sys_pt_regs regs1, regs2; + char *stack1, *stack2; + int size; + + size = get_one_stack(syscall_handler, NULL, &stack1, ®s1); + if(get_one_stack(syscall_handler, NULL, &stack2, ®s2) != size){ + printf("setup_kernel_stack : differing stack sizes\n"); + exit(1); + } + + generic_stack = diff_stacks(size, stack1, ®s1, stack2, ®s2, + &generic_stack_mask, &generic_regs, + &generic_regs_mask); + generic_stack_size = size; + + if((munmap(stack1, PAGE_SIZE) < 0) || + (munmap(stack2, PAGE_SIZE) < 0)){ + perror("setup_kernel_stack : unmapping temp stacks"); + exit(1); + } +} + +int user_context(unsigned long sp) +{ + return((sp & (PAGE_MASK << 1)) != current->thread.kernel_stack); +} + +int get_restore_regs(void *t) +{ + struct task_struct *task = t; + + return(task->thread.request.u.tracing.restore_regs); +} + +extern void remove_umid_dir(void); +__exitcall(remove_umid_dir); + +extern exitcall_t __exitcall_begin, __exitcall_end; + +void do_exitcalls(void) +{ + exitcall_t *call; + + call = &__exitcall_end; + while (--call >= &__exitcall_begin) + (*call)(); +} + +void *round_up(unsigned long addr) +{ + return(ROUND_UP(addr)); +} + +void *round_down(unsigned long addr) +{ + return(ROUND_DOWN(addr)); +} + +char *uml_strdup(char *string) +{ + char *new; + + new = kmalloc(strlen(string) + 1, GFP_KERNEL); + if(new == NULL) return(NULL); + strcpy(new, string); + return(new); +} + +void unprotect_kernel_mem(void) +{ + unsigned long start_stack, end_stack; + + if(current_task == &init_task) return; + start_stack = (unsigned long) current + PAGE_SIZE; + end_stack = (unsigned long) current + PAGE_SIZE * 4; + protect(physmem, start_stack - physmem, 1, 1, 1); + protect(end_stack, high_physmem - end_stack, 1, 1, 1); +} + +void protect_kernel_mem(void) +{ + unsigned long start_stack, end_stack; + + if(current_task == &init_task) return; + start_stack = (unsigned long) current + PAGE_SIZE; + end_stack = (unsigned long) current + PAGE_SIZE * 4; + protect(physmem, start_stack - physmem, 1, 0, 1); + protect(end_stack, high_physmem - end_stack, 1, 0, 1); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/ptrace.c linux.ac/arch/um/kernel/ptrace.c --- linux.vanilla/arch/um/kernel/ptrace.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/ptrace.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/mm.h" +#include "linux/errno.h" +#include "linux/smp_lock.h" +#include "asm/ptrace.h" +#include "asm/uaccess.h" +#include "kern_util.h" + +/* + * Called by kernel/ptrace.c when detaching.. + */ +void ptrace_disable(struct task_struct *child) +{ +} + +int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int i, ret; + + lock_kernel(); + ret = -EPERM; + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + ret = -ESRCH; + if (!(child->ptrace & PT_PTRACED)) + goto out_tsk; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out_tsk; + } + if (child->p_pptr != current) + goto out_tsk; + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + ret = -EIO; + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + + ret = -EIO; + if ((addr & 3) || addr < 0) + break; + + tmp = 0; /* Default return condition */ + if(addr < UM_MAX_REG_OFFSET){ + tmp = getreg(child, addr); + ret = put_user(tmp,(unsigned long *) data); + } + break; + } + + /* when I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = -EIO; + if (access_process_vm(child, addr, &data, sizeof(data), + 1) != sizeof(data)) + break; + ret = 0; + break; + + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ + ret = -EIO; + if ((addr & 3) || addr < 0) + break; + + if (addr < UM_MAX_REG_OFFSET) { + ret = putreg(child, addr, data); + break; + } + + break; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + child->ptrace |= PT_TRACESYS; + else + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + wake_up_process(child); + ret = 0; + break; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~PT_TRACESYS; + child->ptrace |= PT_DTRACE; + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: + /* detach a process that was attached. */ + ret = ptrace_detach(child, data); + break; + +#ifdef PTRACE_GETREGS + case PTRACE_GETREGS: { /* Get all gp regs from the child. */ + if (!access_ok(VERIFY_WRITE, (unsigned *)data, + UM_MAX_REG_OFFSET)) { + ret = -EIO; + break; + } + for ( i = 0; i < UM_MAX_REG_OFFSET; i += sizeof(long) ) { + __put_user(getreg(child, i),(unsigned long *) data); + data += sizeof(long); + } + ret = 0; + break; + } +#endif +#ifdef PTRACE_SETREGS + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp = 0; + if (!access_ok(VERIFY_READ, (unsigned *)data, + UM_MAX_REG_OFFSET)) { + ret = -EIO; + break; + } + for ( i = 0; i < UM_MAX_REG_OFFSET; i += sizeof(long) ) { + __get_user(tmp, (unsigned long *) data); + putreg(child, i, tmp); + data += sizeof(long); + } + ret = 0; + break; + } +#endif +#ifdef PTRACE_GETFPREGS + case PTRACE_GETFPREGS: { /* Get the child FPU state. */ + ret = -EIO; + break; + } +#endif +#ifdef PTRACE_SETFPREGS + case PTRACE_SETFPREGS: { /* Set the child FPU state. */ + ret = -EIO; + break; + } +#endif + default: + ret = -EIO; + break; + } + out_tsk: + free_task_struct(child); + out: + unlock_kernel(); + return ret; +} + +void syscall_trace(void) +{ + if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) + != (PT_PTRACED|PT_TRACESYS)) + return; + current->exit_code = SIGTRAP; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/reboot.c linux.ac/arch/um/kernel/reboot.c --- linux.vanilla/arch/um/kernel/reboot.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/reboot.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" + +static void kill_off_processes(void) +{ + struct task_struct *p; + int me; + + me = getpid(); + for_each_task(p){ + if(p->thread.extern_pid != me) kill_pid(p->thread.extern_pid); + } + if(init_task.thread.extern_pid != me) + kill_pid(init_task.thread.extern_pid); +} + +void machine_restart(char * __unused) +{ + kill_off_processes(); + do_exitcalls(); + tracing_reboot(); + kill_pid(getpid()); +} + +void machine_power_off(void) +{ + kill_off_processes(); + do_exitcalls(); + tracing_halt(); + kill_pid(getpid()); +} + +void machine_halt(void) +{ + machine_power_off(); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/resource.c linux.ac/arch/um/kernel/resource.c --- linux.vanilla/arch/um/kernel/resource.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/resource.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/pci.h" + +unsigned long resource_fixup(struct pci_dev * dev, struct resource * res, + unsigned long start, unsigned long size) +{ + return start; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/setup.c linux.ac/arch/um/kernel/setup.c --- linux.vanilla/arch/um/kernel/setup.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/setup.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "asm/processor.h" + +struct cpuinfo_um boot_cpu_data = { 0, 0, 0, 0 }; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/signal_kern.c linux.ac/arch/um/kernel/signal_kern.c --- linux.vanilla/arch/um/kernel/signal_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/signal_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/stddef.h" +#include "linux/sys.h" +#include "linux/sched.h" +#include "linux/wait.h" +#include "linux/kernel.h" +#include "linux/smp_lock.h" +#include "linux/module.h" +#include "linux/slab.h" +#include "asm/signal.h" +#include "asm/uaccess.h" +#include "user_util.h" +#include "kern_util.h" +#include "signal_kern.h" +#include "signal_user.h" +#include "kern.h" + +EXPORT_SYMBOL(block_signals); +EXPORT_SYMBOL(unblock_signals); + +struct sys_pt_regs *signal_state(void *t) +{ + struct task_struct *task = t; + + return(&task->thread.altstack_regs); +} + +int probe_stack(unsigned long sp, int delta) +{ + int n; + + if((get_user(n, (int *) sp) != 0) || + (put_user(n, (int *) sp) != 0) || + (get_user(n, (int *) (sp - delta)) != 0) || + (put_user(n, (int *) (sp - delta)) != 0)) + return(-EFAULT); + return(0); +} + +int have_signals(void *t) +{ + struct task_struct *task; + int ret; + + if(t == NULL) task = current; + else task = t; + ret = (task->thread.signal.state == SIGNAL_PENDING); + task->thread.signal.state = SIGNAL_NONE; + return(ret); +} + +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + +#define _S(nr) (1<<((nr)-1)) + +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +/* + * OK, we're invoking a handler + */ +static int handle_signal(unsigned long signr, struct k_sigaction *ka, + sigset_t *oldset, unsigned long *error, + int *again_out) +{ + struct signal_context *context; + __sighandler_t handler; + unsigned long sp; + sigset_t save; + int frame_size; + + if(again_out) *again_out = 0; + if((error != NULL) && (*error != 0)){ + switch (*error) { + case -ERESTARTNOHAND: + *error = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + *error = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + if(again_out) *again_out = 1; + break; + } + } + handler = ka->sa.sa_handler; + save = *oldset; + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,signr); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } + + sp = UM_SP(¤t->thread.process_regs); + + if((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0)) + sp = current->sas_ss_sp + current->sas_ss_size; + + frame_size = 4 * sizeof(void *) + sizeof(*context) + + 4 * sizeof(void *) + signal_stack_size; + + if(probe_stack(sp - sizeof(void *), frame_size) < 0){ + if(signr == SIGSEGV){ + struct k_sigaction *ka; + + ka = ¤t->sig->action[SIGSEGV - 1]; + ka->sa.sa_handler = SIG_DFL; + } + force_sig(SIGSEGV, current); + return(1); + } + + current->thread.signal.signal = signr; + current->thread.signal.handler = (unsigned long) handler; + current->thread.signal.state = SIGNAL_PENDING; + + sp -= 4 * sizeof(void *) + sizeof(*context); + context = (struct signal_context *) sp; + if(error != NULL) + UM_SET_SYSCALL_RETURN(¤t->thread.process_regs, *error); + context->regs = current->thread.process_regs; + context->repeat = current->thread.repeat_syscall; + context->sigs = save; + context->prev = current->thread.signal_context; + current->thread.signal_context = context; + + sp -= 4 * sizeof(void *); + setup_stack(sp, ¤t->thread.altstack_regs); + + return(0); +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ + +static int kern_do_signal(sigset_t *oldset, unsigned long *error, + int *again_out) +{ + siginfo_t info; + struct k_sigaction *ka; + int err; + + if (!oldset) + oldset = ¤t->blocked; + + if(again_out) *again_out = 0; + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) + break; + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: { + struct signal_struct *sig; + current->state = TASK_STOPPED; + current->exit_code = signr; + sig = current->p_pptr->sig; + if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + } + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if(do_coredump(signr, NULL)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + sigaddset(¤t->pending.signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + /* Whee! Actually deliver the signal. */ + err = handle_signal(signr, ka, oldset, error, again_out); + if(!err) return(1); + } + return(0); +} + +int do_signal(unsigned long *error, int *again_out) +{ + return(kern_do_signal(NULL, error, again_out)); +} + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +int +sys_sigsuspend(int history0, int history1, old_sigset_t mask) +{ + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (kern_do_signal(&saveset, NULL, NULL)) + return -EINTR; + } +} + +int +sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize) +{ + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (kern_do_signal(&saveset, NULL, NULL)) + return -EINTR; + } +} + +void signal_deliverer(int sig) +{ + unsigned long handler; + int signal; + + stop_pid(getpid()); + signal = current->thread.signal.signal; + handler = current->thread.signal.handler; + signal_handler(current, handler, signal); +} + +void set_sigreturn_syscall(int syscall) +{ + current->thread.sigreturn_syscall = syscall; +} + +int sys_sigreturn(struct sys_pt_regs regs) +{ + struct signal_context *context = current->thread.signal_context; + + sigdelsetmask(&context->sigs, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = context->sigs; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + current->thread.process_regs = context->regs; + current->thread.repeat_syscall = context->repeat; + current->thread.signal_context = context->prev; + UM_SYSCALL_NR(¤t->thread.process_regs) = + current->thread.sigreturn_syscall; + return(UM_SYSCALL_RET(¤t->thread.process_regs)); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/signal_user.c linux.ac/arch/um/kernel/signal_user.c --- linux.vanilla/arch/um/kernel/signal_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/signal_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "signal_user.h" +#include "signal_kern.h" +#include "sysdep/sigcontext.h" + +extern int timer_on; + +char *signal_stack = NULL; +char *signal_stack_mask = NULL; +int signal_stack_size = 0; +struct sys_pt_regs signal_regs; +struct sys_pt_regs signal_regs_mask; + +static int setup_signal_tramp(void *stack) +{ + set_sigstack(stack, page_size()); + set_handler(SIGUSR1, signal_deliverer, SA_ONSTACK, -1); + usr1_pid(getpid()); +} + +void setup_signal_stack(void) +{ + struct sys_pt_regs regs1, regs2; + char *sigstack1, *sigstack2, *stack1, *stack2; + int size; + + sigstack1 = mmap(NULL, page_size(), PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + sigstack2 = mmap(NULL, page_size(), PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if((sigstack1 == MAP_FAILED) || (sigstack2 == MAP_FAILED)){ + perror("setup_signal_stack : couldn't mmap stacks"); + exit(1); + } + size = get_one_stack(setup_signal_tramp, sigstack1, &stack1, ®s1); + if(get_one_stack(setup_signal_tramp, sigstack2, &stack2, + ®s2) != size){ + printf("setup_signal_stack : differing stack sizes\n"); + exit(1); + } + + signal_stack = diff_stacks(size, sigstack1, ®s1, sigstack2, ®s2, + &signal_stack_mask, &signal_regs, + &signal_regs_mask); + signal_stack_size = size; + + if((munmap(sigstack1, page_size()) < 0) || + (munmap(sigstack2, page_size()) < 0) || + (munmap(stack1, page_size()) < 0) || + (munmap(stack2, page_size()) < 0)){ + perror("setup_signal_stack : unmapping temp stacks"); + exit(1); + } +} + +void setup_stack(unsigned long stack_top, struct sys_pt_regs *regs_out) +{ + char *frame; + unsigned long val; + int i, n; + + n = sizeof(signal_regs.regs)/sizeof(signal_regs.regs[0]); + for(i = 0; i < n; i++){ + regs_out->regs[i] = signal_regs.regs[i]; + if(signal_regs_mask.regs[i]) + regs_out->regs[i] += stack_top; + } + + frame = (char *) (stack_top - signal_stack_size); + for(i = 0; i < signal_stack_size;){ + if(signal_stack_mask[i] == 0){ + frame[i] = signal_stack[i]; + i++; + } + else { + val = *((unsigned long *) &signal_stack[i]); + val += stack_top; + *((unsigned long *) &frame[i]) = val; + i += sizeof(unsigned long); + } + } +} + +void set_sigstack(void *sig_stack, int size) +{ + stack_t stack; + + stack.ss_sp = (__ptr_t) sig_stack; + stack.ss_flags = 0; + stack.ss_size = size - sizeof(void *); + if(sigaltstack(&stack, NULL) != 0) + panic("sigaltstack failed"); +} + +void set_handler(int sig, void (*handler)(int), int flags, ...) +{ + struct sigaction action; + va_list ap; + int mask; + + va_start(ap, flags); + action.sa_handler = handler; + sigemptyset(&action.sa_mask); + while((mask = va_arg(ap, int)) != -1){ + sigaddset(&action.sa_mask, mask); + } + action.sa_flags = flags; + action.sa_restorer = NULL; + if(sigaction(sig, &action, NULL) < 0) + panic("sigaction failed"); +} + +void change_sig(int signal, int on) +{ + sigset_t sigset; + + sigemptyset(&sigset); + sigaddset(&sigset, signal); + sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, NULL); +} + +void signal_handler(void *task, unsigned long h, int sig) +{ + void (*handler)(int, struct sigcontext); + void *regs; + unsigned long cr2; + int err; + UM_ALLOCATE_SC(sc); + + regs = process_state(task, &cr2, &err); + fill_in_sigcontext(&sc, regs, cr2, err); + handler = (void (*)(int, struct sigcontext)) h; + (*handler)(sig, sc); +} + +static void change_signals(int type) +{ + sigset_t mask; + + sigemptyset(&mask); + if(type == SIG_BLOCK) + timer_on = 0; + else { + timer_on = 1; + sigaddset(&mask, SIGVTALRM); + sigaddset(&mask, SIGALRM); + } + sigaddset(&mask, SIGIO); + if(sigprocmask(type, &mask, NULL) < 0) + panic("Failed to change signal mask - errno = %d", errno); +} + +void block_signals(void) +{ + change_signals(SIG_BLOCK); +} + +void unblock_signals(void) +{ + change_signals(SIG_UNBLOCK); +} + +#define SIGIO_BIT 0 +#define SIGVTALRM_BIT 1 + +static int enable_mask(sigset_t *mask) +{ + int sigs; + + sigs = sigismember(mask, SIGIO) ? 0 : 1 << SIGIO_BIT; + sigs |= sigismember(mask, SIGVTALRM) ? 0 : 1 << SIGVTALRM_BIT; + sigs |= sigismember(mask, SIGALRM) ? 0 : 1 << SIGVTALRM_BIT; + if(timer_on) sigs |= 1 << SIGVTALRM_BIT; + return(sigs); +} + +int set_signals(int enable) +{ + sigset_t mask; + int ret; + + sigemptyset(&mask); + if(enable & (1 << SIGIO_BIT)) sigaddset(&mask, SIGIO); + if(enable & (1 << SIGVTALRM_BIT)){ + timer_on = 1; + sigaddset(&mask, SIGVTALRM); + sigaddset(&mask, SIGALRM); + } + if(sigprocmask(SIG_UNBLOCK, &mask, &mask) < 0) + panic("Failed to enable signals"); + ret = enable_mask(&mask); + sigemptyset(&mask); + if((enable & (1 << SIGIO_BIT)) == 0) sigaddset(&mask, SIGIO); + if((enable & (1 << SIGVTALRM_BIT)) == 0){ + timer_on = 0; + } + if(sigprocmask(SIG_BLOCK, &mask, NULL) < 0) + panic("Failed to block signals"); + return(ret); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/smp.c linux.ac/arch/um/kernel/smp.c --- linux.vanilla/arch/um/kernel/smp.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/smp.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifdef CONFIG_SMP + +#include "linux/config.h" +#include "linux/sched.h" +#include "linux/threads.h" +#include "linux/smp.h" +#include "asm/processor.h" +#include "asm/spinlock.h" +#include "asm/softirq.h" +#include "asm/hardirq.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" + +/* Total count of live CPUs */ +int smp_num_cpus = 0; + +/* The 'big kernel lock' */ +spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; + +/* Per CPU bogomips and other parameters */ +struct cpuinfo_um cpu_data[NR_CPUS]; + +/* which CPU maps to which logical number */ +int cpu_number_map[NR_CPUS]; + +spinlock_t um_bh_lock = SPIN_LOCK_UNLOCKED; + +atomic_t global_bh_count; +atomic_t global_bh_lock; + +unsigned char global_irq_holder = NO_PROC_ID; +unsigned volatile int global_irq_lock; + +/* Set when the idlers are all forked */ +int smp_threads_ready = 0; +int num_reschedules_sent = 0; + +void smp_send_reschedule(int cpu) +{ + num_reschedules_sent++; +} + +static void show(char * str) +{ + int cpu = smp_processor_id(); + + printk("\n%s, CPU %d:\n", str, cpu); +} + +#define MAXCOUNT 100000000 + +static inline void wait_on_bh(void) +{ + int count = MAXCOUNT; + do { + if (!--count) { + show("wait_on_bh"); + count = ~0; + } + /* nothing .. wait for the other bh's to go away */ + } while (atomic_read(&global_bh_count) != 0); +} + +/* + * This is called when we want to synchronize with + * bottom half handlers. We need to wait until + * no other CPU is executing any bottom half handler. + * + * Don't wait if we're already running in an interrupt + * context or are inside a bh handler. + */ +void synchronize_bh(void) +{ + if (atomic_read(&global_bh_count) && !in_interrupt()) + wait_on_bh(); +} + +void smp_send_stop(void) +{ + printk("Stopping all CPUs\n"); +} + +void smp_commence(void) +{ +} + +void smp_boot_cpus(void) +{ + if(ncpus < 1){ + printk("ncpus set to 1\n"); + ncpus = 1; + } + else if(ncpus > NR_CPUS){ + printk("ncpus can't be greater than NR_CPUS, set to %d\n", NR_CPUS); + ncpus = NR_CPUS; + } +} + +int setup_profiling_timer(unsigned int multiplier) +{ + printk("setup_profiling_timer\n"); + return(0); +} + +int inited_cpus = 1; + +int pid_to_processor_id(int pid) +{ + int i; + + for(i=0;ithread.forking = 1; + ret = do_fork(SIGCHLD, 0, NULL, 0); + current->thread.forking = 0; + return(ret); +} + +long sys_clone(unsigned long clone_flags, unsigned long newsp) +{ + long ret; + + current->thread.forking = 1; + ret = do_fork(clone_flags, newsp, NULL, 0); + current->thread.forking = 0; + return(ret); +} + +long sys_vfork(void) +{ + long ret; + + current->thread.forking = 1; + ret = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0, NULL, 0); + current->thread.forking = 0; + return(ret); +} + +/* common code for old and new mmaps */ +static inline long do_mmap2( + unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + int error = -EBADF; + struct file * file = NULL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); + out: + return error; +} + +long sys_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + return do_mmap2(addr, len, prot, flags, fd, pgoff); +} + +/* + * Perform the select(nd, in, out, ex, tv) and mmap() system + * calls. Linux/i386 didn't use to be able to handle more than + * 4 system call parameters, so these system calls used a memory + * block for parameter passing.. + */ + +struct mmap_arg_struct { + unsigned long addr; + unsigned long len; + unsigned long prot; + unsigned long flags; + unsigned long fd; + unsigned long offset; +}; + +int old_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long offset) +{ + int err = -EINVAL; + if (offset & ~PAGE_MASK) + goto out; + + err = do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); + out: + return err; +} +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way unix traditionally does this, though. + */ +int sys_pipe(unsigned long * fildes) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + +int sys_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +int sys_ipc (uint call, int first, int second, + int third, void *ptr, long fifth) +{ + int version, ret; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + case SEMOP: + return sys_semop (first, (struct sembuf *)ptr, second); + case SEMGET: + return sys_semget (first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void **) ptr)) + return -EFAULT; + return sys_semctl (first, second, third, fourth); + } + + case MSGSND: + return sys_msgsnd (first, (struct msgbuf *) ptr, + second, third); + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + if (!ptr) + return -EINVAL; + + if (copy_from_user(&tmp, + (struct ipc_kludge *) ptr, + sizeof (tmp))) + return -EFAULT; + return sys_msgrcv (first, tmp.msgp, second, + tmp.msgtyp, third); + } + default: + panic("msgrcv with version != 0"); + return sys_msgrcv (first, + (struct msgbuf *) ptr, + second, fifth, third); + } + case MSGGET: + return sys_msgget ((key_t) first, second); + case MSGCTL: + return sys_msgctl (first, second, (struct msqid_ds *) ptr); + + case SHMAT: + switch (version) { + default: { + ulong raddr; + ret = sys_shmat (first, (char *) ptr, second, &raddr); + if (ret) + return ret; + return put_user (raddr, (ulong *) third); + } + case 1: /* iBCS2 emulator entry point */ + if (!segment_eq(get_fs(), get_ds())) + return -EINVAL; + return sys_shmat (first, (char *) ptr, second, (ulong *) third); + } + case SHMDT: + return sys_shmdt ((char *)ptr); + case SHMGET: + return sys_shmget (first, second, third); + case SHMCTL: + return sys_shmctl (first, second, + (struct shmid_ds *) ptr); + default: + return -EINVAL; + } +} + +int sys_uname(struct old_utsname * name) +{ + int err; + if (!name) + return -EFAULT; + down_read(&uts_sem); + err=copy_to_user(name, &system_utsname, sizeof (*name)); + up_read(&uts_sem); + return err?-EFAULT:0; +} + +int sys_olduname(struct oldold_utsname * name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + down_read(&uts_sem); + + error = __copy_to_user(&name->sysname,&system_utsname.sysname, + __OLD_UTS_LEN); + error |= __put_user(0,name->sysname+__OLD_UTS_LEN); + error |= __copy_to_user(&name->nodename,&system_utsname.nodename, + __OLD_UTS_LEN); + error |= __put_user(0,name->nodename+__OLD_UTS_LEN); + error |= __copy_to_user(&name->release,&system_utsname.release, + __OLD_UTS_LEN); + error |= __put_user(0,name->release+__OLD_UTS_LEN); + error |= __copy_to_user(&name->version,&system_utsname.version, + __OLD_UTS_LEN); + error |= __put_user(0,name->version+__OLD_UTS_LEN); + error |= __copy_to_user(&name->machine,&system_utsname.machine, + __OLD_UTS_LEN); + error |= __put_user(0,name->machine+__OLD_UTS_LEN); + + up_read(&uts_sem); + + error = error ? -EFAULT : 0; + + return error; +} + +int sys_sigaltstack(const stack_t *uss, stack_t *uoss) +{ + return(do_sigaltstack(uss, uoss, + UM_SP(¤t->thread.process_regs))); +} + +int nsyscalls = 0; + +extern syscall_handler_t *sys_call_table[]; + +long execute_syscall(struct sys_pt_regs regs) +{ + long res; + int syscall; + + current->thread.nsyscalls++; + nsyscalls++; + syscall = UM_SYSCALL_NR(®s); + if(syscall == -1) + panic("syscall thread activated without a system call"); + if((syscall >= NR_syscalls) || (syscall < 0)) + return(-ENOSYS); + set_fs(KERNEL_DS); + res = EXECUTE_SYSCALL(syscall, regs); + set_fs(USER_DS); + return(res); +} + +spinlock_t syscall_lock = SPIN_LOCK_UNLOCKED; + +void lock_syscall(void) +{ + spin_lock(&syscall_lock); +} + +void unlock_syscall(void) +{ + spin_unlock(&syscall_lock); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/syscall_user.c linux.ac/arch/um/kernel/syscall_user.c --- linux.vanilla/arch/um/kernel/syscall_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/syscall_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +/* XXX FIXME : Ensure that SIGIO and SIGVTALRM can't happen immediately + * after setting up syscall stack + * block SIGVTALRM in any code that's under wait_for_stop + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "signal_kern.h" +#include "sysdep/ptrace.h" + +/* XXX Bogus */ +#define ERESTARTSYS 512 +#define ERESTARTNOINTR 513 +#define ERESTARTNOHAND 514 + +struct { + int syscall; + int pid; + int result; + struct timeval start; + struct timeval end; +} syscall_record[1024]; + +int syscall_index = 0; + +extern int timer_ready; +extern int signal_frame_size; + +int syscall_handler(void *unused) +{ + struct sys_pt_regs *regs; + long result; + int index, syscall, again; + + kill(getpid(), SIGSTOP); + unprotect_kernel_mem(); + timer_ready = 1; + syscall_trace(); + lock_syscall(); + if(syscall_index == 1024) syscall_index = 0; + index = syscall_index; + syscall_index++; + unlock_syscall(); + regs = process_state(NULL, NULL, NULL); + syscall = UM_SYSCALL_NR(regs); + syscall_record[index].syscall = UM_SYSCALL_NR(regs); + syscall_record[index].pid = current_pid(); + syscall_record[index].result = 0xdeadbeef; + gettimeofday(&syscall_record[index].start, NULL); + result = execute_syscall(*regs); + again = 0; + if((result == -ERESTARTNOHAND) || (result == -ERESTARTSYS) || + (result == -ERESTARTNOINTR)) + do_signal(&result, &again); + UM_SET_SYSCALL_RETURN(regs, result); + set_repeat_syscall(again); + syscall_trace(); + syscall_record[index].result = result; + gettimeofday(&syscall_record[index].end, NULL); + ret_from_sys_call(NULL); + + /* XXX + * This is a race, set_user_thread has to be called with signals off + */ + timer_ready = 0; + set_user_thread(NULL, 1, 1, 1); + return(0); +} + +int exit_kernel(int pid, void *task) +{ + void *stack; + struct sys_pt_regs *regs; + int tracing, again, n, restore; + + tracing = 1; + again = get_repeat_syscall(task); + set_repeat_syscall(0); + restore = get_restore_regs(task); + regs = process_state(task, NULL, NULL); + if(restore){ + if(ptrace_setregs(pid, regs) < 0) + tracer_panic("Couldn't restore registers"); + } + if(have_signals(task)){ + if(!restore) ptrace_getregs(pid, regs); + regs = signal_state(task); + if(ptrace_setregs(pid, regs) < 0) + panic("Couldn't set alternate stack state"); + } + else if(again){ + regs = syscall_state(task, &stack, &n); + if(ptrace_setregs(pid, regs) < 0) + panic("Couldn't restart system call"); + memcpy((void *) UM_SP(regs), stack, n); + tracing = 0; + } + return(tracing); +} + +extern unsigned long _stext, _etext; + +int do_syscall(void *task, int pid) +{ + void *stack; + struct sys_pt_regs *regs, proc_regs; + int syscall, n; + + if(ptrace_getregs(pid, &proc_regs) < 0) + tracer_panic("Couldn't read registers"); + + syscall = UM_SYSCALL_NR(&proc_regs); + if(syscall < 1) return(0); + + if((syscall != __NR_sigreturn) && + ((unsigned long *) UM_IP(&proc_regs) >= &_stext) && + ((unsigned long *) UM_IP(&proc_regs) <= &_etext)) + tracer_panic("I'm tracing myself and I can't get out"); + regs = process_state(task, NULL, NULL); + if(syscall == __NR_sigreturn){ + set_sigreturn_syscall(UM_SYSCALL_NR(regs)); + UM_SYSCALL_NR(regs) = __NR_sigreturn; + } + else *regs = proc_regs; + set_tracing(task, 0); + regs = syscall_state(task, &stack, &n); + if(ptrace_setregs(pid, regs) < 0) + tracer_panic("Couldn't set system call state"); + memcpy((void *) UM_SP(regs), stack, n); + + if((ptrace(PTRACE_POKEUSER, pid, UM_SYSCALL_NR_OFFSET, + __NR_getpid) < 0) || + (ptrace(PTRACE_CONT, pid, 0, 0) < 0)){ + printk("Failed to change syscall number to __NR_getpid\n"); + if(errno == EIO){ + printk("You probably didn't apply the ptrace patch " + "to your hosting kernel\n"); + } + else printk("errno = %d\n", errno); + tracer_panic("do_syscall : Couldn't force getpid"); + } + return(1); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/sysrq.c linux.ac/arch/um/kernel/sysrq.c --- linux.vanilla/arch/um/kernel/sysrq.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/sysrq.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,72 @@ +#include "linux/kernel.h" +#include "linux/module.h" +#include "asm/page.h" +#include "asm/processor.h" +#include "sysrq.h" + +extern int _stext, _etext; + + /* + * If the address is either in the .text section of the + * kernel, or in the vmalloc'ed module regions, it *may* + * be the address of a calling routine + */ + +#ifdef CONFIG_MODULES + +extern struct module *module_list; +extern struct module kernel_module; + +static inline int kernel_text_address(unsigned long addr) +{ + int retval = 0; + struct module *mod; + + if (addr >= (unsigned long) &_stext && + addr <= (unsigned long) &_etext) + return 1; + + for (mod = module_list; mod != &kernel_module; mod = mod->next) { + /* mod_bound tests for addr being inside the vmalloc'ed + * module area. Of course it'd be better to test only + * for the .text subset... */ + if (mod_bound(addr, 0, mod)) { + retval = 1; + break; + } + } + + return retval; +} + +#else + +static inline int kernel_text_address(unsigned long addr) +{ + return (addr >= (unsigned long) &_stext && + addr <= (unsigned long) &_etext); +} + +#endif + +void show_trace(unsigned long * stack) +{ + int i; + unsigned long addr; + + if (!stack) + stack = (unsigned long*) &stack; + + printk("Call Trace: "); + i = 1; + while (((long) stack & (THREAD_SIZE-1)) != 0) { + addr = *stack++; + if (kernel_text_address(addr)) { + if (i && ((i % 6) == 0)) + printk("\n "); + printk("[<%08lx>] ", addr); + i++; + } + } + printk("\n"); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/time.c linux.ac/arch/um/kernel/time.c --- linux.vanilla/arch/um/kernel/time.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/time.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#define _GNU_SOURCE /* to get timeradd and timersub */ + +#include +#include + +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "process.h" +#include "signal_user.h" + +extern struct timeval xtime; + +void timer_handler(int sig, void *sc, int usermode) +{ + timer_irq(usermode); +} + +void timer(void) +{ + gettimeofday(&xtime, NULL); +} + +static struct itimerval profile_interval; + +void get_profile_timer(void) +{ + getitimer(ITIMER_PROF, &profile_interval); + profile_interval.it_value = profile_interval.it_interval; +} + +void disable_profile_timer(void) +{ + struct itimerval interval = ((struct itimerval) { { 0, 0 }, { 0, 0 }}); + setitimer(ITIMER_PROF, &interval, NULL); +} + +static void set_interval(int timer_type) +{ + struct itimerval interval; + + interval.it_interval.tv_sec = 0; + interval.it_interval.tv_usec = 1000000/hz(); + interval.it_value.tv_sec = 0; + interval.it_value.tv_usec = 1000000/hz(); + if(setitimer(timer_type, &interval, NULL) == -1) + panic("setitimer failed - errno = %d\n", errno); +} + +void idle_timer(void) +{ + if(signal(SIGVTALRM, SIG_IGN) == SIG_ERR) + panic("Couldn't unset SIGVTALRM handler"); + set_handler(SIGALRM, (__sighandler_t) alarm_handler, + SA_NODEFER | SA_RESTART, SIGUSR1, SIGIO, -1); + set_interval(ITIMER_REAL); +} + +void time_init(void) +{ + if(signal(SIGVTALRM, boot_timer_handler) == SIG_ERR) + panic("Couldn't set SIGVTALRM handler"); + set_interval(ITIMER_VIRTUAL); +} + +void set_timers(int set_signal) +{ + if(set_signal){ + if(signal(SIGVTALRM, (__sighandler_t) alarm_handler) == + SIG_ERR) + panic("Couldn't set SIGVTALRM handler"); + set_interval(ITIMER_VIRTUAL); + } + if(setitimer(ITIMER_PROF, &profile_interval, NULL) == -1) + panic("setitimer ITIMER_PROF failed - errno = %d\n", errno); +} + +struct timeval local_offset = { 0, 0 }; + +void do_gettimeofday(struct timeval *tv) +{ + gettimeofday(tv, NULL); + timeradd(tv, &local_offset, tv); +} + +void do_settimeofday(struct timeval *tv) +{ + struct timeval now; + + gettimeofday(&now, NULL); + timersub(tv, &now, &local_offset); +} + +void idle_sleep(int secs) +{ + struct timespec ts; + + ts.tv_sec = secs; + ts.tv_nsec = 0; + nanosleep(&ts, &ts); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/time_kern.c linux.ac/arch/um/kernel/time_kern.c --- linux.vanilla/arch/um/kernel/time_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/time_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/kernel.h" +#include "linux/unistd.h" +#include "linux/stddef.h" +#include "linux/spinlock.h" +#include "linux/sched.h" +#include "linux/interrupt.h" +#include "linux/init.h" +#include "linux/delay.h" +#include "asm/param.h" +#include "asm/current.h" +#include "kern_util.h" +#include "user_util.h" + +extern rwlock_t xtime_lock; + +int hz(void) +{ + return(HZ); +} + +int timer_irq_inited = 0; + +int timer_on = 0; +int timer_ready = 0; +int missed_ticks = 0; + +void timer_irq(int user_mode) +{ + int ticks = missed_ticks; + + if(!timer_irq_inited) return; + missed_ticks = 0; + while(ticks--) do_IRQ(TIMER_IRQ, user_mode); +} + +void boot_timer_handler(int sig) +{ + struct pt_regs regs; + + regs.user_mode = 0; + do_timer(®s); +} + +void um_timer(int irq, void *dev, struct pt_regs *regs) +{ + do_timer(regs); + write_lock(&xtime_lock); + timer(); + write_unlock(&xtime_lock); +} + +long um_time(int * tloc) +{ + struct timeval now; + + do_gettimeofday(&now); + if (tloc) { + if (put_user(now.tv_sec,tloc)) + now.tv_sec = -EFAULT; + } + return now.tv_sec; +} + +long um_stime(int * tptr) +{ + int value; + struct timeval new; + + if (get_user(value, tptr)) + return -EFAULT; + new.tv_sec = value; + new.tv_usec = 0; + do_settimeofday(&new); + return 0; +} + +void __delay(um_udelay_t time) +{ + int i; + + for(i=0;ithread.extern_pid != -1) && + (current->thread.extern_pid != getpid())) + panic("fix_range fixing wrong address space"); + if(mm == NULL) return; + for(addr=start_addr;addr TASK_SIZE, which is + * only true in the honeypot case. + */ + addr = STACK_TOP - ABOVE_KMEM; + continue; + } + npgd = pgd_offset(mm, addr); + npmd = pmd_offset(npgd, addr); + if(pmd_present(*npmd)){ + npte = pte_offset(npmd, addr); + r = pte_read(*npte); + w = pte_write(*npte); + x = pte_exec(*npte); + if(!pte_dirty(*npte)) w = 0; + if(!pte_young(*npte)){ + r = 0; + w = 0; + } + if(force || !pte_present(*npte) || pte_newpage(*npte)){ + if(munmap((void *) addr, PAGE_SIZE) < 0) + panic("munmap failed, errno = %d\n", + errno); + if(pte_present(*npte)) + map(addr, pte_address(*npte), + PAGE_SIZE, r, w, x); + } + else if(pte_newprot(*npte)) + protect(addr, PAGE_SIZE, r, w, x); + *npte = pte_mkuptodate(*npte); + addr += PAGE_SIZE; + } + else { + if(force || pmd_newpage(*npmd)){ + if(munmap((void *) addr, PMD_SIZE) < 0) + panic("munmap failed, errno = %d\n", + errno); + } + addr += PMD_SIZE; + } + } +} + +void flush_tlb_kernel_vm(void) +{ + struct mm_struct *mm; + pgd_t *npgd; + pmd_t *npmd; + pte_t *npte; + unsigned long addr; + int r, w, x; + + mm = &init_mm; + for(addr = start_vm; addr < end_vm;){ + npgd = pgd_offset(mm, addr); + npmd = pmd_offset(npgd, addr); + if(pmd_present(*npmd)){ + unsigned long mask = PAGE_MASK | _PAGE_NEWPAGE | + _PAGE_NEWPROT; + npte = pte_offset(npmd, addr); + + if((pte_val(*npte) & ~mask) == + pgprot_val(PAGE_KERNEL)){ + r = 1; + w = 1; + x = 1; + } + else { + r = 1; + w = 0; + x = 1; + } + if(!pte_present(*npte) || pte_newpage(*npte)){ + if(munmap((void *) addr, PAGE_SIZE) < 0) + panic("munmap failed, errno = %d\n", + errno); + if(pte_present(*npte)) + map(addr, pte_address(*npte), + PAGE_SIZE, r, w, x); + } + else if(pte_newprot(*npte)) + protect(addr, PAGE_SIZE, r, w, x); + addr += PAGE_SIZE; + + } + else { + if(pmd_newpage(*npmd)){ + if(munmap((void *) addr, PMD_SIZE) < 0) + panic("munmap failed, errno = %d\n", + errno); + } + addr += PMD_SIZE; + } + } +} + +void flush_tlb_range(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + if(mm != current->mm) return; + + /* Assumes that the range start ... end is entirely within + * either process memory or kernel vm + */ + if((start >= start_vm) && (start < end_vm)) flush_tlb_kernel_vm(); + else fix_range(mm, start, end, 0); +} + +void flush_tlb_mm(struct mm_struct *mm) +{ + if(mm != current->mm) return; + + fix_range(mm, 0, STACK_TOP, 0); + flush_tlb_kernel_vm(); +} + +void flush_tlb_page(struct vm_area_struct *vma, unsigned long address) +{ + address &= PAGE_MASK; + flush_tlb_range(vma->vm_mm, address, address + PAGE_SIZE); +} + +void flush_tlb_all(void) +{ + flush_tlb_mm(current->mm); +} + +void force_flush_all(void) +{ + fix_range(current->mm, 0, STACK_TOP, 1); + flush_tlb_kernel_vm(); +} + +pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address) +{ + return(pgd_offset(mm, address)); +} + +pmd_t *pmd_offset_proc(pgd_t *pgd, unsigned long address) +{ + return(pmd_offset(pgd, address)); +} + +pte_t *pte_offset_proc(pmd_t *pmd, unsigned long address) +{ + return(pte_offset(pmd, address)); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/trap_kern.c linux.ac/arch/um/kernel/trap_kern.c --- linux.vanilla/arch/um/kernel/trap_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/trap_kern.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/kernel.h" +#include "linux/sched.h" +#include "linux/mm.h" +#include "linux/spinlock.h" +#include "linux/config.h" +#include "linux/init.h" +#include "asm/semaphore.h" +#include "asm/pgtable.h" +#include "asm/pgalloc.h" +#include "asm/a.out.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "chan.h" +#include "debug.h" +#include "mconsole_kern.h" + +extern int nsyscalls; + +unsigned long segv(unsigned long address, unsigned long ip, int is_write, + int is_user) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + struct siginfo si; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + unsigned long page; + int ok; + + if((address >= start_vm) && (address < end_vm)){ + flush_tlb_kernel_vm(); + return(0); + } + if(mm == NULL) panic("Segfault with no mm"); + down_read(&mm->mmap_sem); + vma = find_vma(mm, address); + ok = 1; + if(!vma) ok = 0; + else if(is_write && !(vma->vm_flags & VM_WRITE)) ok = 0; + else if(vma->vm_start > address){ + if((vma->vm_flags & VM_STACK_FLAGS) != VM_STACK_FLAGS) ok = 0; + else if(expand_stack(vma, address)) ok = 0; + } + if(!ok){ + if (current->thread.fault_catcher != NULL) { + current->thread.fault_addr = (void *) address; + up_read(&mm->mmap_sem); + do_longjmp(current->thread.fault_catcher); + } + else if(current->thread.fault_addr != NULL){ + panic("fault_addr set but no fault catcher"); + } + if(!is_user) + panic("Kernel mode fault at addr 0x%lx, ip 0x%lx", + address, ip); + si.si_signo = SIGSEGV; + si.si_code = SEGV_ACCERR; + si.si_addr = (void *) address; + current->thread.cr2 = address; + current->thread.err = is_write; + force_sig_info(SIGSEGV, &si, current); + up_read(&mm->mmap_sem); + return(0); + } + page = address & PAGE_MASK; + if(page == (unsigned long) current + PAGE_SIZE) + panic("Kernel stack overflow"); + pgd = pgd_offset(mm, page); + pmd = pmd_offset(pgd, page); + do { + survive: + switch (handle_mm_fault(mm, vma, address, is_write)) { + case 1: + current->min_flt++; + break; + case 2: + current->maj_flt++; + break; + default: + if (current->pid == 1) { + current->policy |= SCHED_YIELD; + schedule(); + goto survive; + } + /* Fall through to bad area case */ + case 0: + up_read(&mm->mmap_sem); + force_sig(SIGBUS, current); + return(0); + } + pte = pte_offset(pmd, page); + pte_mkyoung(*pte); + if(is_write) pte_mkdirty(*pte); + } while(is_write && !pte_write(*pte)); + if(is_write && !pte_write(*pte)) panic("page not writeable"); + if(!is_write && !pte_present(*pte)){ + printk("Page disappeared while handling fault"); + force_sigbus(); + } + flush_tlb_page(vma, page); + up_read(&mm->mmap_sem); + return(0); +} + +void bad_segv(unsigned long address, unsigned long ip, int is_write) +{ + struct siginfo si; + + printk(KERN_ERR "Unfixable SEGV in '%s' (pid %d) at 0x%lx " + "(ip 0x%lx)\n", current->comm, current->pid, address, ip); + si.si_signo = SIGSEGV; + si.si_code = SEGV_ACCERR; + si.si_addr = (void *) address; + current->thread.cr2 = address; + current->thread.err = is_write; + force_sig_info(SIGSEGV, &si, current); +} + +void relay_signal(int sig, void *sc, int usermode) +{ + force_sig(sig, current); +} + +void trap_init(void) +{ +} + +spinlock_t trap_lock = SPIN_LOCK_UNLOCKED; + +void lock_trap(void) +{ + spin_lock(&trap_lock); +} + +void unlock_trap(void) +{ + spin_unlock(&trap_lock); +} + +extern int debugger_pid; +extern int debugger_fd; + +#ifdef CONFIG_PT_PROXY + +void debugger_signal(int status, pid_t pid) +{ + debugger_proxy(status, pid); +} + +void child_signal(pid_t pid, int status) +{ + child_proxy(pid, status); +} + +struct io_chan gdb_chan = XTERM_IO_CHAN_INIT(0, "", 1, INIT_STATIC); +char *gdb_init = "xterm"; + +static void exit_debugger_cb(void *unused) +{ + if(debugger_pid != -1){ + if(gdb_pid != -1){ + if(ptrace(PTRACE_DETACH, debugger_pid, 0, 0) < 0) + printk("Detaching debugger failed - " + "errno = %d\n", errno); + gdb_pid = -1; + } + else { + kill(debugger_pid, SIGKILL); + kill(debugger_pid, SIGCONT); + while(waitpid(debugger_pid, NULL, WNOHANG) > 0) ; + } + debugger_pid = -1; + } + close_chan_pair(&gdb_chan); +} + +static void exit_debugger(void) +{ + tracing_cb(exit_debugger_cb, NULL); +} + +__exitcall(exit_debugger); + +static void gdb_announce(char *dev_name, int dev) +{ + printf("gdb assigned device '%s'\n", dev_name); +} + +static struct chan_opts opts = { + announce: gdb_announce, + dev: -1, + xterm_title: "UML kernel debugger", + raw_pty: 0 +}; + +struct gdb_data { + char *str; + int err; +}; + +static void config_gdb_cb(void *arg) +{ + struct gdb_data *data = arg; + int err, pid; + + data->err = -1; + if(debugger_pid != -1) exit_debugger_cb(NULL); + if(!strncmp(data->str, "pid,", strlen("pid,"))){ + data->str += strlen("pid,"); + pid = simple_strtoul(data->str, NULL, 0); + debugger_pid = attach_debugger(current_task->thread.extern_pid, + pid, 0); + if(debugger_pid != -1){ + data->err = 0; + gdb_pid = pid; + } + return; + } + gdb_chan = ((struct io_chan) XTERM_IO_CHAN_INIT(0, "", 1, + INIT_STATIC)); + err = parse_chan_pair(data->str, 0, &gdb_chan, INIT_ONE, &opts); + if(err) return; + data->err = 0; + debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd); + init_proxy(debugger_pid, 0, 0); +} + +int gdb_config(char *str) +{ + struct gdb_data data; + + if(*str++ != '=') return(-1); + data.str = str; + tracing_cb(config_gdb_cb, &data); + return(data.err); +} + +void remove_gdb_cb(void *unused) +{ + exit_debugger_cb(NULL); +} + +int gdb_remove(char *unused) +{ + tracing_cb(remove_gdb_cb, NULL); + return(0); +} + +static struct mc_device gdb_mc = { + name: "gdb", + config: gdb_config, + remove: gdb_remove, +}; + +int gdb_mc_init(void) +{ + mconsole_register_dev(&gdb_mc); + return(0); +} + +__initcall(gdb_mc_init); + +void signal_usr1(int sig) +{ + if(debugger_pid != -1){ + printk("The debugger is already running\n"); + return; + } + if(parse_chan_pair(gdb_init, 0, &gdb_chan, INIT_ONE, &opts)) return; + debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd); + init_proxy(debugger_pid, 0, 0); +} + +int init_ptrace_proxy(int idle_pid, int startup, int stop) +{ + int pid, status; + + if(parse_chan_pair(gdb_init, 0, &gdb_chan, INIT_ONE, &opts)){ + ptrace(PTRACE_CONT, idle_pid, 0, 0); + return(-1); + } + pid = start_debugger(linux_prog, startup, stop, &debugger_fd); + status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT); + if(pid < 0){ + ptrace(PTRACE_CONT, idle_pid, 0, 0); + return(-1); + } + init_proxy(pid, 1, status); + return(pid); +} + +int attach_debugger(int idle_pid, int pid, int stop) +{ + int status = 0; + + if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){ + printf("Failed to attach pid %d, errno = %d\n", pid, errno); + return(-1); + } + if(stop) status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT); + init_proxy(pid, 1, status); + return(pid); +} + +#else + +void debugger_signal(int status, pid_t pid){ } +void child_signal(pid_t pid, int status){ } +int init_ptrace_proxy(int idle_pid, int startup, int stop) +{ + printk("debug requested when CONFIG_PT_PROXY is off\n"); + wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT); + ptrace(PTRACE_CONT, idle_pid, 0, 0); + return(-1); +} + +void signal_usr1(int sig) +{ + printk("debug requested when CONFIG_PT_PROXY is off\n"); +} + +int attach_debugger(int idle_pid, int pid, int stop) +{ + printk(KERN_ERR "attach_debugger called when CONFIG_PT_PROXY " + "is off\n"); + return(-1); +} + +int config_gdb(char *str) +{ + return(-1); +} + +int remove_gdb(void) +{ + return(-1); +} + +#endif +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/trap_user.c linux.ac/arch/um/kernel/trap_user.c --- linux.vanilla/arch/um/kernel/trap_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/trap_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "mem_user.h" +#include "user.h" +#include "process.h" +#include "sysdep/sigcontext.h" + +static void signal_segv(int sig) +{ + write(2, "Seg fault in signals\n", strlen("Seg fault in signals\n")); + for(;;) ; +} + +int detach(int pid) +{ + return(ptrace(PTRACE_DETACH, pid, 0, SIGSTOP)); +} + +int debug = 0; +int debug_stop = 1; + +int honeypot = 0; + +static int signal_tramp(void *arg) +{ + int (*proc)(void *); + + if(honeypot && munmap((void *) (host_task_size - 0x10000000), + 0x10000000)) + panic("Unmapping stack failed"); + if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) + panic("ptrace PTRACE_TRACEME failed"); + signal(SIGUSR1, SIG_IGN); + signal(SIGSEGV, (__sighandler_t) sig_handler); + set_timers(0); + set_cmdline("(idle thread)"); + set_init_pid(getpid()); + proc = arg; + return((*proc)(NULL)); +} + +#ifdef CONFIG_SMP +#error need to make these arrays +#endif + +int debugger_pid = -1; +int debugger_fd = -1; +int gdb_pid = -1; + +struct { + unsigned long address; + int is_write; + int pid; + unsigned long sp; + int is_user; +} segfault_record[1024]; + +int segfault_index = 0; + +struct { + int pid; + int signal; + unsigned long addr; + struct timeval time; +} signal_record[1024]; + +int signal_index = 0; +int nsignals = 0; +int debug_trace = 0; +extern int io_nsignals, io_count, intr_count; + +extern void signal_usr1(int sig); + +int tracing_pid = -1; + +int signals(int (*init_proc)(void *), void *sp) +{ + void *task = NULL; + unsigned long eip = 0; + int status, pid = 0, sig, cont_type, tracing = 0, op = 0; + int last_index, proc_id, save_errno = 0; + + setup_kernel_stack(); + setup_signal_stack(); + calc_sigframe_size(); + signal(SIGSEGV, signal_segv); + signal(SIGUSR1, signal_usr1); + signal(SIGPIPE, SIG_IGN); + tracing_pid = getpid(); + printk("tracing thread pid = %d\n", tracing_pid); + pid = clone(signal_tramp, sp, CLONE_FILES | SIGCHLD, init_proc); + if(debug){ + if(gdb_pid != -1) + debugger_pid = attach_debugger(pid, gdb_pid, 1); + else debugger_pid = init_ptrace_proxy(pid, 1, debug_stop); + } + set_cmdline("(tracing thread)"); + if(debug_trace){ + printk("Tracing thread pausing to be attached\n"); + stop(); + } + while(1){ + if((pid = waitpid(-1, &status, WUNTRACED)) <= 0){ + if(errno != ECHILD){ + printk("wait failed - errno = %d\n", errno); + } + continue; + } + save_errno = errno; + if(pid == debugger_pid){ + if(WIFEXITED(status) || WIFSIGNALED(status)) + debugger_pid = -1; + debugger_signal(status, external_pid(NULL)); + continue; + } + nsignals++; + if(WIFEXITED(status)){ + } +#ifdef notdef + { + printk("Child %d exited with status %d\n", pid, + WEXITSTATUS(status)); + } +#endif + else if(WIFSIGNALED(status)){ + sig = WTERMSIG(status); + if(sig != 9){ + printk("Child %d exited with signal %d\n", pid, + sig); + } + } + else if(WIFSTOPPED(status)){ + sig = WSTOPSIG(status); + if(signal_index == 1024){ + signal_index = 0; + last_index = 1023; + } + else last_index = signal_index - 1; + if(((sig == SIGPROF) || (sig == SIGVTALRM) || + (sig == SIGALRM)) && + (signal_record[last_index].signal == sig) && + (signal_record[last_index].pid == pid)) + signal_index = last_index; + signal_record[signal_index].pid = pid; + gettimeofday(&signal_record[signal_index].time, NULL); + eip = ptrace(PTRACE_PEEKUSER, pid, UM_IP_OFFSET, 0); + signal_record[signal_index].addr = eip; + signal_record[signal_index++].signal = sig; +#ifdef CONFIG_SMP + proc_id = pid_to_processor_id(pid); + task = cpu_tasks[proc_id].task; +#else + proc_id = 0; + task = get_current_task(); +#endif + tracing = is_tracing(task); + switch(sig){ + case SIGUSR1: + sig = 0; + op = do_proc_op(task, proc_id); + switch(op){ + case OP_EXEC: + case OP_SWITCH: + continue; + case OP_TRACE_ON: + tracing = exit_kernel(pid, task); + break; + case OP_TRACE_OFF: + tracing = 0; + break; + case OP_REBOOT: + case OP_HALT: + kmalloc_ok = 0; + ptrace(PTRACE_KILL, pid, 0, 0); + return(op == OP_REBOOT); + case OP_NONE: + printk("Detaching pid %d\n", pid); + detach(pid); + continue; + default: + break; + } + break; + case SIGSTOP: + if(debugger_pid != -1) + child_signal(pid, status); + continue; + case SIGTRAP: + sig = 0; + if(switching_modes(task)) tracing = 0; + else { + if(!tracing && (debugger_pid != -1)) + child_signal(pid, status); + else if(!do_syscall(task, pid)){ + tracing = 0; + sig = SIGTRAP; + break; + } + continue; + } + break; + case SIGCONT: + break; + case SIGSEGV: + tracing = 0; + break; + case SIGIO: + case SIGALRM: + case SIGVTALRM: + case SIGFPE: + case SIGBUS: + case SIGILL: + if(!tracing && (debugger_pid != -1)){ + child_signal(pid, status); + continue; + } + tracing = 0; + break; + case SIGPROF: + if(tracing) sig = 0; + break; + case SIGCHLD: + sig = 0; + break; + case SIGHUP: + if(pid != external_pid(NULL)) continue; + sig = 0; + break; + default: + if(debugger_pid != -1){ + child_signal(pid, status); + continue; + } + tracing = 0; + break; + } + set_tracing(task, tracing); + if(tracing != 0){ + if(singlestepping(task)) + cont_type = PTRACE_SINGLESTEP; + else cont_type = PTRACE_SYSCALL; + } + else cont_type = PTRACE_CONT; + + /* XXX This doesn't close the hole totally - there + * is still a race between the ptrace and the child + * continuing. This thread needs separate data from + * the children. + */ + errno = save_errno; + if(ptrace(cont_type, pid, 0, sig) != 0){ + sleep(1); + if(ptrace(cont_type, pid, 0, sig) != 0){ + tracer_panic("ptrace failed to " + "continue process - " + "errno = %d\n", + errno); + } + } + } + } + return(0); +} + +int nsegfaults = 0; + +void segv_handler(int sig, void *sc, int usermode) +{ + struct sigcontext_struct *context = sc; + int index; + + if(usermode && !SEGV_IS_FIXABLE(context)){ + bad_segv(SC_FAULT_ADDR(context), SC_IP(context), + SC_FAULT_WRITE(context)); + return; + } + lock_trap(); + index = segfault_index++; + if(segfault_index == 1024) segfault_index = 0; + unlock_trap(); + nsegfaults++; + segfault_record[index].address = SC_FAULT_ADDR(context); + segfault_record[index].pid = getpid(); + segfault_record[index].is_write = SC_FAULT_WRITE(context); + segfault_record[index].sp = SC_SP(context); + segfault_record[index].is_user = usermode; + segv(SC_FAULT_ADDR(context), SC_IP(context), SC_FAULT_WRITE(context), + usermode); +} + +extern int timer_ready, timer_on; + +static void (*handlers[])(int, void *, int) = { + [ SIGTRAP ] relay_signal, + [ SIGFPE ] relay_signal, + [ SIGILL ] relay_signal, + [ SIGBUS ] relay_signal, + [ SIGSEGV] segv_handler, + [ SIGIO ] sigio_handler, + [ SIGVTALRM ] timer_handler, + [ SIGALRM ] timer_handler +}; + +void irq_handler(int sig, struct sigcontext sc) +{ + int user_mode, save_errno = errno, save_timer = timer_on; + + unprotect_kernel_mem(); + timer_on = 0; + user_mode = user_context(SC_SP(&sc)); + if(user_mode) timer_ready = 1; + change_sig(SIGUSR1, 1); + (*handlers[sig])(sig, &sc, user_mode); + if(user_mode) interrupt_end(); + block_signals(); + change_sig(SIGUSR1, 0); + set_user_thread(NULL, user_mode, 0, 0); + errno = save_errno; + if(user_mode) timer_ready = 0; + timer_on = save_timer; + if(user_mode) protect_kernel_mem(); +} + +void sig_handler(int sig, struct sigcontext sc) +{ + int user_mode, save_errno = errno, save_timer = timer_on; + + unprotect_kernel_mem(); + timer_on = 0; + user_mode = user_context(SC_SP(&sc)); + if(user_mode) timer_ready = 1; + change_sig(SIGUSR1, 1); + unblock_signals(); + (*handlers[sig])(sig, &sc, user_mode); + if(user_mode) interrupt_end(); + block_signals(); + change_sig(SIGUSR1, 0); + set_user_thread(NULL, user_mode, 0, 0); + errno = save_errno; + if(user_mode) timer_ready = 0; + timer_on = save_timer; + if(user_mode) protect_kernel_mem(); +} + +extern int timer_irq_inited, missed_ticks; + +void alarm_handler(int sig, struct sigcontext sc) +{ + int user_mode; + + if(!timer_irq_inited) return; + missed_ticks++; + user_mode = user_context(SC_SP(&sc)); + if(!user_mode && !timer_ready) return; + if(!timer_on) return; + irq_handler(sig, sc); + timer_ready = 1; +} + +void do_longjmp(void *p) +{ + jmp_buf *jbuf = (jmp_buf *)p; + + longjmp(*jbuf, 1); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/uaccess_user.c linux.ac/arch/um/kernel/uaccess_user.c --- linux.vanilla/arch/um/kernel/uaccess_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/uaccess_user.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk) + * Licensed under the GPL + */ + +#include +#include +#include "user_util.h" + +static unsigned long __do_user_copy(void *to, const void *from, int n, + void **fault_addr, void **fault_catcher, + void (*op)(void *to, const void *from, + int n), int *faulted_out) +{ + unsigned long *faddrp = (unsigned long *) fault_addr, ret; + + jmp_buf jbuf; + *fault_catcher = &jbuf; + if(setjmp(jbuf) == 0){ + (*op)(to, from, n); + ret = 0; + *faulted_out = 0; + } + else { + ret = *faddrp; + *faulted_out = 1; + } + *fault_addr = NULL; + *fault_catcher = NULL; + return ret; +} + +static void __do_copy(void *to, const void *from, int n) +{ + memcpy(to, from, n); +} + +int __do_copy_from_user(void *to, const void *from, int n, + void **fault_addr, void **fault_catcher) +{ + unsigned long fault; + int faulted; + + fault = __do_user_copy(to, from, n, fault_addr, fault_catcher, + __do_copy, &faulted); + if(!faulted) return(0); + else return(n - (fault - (unsigned long) from)); +} + + +int __do_copy_to_user(void *to, const void *from, int n, + void **fault_addr, void **fault_catcher) +{ + unsigned long fault; + int faulted; + + fault = __do_user_copy(to, from, n, fault_addr, fault_catcher, + __do_copy, &faulted); + if(!faulted) return(0); + else return(n - (fault - (unsigned long) to)); +} + +static void __do_strncpy(void *dst, const void *src, int count) +{ + strncpy(dst, src, count); +} + +int __do_strncpy_from_user(char *dst, const char *src, unsigned long count, + void **fault_addr, void **fault_catcher) +{ + unsigned long fault; + int faulted; + + fault = __do_user_copy(dst, src, count, fault_addr, fault_catcher, + __do_strncpy, &faulted); + if(!faulted) return(strlen(dst)); + else return(-1); +} + +static void __do_clear(void *to, const void *from, int n) +{ + memset(to, 0, n); +} + +int __do_clear_user(void *mem, unsigned long len, + void **fault_addr, void **fault_catcher) +{ + unsigned long fault; + int faulted; + + fault = __do_user_copy(mem, NULL, len, fault_addr, fault_catcher, + __do_clear, &faulted); + if(!faulted) return(0); + else return(len - (fault - (unsigned long) mem)); +} + +int __do_strnlen_user(const char *str, unsigned long n, + void **fault_addr, void **fault_catcher) +{ + int ret; + unsigned long *faddrp = (unsigned long *)fault_addr; + jmp_buf jbuf; + + *fault_catcher = &jbuf; + if(setjmp(jbuf) == 0){ + ret = strlen(str) + 1; + } + else { + ret = *faddrp - (unsigned long) str; + } + *fault_addr = NULL; + *fault_catcher = NULL; + return ret; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/um_arch.c linux.ac/arch/um/kernel/um_arch.c --- linux.vanilla/arch/um/kernel/um_arch.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/um_arch.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/sched.h" +#include "linux/mm.h" +#include "linux/types.h" +#include "linux/tty.h" +#include "linux/init.h" +#include "linux/bootmem.h" +#include "linux/spinlock.h" +#include "linux/utsname.h" +#ifdef CONFIG_BLK_DEV_INITRD +#include "linux/blk.h" +#endif +#include "asm/page.h" +#include "asm/pgtable.h" +#include "asm/ptrace.h" +#include "asm/elf.h" +#include "asm/user.h" +#include "asm/delay.h" +#include "ubd_user.h" +#include "asm/current.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "mprot.h" +#include "mem_user.h" +#include "umid.h" + +unsigned long _stext; + +#define DEFAULT_COMMAND_LINE "root=/dev/ubd0" + +unsigned long thread_saved_pc(struct thread_struct *thread) +{ + panic("Someone should implement thread_saved_pc"); + return(0); +} + +/* + * get_cpuinfo - Get information on one CPU for use by procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. + */ + +int get_cpuinfo(char *buffer, unsigned *cpu_np) +{ + char *p = buffer; + unsigned n; + + /* No SMP at the moment, so just toggle 0/1 */ + n = *cpu_np; + *cpu_np = 1; + if (n != 0) { + return (0); + } + p += sprintf(p, "processor\t: user-mode\n"); + p += sprintf(p, "bogomips\t: %lu.%02lu\n", + loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ)) % 100); + p += sprintf(p, "host\t\t: %s\n", host_info); + + return(strlen(buffer)); +} + +pte_t * __bad_pagetable(void) +{ + panic("Someone should implement __bad_pagetable"); + return(NULL); +} + +extern void start_kernel(void); + +extern int debug; +extern int debug_stop; + +static int start_kernel_proc(void *unused) +{ + int pid; + + block_signals(); + pid = getpid(); +#ifdef CONFIG_SMP + cpu_tasks[0].pid = pid; + cpu_tasks[0].task = current; + smp_num_cpus = 1; +#else + current_task = &init_task_union.task; +#endif + if(debug) stop_pid(getpid()); + start_kernel(); + return(0); +} + +extern unsigned long high_physmem; + +#ifdef CONFIG_HOST_2G_2G +#define START 0x60000000 +#else +#define START 0xa0000000 +#endif + +unsigned long physmem; + +unsigned long start_vm; +unsigned long end_vm; + +int ncpus = 1; + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) + +static char *argv1_begin = NULL; +static char *argv1_end = NULL; + +void set_cmdline(char *cmd) +{ + if(honeypot) return; + strcpy(argv1_begin, "["); + strncat(argv1_begin, cmd, argv1_end - argv1_begin - strlen("[]")); + strcat(argv1_begin, "]"); + memset(argv1_begin + strlen(argv1_begin), '\0', + argv1_end - argv1_begin - strlen(argv1_begin)); +} + +static char *usage_string = +"User Mode Linux v%s" +" available at http://user-mode-linux.sourceforge.net/\n\n" +"--help\n Prints this message\n" +"--version\n Gives the version number of the kernel\n" +"root=\n" +" This is actually used by the generic kernel in exactly the same\n" +" way as in any other kernel. If you configure a number of block\n" +" devices and want to boot off something other than ubd0, you \n" +" would use something like:\n" +" root=/dev/ubd5\n\n" +"mem=\n" +" This controls how much \"physical\" memory the kernel allocates\n" +" for the system. The size is specified as a number followed by\n" +" one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n" +" This is not related to the amount of memory in the physical\n" +" machine. It can be more, and the excess, if it's ever used, will\n" +" just be swapped out.\n Example: mem=64M\n\n" +#ifdef CONFIG_SMP +"ncpus=<# of desired CPUs>\n" +" This tells an SMP kernel how many virtual processors to start.\n" +" Currently, this has no effect because SMP isn't enabled.\n\n" +#endif +"debugtrace\n" +" Causes the tracing thread to pause until it is attached by a\n" +" debugger and continued. This is mostly for debugging crashes\n" +" early during boot, and should be pretty much obsoleted by\n" +" the debug switch.\n\n" +"debug\n" +" Starts up the kernel under the control of gdb. See the \n" +" kernel debugging tutorial and the debugging session pages\n" +" at http://user-mode-linux.sourceforge.net/ for more information\n\n" +"umn=\n" +" This sets the ip address of the host side of the slip device \n" +" that the umn device configures. This is necessary if you want\n" +" to set up networking, but your local net isn't 192.168.0.x,\n" +" or you want to run multiple virtual machines on a network,\n" +" in which case, you need to assign different ip addresses to the\n" +" different machines.\n\n" +"ubd=\n" +" This is used to associate a device with a file in the underlying\n" +" filesystem. Usually, there is a filesystem in the file, but \n" +" that's not required. Swap devices containing swap files can be\n" +" specified like this. Also, a file which doesn't contain a\n" +" filesystem can have its contents read in the virtual \n" +" machine by running dd on the device. n must be in the range\n" +" 0 to 7. Appending an 'r' to the number will cause that device\n" +" to be mounted read-only. For example ubd1r=./ext_fs\n\n" +"umid=\n" +" This is used to assign a unique identity to this UML machine\n" +" Used for naming the pid file and console socket\n"; + +static void Usage(void) +{ + printf(usage_string, system_utsname.release); + exit(0); +} + +#ifdef CONFIG_BLK_DEV_INITRD +static void read_initrd(char *initrd) +{ + void *area; + int size; + + size = file_size(initrd); + if(size < 0) return; + area = alloc_bootmem(size); + if(area == NULL) return; + if(load_initrd(initrd, area, size) == -1) return; + initrd_start = (unsigned long) area; + initrd_end = initrd_start + size; +} +#endif + +extern unsigned long _stext, _etext, _sdata, _edata, __bss_start, _end; +extern int debug_trace; + +int linux_main(int argc, char **argv) +{ + unsigned long start_pfn, end_pfn, bootmap_size; + unsigned long physmem_size, virtmem_size; + unsigned int i, have_root, add; + char *retptr; + void *sp; + void *brk_start; +#ifdef CONFIG_BLK_DEV_INITRD + char *initrd = NULL; +#endif + + remap_data(ROUND_DOWN(&_stext), ROUND_UP(&_etext)); + remap_data(ROUND_DOWN(&_sdata), ROUND_UP(&_edata)); + brk_start = sbrk(0); + remap_data(ROUND_DOWN(&__bss_start), ROUND_UP(brk_start)); + + /* Start physical memory at least 4M after the current brk */ + physmem = ROUND_4M(brk_start) + (1 << 22); + + /* Create fake command line from argv[]. */ + have_root = 0; + physmem_size = 32 * 1024 * 1024; + virtmem_size = physmem_size; + for (i = 1; i < argc; i++){ + if((i == 1) && (argv[i][0] == ' ')) continue; + add = 1; + if(!strncmp(argv[i], "root=", strlen("root="))) have_root = 1; + else if(!strncmp(argv[i], "mem=", strlen("mem="))) + physmem_size = memparse(argv[i] + strlen("mem="), + &retptr); +#ifdef CONFIG_SMP + else if(!strncmp(argv[i], "ncpus=", strlen("ncpus="))) + ncpus = strtoul(argv[i] + strlen("ncpus="), NULL, 10); +#endif + else if(!strcmp(argv[i], "debugtrace")) debug_trace = 1; + else if(!strncmp(argv[i], "debug", strlen("debug"))){ + debug = 1; + debug_stop = 1; + if(!strcmp(argv[i], "debug=go")){ + debug_stop = 0; + add = 0; + } + } +#ifdef CONFIG_PT_PROXY + else if(!strncmp(argv[i], "gdb=", strlen("gdb="))) + gdb_init = &argv[i][strlen("gdb=")]; + else if(!strncmp(argv[i], "gdb-pid=", strlen("gdb-pid="))) + gdb_pid = simple_strtoul(&argv[i][strlen("gdb-pid=")], + NULL, 0); +#endif + else if(!strncmp(argv[i], "umid=", strlen("umid="))){ + set_umid(&argv[i][strlen("umid=")]); + } + else if(!strncmp(argv[i], "iomem=", strlen("iomem="))){ + parse_iomem(&argv[i][strlen("iomem=")]); + } + else if(!strcmp(argv[i], "honeypot")){ + honeypot = 1; + } + else if(!strcmp(argv[i], "--version")){ + printf("%s\n", system_utsname.release); + exit(0); + } + else if(!strcmp(argv[i], "--help")){ + Usage(); + } +#ifdef CONFIG_BLK_DEV_INITRD + else if(!strncmp(argv[i], "initrd=", strlen("initrd="))) + initrd = &argv[i][strlen("initrd=")]; +#endif + if(add) add_arg(saved_command_line, argv[i]); + } + if(have_root == 0) add_arg(saved_command_line, DEFAULT_COMMAND_LINE); + + setup_machinename(system_utsname.machine); + + argv1_begin = argv[1]; + argv1_end = &argv[1][strlen(argv[1])]; + + /* Kernel vm starts after physical memory and is either the size + * of physical memory or the remaining space left in the kernel + * area of the address space, whichever is smaller. + */ + start_vm = physmem + physmem_size + VMALLOC_OFFSET; + if(start_vm >= get_kmem_end()) + panic("Physical memory too large to allow any kernel " + "virtual memory"); + + virtmem_size = physmem_size; + if(physmem_size > get_kmem_end() - start_vm) + virtmem_size = get_kmem_end() - start_vm; + end_vm = start_vm + virtmem_size; + + if(virtmem_size < physmem_size) + printk(KERN_INFO "Kernel virtual memory size shrunk to %ld " + "bytes\n", virtmem_size); + + setup_range(-1, NULL, physmem, physmem_size, + physmem_size + VMALLOC_OFFSET + virtmem_size); + setup_memory(); + high_physmem = physmem + physmem_size; + + start_pfn = PFN_UP(__pa(physmem)); + end_pfn = PFN_DOWN(__pa(high_physmem)); + bootmap_size = init_bootmem(start_pfn, end_pfn - start_pfn); + free_bootmem(__pa(physmem) + bootmap_size, + high_physmem - physmem - bootmap_size); + +#ifdef CONFIG_BLK_DEV_INITRD + if(initrd != NULL) read_initrd(initrd); +#endif + init_task.thread.kernel_stack = (unsigned long) &init_task + + 2 * PAGE_SIZE; +#ifndef CONFIG_SMP + current = &init_task; +#endif + task_protections((unsigned long) &init_task); + sp = (void *) init_task.thread.kernel_stack + 2 * PAGE_SIZE - + sizeof(unsigned long); + return(signals(start_kernel_proc, sp)); +} + +void setup_arch(char **cmdline_p) +{ + paging_init(); + strcpy(command_line, saved_command_line); + *cmdline_p = command_line; + setup_hostinfo(); +} + +void check_bugs(void) +{ + return; +} + +spinlock_t pid_lock = SPIN_LOCK_UNLOCKED; + +void lock_pid(void) +{ + spin_lock(&pid_lock); +} + +void unlock_pid(void) +{ + spin_unlock(&pid_lock); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/umid.c linux.ac/arch/um/kernel/umid.c --- linux.vanilla/arch/um/kernel/umid.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/umid.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user.h" +#include "umid.h" + +#define UMID_LEN 64 + +static char umid[UMID_LEN] = { 0 }; + +static int umid_inited = 0; + +int umid_file_name(char *name, char *buf, int len) +{ + int n; + + if(!umid_inited && set_umid(NULL)) return(-1); + + n = sizeof("/tmp/uml") + strlen(umid) + sizeof("/") + strlen(name) + 1; + if(n > len){ + printk("umid_file_name : buffer too short\n"); + return(-1); + } + + sprintf(buf, "/tmp/uml/%s/%s", umid, name); + return(0); +} + +extern int tracing_pid; + +static void create_pid_file(void) +{ + char file[sizeof("/tmp/uml/") + UMID_LEN + sizeof("/pid\0")]; + char pid[sizeof("nnnnn\0")]; + int fd; + + if(umid_file_name("pid", file, sizeof(file))) return; + + if((fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644)) < 0){ + printk("Open of machine pid file \"%s\" failed - " + "errno = %d\n", file, errno); + return; + } + + sprintf(pid, "%d\n", (tracing_pid == -1) ? getpid() : tracing_pid); + if(write(fd, pid, strlen(pid)) != strlen(pid)) + printk("Write of pid file failed - errno = %d\n", errno); + close(fd); +} + +static int actually_do_remove(char *dir) +{ + DIR *directory; + struct dirent *ent; + int len; + char file[256]; + + if((directory = opendir(dir)) == NULL){ + printk("actually_do_remove : couldn't open directory '%s', " + "errno = %d", dir, errno); + return(1); + } + while((ent = readdir(directory)) != NULL){ + if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + continue; + len = strlen(dir) + sizeof("/") + strlen(ent->d_name) + 1; + if(len > sizeof(file)){ + printk("Not deleting '%s' from '%s' - name too long\n", + ent->d_name, dir); + continue; + } + sprintf(file, "%s/%s", dir, ent->d_name); + if(unlink(file) < 0){ + printk("actually_do_remove : couldn't remove '%s' " + "from '%s', errno = %d\n", ent->d_name, dir, + errno); + return(1); + } + } + if(rmdir(dir) < 0){ + printk("actually_do_remove : couldn't rmdir '%s', " + "errno = %d\n", dir, errno); + return(1); + } + return(0); +} + +void remove_umid_dir(void) +{ + char dir[sizeof("/tmp/uml/") + UMID_LEN + 1]; + if(!umid_inited) return; + + sprintf(dir, "/tmp/uml/%s", umid); + actually_do_remove(dir); +} + +char *get_umid(void) +{ + return(umid); +} + +static int not_dead_yet(char *dir) +{ + char file[sizeof("/tmp/uml/") + UMID_LEN + sizeof("/pid\0")]; + char pid[sizeof("nnnnn\0")], *end; + int dead, fd, p; + + sprintf(file, "%s/pid", dir); + dead = 0; + if((fd = open(file, O_RDONLY)) < 0){ + if(errno != ENOENT){ + printk("not_dead_yet : couldn't open pid file '%s', " + "errno = %d\n", file, errno); + return(1); + } + dead = 1; + } + if(fd > 0){ + if(read(fd, pid, sizeof(pid)) < 0){ + printk("not_dead_yet : couldn't read pid file '%s', " + "errno = %d\n", file, errno); + return(1); + } + p = strtoul(pid, &end, 0); + if(end == pid){ + printk("not_dead_yet : couldn't parse pid file '%s', " + "errno = %d\n", file, errno); + dead = 1; + } + if((kill(p, 0) < 0) && (errno == ESRCH)) dead = 1; + } + if(!dead) return(1); + return(actually_do_remove(dir)); + return(0); +} + +int set_umid(char *name) +{ + int fd, err; + char tmp[sizeof("/tmp/uml/") + UMID_LEN + 1]; + + if(umid_inited){ + printk("Unique machine name can't be set twice\n"); + return(-1); + } + + if((mkdir("/tmp/uml", 0777) < 0) && (errno != EEXIST)){ + printk("Failed to create /tmp/uml - errno = %s\n", errno); + return(-1); + } + + strcpy(tmp, "/tmp/uml/"); + + if(name == NULL){ + strcat(tmp, "XXXXXX"); + fd = mkstemp(tmp); + if(fd < 0){ + printk("set_umid - mkstemp failed, errno = %d\n", + errno); + return(-1); + } + + close(fd); + /* There's a nice tiny little race between this unlink and + * the mkdir below. It'd be nice if there were a mkstemp + * for directories. + */ + unlink(tmp); + name = &tmp[strlen("/tmp/uml/")]; + } + + if(strlen(name) > UMID_LEN - 1) + printk("Unique machine name is being truncated to %s " + "characters\n", UMID_LEN); + strncpy(umid, name, UMID_LEN - 1); + umid[UMID_LEN - 1] = '\0'; + + sprintf(tmp, "/tmp/uml/%s", umid); + + if((err = mkdir(tmp, 0777)) < 0){ + if(errno == EEXIST){ + if(not_dead_yet(tmp)){ + printk("umid '%s' is in use\n", umid); + return(-1); + } + err = mkdir(tmp, 0777); + } + } + if(err < 0){ + printk("Failed to create %s - errno = %d\n", umid, errno); + return(-1); + } + + umid_inited = 1; + create_pid_file(); + return(0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/unmap.c linux.ac/arch/um/kernel/unmap.c --- linux.vanilla/arch/um/kernel/unmap.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/unmap.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include "user.h" + +int switcheroo(int fd, int prot, void *from, void *to, int size) +{ + if(munmap(to, size) < 0){ + return(-1); + } + if(mmap(to, size, prot, MAP_SHARED | MAP_FIXED, fd, 0) != to){ + return(-1); + } + if(munmap(from, size) < 0){ + return(-1); + } + return(0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/user_syms.c linux.ac/arch/um/kernel/user_syms.c --- linux.vanilla/arch/um/kernel/user_syms.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/user_syms.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "mem_user.h" + +/* XXX All the __CONFIG_* stuff is broken because this file can't include + * config.h + */ + +/* Had to steal this from linux/module.h because that file can't be included + * since this includes various user-level headers. + */ + +struct module_symbol +{ + unsigned long value; + const char *name; +}; + +/* Indirect stringification. */ + +#define __MODULE_STRING_1(x) #x +#define __MODULE_STRING(x) __MODULE_STRING_1(x) + +#if !defined(__AUTOCONF_INCLUDED__) + +#define __EXPORT_SYMBOL(sym,str) error config_must_be_included_before_module +#define EXPORT_SYMBOL(var) error config_must_be_included_before_module +#define EXPORT_SYMBOL_NOVERS(var) error config_must_be_included_before_module + +#elif !defined(__CONFIG_MODULES__) + +#define __EXPORT_SYMBOL(sym,str) +#define EXPORT_SYMBOL(var) +#define EXPORT_SYMBOL_NOVERS(var) + +#else + +#define __EXPORT_SYMBOL(sym, str) \ +const char __kstrtab_##sym[] \ +__attribute__((section(".kstrtab"))) = str; \ +const struct module_symbol __ksymtab_##sym \ +__attribute__((section("__ksymtab"))) = \ +{ (unsigned long)&sym, __kstrtab_##sym } + +#if defined(__MODVERSIONS__) || !defined(__CONFIG_MODVERSIONS__) +#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var)) +#else +#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(__VERSIONED_SYMBOL(var))) +#endif + +#define EXPORT_SYMBOL_NOVERS(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var)) + +#endif + +EXPORT_SYMBOL(__errno_location); + +EXPORT_SYMBOL(access); +EXPORT_SYMBOL(open); +EXPORT_SYMBOL(open64); +EXPORT_SYMBOL(close); +EXPORT_SYMBOL(read); +EXPORT_SYMBOL(write); +EXPORT_SYMBOL(__xstat); +EXPORT_SYMBOL(__lxstat); +EXPORT_SYMBOL(__lxstat64); +EXPORT_SYMBOL(lseek); +EXPORT_SYMBOL(lseek64); +EXPORT_SYMBOL(chown); +EXPORT_SYMBOL(truncate); +EXPORT_SYMBOL(utime); +EXPORT_SYMBOL(chmod); +EXPORT_SYMBOL(rename); +EXPORT_SYMBOL(__xmknod); + +EXPORT_SYMBOL(symlink); +EXPORT_SYMBOL(link); +EXPORT_SYMBOL(unlink); +EXPORT_SYMBOL(readlink); + +EXPORT_SYMBOL(mkdir); +EXPORT_SYMBOL(rmdir); +EXPORT_SYMBOL(opendir); +EXPORT_SYMBOL(readdir); +EXPORT_SYMBOL(closedir); +EXPORT_SYMBOL(seekdir); +EXPORT_SYMBOL(telldir); + +extern ssize_t pread64 (int __fd, void *__buf, size_t __nbytes, + __off64_t __offset); +extern ssize_t pwrite64 (int __fd, __const void *__buf, size_t __n, + __off64_t __offset); +EXPORT_SYMBOL(pread64); +EXPORT_SYMBOL(pwrite64); + +EXPORT_SYMBOL(statfs); +EXPORT_SYMBOL(statfs64); + +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(getuid); + +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(strstr); + +EXPORT_SYMBOL(find_iomem); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/user_util.c linux.ac/arch/um/kernel/user_util.c --- linux.vanilla/arch/um/kernel/user_util.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/user_util.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "mem_user.h" + +#define COMMAND_LINE_SIZE _POSIX_ARG_MAX + +char saved_command_line[COMMAND_LINE_SIZE] = { 0 }; +char command_line[COMMAND_LINE_SIZE] = { 0 }; + +void add_arg(char *cmd_line, char *arg) +{ + if (strlen(cmd_line) + strlen(arg) + 1 > COMMAND_LINE_SIZE) { + printf("add_arg: Too much command line!\n"); + exit(1); + } + if(strlen(cmd_line) > 0) strcat(cmd_line, " "); + strcat(cmd_line, arg); +} + +void *open_maps(void) +{ + void *maps; + + maps = fopen("/proc/self/maps", "r"); + if(!maps){ + perror("open_maps"); + exit(1); + } + return(maps); +} + +int read_map(void *maps, unsigned long *start_out, unsigned long *end_out, + char *r_out, char *w_out, char *x_out, char *p_out) +{ + unsigned int inode; + unsigned long long offset; + unsigned long start, end, major, minor; + int ret; + char r, w, x, p; + + ret = fscanf(maps, "%lx-%lx %c%c%c%c %Lx %lx:%lx %d%*[^\n]", &start, + &end, &r, &w, &x, &p, &offset, &major, &minor, + &inode); + if (ret == EOF) return 0; + else if (ret != 10){ + perror("Scanning a map line"); + exit(1); + } + if(start_out != NULL) *start_out = start; + if(end_out != NULL) *end_out = end; + if(r_out != NULL) *r_out = r; + if(w_out != NULL) *w_out = w; + if(x_out != NULL) *x_out = x; + if(p_out != NULL) *p_out = p; + return(1); +} + +void close_maps(void *maps) +{ + fclose(maps); +} + +void remap_data(void *segment_start, void *segment_end) +{ + void *addr; + unsigned long start, end, size; + void *maps; + int data, prot; + char r, w, x; + + maps= open_maps(); + while(read_map(maps, &start, &end, &r, &w, &x, NULL) != 0){ + if(((unsigned long) segment_start >= start) && + ((unsigned long) segment_end <= end)){ + prot = PROT_READ | PROT_WRITE | PROT_EXEC; + size = (unsigned long) segment_end - + (unsigned long) segment_start; + data = create_mem_file(size); + if((addr = mmap(NULL, size, PROT_WRITE | PROT_READ, + MAP_SHARED, data, 0)) < 0){ + perror("mapping new data segment"); + exit(1); + } + memcpy(addr, segment_start, size); + if(switcheroo(data, prot, addr, segment_start, + size) < 0){ + printf("switcheroo failed\n"); + exit(1); + } + close_maps(maps); + return; + } + } + fprintf(stderr, "remap_data couldn't find data segment 0x%lx - 0x%lx " + "in /proc/self/maps\n", (unsigned long) segment_start, + (unsigned long) segment_end); + exit(1); +} + +__u64 file_size(char *file) +{ + struct stat64 buf; + + if(stat64(file, &buf) == -1){ + printk("Couldn't stat \"%s\" : errno = %d\n", file, errno); + return(-errno); + } + if(S_ISBLK(buf.st_mode)){ + long long size; + int fd; + + if((fd = open64(file, O_RDONLY)) < 0){ + printk("Couldn't open \"%s\", errno = %d\n", file, + errno); + return(-errno); + } + if(ioctl(fd, BLKGETSIZE, &size) < 0){ + printk("Couldn't get the block size of \"%s\", " + "errno = %d\n", file, errno); + close(fd); + return(-errno); + } + size *= 512; + close(fd); + return(size); + } + return(buf.st_size); +} + +int load_initrd(char *filename, void *buf, int size) +{ + int fd, n; + + if((fd = open(filename, O_RDONLY)) == -1){ + printk("Opening '%s' failed - errno = %d\n", filename, errno); + return(-1); + } + if((n = read(fd, buf, size)) != size){ + printk("Read of %d bytes from '%s' returned %d, errno = %d\n", + size, filename, n, errno); + return(-1); + } + return(0); +} + +void stop(void) +{ + while(1) sleep(1000000); +} + +void stack_protections(unsigned long address) +{ + int prot = PROT_READ | PROT_WRITE | PROT_EXEC; + + if(mprotect((void *) address, page_size(), prot) < 0) + panic("protecting stack failed, errno = %d", errno); +} + +void task_protections(unsigned long address) +{ + unsigned long guard = address + page_size(); + unsigned long stack = guard + page_size(); + int prot = 0; + + if(mprotect((void *) stack, page_size(), prot) < 0) + panic("protecting guard page failed, errno = %d", errno); + prot = PROT_READ | PROT_WRITE | PROT_EXEC; + if(mprotect((void *) stack, 2 * page_size(), prot) < 0) + panic("protecting stack failed, errno = %d", errno); +} + +void protect(unsigned long addr, unsigned long len, int r, int w, int x) +{ + int prot; + + prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | + (x ? PROT_EXEC : 0); + if(mprotect((void *) addr, len, prot) == -1) + panic("protect failed, errno = %d", errno); +} + +int wait_for_stop(int pid, int sig, int cont_type) +{ + int status, ret; + + while(1){ + if(((ret = waitpid(pid, &status, WUNTRACED)) < 0) || + !WIFSTOPPED(status) || (WSTOPSIG(status) != sig)){ + if(ret < 0){ + if(errno == EINTR) continue; + printk("wait failed, errno = %d\n", errno); + } + else if(WIFEXITED(status)) + printk("process exited with status %d\n", + WEXITSTATUS(status)); + else if(WIFSIGNALED(status)) + printk("process exited with signal %d\n", + WTERMSIG(status)); + else if((WSTOPSIG(status) == SIGVTALRM) || + (WSTOPSIG(status) == SIGALRM) || + (WSTOPSIG(status) == SIGIO) || + (WSTOPSIG(status) == SIGPROF) || + (WSTOPSIG(status) == SIGCHLD)){ + ptrace(cont_type, pid, 0, WSTOPSIG(status)); + continue; + } + else printk("process stopped with signal %d\n", + WSTOPSIG(status)); + panic("wait_for_stop failed to wait for %d to stop " + "with %d\n", pid, sig); + } + return(status); + } +} + +int clone_and_wait(int (*fn)(void *), void *arg, void *sp, int flags) +{ + int pid; + + pid = clone(fn, sp, flags, arg); + if(pid < 0) return(-1); + wait_for_stop(pid, SIGSTOP, PTRACE_CONT); + return(pid); +} + +struct grantpt_info { + int fd; + int res; + int err; +}; + +static void grantpt_cb(void *arg) +{ + struct grantpt_info *info = arg; + + info->res = grantpt(info->fd); + info->err = errno; +} + +int get_pty(void) +{ + struct grantpt_info info; + int fd; + + if((fd = open("/dev/ptmx", O_RDWR)) < 0){ + printk("get_pty : Couldn't open /dev/ptmx - errno = %d\n", + errno); + return(-1); + } + info.fd = fd; + tracing_cb(grantpt_cb, &info); + if(info.res < 0){ + printk("get_pty : Couldn't grant pty - errno = %d\n", + info.err); + return(-1); + } + if(unlockpt(fd) < 0){ + printk("get_pty : Couldn't unlock pty - errno = %d\n", errno); + return(-1); + } + return(fd); +} + +int raw(int fd, int complain) +{ + struct termios tt; + int err; + + tcgetattr(fd, &tt); + cfmakeraw(&tt); + err = tcsetattr(fd, TCSADRAIN, &tt); + if((err < 0) && complain){ + printk("tcsetattr failed, errno = %d\n", errno); + return(-errno); + } + return(0); +} + +void setup_machinename(char *machine_out) +{ + struct utsname host; + + uname(&host); + strcpy(machine_out, host.machine); +} + +char host_info[(_UTSNAME_LENGTH + 1) * 4 + _UTSNAME_NODENAME_LENGTH + 1]; + +void setup_hostinfo(void) +{ + struct utsname host; + + uname(&host); + sprintf(host_info, "%s %s %s %s %s", host.sysname, host.nodename, + host.release, host.version, host.machine); +} + +void close_fd(int fd) +{ + close(fd); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/link.ld.in linux.ac/arch/um/link.ld.in --- linux.vanilla/arch/um/link.ld.in Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/link.ld.in Wed Oct 10 01:47:36 2001 @@ -0,0 +1,105 @@ +OUTPUT_FORMAT("elf32-ELF_SUBARCH") +OUTPUT_ARCH(ELF_SUBARCH) +ENTRY(_start) + +SECTIONS +{ + . = START() + SIZEOF_HEADERS; + .remap : { arch/um/kernel/unmap_fin.o (.text) } + + . = ALIGN(4096); /* Init code and data */ + _stext = .; + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(16); + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { *(.initcall.init) } + __initcall_end = .; + . = ALIGN(4096); + __init_end = .; + __exitcall_begin = .; + .exitcall : { *(.exitcall.exit) } + __exitcall_end = .; + + .text : + { + *(.text) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.gnu.linkonce.t*) + } =0x9090 + .kstrtab : { *(.kstrtab) } + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + .fini : { *(.fini) } =0x9090 + .rodata : { *(.rodata) *(.gnu.linkonce.r*) } + .rodata1 : { *(.rodata1) } + _etext = .; + PROVIDE (etext = .); + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN(0x1000) + (. & (0x1000 - 1)); + .data : + { + _sdata = .; + PROVIDE (sdata = .); + . = ALIGN(16384); /* init_task */ + *(.data.init_task) + *(.data) + *(.gnu.linkonce.d*) + CONSTRUCTORS + } + .data1 : { *(.data1) } + .ctors : + { + *(.ctors) + } + .dtors : + { + *(.dtors) + } + + .got : { *(.got.plt) *(.got) } + .dynamic : { *(.dynamic) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : { *(.sdata) } + _edata = .; + PROVIDE (edata = .); + . = ALIGN(0x1000); + .sbss : + { + __bss_start = .; + PROVIDE(_bss_start = .); + *(.sbss) + *(.scommon) + } + .bss : + { + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/main.c linux.ac/arch/um/main.c --- linux.vanilla/arch/um/main.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/main.c Wed Oct 10 01:47:36 2001 @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "mem_user.h" +#include "user.h" + +unsigned long stacksizelim; + +char *linux_prog; + +#define PGD_BOUND (4 * 1024 * 1024) +#define STACKSIZE (8 * 1024 * 1024) +#define THREAD_NAME_LEN (256) + +char padding[THREAD_NAME_LEN] = { [ 0 ... THREAD_NAME_LEN - 2] = ' ', '\0' }; + +static void set_stklim(void) +{ + struct rlimit lim; + + if(getrlimit(RLIMIT_STACK, &lim) < 0){ + perror("getrlimit"); + exit(1); + } + if((lim.rlim_cur == RLIM_INFINITY) || (lim.rlim_cur > STACKSIZE)){ + lim.rlim_cur = STACKSIZE; + if(setrlimit(RLIMIT_STACK, &lim) < 0){ + perror("setrlimit"); + exit(1); + } + } + stacksizelim = (lim.rlim_cur + PGD_BOUND - 1) & ~(PGD_BOUND - 1); +} + +unsigned long host_task_size; +unsigned long task_size; + +static void set_task_sizes(int arg) +{ + /* Round up to the nearest 4M */ + host_task_size = ROUND_4M((unsigned long) &arg); + task_size = host_task_size - 0x20000000; +} + +int main(int argc, char **argv, char **envp) +{ + struct termios tt; + int ret, i; + char **new_argv; + + /* Allocate memory for thread command lines */ + if(argc < 2 || strlen(argv[1]) < THREAD_NAME_LEN - 1){ + new_argv = malloc((argc + 2) * sizeof(char*)); + if(!new_argv) { + perror("Allocating extended argv"); + exit(1); + } + + new_argv[0] = argv[0]; + new_argv[1] = padding; + + for(i = 2; i <= argc; i++) + new_argv[i] = argv[i - 1]; + new_argv[argc + 1] = NULL; + +#ifdef PROFILING + disable_profile_timer(); +#endif + execvp(new_argv[0], new_argv); + perror("execing with extended args"); + exit(1); + } + + linux_prog = argv[0]; + + set_stklim(); + set_task_sizes(0); + + if(isatty(0)) tcgetattr(0, &tt); + else if(isatty(1)) tcgetattr(1, &tt); + else tcgetattr(2, &tt); + + if((new_argv = malloc((argc + 1) * sizeof(char *))) == NULL){ + perror("Mallocing argv"); + exit(1); + } + for(i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ptproxy.h" +#include "sysdep.h" +#include "wait.h" + +#include "user_util.h" +#include "chan.h" +#include "user.h" + +static void debugger_normal_return (debugger_state *, pid_t); + +/* + * Handle debugger trap, i.e. syscall. + */ + +void +debugger_syscall (debugger_state *debugger, pid_t child) +{ + int cancel = 0; + long arg1, arg2, arg3, arg4, arg5; + int syscall; + + syscall = syscall_get_number (debugger->pid); + syscall_get_args (debugger->pid, &arg1, &arg2, &arg3, &arg4, &arg5); + + switch (syscall) + { + case __NR_execve: + debugger->handle_trace = debugger_syscall; /* execve never returns */ + break; + case __NR_ptrace: + if(debugger->debugee->pid != 0) arg2 = debugger->debugee->pid; + proxy_ptrace (debugger, arg1, arg2, arg3, arg4, child); + cancel = 1; + debugger->handle_trace = debugger_cancelled_return; + break; + case __NR_waitpid: + case __NR_wait4: + proxy_wait (debugger, + syscall == __NR_waitpid ? WAIT_WAITPID : + syscall == __NR_wait4 ? WAIT_WAIT4 : -1, + debugger->debugee->pid, (int *)arg2, arg3, + (void *)arg4); + cancel = 1; + debugger->handle_trace = proxy_wait_return; + break; + case __NR_kill: + if(arg1 == debugger->debugee->pid){ + debugger->result = kill(child, arg2); + cancel = 1; + debugger->handle_trace = debugger_cancelled_return; + } + else { + debugger->handle_trace = debugger_normal_return; + } + break; + default: + debugger->handle_trace = debugger_normal_return; + } + + if (cancel) + syscall_cancel (debugger->pid); + + syscall_continue (debugger->pid); +} + +void +debugger_normal_return (debugger_state *debugger, pid_t unused) +{ + debugger->handle_trace = debugger_syscall; + syscall_continue (debugger->pid); +} + +void +debugger_cancelled_return (debugger_state *debugger, pid_t unused) +{ + syscall_set_result (debugger->pid, debugger->result); + debugger->handle_trace = debugger_syscall; + syscall_continue (debugger->pid); +} + +#ifdef CONFIG_SMP +#error need to make these arrays +#endif + +static debugger_state debugger; +static debugee_state debugee; + +void +init_proxy (pid_t debugger_pid, int stopped, int status) +{ + debugger.pid = debugger_pid; + debugger.handle_trace = debugger_syscall; + debugger.debugee = &debugee; + debugger.stopped = 0; + + debugee.pid = 0; + debugee.traced = 0; + debugee.stopped = stopped; + debugee.event = 0; + debugee.zombie = 0; + debugee.died = 0; + debugee.debugger = &debugger; + debugee.wait_status = status; +} + +void debugger_proxy(int status, int pid) +{ + if (WIFSTOPPED (status)) + { + /* debugger got a signal */ + + if (WSTOPSIG (status) == SIGTRAP) + { + debugger.handle_trace (&debugger, pid); + } + else + { + /* go on as usual */ + ptrace (PTRACE_SYSCALL, debugger.pid, + 0, WSTOPSIG (status)); + } + } + else if (WIFEXITED (status)) + { + /* debugger exited with status + WEXITSTATUS (status) */ + } + else if (WIFSIGNALED (status)) + { + /* debugger died from signal + WTERMSIG (status) */ + } + else + { + /* unknown event */ + panic("proxy got unknown status (0x%x) on debugger " + "(pid %d)", status, debugger.pid); + exit (1); + } +} + +void child_proxy(pid_t pid, int status) +{ + debugee.stopped = 1; + debugee.event = 1; + debugee.wait_status = status; + + if (WIFSTOPPED (status)) + { + /* child got a signal */ + } + else if (WIFEXITED (status)) + { + /* child exited with status WEXITSTATUS + (status) */ + debugee.zombie = 1; + } + else if (WIFSIGNALED (status)) + { + /* child died from signal WTERMSIG + (status) */ + debugee.zombie = 1; + } + else + { + /* unknown event */ + panic("proxy got unknown status (0x%x) on child " + "(pid %d)", status, pid); + } + + if (debugee.stopped) + { + int r; + + r = kill (debugger.pid, SIGCHLD); + if (debugger.stopped) + proxy_wait_return (&debugger, -1); + } +} + +extern struct io_chan gdb_chan; + +int start_debugger(char *prog, int startup, int stop, int *fd_out) +{ + int err, slave, child; + + err = open_chan_pair(&gdb_chan, NULL, NULL); + if(err) return(err); + slave = CHAN_OUT_FD(gdb_chan); + if((child = fork()) == 0){ + char tempname[sizeof("/tmp/gdb_init-XXXXXX")]; + char arg[sizeof("--command=/tmp/gdb_init-XXXXXX")]; + int fd; + + if(setsid() < 0) perror("setsid"); + if((dup2(slave, 0) < 0) || (dup2(slave, 1) < 0) || + (dup2(slave, 2) < 0)){ + printk("start_debugger : dup2 failed, errno = %d\n", + errno); + exit(1); + } + if(ioctl(0, TIOCSCTTY, 0) < 0){ + printk("start_debugger : TIOCSCTTY failed, " + "errno = %d\n", errno); + exit(1); + } + if(tcsetpgrp (1, getpid()) < 0){ + printk("start_debugger : tcsetpgrp failed, " + "errno = %d\n", errno); +#ifdef notdef + exit(1); +#endif + } + strcpy(tempname, "/tmp/gdb_init-XXXXXX"); + if((fd = mkstemp(tempname)) < 0){ + printk("start_debugger : mkstmp failed, errno = %d\n", + errno); + exit(1); + } + sprintf(arg, "--command=%s", tempname); + write(fd, "att 1\n", strlen("att 1\n")); + write(fd, "b panic\n", strlen("b panic\n")); + write(fd, "b stop\n", strlen("b stop\n")); + write(fd, "handle SIGWINCH nostop noprint\n", + strlen("handle SIGWINCH nostop noprint\n")); + if(startup){ + if(stop){ + write(fd, "b start_kernel\n", + strlen("b start_kernel\n")); + } + write(fd, "c\n", strlen("c\n")); + } + if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){ + printk("start_debugger : PTRACE_TRACEME failed, " + "errno = %d\n", errno); + exit(1); + } + execlp("gdb", "gdb", arg, prog, NULL); + printk("start_debugger : exec of gdb failed, errno = %d\n", + errno); + } + if(child < 0){ + printk("start_debugger : fork for gdb failed, errno = %d\n", + errno); + return(-1); + } + *fd_out = slave; + return(child); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/ptproxy/ptproxy.h linux.ac/arch/um/ptproxy/ptproxy.h --- linux.vanilla/arch/um/ptproxy/ptproxy.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/ptproxy/ptproxy.h Wed Oct 10 01:47:38 2001 @@ -0,0 +1,42 @@ +/********************************************************************** +ptproxy.h + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. +**********************************************************************/ + +#include + +typedef struct debugger debugger_state; +typedef struct debugee debugee_state; + +struct debugger +{ + pid_t pid; + long result; + int wait_options; + int *wait_status_ptr; + unsigned stopped : 1; + void (*handle_trace) (debugger_state *, pid_t); + + debugee_state *debugee; +}; + +struct debugee +{ + pid_t pid; + int wait_status; + unsigned died : 1; + unsigned event : 1; + unsigned stopped : 1; + unsigned trace_singlestep : 1; + unsigned trace_syscall : 1; + unsigned traced : 1; + unsigned zombie : 1; + + debugger_state *debugger; +}; + +extern void debugger_syscall (debugger_state *, pid_t); +extern void debugger_cancelled_return (debugger_state *, pid_t); +extern void proxy_ptrace (struct debugger *, int, pid_t, long, long, pid_t); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/ptproxy/ptrace.c linux.ac/arch/um/ptproxy/ptrace.c --- linux.vanilla/arch/um/ptproxy/ptrace.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/ptproxy/ptrace.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,208 @@ +/********************************************************************** +ptrace.c + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. + +Jeff Dike (jdike@karaya.com) : Modified for integration into uml +**********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "ptproxy.h" + +struct { + int op; + pid_t pid; + long addr; + long data; + pid_t child; + struct timeval tv; +} ptrace_record[1024]; + +int ptrace_index = 0; + +void +proxy_ptrace (struct debugger *debugger, int arg1, pid_t arg2, + long arg3, long arg4, pid_t child) +{ + int status; + + if(ptrace_index == 1024) ptrace_index = 0; + ptrace_record[ptrace_index].op = arg1; + ptrace_record[ptrace_index].pid = arg2; + ptrace_record[ptrace_index].data = arg3; + ptrace_record[ptrace_index].addr = arg4; + ptrace_record[ptrace_index].child = child; + gettimeofday(&ptrace_record[ptrace_index++].tv, NULL); + if (debugger->debugee->died) + { + debugger->result = -ESRCH; + debugger->handle_trace = debugger_cancelled_return; + } + + switch (arg1) + { + case PTRACE_ATTACH: + if (debugger->debugee->traced) + debugger->result = -EPERM; + else + { + debugger->result = 0; + debugger->debugee->pid = arg2; + debugger->debugee->traced = 1; + if(!debugger->debugee->stopped) kill(child, SIGSTOP); + else { + child_proxy(child, + debugger->debugee->wait_status); + } + } + break; + + case PTRACE_CONT: + debugger->result = ptrace (PTRACE_CONT, child, arg3, arg4); + break; + + case PTRACE_DETACH: + if (debugger->debugee->traced) + { + debugger->result = 0; + debugger->debugee->traced = 0; + kill(child, SIGCONT); + } + else + debugger->result = -EPERM; + break; + + case PTRACE_GETFPREGS: + { + long regs[27]; + int i; + + debugger->result = ptrace (PTRACE_GETFPREGS, child, 0, regs); + if (debugger->result == -1) + debugger->result = -errno; + else + { + for (i = 0; i < 27; i++) + ptrace (PTRACE_POKEDATA, debugger->pid, + arg4 + 4 * i, regs[i]); + } + } + break; + + case PTRACE_GETREGS: + { + long regs[17]; + int i; + + debugger->result = ptrace (PTRACE_GETREGS, child, 0, regs); + if (debugger->result == -1) + debugger->result = -errno; + else + { + for (i = 0; i < 17; i++) + ptrace (PTRACE_POKEDATA, debugger->pid, + arg4 + 4 * i, regs[i]); + } + } + break; + + case PTRACE_KILL: + debugger->result = ptrace (PTRACE_KILL, child, arg3, arg4); + if (debugger->result == -1) + debugger->result = -errno; + break; + + case PTRACE_PEEKDATA: + case PTRACE_PEEKTEXT: + case PTRACE_PEEKUSER: + errno = 0; + debugger->result = ptrace (arg1, child, arg3, 0); + if (debugger->result == -1 && errno != 0) + debugger->result = -errno; + else + { + ptrace (PTRACE_POKEDATA, debugger->pid, arg4, + debugger->result); + debugger->result = 0; + } + break; + case PTRACE_POKEDATA: + case PTRACE_POKETEXT: + case PTRACE_POKEUSER: + debugger->result = ptrace (arg1, child, arg3, arg4); + if (debugger->result == -1) + debugger->result = -errno; + break; + + case PTRACE_SETFPREGS: + { + long regs[27]; + int i; + + for (i = 0; i < 27; i++) + regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid, + arg4 + 4 * i, 0); + debugger->result = ptrace (PTRACE_SETFPREGS, child, 0, regs); + if (debugger->result == -1) + debugger->result = -errno; + } + break; + + case PTRACE_SETREGS: + { + long regs[17]; + int i; + + for (i = 0; i < 17; i++) + regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid, + arg4 + 4 * i, 0); + debugger->result = ptrace (PTRACE_SETREGS, child, 0, regs); + if (debugger->result == -1) + debugger->result = -errno; + } + break; + + case PTRACE_SINGLESTEP: + debugger->result = ptrace (PTRACE_SINGLESTEP, child, arg3, + arg4); + if (debugger->result == -1) + debugger->result = -errno; + else { + status = wait_for_stop(child, SIGTRAP, + PTRACE_SINGLESTEP); + child_proxy(child, status); + } + break; + + case PTRACE_SYSCALL: + debugger->result = ptrace (PTRACE_SYSCALL, child, arg3, arg4); + if (debugger->result == -1) + debugger->result = -errno; + break; + + case PTRACE_TRACEME: + debugger->result = -EINVAL; + break; + + default: + debugger->result = -EINVAL; + } +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/ptproxy/sysdep.c linux.ac/arch/um/ptproxy/sysdep.c --- linux.vanilla/arch/um/ptproxy/sysdep.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/ptproxy/sysdep.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,70 @@ +/********************************************************************** +sysdep.c + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. +**********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +int +syscall_get_number (pid_t pid) +{ + return ptrace (PTRACE_PEEKUSER, pid, UM_SYSCALL_NR_OFFSET, 0); +} + +void +syscall_cancel (pid_t pid) +{ + int err; + + err = ptrace (PTRACE_POKEUSER, pid, UM_SYSCALL_NR_OFFSET, __NR_getpid); + if (err == -1) + { + fprintf (stderr, "ptproxy: couldn't cancel syscall: " + "errno = %d\n", errno); + exit (1); + } +} + +void +syscall_get_args (pid_t pid, long *arg1, long *arg2, + long *arg3, long *arg4, long *arg5) +{ + *arg1 = ptrace (PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG1_OFFSET, 0); + *arg2 = ptrace (PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG2_OFFSET, 0); + *arg3 = ptrace (PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG3_OFFSET, 0); + *arg4 = ptrace (PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG4_OFFSET, 0); + *arg5 = ptrace (PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG5_OFFSET, 0); +} + +void +syscall_set_result (pid_t pid, long result) +{ + ptrace (PTRACE_POKEUSER, pid, UM_SYSCALL_RET_OFFSET, result); +} + +void +syscall_continue (pid_t pid) +{ + ptrace (PTRACE_SYSCALL, pid, 0, 0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/ptproxy/sysdep.h linux.ac/arch/um/ptproxy/sysdep.h --- linux.vanilla/arch/um/ptproxy/sysdep.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/ptproxy/sysdep.h Wed Oct 10 01:47:38 2001 @@ -0,0 +1,13 @@ +/********************************************************************** +sysdep.h + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. +**********************************************************************/ + +extern int syscall_get_number (pid_t pid); +extern void syscall_cancel (pid_t pid); +extern void syscall_get_args (pid_t pid, long *arg1, long *arg2, + long *arg3, long *arg4, long *arg5); +extern void syscall_set_result (pid_t pid, long result); +extern void syscall_continue (pid_t pid); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/ptproxy/wait.c linux.ac/arch/um/ptproxy/wait.c --- linux.vanilla/arch/um/ptproxy/wait.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/ptproxy/wait.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,79 @@ +/********************************************************************** +wait.c + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. + +**********************************************************************/ + +#include +#include +#include + +#include "ptproxy.h" +#include "sysdep.h" +#include "wait.h" + +void +proxy_wait (struct debugger *debugger, enum wait_type type, pid_t pid, + int *status, int options, void *rusage) +{ + debugger->wait_status_ptr = status; + debugger->wait_options = options; +} + +void +proxy_wait_return (struct debugger *debugger, pid_t unused) +{ + debugger->stopped = 0; + + if (debugger->debugee->died) + { + debugger->result = -ECHILD; + debugger_cancelled_return (debugger, -1); + return; + } + + if (debugger->wait_options & __WCLONE) + { + debugger->result = -ECHILD; + debugger_cancelled_return (debugger, -1); + return; + } + + if (debugger->debugee->zombie && debugger->debugee->event) + debugger->debugee->died = 1; + + if (debugger->wait_options & WNOHANG) + { + if (debugger->debugee->event) + { + debugger->debugee->event = 0; + ptrace (PTRACE_POKEDATA, debugger->pid, + debugger->wait_status_ptr, debugger->debugee->wait_status); + /* if (wait4) + ptrace (PTRACE_POKEDATA, pid, rusage_ptr, ...); */ + debugger->result = debugger->debugee->pid; + } + else + { + debugger->result = 0; + } + debugger_cancelled_return (debugger, -1); + return; + } + + if (debugger->debugee->event) + { + debugger->debugee->event = 0; + ptrace (PTRACE_POKEDATA, debugger->pid, + debugger->wait_status_ptr, debugger->debugee->wait_status); + /* if (wait4) + ptrace (PTRACE_POKEDATA, pid, rusage_ptr, ...); */ + debugger->result = debugger->debugee->pid; + debugger_cancelled_return (debugger, -1); + return; + } + + debugger->stopped = 1; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/ptproxy/wait.h linux.ac/arch/um/ptproxy/wait.h --- linux.vanilla/arch/um/ptproxy/wait.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/ptproxy/wait.h Wed Oct 10 01:47:38 2001 @@ -0,0 +1,18 @@ +/********************************************************************** +wait.h + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. +**********************************************************************/ + +enum wait_type +{ + WAIT_WAIT, + WAIT_WAITPID, + WAIT_WAIT3, + WAIT_WAIT4 +}; + +extern void proxy_wait (struct debugger *debugger, enum wait_type type, + pid_t arg1, int *arg2, int arg3, void *arg4); +extern void proxy_wait_return (struct debugger *debugger, pid_t unused); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-i386/Makefile linux.ac/arch/um/sys-i386/Makefile --- linux.vanilla/arch/um/sys-i386/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-i386/Makefile Wed Oct 10 01:47:38 2001 @@ -0,0 +1,49 @@ +OBJ = sys.o + +OBJS = checksum.o ldt.o old-checksum.o ptrace.o ptrace_user.o semaphore.o \ + sigcontext.o syscalls.o sysrq.o +OX_OBJS = ksyms.o + +SYMLINKS = semaphore.c old-checksum.c checksum.S + +all: $(OBJ) + +$(OBJ): $(OBJS) $(OX_OBJS) + rm -f $@ + $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ + +sigcontext.o: sigcontext.c + $(CC) $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +ldt.o: ldt.c + $(CC) $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +ptrace_user.o: ptrace_user.c + $(CC) -D__KERNEL__ $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +checksum.S old-checksum.c: + -rm -f $@ + -ln -s $(TOPDIR)/arch/i386/lib/$@ $@ + +semaphore.c: + -rm -f $@ + -ln -s $(TOPDIR)/arch/i386/kernel/$@ $@ + +clean: + rm -f $(OBJS) $(OX_OBJS) + +fastdep: + +archmrproper: + rm -f $(SYMLINKS) + +archclean: + rm -f link.ld + @$(MAKEBOOT) clean + +archdep: + @$(MAKEBOOT) dep + +modules: + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-i386/ksyms.c linux.ac/arch/um/sys-i386/ksyms.c --- linux.vanilla/arch/um/sys-i386/ksyms.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-i386/ksyms.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,16 @@ +#include "linux/module.h" +#include "linux/in6.h" +#include "linux/rwsem.h" +#include "asm/byteorder.h" +#include "asm/semaphore.h" +#include "asm/uaccess.h" +#include "asm/checksum.h" +#include "asm/errno.h" + +EXPORT_SYMBOL(__down_failed); +EXPORT_SYMBOL(__down_failed_interruptible); +EXPORT_SYMBOL(__down_failed_trylock); +EXPORT_SYMBOL(__up_wakeup); + +/* Networking helper routines. */ +EXPORT_SYMBOL(csum_partial_copy_generic); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-i386/ldt.c linux.ac/arch/um/sys-i386/ldt.c --- linux.vanilla/arch/um/sys-i386/ldt.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-i386/ldt.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +extern int modify_ldt(int func, void *ptr, unsigned long bytecount); + +int sys_modify_ldt(int func, void *ptr, unsigned long bytecount) +{ + return modify_ldt(func, ptr, bytecount); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-i386/ptrace.c linux.ac/arch/um/sys-i386/ptrace.c --- linux.vanilla/arch/um/sys-i386/ptrace.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-i386/ptrace.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "asm/ptrace.h" + +/* determines which flags the user has access to. */ +/* 1 = access 0 = no access */ +#define FLAG_MASK 0x00044dd5 + +int putreg(struct task_struct *child, unsigned long regno, + unsigned long value) +{ + switch (regno >> 2) { + case FS: + if (value && (value & 3) != 3) + return -EIO; + child->thread.process_regs.regs[FS] = value; + return 0; + case GS: + if (value && (value & 3) != 3) + return -EIO; + child->thread.process_regs.regs[GS] = value; + return 0; + case DS: + case ES: + if (value && (value & 3) != 3) + return -EIO; + value &= 0xffff; + break; + case SS: + case CS: + if ((value & 3) != 3) + return -EIO; + value &= 0xffff; + break; + case EFL: + value &= FLAG_MASK; + value |= child->thread.process_regs.regs[EFL]; + break; + } + child->thread.process_regs.regs[regno >> 2] = value; + return 0; +} + +unsigned long getreg(struct task_struct *child, unsigned long regno) +{ + unsigned long retval = ~0UL; + + switch (regno >> 2) { + case FS: + case GS: + case DS: + case ES: + case SS: + case CS: + retval = 0xffff; + /* fall through */ + default: + retval &= child->thread.process_regs.regs[regno >> 2]; + } + return retval; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-i386/ptrace_user.c linux.ac/arch/um/sys-i386/ptrace_user.c --- linux.vanilla/arch/um/sys-i386/ptrace_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-i386/ptrace_user.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,25 @@ +#include +#include +#include +#include "sysdep/ptrace.h" + +int ptrace_getregs(long pid, struct sys_pt_regs *regs_out) +{ + return(ptrace(PTRACE_GETREGS, pid, 0, regs_out)); +} + +int ptrace_setregs(long pid, struct sys_pt_regs *regs) +{ + return(ptrace(PTRACE_SETREGS, pid, 0, regs)); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-i386/sigcontext.c linux.ac/arch/um/sys-i386/sigcontext.c --- linux.vanilla/arch/um/sys-i386/sigcontext.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-i386/sigcontext.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include "sysdep/ptrace.h" +#include "user_util.h" + +void fill_in_sigcontext(void *sc_ptr, struct sys_pt_regs *regs, + unsigned long cr2, int err) +{ + struct sigcontext *sc; + + sc = sc_ptr; + sc->ebx = regs->regs[EBX]; + sc->ecx = regs->regs[ECX]; + sc->edx = regs->regs[EDX]; + sc->esi = regs->regs[ESI]; + sc->edi = regs->regs[EDI]; + sc->ebp = regs->regs[EBP]; + sc->eax = regs->regs[EAX]; + sc->ds = regs->regs[DS]; + sc->es = regs->regs[ES]; + sc->fs = regs->regs[FS]; + sc->gs = regs->regs[GS]; + sc->eip = regs->regs[EIP]; + sc->cs = regs->regs[CS]; + sc->eflags = regs->regs[EFL]; + sc->esp_at_signal = regs->regs[UESP]; + sc->ss = regs->regs[SS]; + sc->err = err; + sc->cr2 = cr2; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-i386/syscalls.c linux.ac/arch/um/sys-i386/syscalls.c --- linux.vanilla/arch/um/sys-i386/syscalls.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-i386/syscalls.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "asm/mman.h" +#include "asm/uaccess.h" +#include "asm/unistd.h" + +/* + * Perform the select(nd, in, out, ex, tv) and mmap() system + * calls. Linux/i386 didn't use to be able to handle more than + * 4 system call parameters, so these system calls used a memory + * block for parameter passing.. + */ + +struct mmap_arg_struct { + unsigned long addr; + unsigned long len; + unsigned long prot; + unsigned long flags; + unsigned long fd; + unsigned long offset; +}; + +extern int old_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long offset); + +int old_mmap_i386(struct mmap_arg_struct *arg) +{ + struct mmap_arg_struct a; + int err = -EFAULT; + + if (copy_from_user(&a, arg, sizeof(a))) + goto out; + + err = old_mmap(a.addr, a.len, a.prot, a.flags, a.fd, a.offset); + out: + return err; +} + +struct sel_arg_struct { + unsigned long n; + fd_set *inp, *outp, *exp; + struct timeval *tvp; +}; + +int old_select(struct sel_arg_struct *arg) +{ + struct sel_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + /* sys_select() does the appropriate kernel locking */ + return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-i386/sysrq.c linux.ac/arch/um/sys-i386/sysrq.c --- linux.vanilla/arch/um/sys-i386/sysrq.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-i386/sysrq.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,22 @@ +#include "linux/kernel.h" +#include "linux/smp.h" +#include "asm/ptrace.h" +#include "sysrq.h" + +void show_regs(struct pt_regs_subarch *regs) +{ + printk("\n"); + printk("EIP: %04x:[<%08lx>] CPU: %d %s",0xffff & regs->xcs, regs->eip, + smp_processor_id(), print_tainted()); + if (regs->xcs & 3) + printk(" ESP: %04x:%08lx",0xffff & regs->xss, regs->esp); + printk(" EFLAGS: %08lx\n", regs->eflags); + printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n", + regs->eax, regs->ebx, regs->ecx, regs->edx); + printk("ESI: %08lx EDI: %08lx EBP: %08lx", + regs->esi, regs->edi, regs->ebp); + printk(" DS: %04x ES: %04x\n", + 0xffff & regs->xds, 0xffff & regs->xes); + + show_trace(®s->esp); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-ia64/Makefile linux.ac/arch/um/sys-ia64/Makefile --- linux.vanilla/arch/um/sys-ia64/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-ia64/Makefile Wed Oct 10 01:47:38 2001 @@ -0,0 +1,26 @@ +OBJ = sys.o + +OBJS = + +all: $(OBJ) + +$(OBJ): $(OBJS) + rm -f $@ + $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ +clean: + rm -f $(OBJS) + +fastdep: + +archmrproper: + +archclean: + rm -f link.ld + @$(MAKEBOOT) clean + +archdep: + @$(MAKEBOOT) dep + +modules: + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-ppc/Makefile linux.ac/arch/um/sys-ppc/Makefile --- linux.vanilla/arch/um/sys-ppc/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-ppc/Makefile Wed Oct 10 01:47:38 2001 @@ -0,0 +1,78 @@ +OBJ = sys.o + +.S.o: + $(CC) $(AFLAGS) -D__ASSEMBLY__ -D__UM_PPC__ -c $< -o $*.o + +OBJS = ptrace.o sigcontext.o semaphore.o checksum.o miscthings.o misc.o \ + ptrace_user.o + +EXTRA_AFLAGS := -DCONFIG_ALL_PPC -I. -I$(TOPDIR)/arch/ppc/kernel + +all: $(OBJ) + +$(OBJ): $(OBJS) + rm -f $@ + $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ + +ptrace_user.o: ptrace_user.c + $(CC) -D__KERNEL__ $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +sigcontext.o: sigcontext.c + $(CC) $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +semaphore.c: + rm -f $@ + ln -s $(TOPDIR)/arch/ppc/kernel/$@ $@ + +checksum.S: + rm -f $@ + ln -s $(TOPDIR)/arch/ppc/lib/$@ $@ + +mk_defs.c: + rm -f $@ + ln -s $(TOPDIR)/arch/ppc/kernel/$@ $@ + +ppc_defs.head: + rm -f $@ + ln -s $(TOPDIR)/arch/ppc/kernel/$@ $@ + +ppc_defs.h: mk_defs.c ppc_defs.head \ + $(TOPDIR)/include/asm-ppc/mmu.h \ + $(TOPDIR)/include/asm-ppc/processor.h \ + $(TOPDIR)/include/asm-ppc/pgtable.h \ + $(TOPDIR)/include/asm-ppc/ptrace.h +# $(CC) $(CFLAGS) -S mk_defs.c + cp ppc_defs.head ppc_defs.h +# for bk, this way we can write to the file even if it's not checked out + echo '#define THREAD 608' >> ppc_defs.h + echo '#define PT_REGS 8' >> ppc_defs.h + echo '#define CLONE_VM 256' >> ppc_defs.h +# chmod u+w ppc_defs.h +# grep '^#define' mk_defs.s >> ppc_defs.h +# rm mk_defs.s + +# the asm link is horrible, and breaks the other targets. This is also +# not going to work with parallel makes. + +checksum.o: checksum.S + rm -f asm + ln -s $(TOPDIR)/include/asm-ppc asm + $(CC) $(EXTRA_AFLAGS) $(AFLAGS) -D__ASSEMBLY__ -D__UM_PPC__ -c $< -o $*.o + rm -f asm + +misc.o: misc.S ppc_defs.h + rm -f asm + ln -s $(TOPDIR)/include/asm-ppc asm + $(CC) $(EXTRA_AFLAGS) $(AFLAGS) -D__ASSEMBLY__ -D__UM_PPC__ -c $< -o $*.o + rm -f asm + +clean: + rm -f $(OBJS) + rm -f ppc_defs.h + rm -f checksum.S semaphore.c mk_defs.c + +fastdep: + +modules: + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-ppc/misc.S linux.ac/arch/um/sys-ppc/misc.S --- linux.vanilla/arch/um/sys-ppc/misc.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-ppc/misc.S Wed Oct 10 01:47:38 2001 @@ -0,0 +1,116 @@ +/* + * This file contains miscellaneous low-level functions. + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras. + * + * A couple of functions stolen from arch/ppc/kernel/misc.S for UML + * by Chris Emerson. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include "ppc_asm.h" + +#if defined(CONFIG_4xx) || defined(CONFIG_8xx) +#define CACHE_LINE_SIZE 16 +#define LG_CACHE_LINE_SIZE 4 +#define MAX_COPY_PREFETCH 1 +#elif !defined(CONFIG_PPC64BRIDGE) +#define CACHE_LINE_SIZE 32 +#define LG_CACHE_LINE_SIZE 5 +#define MAX_COPY_PREFETCH 4 +#else +#define CACHE_LINE_SIZE 128 +#define LG_CACHE_LINE_SIZE 7 +#define MAX_COPY_PREFETCH 1 +#endif /* CONFIG_4xx || CONFIG_8xx */ + + .text + +/* + * Clear a page using the dcbz instruction, which doesn't cause any + * memory traffic (except to write out any cache lines which get + * displaced). This only works on cacheable memory. + */ +_GLOBAL(clear_page) + li r0,4096/CACHE_LINE_SIZE + mtctr r0 +#ifdef CONFIG_8xx + li r4, 0 +1: stw r4, 0(r3) + stw r4, 4(r3) + stw r4, 8(r3) + stw r4, 12(r3) +#else +1: dcbz 0,r3 +#endif + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + blr + +/* + * Copy a whole page. We use the dcbz instruction on the destination + * to reduce memory traffic (it eliminates the unnecessary reads of + * the destination into cache). This requires that the destination + * is cacheable. + */ +#define COPY_16_BYTES \ + lwz r6,4(r4); \ + lwz r7,8(r4); \ + lwz r8,12(r4); \ + lwzu r9,16(r4); \ + stw r6,4(r3); \ + stw r7,8(r3); \ + stw r8,12(r3); \ + stwu r9,16(r3) + +_GLOBAL(copy_page) + addi r3,r3,-4 + addi r4,r4,-4 + li r5,4 + +#ifndef CONFIG_8xx +#if MAX_COPY_PREFETCH > 1 + li r0,MAX_COPY_PREFETCH + li r11,4 + mtctr r0 +11: dcbt r11,r4 + addi r11,r11,CACHE_LINE_SIZE + bdnz 11b +#else /* MAX_COPY_PREFETCH == 1 */ + dcbt r5,r4 + li r11,CACHE_LINE_SIZE+4 +#endif /* MAX_COPY_PREFETCH */ +#endif /* CONFIG_8xx */ + + li r0,4096/CACHE_LINE_SIZE + mtctr r0 +1: +#ifndef CONFIG_8xx + dcbt r11,r4 + dcbz r5,r3 +#endif + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 32 + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 64 + COPY_16_BYTES + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 128 + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES +#endif +#endif +#endif + bdnz 1b + blr diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-ppc/miscthings.c linux.ac/arch/um/sys-ppc/miscthings.c --- linux.vanilla/arch/um/sys-ppc/miscthings.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-ppc/miscthings.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,56 @@ +#include "linux/threads.h" +#include "linux/stddef.h" // for NULL +#include "linux/elf.h" // for AT_NULL + +/* unsigned int local_bh_count[NR_CPUS]; */ +unsigned long isa_io_base = 0; + +/* The following function nicked from arch/ppc/kernel/process.c and + * adapted slightly */ +/* + * XXX ld.so expects the auxiliary table to start on + * a 16-byte boundary, so we have to find it and + * move it up. :-( + */ +void shove_aux_table(unsigned long sp) +{ + int argc; + char *p; + unsigned long e; + unsigned long aux_start, offset; + + argc = *(int *)sp; + sp += sizeof(int) + (argc + 1) * sizeof(char *); + /* skip over the environment pointers */ + do { + p = *(char **)sp; + sp += sizeof(char *); + } while (p != NULL); + aux_start = sp; + /* skip to the end of the auxiliary table */ + do { + e = *(unsigned long *)sp; + sp += 2 * sizeof(unsigned long); + } while (e != AT_NULL); + offset = ((aux_start + 15) & ~15) - aux_start; + if (offset != 0) { + do { + sp -= sizeof(unsigned long); + e = *(unsigned long *)sp; + *(unsigned long *)(sp + offset) = e; + } while (sp > aux_start); + } +} +/* END stuff taken from arch/ppc/kernel/process.c */ + + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-ppc/ptrace.c linux.ac/arch/um/sys-ppc/ptrace.c --- linux.vanilla/arch/um/sys-ppc/ptrace.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-ppc/ptrace.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,28 @@ +#include "linux/sched.h" +#include "asm/ptrace.h" + +int putreg(struct task_struct *child, unsigned long regno, + unsigned long value) +{ + child->thread.process_regs.regs[regno >> 2] = value; + return 0; +} + +unsigned long getreg(struct task_struct *child, unsigned long regno) +{ + unsigned long retval = ~0UL; + + retval &= child->thread.process_regs.regs[regno >> 2]; + return retval; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-ppc/ptrace_user.c linux.ac/arch/um/sys-ppc/ptrace_user.c --- linux.vanilla/arch/um/sys-ppc/ptrace_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-ppc/ptrace_user.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,39 @@ +#include +#include +#include + +int ptrace_getregs(long pid, struct sys_pt_regs *regs_out) +{ + int i; + for (i=0; i < sizeof(struct sys_pt_regs)/sizeof(PPC_REG); ++i) { + errno = 0; + regs_out->regs[i] = ptrace(PTRACE_PEEKUSER, pid, i*4, 0); + if (errno) { + return -errno; + } + } + return 0; +} + +int ptrace_setregs(long pid, struct sys_pt_regs *regs_in) +{ + int i; + for (i=0; i < sizeof(struct sys_pt_regs)/sizeof(PPC_REG); ++i) { + if (i != 34 /* FIXME: PT_ORIG_R3 */ && i <= PT_MQ) { + if (ptrace(PTRACE_POKEUSER, pid, i*4, regs_in->regs[i]) < 0) { + return -errno; + } + } + } + return 0; +} +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-ppc/sigcontext.c linux.ac/arch/um/sys-ppc/sigcontext.c --- linux.vanilla/arch/um/sys-ppc/sigcontext.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-ppc/sigcontext.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,43 @@ +#include "asm/ptrace.h" +#include "asm/sigcontext.h" +#include "sysdep/ptrace.h" +#include "user_util.h" + +void fill_in_sigcontext(void *scontext, struct sys_pt_regs *regs, + unsigned long cr2, int err) +{ + struct sigcontext_struct *sc = scontext; +#if 0 + int i; + // general purpose regs + for (i=0; i<32; ++i) { + sc->regs->gpr[i] = regs->regs[PT_R0 + i]; + } + sc->regs->nip = regs->regs[PT_NIP]; + sc->regs->msr = regs->regs[PT_MSR]; + sc->regs->orig_gpr3 = regs->regs[PT_ORIG_R3]; + sc->regs->ctr = regs->regs[PT_CTR]; + sc->regs->link = regs->regs[PT_LNK]; + sc->regs->xer = regs->regs[PT_XER]; + sc->regs->ccr = regs->regs[PT_CCR]; + sc->regs->mq = regs->regs[PT_MQ]; + sc->regs->trap = err; + sc->regs->dar = cr2; +#endif + /* This is a bit of a hack; there's some confusion with the + * various definitions of [sys_]pt_regs, and everything isn't + * quite coming together quite right. */ + memcpy(sc->regs, regs, sizeof(struct sys_pt_regs)); + /*(sc->regs) = *regs; */ +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/Makefile linux.ac/arch/x86_64/Makefile --- linux.vanilla/arch/x86_64/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/Makefile Wed Oct 10 01:47:38 2001 @@ -0,0 +1,132 @@ +# +# x86_64/Makefile +# +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. Remember to do have actions +# for "archclean" and "archdep" for cleaning up and making dependencies for +# this architecture +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# +# 19990713 Artur Skawina +# Added '-march' and '-mpreferred-stack-boundary' support +# 20000913 Pavel Machek +# Converted for x86_64 architecture +# 20010105 Andi Kleen, add IA32 compiler. +# +# $Id: Makefile,v 1.28 2001/06/29 17:47:43 aj Exp $ + + +# +# boot system currently needs IA32 tools to link (to be fixed) +# +# Change this to your i386 compiler/binutils +IA32_PREFIX := /usr/bin/ +IA32_CC := $(IA32_PREFIX)gcc -O2 -fomit-frame-pointer -nostdinc -I $(HPATH) +IA32_LD := $(IA32_PREFIX)ld +IA32_AS := $(IA32_PREFIX)gcc -D__ASSEMBLY__ -traditional -c -nostdinc -I $(HPATH) +IA32_OBJCOPY := $(IA32_PREFIX)objcopy +IA32_CPP := $(IA32_PREFIX)gcc -E +export IA32_CC IA32_LD IA32_AS IA32_OBJCOPY IA32_CPP + + +LD=$(CROSS_COMPILE)ld -m elf_x86_64 +OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S +LDFLAGS=-e stext +LINKFLAGS =-T $(TOPDIR)/arch/x86_64/vmlinux.lds $(LDFLAGS) + +CFLAGS += $(shell if $(CC) -mno-red-zone -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mno-red-zone"; fi ) +CFLAGS += -mcmodel=kernel +CFLAGS += -pipe +CFLAGS += -fno-strict-aliasing +CFLAGS += -fno-reorder-blocks +#CFLAGS += -g + +ifdef SIMULATOR +CFLAGS += -DSIMULATOR +AFLAGS += -DSIMULATOR +endif + +# prevent gcc from keeping the stack 16 byte aligned (FIXME) +#CFLAGS += -mpreferred-stack-boundary=2 + +HEAD := arch/x86_64/kernel/head.o arch/x86_64/kernel/head64.o arch/x86_64/kernel/init_task.o + +SUBDIRS := arch/x86_64/tools $(SUBDIRS) arch/x86_64/kernel arch/x86_64/mm arch/x86_64/lib +CORE_FILES := arch/x86_64/kernel/kernel.o $(CORE_FILES) +CORE_FILES += arch/x86_64/mm/mm.o +LIBS := $(TOPDIR)/arch/x86_64/lib/lib.a $(LIBS) + +ifdef CONFIG_IA32_EMULATION +SUBDIRS += arch/x86_64/ia32 +CORE_FILES += arch/x86_64/ia32/ia32.o +endif + +ifdef CONFIG_HOSTFS +SUBDIRS += arch/x86_64/hostfs +core-$(CONFIG_HOSTFS) += arch/x86_64/hostfs/hostfs.o +endif + +CORE_FILES += $(core-y) + +arch/x86_64/tools: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/x86_64/tools + +arch/x86_64/kernel: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/x86_64/kernel + +arch/x86_64/mm: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/x86_64/mm + +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot + +vmlinux: arch/x86_64/vmlinux.lds + +FORCE: ; + +.PHONY: zImage bzImage compressed zlilo bzlilo zdisk bzdisk install \ + clean archclean archmrproper archdep + +zImage: vmlinux + @$(MAKEBOOT) zImage + +bzImage: vmlinux + @$(MAKEBOOT) bzImage + +bzImage-padded: vmlinux + @$(MAKEBOOT) bzImage-padded + +compressed: zImage + +zlilo: vmlinux + @$(MAKEBOOT) BOOTIMAGE=zImage zlilo + +tmp: + @$(MAKEBOOT) BOOTIMAGE=bzImage zlilo +bzlilo: vmlinux + @$(MAKEBOOT) BOOTIMAGE=bzImage zlilo + +zdisk: vmlinux + @$(MAKEBOOT) BOOTIMAGE=zImage zdisk + +bzdisk: vmlinux + @$(MAKEBOOT) BOOTIMAGE=bzImage zdisk + +install: vmlinux + @$(MAKEBOOT) BOOTIMAGE=bzImage install + +archclean: + @$(MAKEBOOT) clean + +archmrproper: + rm -f $(TOPDIR)/arch/x86_64/tools/offset.h + rm -f $(TOPDIR)/arch/x86_64/tools/offset.tmp + rm -f $(TOPDIR)/include/asm-x86_64/offset.h + +archdep: + @$(MAKE) -C $(TOPDIR)/arch/x86_64/tools all + @$(MAKEBOOT) dep diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/boot/Makefile linux.ac/arch/x86_64/boot/Makefile --- linux.vanilla/arch/x86_64/boot/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/boot/Makefile Wed Oct 10 01:47:38 2001 @@ -0,0 +1,92 @@ +# +# arch/x86_64/boot/Makefile +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# + +BOOT_INCL = $(TOPDIR)/include/linux/config.h \ + $(TOPDIR)/include/linux/autoconf.h \ + $(TOPDIR)/include/asm/boot.h + +zImage: $(CONFIGURE) bootsect setup compressed/vmlinux tools/build + $(OBJCOPY) compressed/vmlinux compressed/vmlinux.out + tools/build bootsect setup compressed/vmlinux.out $(ROOT_DEV) > zImage + +bzImage: $(CONFIGURE) bbootsect bsetup compressed/bvmlinux tools/build + $(OBJCOPY) compressed/bvmlinux compressed/bvmlinux.out + tools/build -b bbootsect bsetup compressed/bvmlinux.out $(ROOT_DEV) > bzImage + +bzImage-padded: bzImage + dd if=/dev/zero bs=1k count=70 >> bzImage + +compressed/vmlinux: $(TOPDIR)/vmlinux + @$(MAKE) -C compressed vmlinux + +compressed/bvmlinux: $(TOPDIR)/vmlinux + @$(MAKE) -C compressed bvmlinux + +zdisk: $(BOOTIMAGE) + dd bs=8192 if=$(BOOTIMAGE) of=/dev/fd0 + +zlilo: $(CONFIGURE) $(BOOTIMAGE) + if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi + if [ -f $(INSTALL_PATH)/System.map ]; then mv $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi + cat $(BOOTIMAGE) > $(INSTALL_PATH)/vmlinuz + cp $(TOPDIR)/System.map $(INSTALL_PATH)/ + if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi + +install: $(CONFIGURE) $(BOOTIMAGE) + sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) $(TOPDIR)/System.map "$(INSTALL_PATH)" + +tools/build: tools/build.c + $(HOSTCC) $(HOSTCFLAGS) -o $@ $< + +bootsect: bootsect.o + $(IA32_LD) -Ttext 0x0 -s --oformat binary -o $@ $< + +bootsect.o: bootsect.s + $(IA32_AS) -o $@ $< + +bootsect.s: bootsect.S Makefile $(BOOT_INCL) + $(IA32_CPP) $(CPPFLAGS) -traditional -D__ASSEMBLY__ $(SVGA_MODE) $(RAMDISK) $< -o $@ + +bbootsect: bbootsect.o + $(IA32_LD) -Ttext 0x0 -s --oformat binary $< -o $@ + +bbootsect.o: bbootsect.s + $(IA32_AS) -o $@ $< + +bbootsect.s: bootsect.S Makefile $(BOOT_INCL) + $(IA32_CPP) $(CPPFLAGS) -D__BIG_KERNEL__ -D__ASSEMBLY__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + +setup: setup.o + $(IA32_LD) -Ttext 0x0 -s --oformat binary -e begtext -o $@ $< + +setup.o: setup.s + $(IA32_AS) -o $@ $< + +setup.s: setup.S video.S Makefile $(BOOT_INCL) $(TOPDIR)/include/linux/version.h $(TOPDIR)/include/linux/compile.h + $(IA32_CPP) $(CPPFLAGS) -traditional -D__ASSEMBLY__ $(SVGA_MODE) $(RAMDISK) $< -o $@ + +bsetup: bsetup.o + $(IA32_LD) -Ttext 0x0 -s --oformat binary -e begtext -o $@ $< + +bsetup.o: bsetup.s + $(IA32_AS) -o $@ $< + +bsetup.s: setup.S video.S Makefile $(BOOT_INCL) $(TOPDIR)/include/linux/version.h $(TOPDIR)/include/linux/compile.h + $(IA32_CPP) $(CPPFLAGS) -D__BIG_KERNEL__ -D__ASSEMBLY__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + +dep: + +clean: + rm -f tools/build + rm -f setup bootsect zImage compressed/vmlinux.out + rm -f bsetup bbootsect bzImage compressed/bvmlinux.out + @$(MAKE) -C compressed clean + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/boot/bootsect.S linux.ac/arch/x86_64/boot/bootsect.S --- linux.vanilla/arch/x86_64/boot/bootsect.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/boot/bootsect.S Wed Oct 10 01:47:38 2001 @@ -0,0 +1,8 @@ +/* + * The submitted file matched the 386 one and this is 16bit code + * so I've marked it this way so we have one copy to debug only + * + * Alan + */ + +#include <../arch/i386/boot/bootsect.S> diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/boot/compressed/Makefile linux.ac/arch/x86_64/boot/compressed/Makefile --- linux.vanilla/arch/x86_64/boot/compressed/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/boot/compressed/Makefile Wed Oct 10 01:47:38 2001 @@ -0,0 +1,43 @@ +# +# linux/arch/i386/boot/compressed/Makefile +# +# create a compressed vmlinux image from the original vmlinux +# + +HEAD = head.o +SYSTEM = $(TOPDIR)/vmlinux + +OBJECTS = $(HEAD) misc.o + +IA32_CFLAGS := -O2 -DSTDC_HEADERS + +# +# ZIMAGE_OFFSET is the load offset of the compression loader +# BZIMAGE_OFFSET is the load offset of the high loaded compression loader +# +BZIMAGE_OFFSET = 0x100000 + +BZLINKFLAGS = -Ttext $(BZIMAGE_OFFSET) $(ZLDFLAGS) + +all: vmlinux + +bvmlinux: piggy.o $(OBJECTS) + $(IA32_LD) $(BZLINKFLAGS) -o bvmlinux $(OBJECTS) piggy.o + +head.o: head.S + $(IA32_AS) -c head.S + +misc.o: misc.c + $(IA32_CC) $(IA32_CFLAGS) -c misc.c + +piggy.o: $(SYSTEM) + tmppiggy=_tmp_$$$$piggy; \ + rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \ + $(OBJCOPY) $(SYSTEM) $$tmppiggy; \ + gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \ + echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \ + $(IA32_LD) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 -T $$tmppiggy.lnk; \ + rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk + +clean: + rm -f vmlinux bvmlinux _tmp_* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/boot/compressed/head.S linux.ac/arch/x86_64/boot/compressed/head.S --- linux.vanilla/arch/x86_64/boot/compressed/head.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/boot/compressed/head.S Wed Oct 10 01:47:38 2001 @@ -0,0 +1,142 @@ +/* + * linux/boot/head.S + * + * Copyright (C) 1991, 1992, 1993 Linus Torvalds + * + * $Id: head.S,v 1.3 2001/04/20 00:59:28 ak Exp $ + */ + +/* + * head.S contains the 32-bit startup code. + * + * NOTE!!! Startup happens at absolute address 0x00001000, which is also where + * the page directory will exist. The startup code will be overwritten by + * the page directory. [According to comments etc elsewhere on a compressed + * kernel it will end up at 0x1000 + 1Mb I hope so as I assume this. - AC] + * + * Page 0 is deliberately kept safe, since System Management Mode code in + * laptops may need to access the BIOS data stored there. This is also + * useful for future device drivers that either access the BIOS via VM86 + * mode. + */ + +/* + * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 + */ +.code32 +.text + +#include +#include + + .code32 + .globl startup_32 + +startup_32: + cld + cli + movl $(__KERNEL_DS),%eax + movl %eax,%ds + movl %eax,%es + movl %eax,%fs + movl %eax,%gs + + lss SYMBOL_NAME(stack_start),%esp + xorl %eax,%eax +1: incl %eax # check that A20 really IS enabled + movl %eax,0x000000 # loop forever if it isn't + cmpl %eax,0x100000 + je 1b + +/* + * Initialize eflags. Some BIOS's leave bits like NT set. This would + * confuse the debugger if this code is traced. + * XXX - best to initialize before switching to protected mode. + */ + pushl $0 + popfl +/* + * Clear BSS + */ + xorl %eax,%eax + movl $ SYMBOL_NAME(_edata),%edi + movl $ SYMBOL_NAME(_end),%ecx + subl %edi,%ecx + cld + rep + stosb +/* + * Do the decompression, and jump to the new kernel.. + */ + subl $16,%esp # place for structure on the stack + movl %esp,%eax + pushl %esi # real mode pointer as second arg + pushl %eax # address of structure as first arg + call SYMBOL_NAME(decompress_kernel) + orl %eax,%eax + jnz 3f + addl $8,%esp + xorl %ebx,%ebx + ljmp $(__KERNEL_CS), $0x100000 + +/* + * We come here, if we were loaded high. + * We need to move the move-in-place routine down to 0x1000 + * and then start it with the buffer addresses in registers, + * which we got from the stack. + */ +3: + movl %esi,%ebx + movl $move_routine_start,%esi + movl $0x1000,%edi + movl $move_routine_end,%ecx + subl %esi,%ecx + addl $3,%ecx + shrl $2,%ecx + cld + rep + movsl + + popl %esi # discard the address + addl $4,%esp # real mode pointer + popl %esi # low_buffer_start + popl %ecx # lcount + popl %edx # high_buffer_start + popl %eax # hcount + movl $0x100000,%edi + cli # make sure we don't get interrupted + ljmp $(__KERNEL_CS), $0x1000 # and jump to the move routine + +/* + * Routine (template) for moving the decompressed kernel in place, + * if we were high loaded. This _must_ PIC-code ! + */ +move_routine_start: + movl %ecx,%ebp + shrl $2,%ecx + rep + movsl + movl %ebp,%ecx + andl $3,%ecx + rep + movsb + movl %edx,%esi + movl %eax,%ecx # NOTE: rep movsb won't move if %ecx == 0 + addl $3,%ecx + shrl $2,%ecx + rep + movsl + movl %ebx,%esi # Restore setup pointer + xorl %ebx,%ebx + ljmp $(__KERNEL_CS), $0x100000 +move_routine_end: + + +/* Stack for uncompression */ + .align 32 +user_stack: + .fill 4096,4,0 +stack_start: + .long user_stack+4096 + .word __KERNEL_DS + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/boot/compressed/misc.c linux.ac/arch/x86_64/boot/compressed/misc.c --- linux.vanilla/arch/x86_64/boot/compressed/misc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/boot/compressed/misc.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,431 @@ +/* + * misc.c + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * puts by Nick Holloway 1993, better puts by Martin Mares 1995 + * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 + */ + +#include "miscsetup.h" +#include + +/* + * gzip declarations + */ + +#define OF(args) args +#define STATIC static + +#undef memset +#undef memcpy +#define memzero(s, n) memset ((s), 0, (n)) + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +#define WSIZE 0x8000 /* Window size must be at least 32k, */ + /* and a power of two */ + +static uch *inbuf; /* input buffer */ +static uch window[WSIZE]; /* Sliding window buffer */ + +static unsigned insize = 0; /* valid bytes in inbuf */ +static unsigned inptr = 0; /* index of next byte to be processed in inbuf */ +static unsigned outcnt = 0; /* bytes in output buffer */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +static int fill_inbuf(void); +static void flush_window(void); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +/* + * This is set up by the setup-routine at boot-time + */ +static unsigned char *real_mode; /* Pointer to real-mode data */ + +#define EXT_MEM_K (*(unsigned short *)(real_mode + 0x2)) +#ifndef STANDARD_MEMORY_BIOS_CALL +#define ALT_MEM_K (*(unsigned long *)(real_mode + 0x1e0)) +#endif +#define SCREEN_INFO (*(struct screen_info *)(real_mode+0)) + +extern char input_data[]; +extern int input_len; + +static long bytes_out = 0; +static uch *output_data; +static unsigned long output_ptr = 0; + + +static void *malloc(int size); +static void free(void *where); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +static void puts(const char *); + +extern int end; +static long free_mem_ptr = (long)&end; +static long free_mem_end_ptr; + +#define INPLACE_MOVE_ROUTINE 0x1000 +#define LOW_BUFFER_START 0x2000 +#define LOW_BUFFER_MAX 0x90000 +#define HEAP_SIZE 0x3000 +static unsigned int low_buffer_end, low_buffer_size; +static int high_loaded =0; +static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/; + +static char *vidmem = (char *)0xb8000; +static int vidport; +static int lines, cols; + +#include "../../../../lib/inflate.c" + +static void *malloc(int size) +{ + void *p; + + if (size <0) error("Malloc error\n"); + if (free_mem_ptr <= 0) error("Memory error\n"); + + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ + + p = (void *)free_mem_ptr; + free_mem_ptr += size; + + if (free_mem_ptr >= free_mem_end_ptr) + error("\nOut of memory\n"); + + return p; +} + +static void free(void *where) +{ /* Don't care */ +} + +static void gzip_mark(void **ptr) +{ + *ptr = (void *) free_mem_ptr; +} + +static void gzip_release(void **ptr) +{ + free_mem_ptr = (long) *ptr; +} + +static void scroll(void) +{ + int i; + + memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 ); + for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 ) + vidmem[i] = ' '; +} + +static void puts(const char *s) +{ + int x,y,pos; + char c; + + x = SCREEN_INFO.orig_x; + y = SCREEN_INFO.orig_y; + + while ( ( c = *s++ ) != '\0' ) { + if ( c == '\n' ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } else { + vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } + } + } + + SCREEN_INFO.orig_x = x; + SCREEN_INFO.orig_y = y; + + pos = (x + cols * y) * 2; /* Update cursor position */ + outb_p(14, vidport); + outb_p(0xff & (pos >> 9), vidport+1); + outb_p(15, vidport); + outb_p(0xff & (pos >> 1), vidport+1); +} + +void* memset(void* s, int c, size_t n) +{ + int i; + char *ss = (char*)s; + + for (i=0;i> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + output_ptr += (ulg)outcnt; + outcnt = 0; +} + +static void flush_window_high(void) +{ + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, ch; + in = window; + for (n = 0; n < outcnt; n++) { + ch = *output_data++ = *in++; + if ((ulg)output_data == low_buffer_end) output_data=high_buffer_start; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + outcnt = 0; +} + +static void flush_window(void) +{ + if (high_loaded) flush_window_high(); + else flush_window_low(); +} + +static void error(char *x) +{ + puts("\n\n"); + puts(x); + puts("\n\n -- System halted"); + + while(1); /* Halt */ +} + +void setup_normal_output_buffer(void) +{ +#ifdef STANDARD_MEMORY_BIOS_CALL + if (EXT_MEM_K < 1024) error("Less than 2MB of memory.\n"); +#else + if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < 1024) error("Less than 2MB of memory.\n"); +#endif + output_data = (char *)0x100000; /* Points to 1M */ + free_mem_end_ptr = (long)real_mode; +} + +struct moveparams { + uch *low_buffer_start; int lcount; + uch *high_buffer_start; int hcount; +}; + +void setup_output_buffer_if_we_run_high(struct moveparams *mv) +{ + high_buffer_start = (uch *)(((ulg)&end) + HEAP_SIZE); +#ifdef STANDARD_MEMORY_BIOS_CALL + if (EXT_MEM_K < (3*1024)) error("Less than 4MB of memory.\n"); +#else + if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < (3*1024)) error("Less than 4MB of memory.\n"); +#endif + mv->low_buffer_start = output_data = (char *)LOW_BUFFER_START; + low_buffer_end = ((unsigned int)real_mode > LOW_BUFFER_MAX + ? LOW_BUFFER_MAX : (unsigned int)real_mode) & ~0xfff; + low_buffer_size = low_buffer_end - LOW_BUFFER_START; + high_loaded = 1; + free_mem_end_ptr = (long)high_buffer_start; + if ( (0x100000 + low_buffer_size) > ((ulg)high_buffer_start)) { + high_buffer_start = (uch *)(0x100000 + low_buffer_size); + mv->hcount = 0; /* say: we need not to move high_buffer */ + } + else mv->hcount = -1; + mv->high_buffer_start = high_buffer_start; +} + +void close_output_buffer_if_we_run_high(struct moveparams *mv) +{ + if (bytes_out > low_buffer_size) { + mv->lcount = low_buffer_size; + if (mv->hcount) + mv->hcount = bytes_out - low_buffer_size; + } else { + mv->lcount = bytes_out; + mv->hcount = 0; + } +} + +void check_cpu(void) +{ + int res = 0; + asm volatile( " \n\ + movl $3,%%edx # at least 386 \n\ + pushfl # push EFLAGS \n\ + popl %%eax # get EFLAGS \n\ + movl %%eax,%%ecx # save original EFLAGS \n\ + xorl $0x40000,%%eax # flip AC bit in EFLAGS \n\ + pushl %%eax # copy to EFLAGS \n\ + popfl # set EFLAGS \n\ + pushfl # get new EFLAGS \n\ + popl %%eax # put it in eax \n\ + xorl %%ecx,%%eax # change in flags \n\ + andl $0x40000,%%eax # check if AC bit changed \n\ + je 1f \n\ +\n\ + movl $4,%%edx # at least 486 \n\ + movl %%ecx,%%eax \n\ + xorl $0x200000,%%eax # check ID flag \n\ + pushl %%eax \n\ + popfl # if we are on a straight 486DX, SX, or \n\ + pushfl # 487SX we can't change it \n\ + popl %%eax \n\ + xorl %%ecx,%%eax \n\ + pushl %%ecx # restore original EFLAGS \n\ + popfl \n\ + andl $0x200000,%%eax \n\ + je 1f \n\ +\n\ + /* get vendor info */ \n\ +# xorl %%eax,%%eax # call CPUID with 0 -> return vendor ID \n\ +# cpuid \n\ +# movl $5, %%edx \n\ +# cmpl $0x41757468,%%ebx # check thats amd \n\ +# jne 1f \n\ +\n\ + mov $0x80000000,%%eax # Is extended cpuid supported?\n\ + cpuid\n\ + test $0x80000000,%%eax\n\ + movl $5, %%edx \n\ + jz 1f\n\ +\n\ + movl $0x80000001,%%eax \n\ + cpuid \n\ + andl $0x20000000,%%edx \n\ + movl $6, %%edx \n\ + jz 1f \n\ +\n\ + movl $7, %%edx \n\ +1:" : "=d" (res) : : "eax", "ebx", "ecx" ); + + switch (res) { + case 3: puts( "386" ); + break; + case 4: puts( "486" ); + break; + case 5: puts( "no extended cpuid" ); + break; + case 6: puts( "non-64bit 586+" ); + break; + case 7: puts( "64bit" ); + break; + default:puts( "internal error" ); + break; + } + if (res !=7) + error( "Sorry, your CPU is not capable of running 64-bit kernel." ); +} + +int decompress_kernel(struct moveparams *mv, void *rmode) +{ + real_mode = rmode; + + if (SCREEN_INFO.orig_video_mode == 7) { + vidmem = (char *) 0xb0000; + vidport = 0x3b4; + } else { + vidmem = (char *) 0xb8000; + vidport = 0x3d4; + } + + lines = SCREEN_INFO.orig_video_lines; + cols = SCREEN_INFO.orig_video_cols; + + if (free_mem_ptr < 0x100000) setup_normal_output_buffer(); + else setup_output_buffer_if_we_run_high(mv); + + makecrc(); + puts("Checking CPU type..."); + check_cpu(); + puts(".\nDecompressing Linux..."); + gunzip(); + puts("done.\nBooting the kernel.\n"); + if (high_loaded) close_output_buffer_if_we_run_high(mv); + return high_loaded; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/boot/compressed/miscsetup.h linux.ac/arch/x86_64/boot/compressed/miscsetup.h --- linux.vanilla/arch/x86_64/boot/compressed/miscsetup.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/boot/compressed/miscsetup.h Wed Oct 10 01:47:38 2001 @@ -0,0 +1,39 @@ +#define NULL 0 +typedef unsigned int size_t; + + +struct screen_info { + unsigned char orig_x; /* 0x00 */ + unsigned char orig_y; /* 0x01 */ + unsigned short dontuse1; /* 0x02 -- EXT_MEM_K sits here */ + unsigned short orig_video_page; /* 0x04 */ + unsigned char orig_video_mode; /* 0x06 */ + unsigned char orig_video_cols; /* 0x07 */ + unsigned short unused2; /* 0x08 */ + unsigned short orig_video_ega_bx; /* 0x0a */ + unsigned short unused3; /* 0x0c */ + unsigned char orig_video_lines; /* 0x0e */ + unsigned char orig_video_isVGA; /* 0x0f */ + unsigned short orig_video_points; /* 0x10 */ + + /* VESA graphic mode -- linear frame buffer */ + unsigned short lfb_width; /* 0x12 */ + unsigned short lfb_height; /* 0x14 */ + unsigned short lfb_depth; /* 0x16 */ + unsigned long lfb_base; /* 0x18 */ + unsigned long lfb_size; /* 0x1c */ + unsigned short dontuse2, dontuse3; /* 0x20 -- CL_MAGIC and CL_OFFSET here */ + unsigned short lfb_linelength; /* 0x24 */ + unsigned char red_size; /* 0x26 */ + unsigned char red_pos; /* 0x27 */ + unsigned char green_size; /* 0x28 */ + unsigned char green_pos; /* 0x29 */ + unsigned char blue_size; /* 0x2a */ + unsigned char blue_pos; /* 0x2b */ + unsigned char rsvd_size; /* 0x2c */ + unsigned char rsvd_pos; /* 0x2d */ + unsigned short vesapm_seg; /* 0x2e */ + unsigned short vesapm_off; /* 0x30 */ + unsigned short pages; /* 0x32 */ + /* 0x34 -- 0x3f reserved for future expansion */ +}; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/boot/install.sh linux.ac/arch/x86_64/boot/install.sh --- linux.vanilla/arch/x86_64/boot/install.sh Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/boot/install.sh Wed Oct 10 01:47:38 2001 @@ -0,0 +1,40 @@ +#!/bin/sh +# +# arch/i386/boot/install.sh +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1995 by Linus Torvalds +# +# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin +# +# "make install" script for i386 architecture +# +# Arguments: +# $1 - kernel version +# $2 - kernel image file +# $3 - kernel map file +# $4 - default install path (blank if root directory) +# + +# User may have a custom install script + +if [ -x ~/bin/installkernel ]; then exec ~/bin/installkernel "$@"; fi +if [ -x /sbin/installkernel ]; then exec /sbin/installkernel "$@"; fi + +# Default install - same as make zlilo + +if [ -f $4/vmlinuz ]; then + mv $4/vmlinuz $4/vmlinuz.old +fi + +if [ -f $4/System.map ]; then + mv $4/System.map $4/System.old +fi + +cat $2 > $4/vmlinuz +cp $3 $4/System.map + +if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/boot/setup.S linux.ac/arch/x86_64/boot/setup.S --- linux.vanilla/arch/x86_64/boot/setup.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/boot/setup.S Wed Oct 10 01:47:38 2001 @@ -0,0 +1,1039 @@ +/* + * setup.S Copyright (C) 1991, 1992 Linus Torvalds + * + * setup.s is responsible for getting the system data from the BIOS, + * and putting them into the appropriate places in system memory. + * both setup.s and system has been loaded by the bootblock. + * + * This code asks the bios for memory/disk/other parameters, and + * puts them in a "safe" place: 0x90000-0x901FF, ie where the + * boot-block used to be. It is then up to the protected mode + * system to read them from there before the area is overwritten + * for buffer-blocks. + * + * Move PS/2 aux init code to psaux.c + * (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92 + * + * some changes and additional features by Christoph Niemann, + * March 1993/June 1994 (Christoph.Niemann@linux.org) + * + * add APM BIOS checking by Stephen Rothwell, May 1994 + * (sfr@canb.auug.org.au) + * + * High load stuff, initrd support and position independency + * by Hans Lermen & Werner Almesberger, February 1996 + * , + * + * Video handling moved to video.S by Martin Mares, March 1996 + * + * + * Extended memory detection scheme retwiddled by orc@pell.chi.il.us (david + * parsons) to avoid loadlin confusion, July 1997 + * + * Transcribed from Intel (as86) -> AT&T (gas) by Chris Noe, May 1999. + * + * + * Fix to work around buggy BIOSes which dont use carry bit correctly + * and/or report extended memory in CX/DX for e801h memory size detection + * call. As a result the kernel got wrong figures. The int15/e801h docs + * from Ralf Brown interrupt list seem to indicate AX/BX should be used + * anyway. So to avoid breaking many machines (presumably there was a reason + * to orginally use CX/DX instead of AX/BX), we do a kludge to see + * if CX/DX have been changed in the e801 call and if so use AX/BX . + * Michael Miller, April 2001 + * + */ + +#include +#include +#include +#include +#include +#include + +/* Signature words to ensure LILO loaded us right */ +#define SIG1 0xAA55 +#define SIG2 0x5A5A + +INITSEG = DEF_INITSEG # 0x9000, we move boot here, out of the way +SYSSEG = DEF_SYSSEG # 0x1000, system loaded at 0x10000 (65536). +SETUPSEG = DEF_SETUPSEG # 0x9020, this is the current segment + # ... and the former contents of CS + +DELTA_INITSEG = SETUPSEG - INITSEG # 0x0020 + +.code16 +.globl begtext, begdata, begbss, endtext, enddata, endbss + +.text +begtext: +.data +begdata: +.bss +begbss: +.text + +start: + jmp trampoline + +# This is the setup header, and it must start at %cs:2 (old 0x9020:2) + + .ascii "HdrS" # header signature + .word 0x0202 # header version number (>= 0x0105) + # or else old loadlin-1.5 will fail) +realmode_swtch: .word 0, 0 # default_switch, SETUPSEG +start_sys_seg: .word SYSSEG + .word kernel_version # pointing to kernel version string + # above section of header is compatible + # with loadlin-1.5 (header v1.5). Don't + # change it. + +type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin, + # Bootlin, SYSLX, bootsect...) + # See Documentation/i386/boot.txt for + # assigned ids + +# flags, unused bits must be zero (RFU) bit within loadflags +loadflags: +LOADED_HIGH = 1 # If set, the kernel is loaded high +CAN_USE_HEAP = 0x80 # If set, the loader also has set + # heap_end_ptr to tell how much + # space behind setup.S can be used for + # heap purposes. + # Only the loader knows what is free +#ifndef __BIG_KERNEL__ + .byte 0 +#else + .byte LOADED_HIGH +#endif + +setup_move_size: .word 0x8000 # size to move, when setup is not + # loaded at 0x90000. We will move setup + # to 0x90000 then just before jumping + # into the kernel. However, only the + # loader knows how much data behind + # us also needs to be loaded. + +code32_start: # here loaders can put a different + # start address for 32-bit code. +#ifndef __BIG_KERNEL__ + .long 0x1000 # 0x1000 = default for zImage +#else + .long 0x100000 # 0x100000 = default for big kernel +#endif + +ramdisk_image: .long 0 # address of loaded ramdisk image + # Here the loader puts the 32-bit + # address where it loaded the image. + # This only will be read by the kernel. + +ramdisk_size: .long 0 # its size in bytes + +bootsect_kludge: + .word bootsect_helper, SETUPSEG + +heap_end_ptr: .word modelist+1024 # (Header version 0x0201 or later) + # space from here (exclusive) down to + # end of setup code can be used by setup + # for local heap purposes. + +pad1: .word 0 +cmd_line_ptr: .long 0 # (Header version 0x0202 or later) + # If nonzero, a 32-bit pointer + # to the kernel command line. + # The command line should be + # located between the start of + # setup and the end of low + # memory (0xa0000), or it may + # get overwritten before it + # gets read. If this field is + # used, there is no longer + # anything magical about the + # 0x90000 segment; the setup + # can be located anywhere in + # low memory 0x10000 or higher. + +trampoline: call start_of_setup + .space 1024 +# End of setup header ##################################################### + +start_of_setup: +# Bootlin depends on this being done early + movw $0x01500, %ax + movb $0x81, %dl + int $0x13 + +#ifdef SAFE_RESET_DISK_CONTROLLER +# Reset the disk controller. + movw $0x0000, %ax + movb $0x80, %dl + int $0x13 +#endif + +# Set %ds = %cs, we know that SETUPSEG = %cs at this point + movw %cs, %ax # aka SETUPSEG + movw %ax, %ds +# Check signature at end of setup + cmpw $SIG1, setup_sig1 + jne bad_sig + + cmpw $SIG2, setup_sig2 + jne bad_sig + + jmp good_sig1 + +# Routine to print asciiz string at ds:si +prtstr: + lodsb + andb %al, %al + jz fin + + call prtchr + jmp prtstr + +fin: ret + +# Space printing +prtsp2: call prtspc # Print double space +prtspc: movb $0x20, %al # Print single space (note: fall-thru) + +# Part of above routine, this one just prints ascii al +prtchr: pushw %ax + pushw %cx + xorb %bh, %bh + movw $0x01, %cx + movb $0x0e, %ah + int $0x10 + popw %cx + popw %ax + ret + +beep: movb $0x07, %al + jmp prtchr + +no_sig_mess: .string "No setup signature found ..." + +good_sig1: + jmp good_sig + +# We now have to find the rest of the setup code/data +bad_sig: + movw %cs, %ax # SETUPSEG + subw $DELTA_INITSEG, %ax # INITSEG + movw %ax, %ds + xorb %bh, %bh + movb (497), %bl # get setup sect from bootsect + subw $4, %bx # LILO loads 4 sectors of setup + shlw $8, %bx # convert to words (1sect=2^8 words) + movw %bx, %cx + shrw $3, %bx # convert to segment + addw $SYSSEG, %bx + movw %bx, %cs:start_sys_seg +# Move rest of setup code/data to here + movw $2048, %di # four sectors loaded by LILO + subw %si, %si + pushw %cs + popw %es + movw $SYSSEG, %ax + movw %ax, %ds + rep + movsw + movw %cs, %ax # aka SETUPSEG + movw %ax, %ds + cmpw $SIG1, setup_sig1 + jne no_sig + + cmpw $SIG2, setup_sig2 + jne no_sig + + jmp good_sig + +no_sig: + lea no_sig_mess, %si + call prtstr + +no_sig_loop: + hlt + jmp no_sig_loop + +good_sig: + movw %cs, %ax # aka SETUPSEG + subw $DELTA_INITSEG, %ax # aka INITSEG + movw %ax, %ds +# Check if an old loader tries to load a big-kernel + testb $LOADED_HIGH, %cs:loadflags # Do we have a big kernel? + jz loader_ok # No, no danger for old loaders. + + cmpb $0, %cs:type_of_loader # Do we have a loader that + # can deal with us? + jnz loader_ok # Yes, continue. + + pushw %cs # No, we have an old loader, + popw %ds # die. + lea loader_panic_mess, %si + call prtstr + + jmp no_sig_loop + +loader_panic_mess: .string "Wrong loader, giving up..." + +loader_ok: +# Get memory size (extended mem, kB) + + xorl %eax, %eax + movl %eax, (0x1e0) +#ifndef STANDARD_MEMORY_BIOS_CALL + movb %al, (E820NR) +# Try three different memory detection schemes. First, try +# e820h, which lets us assemble a memory map, then try e801h, +# which returns a 32-bit memory size, and finally 88h, which +# returns 0-64m + +# method E820H: +# the memory map from hell. e820h returns memory classified into +# a whole bunch of different types, and allows memory holes and +# everything. We scan through this memory map and build a list +# of the first 32 memory areas, which we return at [E820MAP]. +# This is documented at http://www.teleport.com/~acpi/acpihtml/topic245.htm + +#define SMAP 0x534d4150 + +meme820: + xorl %ebx, %ebx # continuation counter + movw $E820MAP, %di # point into the whitelist + # so we can have the bios + # directly write into it. + +jmpe820: + movl $0x0000e820, %eax # e820, upper word zeroed + movl $SMAP, %edx # ascii 'SMAP' + movl $20, %ecx # size of the e820rec + pushw %ds # data record. + popw %es + int $0x15 # make the call + jc bail820 # fall to e801 if it fails + + cmpl $SMAP, %eax # check the return is `SMAP' + jne bail820 # fall to e801 if it fails + +# cmpl $1, 16(%di) # is this usable memory? +# jne again820 + + # If this is usable memory, we save it by simply advancing %di by + # sizeof(e820rec). + # +good820: + movb (E820NR), %al # up to 32 entries + cmpb $E820MAX, %al + jnl bail820 + + incb (E820NR) + movw %di, %ax + addw $20, %ax + movw %ax, %di +again820: + cmpl $0, %ebx # check to see if + jne jmpe820 # %ebx is set to EOF +bail820: + + +# method E801H: +# memory size is in 1k chunksizes, to avoid confusing loadlin. +# we store the 0xe801 memory size in a completely different place, +# because it will most likely be longer than 16 bits. +# (use 1e0 because that's what Larry Augustine uses in his +# alternative new memory detection scheme, and it's sensible +# to write everything into the same place.) + +meme801: + stc # fix to work around buggy + xorw %cx,%cx # BIOSes which dont clear/set + xorw %dx,%dx # carry on pass/error of + # e801h memory size call + # or merely pass cx,dx though + # without changing them. + movw $0xe801, %ax + int $0x15 + jc mem88 + + cmpw $0x0, %cx # Kludge to handle BIOSes + jne e801usecxdx # which report their extended + cmpw $0x0, %dx # memory in AX/BX rather than + jne e801usecxdx # CX/DX. The spec I have read + movw %ax, %cx # seems to indicate AX/BX + movw %bx, %dx # are more reasonable anyway... + +e801usecxdx: + andl $0xffff, %edx # clear sign extend + shll $6, %edx # and go from 64k to 1k chunks + movl %edx, (0x1e0) # store extended memory size + andl $0xffff, %ecx # clear sign extend + addl %ecx, (0x1e0) # and add lower memory into + # total size. + +# Ye Olde Traditional Methode. Returns the memory size (up to 16mb or +# 64mb, depending on the bios) in ax. +mem88: + +#endif + movb $0x88, %ah + int $0x15 + movw %ax, (2) + +# Set the keyboard repeat rate to the max + movw $0x0305, %ax + xorw %bx, %bx + int $0x16 + +# Check for video adapter and its parameters and allow the +# user to browse video modes. + call video # NOTE: we need %ds pointing + # to bootsector + +# Get hd0 data... + xorw %ax, %ax + movw %ax, %ds + ldsw (4 * 0x41), %si + movw %cs, %ax # aka SETUPSEG + subw $DELTA_INITSEG, %ax # aka INITSEG + pushw %ax + movw %ax, %es + movw $0x0080, %di + movw $0x10, %cx + pushw %cx + cld + rep + movsb +# Get hd1 data... + xorw %ax, %ax + movw %ax, %ds + ldsw (4 * 0x46), %si + popw %cx + popw %es + movw $0x0090, %di + rep + movsb +# Check that there IS a hd1 :-) + movw $0x01500, %ax + movb $0x81, %dl + int $0x13 + jc no_disk1 + + cmpb $3, %ah + je is_disk1 + +no_disk1: + movw %cs, %ax # aka SETUPSEG + subw $DELTA_INITSEG, %ax # aka INITSEG + movw %ax, %es + movw $0x0090, %di + movw $0x10, %cx + xorw %ax, %ax + cld + rep + stosb +is_disk1: +# check for Micro Channel (MCA) bus + movw %cs, %ax # aka SETUPSEG + subw $DELTA_INITSEG, %ax # aka INITSEG + movw %ax, %ds + xorw %ax, %ax + movw %ax, (0xa0) # set table length to 0 + movb $0xc0, %ah + stc + int $0x15 # moves feature table to es:bx + jc no_mca + + pushw %ds + movw %es, %ax + movw %ax, %ds + movw %cs, %ax # aka SETUPSEG + subw $DELTA_INITSEG, %ax # aka INITSEG + movw %ax, %es + movw %bx, %si + movw $0xa0, %di + movw (%si), %cx + addw $2, %cx # table length is a short + cmpw $0x10, %cx + jc sysdesc_ok + + movw $0x10, %cx # we keep only first 16 bytes +sysdesc_ok: + rep + movsb + popw %ds +no_mca: +# Check for PS/2 pointing device + movw %cs, %ax # aka SETUPSEG + subw $DELTA_INITSEG, %ax # aka INITSEG + movw %ax, %ds + movw $0, (0x1ff) # default is no pointing device + int $0x11 # int 0x11: equipment list + testb $0x04, %al # check if mouse installed + jz no_psmouse + + movw $0xAA, (0x1ff) # device present +no_psmouse: + +#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) +# Then check for an APM BIOS... + # %ds points to the bootsector + movw $0, 0x40 # version = 0 means no APM BIOS + movw $0x05300, %ax # APM BIOS installation check + xorw %bx, %bx + int $0x15 + jc done_apm_bios # Nope, no APM BIOS + + cmpw $0x0504d, %bx # Check for "PM" signature + jne done_apm_bios # No signature, no APM BIOS + + andw $0x02, %cx # Is 32 bit supported? + je done_apm_bios # No 32-bit, no (good) APM BIOS + + movw $0x05304, %ax # Disconnect first just in case + xorw %bx, %bx + int $0x15 # ignore return code + movw $0x05303, %ax # 32 bit connect + xorl %ebx, %ebx + xorw %cx, %cx # paranoia :-) + xorw %dx, %dx # ... + xorl %esi, %esi # ... + xorw %di, %di # ... + int $0x15 + jc no_32_apm_bios # Ack, error. + + movw %ax, (66) # BIOS code segment + movl %ebx, (68) # BIOS entry point offset + movw %cx, (72) # BIOS 16 bit code segment + movw %dx, (74) # BIOS data segment + movl %esi, (78) # BIOS code segment lengths + movw %di, (82) # BIOS data segment length +# Redo the installation check as the 32 bit connect +# modifies the flags returned on some BIOSs + movw $0x05300, %ax # APM BIOS installation check + xorw %bx, %bx + xorw %cx, %cx # paranoia + int $0x15 + jc apm_disconnect # error -> shouldn't happen + + cmpw $0x0504d, %bx # check for "PM" signature + jne apm_disconnect # no sig -> shouldn't happen + + movw %ax, (64) # record the APM BIOS version + movw %cx, (76) # and flags + jmp done_apm_bios + +apm_disconnect: # Tidy up + movw $0x05304, %ax # Disconnect + xorw %bx, %bx + int $0x15 # ignore return code + + jmp done_apm_bios + +no_32_apm_bios: + andw $0xfffd, (76) # remove 32 bit support bit +done_apm_bios: +#endif + +# Now we want to move to protected mode ... + cmpw $0, %cs:realmode_swtch + jz rmodeswtch_normal + + lcall %cs:realmode_swtch + + jmp rmodeswtch_end + +rmodeswtch_normal: + pushw %cs + call default_switch + +rmodeswtch_end: +# we get the code32 start address and modify the below 'jmpi' +# (loader may have changed it) + movl %cs:code32_start, %eax + movl %eax, %cs:code32 + +# Now we move the system to its rightful place ... but we check if we have a +# big-kernel. In that case we *must* not move it ... + testb $LOADED_HIGH, %cs:loadflags + jz do_move0 # .. then we have a normal low + # loaded zImage + # .. or else we have a high + # loaded bzImage + jmp end_move # ... and we skip moving + +do_move0: + movw $0x100, %ax # start of destination segment + movw %cs, %bp # aka SETUPSEG + subw $DELTA_INITSEG, %bp # aka INITSEG + movw %cs:start_sys_seg, %bx # start of source segment + cld +do_move: + movw %ax, %es # destination segment + incb %ah # instead of add ax,#0x100 + movw %bx, %ds # source segment + addw $0x100, %bx + subw %di, %di + subw %si, %si + movw $0x800, %cx + rep + movsw + cmpw %bp, %bx # assume start_sys_seg > 0x200, + # so we will perhaps read one + # page more than needed, but + # never overwrite INITSEG + # because destination is a + # minimum one page below source + jb do_move + +end_move: +# then we load the segment descriptors + movw %cs, %ax # aka SETUPSEG + movw %ax, %ds + +# Check whether we need to be downward compatible with version <=201 + cmpl $0, cmd_line_ptr + jne end_move_self # loader uses version >=202 features + cmpb $0x20, type_of_loader + je end_move_self # bootsect loader, we know of it + +# Boot loader doesnt support boot protocol version 2.02. +# If we have our code not at 0x90000, we need to move it there now. +# We also then need to move the params behind it (commandline) +# Because we would overwrite the code on the current IP, we move +# it in two steps, jumping high after the first one. + movw %cs, %ax + cmpw $SETUPSEG, %ax + je end_move_self + + cli # make sure we really have + # interrupts disabled ! + # because after this the stack + # should not be used + subw $DELTA_INITSEG, %ax # aka INITSEG + movw %ss, %dx + cmpw %ax, %dx + jb move_self_1 + + addw $INITSEG, %dx + subw %ax, %dx # this will go into %ss after + # the move +move_self_1: + movw %ax, %ds + movw $INITSEG, %ax # real INITSEG + movw %ax, %es + movw %cs:setup_move_size, %cx + std # we have to move up, so we use + # direction down because the + # areas may overlap + movw %cx, %di + decw %di + movw %di, %si + subw $move_self_here+0x200, %cx + rep + movsb + ljmp $SETUPSEG, $move_self_here + +move_self_here: + movw $move_self_here+0x200, %cx + rep + movsb + movw $SETUPSEG, %ax + movw %ax, %ds + movw %dx, %ss +end_move_self: # now we are at the right place + +# +# Enable A20. This is at the very best an annoying procedure. +# A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin. +# + +A20_TEST_LOOPS = 32 # Iterations per wait +A20_ENABLE_LOOPS = 255 # Total loops to try + + +a20_try_loop: + + # First, see if we are on a system with no A20 gate. +a20_none: + call a20_test + jnz a20_done + + # Next, try the BIOS (INT 0x15, AX=0x2401) +a20_bios: + movw $0x2401, %ax + pushfl # Be paranoid about flags + int $0x15 + popfl + + call a20_test + jnz a20_done + + # Try enabling A20 through the keyboard controller +a20_kbc: + call empty_8042 + + call a20_test # Just in case the BIOS worked + jnz a20_done # but had a delayed reaction. + + movb $0xD1, %al # command write + outb %al, $0x64 + call empty_8042 + + movb $0xDF, %al # A20 on + outb %al, $0x60 + call empty_8042 + + # Wait until a20 really *is* enabled; it can take a fair amount of + # time on certain systems; Toshiba Tecras are known to have this + # problem. +a20_kbc_wait: + xorw %cx, %cx +a20_kbc_wait_loop: + call a20_test + jnz a20_done + loop a20_kbc_wait_loop + + # Final attempt: use "configuration port A" +a20_fast: + inb $0x92, %al # Configuration Port A + orb $0x02, %al # "fast A20" version + andb $0xFE, %al # don't accidentally reset + outb %al, $0x92 + + # Wait for configuration port A to take effect +a20_fast_wait: + xorw %cx, %cx +a20_fast_wait_loop: + call a20_test + jnz a20_done + loop a20_fast_wait_loop + + # A20 is still not responding. Try frobbing it again. + # + decb (a20_tries) + jnz a20_try_loop + + movw $a20_err_msg, %si + call prtstr + +a20_die: + hlt + jmp a20_die + +a20_tries: + .byte A20_ENABLE_LOOPS + +a20_err_msg: + .ascii "linux: fatal error: A20 gate not responding!" + .byte 13, 10, 0 + + # If we get here, all is good +a20_done: + +# set up gdt and idt + lidt idt_48 # load idt with 0,0 + xorl %eax, %eax # Compute gdt_base + movw %ds, %ax # (Convert %ds:gdt to a linear ptr) + shll $4, %eax + addl $gdt, %eax + movl %eax, (gdt_48+2) + lgdt gdt_48 # load gdt with whatever is + # appropriate + +# make sure any possible coprocessor is properly reset.. + xorw %ax, %ax + outb %al, $0xf0 + call delay + + outb %al, $0xf1 + call delay + +# well, that went ok, I hope. Now we mask all interrupts - the rest +# is done in init_IRQ(). + movb $0xFF, %al # mask all interrupts for now + outb %al, $0xA1 + call delay + + movb $0xFB, %al # mask all irq's but irq2 which + outb %al, $0x21 # is cascaded + +# Well, that certainly wasn't fun :-(. Hopefully it works, and we don't +# need no steenking BIOS anyway (except for the initial loading :-). +# The BIOS-routine wants lots of unnecessary data, and it's less +# "interesting" anyway. This is how REAL programmers do it. +# +# Well, now's the time to actually move into protected mode. To make +# things as simple as possible, we do no register set-up or anything, +# we let the gnu-compiled 32-bit programs do that. We just jump to +# absolute address 0x1000 (or the loader supplied one), +# in 32-bit protected mode. +# +# Note that the short jump isn't strictly needed, although there are +# reasons why it might be a good idea. It won't hurt in any case. + movw $1, %ax # protected mode (PE) bit + lmsw %ax # This is it! + jmp flush_instr + +flush_instr: + xorw %bx, %bx # Flag to indicate a boot + xorl %esi, %esi # Pointer to real-mode code + movw %cs, %si + subw $DELTA_INITSEG, %si + shll $4, %esi # Convert to 32-bit pointer +# NOTE: For high loaded big kernels we need a +# jmpi 0x100000,__KERNEL_CS +# +# but we yet haven't reloaded the CS register, so the default size +# of the target offset still is 16 bit. +# However, using an operant prefix (0x66), the CPU will properly +# take our 48 bit far pointer. (INTeL 80386 Programmer's Reference +# Manual, Mixing 16-bit and 32-bit code, page 16-6) + + .byte 0x66, 0xea # prefix + jmpi-opcode +code32: .long 0x1000 # will be set to 0x100000 + # for big kernels + .word __KERNEL_CS + +# Here's a bunch of information about your current kernel.. +kernel_version: .ascii UTS_RELEASE + .ascii " (" + .ascii LINUX_COMPILE_BY + .ascii "@" + .ascii LINUX_COMPILE_HOST + .ascii ") " + .ascii UTS_VERSION + .byte 0 + +# This is the default real mode switch routine. +# to be called just before protected mode transition +default_switch: + cli # no interrupts allowed ! + movb $0x80, %al # disable NMI for bootup + # sequence + outb %al, $0x70 + lret + +# This routine only gets called, if we get loaded by the simple +# bootsect loader _and_ have a bzImage to load. +# Because there is no place left in the 512 bytes of the boot sector, +# we must emigrate to code space here. +bootsect_helper: + cmpw $0, %cs:bootsect_es + jnz bootsect_second + + movb $0x20, %cs:type_of_loader + movw %es, %ax + shrw $4, %ax + movb %ah, %cs:bootsect_src_base+2 + movw %es, %ax + movw %ax, %cs:bootsect_es + subw $SYSSEG, %ax + lret # nothing else to do for now + +bootsect_second: + pushw %cx + pushw %si + pushw %bx + testw %bx, %bx # 64K full? + jne bootsect_ex + + movw $0x8000, %cx # full 64K, INT15 moves words + pushw %cs + popw %es + movw $bootsect_gdt, %si + movw $0x8700, %ax + int $0x15 + jc bootsect_panic # this, if INT15 fails + + movw %cs:bootsect_es, %es # we reset %es to always point + incb %cs:bootsect_dst_base+2 # to 0x10000 +bootsect_ex: + movb %cs:bootsect_dst_base+2, %ah + shlb $4, %ah # we now have the number of + # moved frames in %ax + xorb %al, %al + popw %bx + popw %si + popw %cx + lret + +bootsect_gdt: + .word 0, 0, 0, 0 + .word 0, 0, 0, 0 + +bootsect_src: + .word 0xffff + +bootsect_src_base: + .byte 0x00, 0x00, 0x01 # base = 0x010000 + .byte 0x93 # typbyte + .word 0 # limit16,base24 =0 + +bootsect_dst: + .word 0xffff + +bootsect_dst_base: + .byte 0x00, 0x00, 0x10 # base = 0x100000 + .byte 0x93 # typbyte + .word 0 # limit16,base24 =0 + .word 0, 0, 0, 0 # BIOS CS + .word 0, 0, 0, 0 # BIOS DS + +bootsect_es: + .word 0 + +bootsect_panic: + pushw %cs + popw %ds + cld + leaw bootsect_panic_mess, %si + call prtstr + +bootsect_panic_loop: + jmp bootsect_panic_loop + +bootsect_panic_mess: + .string "INT15 refuses to access high mem, giving up." + + +# This routine tests whether or not A20 is enabled. If so, it +# exits with zf = 0. +# +# The memory address used, 0x200, is the int $0x80 vector, which +# should be safe. + +A20_TEST_ADDR = 4*0x80 + +a20_test: + pushw %cx + pushw %ax + xorw %cx, %cx + movw %cx, %fs # Low memory + decw %cx + movw %cx, %gs # High memory area + movw $A20_TEST_LOOPS, %cx + movw %fs:(A20_TEST_ADDR), %ax + pushw %ax +a20_test_wait: + incw %ax + movw %ax, %fs:(A20_TEST_ADDR) + call delay # Serialize and make delay constant + cmpw %gs:(A20_TEST_ADDR+0x10), %ax + loope a20_test_wait + + popw %fs:(A20_TEST_ADDR) + popw %ax + popw %cx + ret + +# This routine checks that the keyboard command queue is empty +# (after emptying the output buffers) +# +# Some machines have delusions that the keyboard buffer is always full +# with no keyboard attached... +# +# If there is no keyboard controller, we will usually get 0xff +# to all the reads. With each IO taking a microsecond and +# a timeout of 100,000 iterations, this can take about half a +# second ("delay" == outb to port 0x80). That should be ok, +# and should also be plenty of time for a real keyboard controller +# to empty. +# + +empty_8042: + pushl %ecx + movl $100000, %ecx + +empty_8042_loop: + decl %ecx + jz empty_8042_end_loop + + call delay + + inb $0x64, %al # 8042 status port + testb $1, %al # output buffer? + jz no_output + + call delay + inb $0x60, %al # read it + jmp empty_8042_loop + +no_output: + testb $2, %al # is input buffer full? + jnz empty_8042_loop # yes - loop +empty_8042_end_loop: + popl %ecx + ret + +# Read the cmos clock. Return the seconds in al +gettime: + pushw %cx + movb $0x02, %ah + int $0x1a + movb %dh, %al # %dh contains the seconds + andb $0x0f, %al + movb %dh, %ah + movb $0x04, %cl + shrb %cl, %ah + aad + popw %cx + ret + +# Delay is needed after doing I/O +delay: + outb %al,$0x80 + ret + +# Descriptor tables +gdt: + .word 0, 0, 0, 0 # dummy + + .word 0, 0, 0, 0 # unused + + .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) + .word 0 # base address = 0 + .word 0x9A00 # code read/exec + .word 0x00CF # granularity = 4096, 386 + # (+5th nibble of limit) + + .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) + .word 0 # base address = 0 + .word 0x9200 # data read/write + .word 0x00CF # granularity = 4096, 386 + # (+5th nibble of limit) +# this is 64bit descriptor for code + .word 0xFFFF + .word 0 + .word 0x9A00 # code read/exec + .word 0x00AF # as above, but it is long mode and with D=0 + # it does not seem to do the trick. + +idt_48: + .word 0 # idt limit = 0 + .word 0, 0 # idt base = 0L +gdt_48: + .word 0x8000 # gdt limit=2048, + # 256 GDT entries + + .word 0, 0 # gdt base (filled in later) + +# Include video setup & detection code + +#include "video.S" + +# Setup signature -- must be last +setup_sig1: .word SIG1 +setup_sig2: .word SIG2 + +# After this point, there is some free space which is used by the video mode +# handling code to store the temporary mode table (not used by the kernel). + +modelist: + +.text +endtext: +.data +enddata: +.bss +endbss: diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/boot/tools/build.c linux.ac/arch/x86_64/boot/tools/build.c --- linux.vanilla/arch/x86_64/boot/tools/build.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/boot/tools/build.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,189 @@ +/* + * $Id: build.c,v 1.3 2001/06/26 15:14:50 pavel Exp $ + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1997 Martin Mares + */ + +/* + * This file builds a disk-image from three different files: + * + * - bootsect: exactly 512 bytes of 8086 machine code, loads the rest + * - setup: 8086 machine code, sets up system parm + * - system: 80386 code for actual system + * + * It does some checking that all files are of the correct type, and + * just writes the result to stdout, removing headers and padding to + * the right amount. It also writes some system data to stderr. + */ + +/* + * Changes by tytso to allow root device specification + * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 + * Cross compiling fixes by Gertjan van Wingerde, July 1996 + * Rewritten by Martin Mares, April 1997 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long u32; + +#define DEFAULT_MAJOR_ROOT 0 +#define DEFAULT_MINOR_ROOT 0 + +/* Minimal number of setup sectors (see also bootsect.S) */ +#define SETUP_SECTS 4 + +byte buf[1024]; +int fd; +int is_big_kernel; + +void die(const char * str, ...) +{ + va_list args; + va_start(args, str); + vfprintf(stderr, str, args); + fputc('\n', stderr); + exit(1); +} + +void file_open(const char *name) +{ + if ((fd = open(name, O_RDONLY, 0)) < 0) + die("Unable to open `%s': %m", name); +} + +void usage(void) +{ + die("Usage: build [-b] bootsect setup system [rootdev] [> image]"); +} + +int main(int argc, char ** argv) +{ + unsigned int i, c, sz, setup_sectors; + u32 sys_size; + byte major_root, minor_root; + struct stat sb; + + if (argc > 2 && !strcmp(argv[1], "-b")) + { + is_big_kernel = 1; + argc--, argv++; + } + if ((argc < 4) || (argc > 5)) + usage(); + if (argc > 4) { + if (!strcmp(argv[4], "CURRENT")) { + if (stat("/", &sb)) { + perror("/"); + die("Couldn't stat /"); + } + major_root = major(sb.st_dev); + minor_root = minor(sb.st_dev); + } else if (strcmp(argv[4], "FLOPPY")) { + if (stat(argv[4], &sb)) { + perror(argv[4]); + die("Couldn't stat root device."); + } + major_root = major(sb.st_rdev); + minor_root = minor(sb.st_rdev); + } else { + major_root = 0; + minor_root = 0; + } + } else { + major_root = DEFAULT_MAJOR_ROOT; + minor_root = DEFAULT_MINOR_ROOT; + } + fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root); + + file_open(argv[1]); + i = read(fd, buf, sizeof(buf)); + fprintf(stderr,"Boot sector %d bytes.\n",i); + if (i != 512) + die("Boot block must be exactly 512 bytes"); + if (buf[510] != 0x55 || buf[511] != 0xaa) + die("Boot block hasn't got boot flag (0xAA55)"); + buf[508] = minor_root; + buf[509] = major_root; + if (write(1, buf, 512) != 512) + die("Write call failed"); + close (fd); + + file_open(argv[2]); /* Copy the setup code */ + for (i=0 ; (c=read(fd, buf, sizeof(buf)))>0 ; i+=c ) + if (write(1, buf, c) != c) + die("Write call failed"); + if (c != 0) + die("read-error on `setup'"); + close (fd); + + setup_sectors = (i + 511) / 512; /* Pad unused space with zeros */ + /* for compatibility with ancient versions of LILO. */ + if (setup_sectors < SETUP_SECTS) + setup_sectors = SETUP_SECTS; + fprintf(stderr, "Setup is %d bytes.\n", i); + memset(buf, 0, sizeof(buf)); + while (i < setup_sectors * 512) { + c = setup_sectors * 512 - i; + if (c > sizeof(buf)) + c = sizeof(buf); + if (write(1, buf, c) != c) + die("Write call failed"); + i += c; + } + + file_open(argv[3]); + if (fstat (fd, &sb)) + die("Unable to stat `%s': %m", argv[3]); + sz = sb.st_size; + fprintf (stderr, "System is %d kB\n", sz/1024); + sys_size = (sz + 15) / 16; + /* 0x28000*16 = 2.5 MB, conservative estimate for the current maximum */ + if (sys_size > (is_big_kernel ? 0x28000 : DEF_SYSSIZE)) + die("System is too big. Try using %smodules.", + is_big_kernel ? "" : "bzImage or "); + if (sys_size > 0xefff) + fprintf(stderr,"warning: kernel is too big for standalone boot " + "from floppy\n"); + while (sz > 0) { + int l, n; + + l = (sz > sizeof(buf)) ? sizeof(buf) : sz; + if ((n=read(fd, buf, l)) != l) { + if (n < 0) + die("Error reading %s: %m", argv[3]); + else + die("%s: Unexpected EOF", argv[3]); + } + if (write(1, buf, l) != l) + die("Write failed"); + sz -= l; + } + close(fd); + + if (lseek(1, 497, SEEK_SET) != 497) /* Write sizes to the bootsector */ + die("Output: seek failed"); + buf[0] = setup_sectors; + if (write(1, buf, 1) != 1) + die("Write of setup sector count failed"); + if (lseek(1, 500, SEEK_SET) != 500) + die("Output: seek failed"); + buf[0] = (sys_size & 0xff); + buf[1] = ((sys_size >> 8) & 0xff); + if (write(1, buf, 2) != 2) + die("Write of image length failed"); + + return 0; /* Everything is OK */ +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/boot/video.S linux.ac/arch/x86_64/boot/video.S --- linux.vanilla/arch/x86_64/boot/video.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/boot/video.S Wed Oct 10 01:47:38 2001 @@ -0,0 +1,1934 @@ +/* video.S + * + * Display adapter & video mode setup, version 2.13 (14-May-99) + * + * Copyright (C) 1995 -- 1998 Martin Mares + * Based on the original setup.S code (C) Linus Torvalds and Mats Anderson + * + * Rewritten to use GNU 'as' by Chris Noe May 1999 + * + * For further information, look at Documentation/svga.txt. + * + */ + +#include /* for CONFIG_VIDEO_* */ + +/* Enable autodetection of SVGA adapters and modes. */ +#undef CONFIG_VIDEO_SVGA + +/* Enable autodetection of VESA modes */ +#define CONFIG_VIDEO_VESA + +/* Enable compacting of mode table */ +#define CONFIG_VIDEO_COMPACT + +/* Retain screen contents when switching modes */ +#define CONFIG_VIDEO_RETAIN + +/* Enable local mode list */ +#undef CONFIG_VIDEO_LOCAL + +/* Force 400 scan lines for standard modes (hack to fix bad BIOS behaviour */ +#undef CONFIG_VIDEO_400_HACK + +/* Hack that lets you force specific BIOS mode ID and specific dimensions */ +#undef CONFIG_VIDEO_GFX_HACK +#define VIDEO_GFX_BIOS_AX 0x4f02 /* 800x600 on ThinkPad */ +#define VIDEO_GFX_BIOS_BX 0x0102 +#define VIDEO_GFX_DUMMY_RESOLUTION 0x6425 /* 100x37 */ + +/* This code uses an extended set of video mode numbers. These include: + * Aliases for standard modes + * NORMAL_VGA (-1) + * EXTENDED_VGA (-2) + * ASK_VGA (-3) + * Video modes numbered by menu position -- NOT RECOMMENDED because of lack + * of compatibility when extending the table. These are between 0x00 and 0xff. + */ +#define VIDEO_FIRST_MENU 0x0000 + +/* Standard BIOS video modes (BIOS number + 0x0100) */ +#define VIDEO_FIRST_BIOS 0x0100 + +/* VESA BIOS video modes (VESA number + 0x0200) */ +#define VIDEO_FIRST_VESA 0x0200 + +/* Video7 special modes (BIOS number + 0x0900) */ +#define VIDEO_FIRST_V7 0x0900 + +/* Special video modes */ +#define VIDEO_FIRST_SPECIAL 0x0f00 +#define VIDEO_80x25 0x0f00 +#define VIDEO_8POINT 0x0f01 +#define VIDEO_80x43 0x0f02 +#define VIDEO_80x28 0x0f03 +#define VIDEO_CURRENT_MODE 0x0f04 +#define VIDEO_80x30 0x0f05 +#define VIDEO_80x34 0x0f06 +#define VIDEO_80x60 0x0f07 +#define VIDEO_GFX_HACK 0x0f08 +#define VIDEO_LAST_SPECIAL 0x0f09 + +/* Video modes given by resolution */ +#define VIDEO_FIRST_RESOLUTION 0x1000 + +/* The "recalculate timings" flag */ +#define VIDEO_RECALC 0x8000 + +/* Positions of various video parameters passed to the kernel */ +/* (see also include/linux/tty.h) */ +#define PARAM_CURSOR_POS 0x00 +#define PARAM_VIDEO_PAGE 0x04 +#define PARAM_VIDEO_MODE 0x06 +#define PARAM_VIDEO_COLS 0x07 +#define PARAM_VIDEO_EGA_BX 0x0a +#define PARAM_VIDEO_LINES 0x0e +#define PARAM_HAVE_VGA 0x0f +#define PARAM_FONT_POINTS 0x10 + +#define PARAM_LFB_WIDTH 0x12 +#define PARAM_LFB_HEIGHT 0x14 +#define PARAM_LFB_DEPTH 0x16 +#define PARAM_LFB_BASE 0x18 +#define PARAM_LFB_SIZE 0x1c +#define PARAM_LFB_LINELENGTH 0x24 +#define PARAM_LFB_COLORS 0x26 +#define PARAM_VESAPM_SEG 0x2e +#define PARAM_VESAPM_OFF 0x30 +#define PARAM_LFB_PAGES 0x32 + + +/* Define DO_STORE according to CONFIG_VIDEO_RETAIN */ +#ifdef CONFIG_VIDEO_RETAIN +#define DO_STORE call store_screen +#else +#define DO_STORE +#endif /* CONFIG_VIDEO_RETAIN */ + +# This is the main entry point called by setup.S +# %ds *must* be pointing to the bootsector +video: pushw %ds # We use different segments + pushw %ds # FS contains original DS + popw %fs + pushw %cs # DS is equal to CS + popw %ds + pushw %cs # ES is equal to CS + popw %es + xorw %ax, %ax + movw %ax, %gs # GS is zero + cld + call basic_detect # Basic adapter type testing (EGA/VGA/MDA/CGA) +#ifdef CONFIG_VIDEO_SELECT + movw %fs:(0x01fa), %ax # User selected video mode + cmpw $ASK_VGA, %ax # Bring up the menu + jz vid2 + + call mode_set # Set the mode + jc vid1 + + leaw badmdt, %si # Invalid mode ID + call prtstr +vid2: call mode_menu +vid1: +#ifdef CONFIG_VIDEO_RETAIN + call restore_screen # Restore screen contents +#endif /* CONFIG_VIDEO_RETAIN */ +#endif /* CONFIG_VIDEO_SELECT */ + call mode_params # Store mode parameters + popw %ds # Restore original DS + ret + +# Detect if we have CGA, MDA, EGA or VGA and pass it to the kernel. +basic_detect: + movb $0, %fs:(PARAM_HAVE_VGA) + movb $0x12, %ah # Check EGA/VGA + movb $0x10, %bl + int $0x10 + movw %bx, %fs:(PARAM_VIDEO_EGA_BX) # Identifies EGA to the kernel + cmpb $0x10, %bl # No, it's a CGA/MDA/HGA card. + je basret + + incb adapter + movw $0x1a00, %ax # Check EGA or VGA? + int $0x10 + cmpb $0x1a, %al # 1a means VGA... + jne basret # anything else is EGA. + + incb %fs:(PARAM_HAVE_VGA) # We've detected a VGA + incb adapter +basret: ret + +# Store the video mode parameters for later usage by the kernel. +# This is done by asking the BIOS except for the rows/columns +# parameters in the default 80x25 mode -- these are set directly, +# because some very obscure BIOSes supply insane values. +mode_params: +#ifdef CONFIG_VIDEO_SELECT + cmpb $0, graphic_mode + jnz mopar_gr +#endif + movb $0x03, %ah # Read cursor position + xorb %bh, %bh + int $0x10 + movw %dx, %fs:(PARAM_CURSOR_POS) + movb $0x0f, %ah # Read page/mode/width + int $0x10 + movw %bx, %fs:(PARAM_VIDEO_PAGE) + movw %ax, %fs:(PARAM_VIDEO_MODE) # Video mode and screen width + cmpb $0x7, %al # MDA/HGA => segment differs + jnz mopar0 + + movw $0xb000, video_segment +mopar0: movw %gs:(0x485), %ax # Font size + movw %ax, %fs:(PARAM_FONT_POINTS) # (valid only on EGA/VGA) + movw force_size, %ax # Forced size? + orw %ax, %ax + jz mopar1 + + movb %ah, %fs:(PARAM_VIDEO_COLS) + movb %al, %fs:(PARAM_VIDEO_LINES) + ret + +mopar1: movb $25, %al + cmpb $0, adapter # If we are on CGA/MDA/HGA, the + jz mopar2 # screen must have 25 lines. + + movb %gs:(0x484), %al # On EGA/VGA, use the EGA+ BIOS + incb %al # location of max lines. +mopar2: movb %al, %fs:(PARAM_VIDEO_LINES) + ret + +#ifdef CONFIG_VIDEO_SELECT +# Fetching of VESA frame buffer parameters +mopar_gr: + leaw modelist+1024, %di + movb $0x23, %fs:(PARAM_HAVE_VGA) + movw 16(%di), %ax + movw %ax, %fs:(PARAM_LFB_LINELENGTH) + movw 18(%di), %ax + movw %ax, %fs:(PARAM_LFB_WIDTH) + movw 20(%di), %ax + movw %ax, %fs:(PARAM_LFB_HEIGHT) + movb 25(%di), %al + movb $0, %ah + movw %ax, %fs:(PARAM_LFB_DEPTH) + movb 29(%di), %al + movb $0, %ah + movw %ax, %fs:(PARAM_LFB_PAGES) + movl 40(%di), %eax + movl %eax, %fs:(PARAM_LFB_BASE) + movl 31(%di), %eax + movl %eax, %fs:(PARAM_LFB_COLORS) + movl 35(%di), %eax + movl %eax, %fs:(PARAM_LFB_COLORS+4) + +# get video mem size + leaw modelist+1024, %di + movw $0x4f00, %ax + int $0x10 + xorl %eax, %eax + movw 18(%di), %ax + movl %eax, %fs:(PARAM_LFB_SIZE) +# get protected mode interface informations + movw $0x4f0a, %ax + xorw %bx, %bx + xorw %di, %di + int $0x10 + cmp $0x004f, %ax + jnz no_pm + + movw %es, %fs:(PARAM_VESAPM_SEG) + movw %di, %fs:(PARAM_VESAPM_OFF) +no_pm: ret + +# The video mode menu +mode_menu: + leaw keymsg, %si # "Return/Space/Timeout" message + call prtstr + call flush +nokey: call getkt + + cmpb $0x0d, %al # ENTER ? + je listm # yes - manual mode selection + + cmpb $0x20, %al # SPACE ? + je defmd1 # no - repeat + + call beep + jmp nokey + +defmd1: ret # No mode chosen? Default 80x25 + +listm: call mode_table # List mode table +listm0: leaw name_bann, %si # Print adapter name + call prtstr + movw card_name, %si + orw %si, %si + jnz an2 + + movb adapter, %al + leaw old_name, %si + orb %al, %al + jz an1 + + leaw ega_name, %si + decb %al + jz an1 + + leaw vga_name, %si + jmp an1 + +an2: call prtstr + leaw svga_name, %si +an1: call prtstr + leaw listhdr, %si # Table header + call prtstr + movb $0x30, %dl # DL holds mode number + leaw modelist, %si +lm1: cmpw $ASK_VGA, (%si) # End? + jz lm2 + + movb %dl, %al # Menu selection number + call prtchr + call prtsp2 + lodsw + call prthw # Mode ID + call prtsp2 + movb 0x1(%si), %al + call prtdec # Rows + movb $0x78, %al # the letter 'x' + call prtchr + lodsw + call prtdec # Columns + movb $0x0d, %al # New line + call prtchr + movb $0x0a, %al + call prtchr + incb %dl # Next character + cmpb $0x3a, %dl + jnz lm1 + + movb $0x61, %dl + jmp lm1 + +lm2: leaw prompt, %si # Mode prompt + call prtstr + leaw edit_buf, %di # Editor buffer +lm3: call getkey + cmpb $0x0d, %al # Enter? + jz lment + + cmpb $0x08, %al # Backspace? + jz lmbs + + cmpb $0x20, %al # Printable? + jc lm3 + + cmpw $edit_buf+4, %di # Enough space? + jz lm3 + + stosb + call prtchr + jmp lm3 + +lmbs: cmpw $edit_buf, %di # Backspace + jz lm3 + + decw %di + movb $0x08, %al + call prtchr + call prtspc + movb $0x08, %al + call prtchr + jmp lm3 + +lment: movb $0, (%di) + leaw crlft, %si + call prtstr + leaw edit_buf, %si + cmpb $0, (%si) # Empty string = default mode + jz lmdef + + cmpb $0, 1(%si) # One character = menu selection + jz mnusel + + cmpw $0x6373, (%si) # "scan" => mode scanning + jnz lmhx + + cmpw $0x6e61, 2(%si) + jz lmscan + +lmhx: xorw %bx, %bx # Else => mode ID in hex +lmhex: lodsb + orb %al, %al + jz lmuse1 + + subb $0x30, %al + jc lmbad + + cmpb $10, %al + jc lmhx1 + + subb $7, %al + andb $0xdf, %al + cmpb $10, %al + jc lmbad + + cmpb $16, %al + jnc lmbad + +lmhx1: shlw $4, %bx + orb %al, %bl + jmp lmhex + +lmuse1: movw %bx, %ax + jmp lmuse + +mnusel: lodsb # Menu selection + xorb %ah, %ah + subb $0x30, %al + jc lmbad + + cmpb $10, %al + jc lmuse + + cmpb $0x61-0x30, %al + jc lmbad + + subb $0x61-0x30-10, %al + cmpb $36, %al + jnc lmbad + +lmuse: call mode_set + jc lmdef + +lmbad: leaw unknt, %si + call prtstr + jmp lm2 +lmscan: cmpb $0, adapter # Scanning only on EGA/VGA + jz lmbad + + movw $0, mt_end # Scanning of modes is + movb $1, scanning # done as new autodetection. + call mode_table + jmp listm0 +lmdef: ret + +# Additional parts of mode_set... (relative jumps, you know) +setv7: # Video7 extended modes + DO_STORE + subb $VIDEO_FIRST_V7>>8, %bh + movw $0x6f05, %ax + int $0x10 + stc + ret + +_setrec: jmp setrec # Ugly... +_set_80x25: jmp set_80x25 + +# Aliases for backward compatibility. +setalias: + movw $VIDEO_80x25, %ax + incw %bx + jz mode_set + + movb $VIDEO_8POINT-VIDEO_FIRST_SPECIAL, %al + incw %bx + jnz setbad # Fall-through! + +# Setting of user mode (AX=mode ID) => CF=success +mode_set: + movw %ax, %bx + cmpb $0xff, %ah + jz setalias + + testb $VIDEO_RECALC>>8, %ah + jnz _setrec + + cmpb $VIDEO_FIRST_RESOLUTION>>8, %ah + jnc setres + + cmpb $VIDEO_FIRST_SPECIAL>>8, %ah + jz setspc + + cmpb $VIDEO_FIRST_V7>>8, %ah + jz setv7 + + cmpb $VIDEO_FIRST_VESA>>8, %ah + jnc check_vesa + + orb %ah, %ah + jz setmenu + + decb %ah + jz setbios + +setbad: clc + movb $0, do_restore # The screen needn't be restored + ret + +setvesa: + DO_STORE + subb $VIDEO_FIRST_VESA>>8, %bh + movw $0x4f02, %ax # VESA BIOS mode set call + int $0x10 + cmpw $0x004f, %ax # AL=4f if implemented + jnz setbad # AH=0 if OK + + stc + ret + +setbios: + DO_STORE + int $0x10 # Standard BIOS mode set call + pushw %bx + movb $0x0f, %ah # Check if really set + int $0x10 + popw %bx + cmpb %bl, %al + jnz setbad + + stc + ret + +setspc: xorb %bh, %bh # Set special mode + cmpb $VIDEO_LAST_SPECIAL-VIDEO_FIRST_SPECIAL, %bl + jnc setbad + + addw %bx, %bx + jmp *spec_inits(%bx) + +setmenu: + orb %al, %al # 80x25 is an exception + jz _set_80x25 + + pushw %bx # Set mode chosen from menu + call mode_table # Build the mode table + popw %ax + shlw $2, %ax + addw %ax, %si + cmpw %di, %si + jnc setbad + + movw (%si), %ax # Fetch mode ID +_m_s: jmp mode_set + +setres: pushw %bx # Set mode chosen by resolution + call mode_table + popw %bx + xchgb %bl, %bh +setr1: lodsw + cmpw $ASK_VGA, %ax # End of the list? + jz setbad + + lodsw + cmpw %bx, %ax + jnz setr1 + + movw -4(%si), %ax # Fetch mode ID + jmp _m_s + +check_vesa: + leaw modelist+1024, %di + subb $VIDEO_FIRST_VESA>>8, %bh + movw %bx, %cx # Get mode information structure + movw $0x4f01, %ax + int $0x10 + addb $VIDEO_FIRST_VESA>>8, %bh + cmpw $0x004f, %ax + jnz setbad + + movb (%di), %al # Check capabilities. + andb $0x19, %al + cmpb $0x09, %al + jz setvesa # This is a text mode + + movb (%di), %al # Check capabilities. + andb $0x99, %al + cmpb $0x99, %al + jnz _setbad # Doh! No linear frame buffer. + + subb $VIDEO_FIRST_VESA>>8, %bh + orw $0x4000, %bx # Use linear frame buffer + movw $0x4f02, %ax # VESA BIOS mode set call + int $0x10 + cmpw $0x004f, %ax # AL=4f if implemented + jnz _setbad # AH=0 if OK + + movb $1, graphic_mode # flag graphic mode + movb $0, do_restore # no screen restore + stc + ret + +_setbad: jmp setbad # Ugly... + +# Recalculate vertical display end registers -- this fixes various +# inconsistencies of extended modes on many adapters. Called when +# the VIDEO_RECALC flag is set in the mode ID. + +setrec: subb $VIDEO_RECALC>>8, %ah # Set the base mode + call mode_set + jnc rct3 + + movw %gs:(0x485), %ax # Font size in pixels + movb %gs:(0x484), %bl # Number of rows + incb %bl + mulb %bl # Number of visible + decw %ax # scan lines - 1 + movw $0x3d4, %dx + movw %ax, %bx + movb $0x12, %al # Lower 8 bits + movb %bl, %ah + outw %ax, %dx + movb $0x07, %al # Bits 8 and 9 in the overflow register + call inidx + xchgb %al, %ah + andb $0xbd, %ah + shrb %bh + jnc rct1 + orb $0x02, %ah +rct1: shrb %bh + jnc rct2 + orb $0x40, %ah +rct2: movb $0x07, %al + outw %ax, %dx + stc +rct3: ret + +# Table of routines for setting of the special modes. +spec_inits: + .word set_80x25 + .word set_8pixel + .word set_80x43 + .word set_80x28 + .word set_current + .word set_80x30 + .word set_80x34 + .word set_80x60 + .word set_gfx + +# Set the 80x25 mode. If already set, do nothing. +set_80x25: + movw $0x5019, force_size # Override possibly broken BIOS +use_80x25: +#ifdef CONFIG_VIDEO_400_HACK + movw $0x1202, %ax # Force 400 scan lines + movb $0x30, %bl + int $0x10 +#else + movb $0x0f, %ah # Get current mode ID + int $0x10 + cmpw $0x5007, %ax # Mode 7 (80x25 mono) is the only one available + jz st80 # on CGA/MDA/HGA and is also available on EGAM + + cmpw $0x5003, %ax # Unknown mode, force 80x25 color + jnz force3 + +st80: cmpb $0, adapter # CGA/MDA/HGA => mode 3/7 is always 80x25 + jz set80 + + movb %gs:(0x0484), %al # This is EGA+ -- beware of 80x50 etc. + orb %al, %al # Some buggy BIOS'es set 0 rows + jz set80 + + cmpb $24, %al # It's hopefully correct + jz set80 +#endif /* CONFIG_VIDEO_400_HACK */ +force3: DO_STORE + movw $0x0003, %ax # Forced set + int $0x10 +set80: stc + ret + +# Set the 80x50/80x43 8-pixel mode. Simple BIOS calls. +set_8pixel: + DO_STORE + call use_80x25 # The base is 80x25 +set_8pt: + movw $0x1112, %ax # Use 8x8 font + xorb %bl, %bl + int $0x10 + movw $0x1200, %ax # Use alternate print screen + movb $0x20, %bl + int $0x10 + movw $0x1201, %ax # Turn off cursor emulation + movb $0x34, %bl + int $0x10 + movb $0x01, %ah # Define cursor scan lines 6-7 + movw $0x0607, %cx + int $0x10 +set_current: + stc + ret + +# Set the 80x28 mode. This mode works on all VGA's, because it's a standard +# 80x25 mode with 14-point fonts instead of 16-point. +set_80x28: + DO_STORE + call use_80x25 # The base is 80x25 +set14: movw $0x1111, %ax # Use 9x14 font + xorb %bl, %bl + int $0x10 + movb $0x01, %ah # Define cursor scan lines 11-12 + movw $0x0b0c, %cx + int $0x10 + stc + ret + +# Set the 80x43 mode. This mode is works on all VGA's. +# It's a 350-scanline mode with 8-pixel font. +set_80x43: + DO_STORE + movw $0x1201, %ax # Set 350 scans + movb $0x30, %bl + int $0x10 + movw $0x0003, %ax # Reset video mode + int $0x10 + jmp set_8pt # Use 8-pixel font + +# Set the 80x30 mode (all VGA's). 480 scanlines, 16-pixel font. +set_80x30: + call use_80x25 # Start with real 80x25 + DO_STORE + movw $0x3cc, %dx # Get CRTC port + inb %dx, %al + movb $0xd4, %dl + rorb %al # Mono or color? + jc set48a + + movb $0xb4, %dl +set48a: movw $0x0c11, %ax # Vertical sync end (also unlocks CR0-7) + call outidx + movw $0x0b06, %ax # Vertical total + call outidx + movw $0x3e07, %ax # (Vertical) overflow + call outidx + movw $0xea10, %ax # Vertical sync start + call outidx + movw $0xdf12, %ax # Vertical display end + call outidx + movw $0xe715, %ax # Vertical blank start + call outidx + movw $0x0416, %ax # Vertical blank end + call outidx + pushw %dx + movb $0xcc, %dl # Misc output register (read) + inb %dx, %al + movb $0xc2, %dl # (write) + andb $0x0d, %al # Preserve clock select bits and color bit + orb $0xe2, %al # Set correct sync polarity + outb %al, %dx + popw %dx + movw $0x501e, force_size + stc # That's all. + ret + +# Set the 80x34 mode (all VGA's). 480 scans, 14-pixel font. +set_80x34: + call set_80x30 # Set 480 scans + call set14 # And 14-pt font + movw $0xdb12, %ax # VGA vertical display end + movw $0x5022, force_size +setvde: call outidx + stc + ret + +# Set the 80x60 mode (all VGA's). 480 scans, 8-pixel font. +set_80x60: + call set_80x30 # Set 480 scans + call set_8pt # And 8-pt font + movw $0xdf12, %ax # VGA vertical display end + movw $0x503c, force_size + jmp setvde + +# Special hack for ThinkPad graphics +set_gfx: +#ifdef CONFIG_VIDEO_GFX_HACK + movw $VIDEO_GFX_BIOS_AX, %ax + movw $VIDEO_GFX_BIOS_BX, %bx + int $0x10 + movw $VIDEO_GFX_DUMMY_RESOLUTION, force_size + stc +#endif + ret + +#ifdef CONFIG_VIDEO_RETAIN + +# Store screen contents to temporary buffer. +store_screen: + cmpb $0, do_restore # Already stored? + jnz stsr + + testb $CAN_USE_HEAP, loadflags # Have we space for storing? + jz stsr + + pushw %ax + pushw %bx + pushw force_size # Don't force specific size + movw $0, force_size + call mode_params # Obtain params of current mode + popw force_size + movb %fs:(PARAM_VIDEO_LINES), %ah + movb %fs:(PARAM_VIDEO_COLS), %al + movw %ax, %bx # BX=dimensions + mulb %ah + movw %ax, %cx # CX=number of characters + addw %ax, %ax # Calculate image size + addw $modelist+1024+4, %ax + cmpw heap_end_ptr, %ax + jnc sts1 # Unfortunately, out of memory + + movw %fs:(PARAM_CURSOR_POS), %ax # Store mode params + leaw modelist+1024, %di + stosw + movw %bx, %ax + stosw + pushw %ds # Store the screen + movw video_segment, %ds + xorw %si, %si + rep + movsw + popw %ds + incb do_restore # Screen will be restored later +sts1: popw %bx + popw %ax +stsr: ret + +# Restore screen contents from temporary buffer. +restore_screen: + cmpb $0, do_restore # Has the screen been stored? + jz res1 + + call mode_params # Get parameters of current mode + movb %fs:(PARAM_VIDEO_LINES), %cl + movb %fs:(PARAM_VIDEO_COLS), %ch + leaw modelist+1024, %si # Screen buffer + lodsw # Set cursor position + movw %ax, %dx + cmpb %cl, %dh + jc res2 + + movb %cl, %dh + decb %dh +res2: cmpb %ch, %dl + jc res3 + + movb %ch, %dl + decb %dl +res3: movb $0x02, %ah + movb $0x00, %bh + int $0x10 + lodsw # Display size + movb %ah, %dl # DL=number of lines + movb $0, %ah # BX=phys. length of orig. line + movw %ax, %bx + cmpb %cl, %dl # Too many? + jc res4 + + pushw %ax + movb %dl, %al + subb %cl, %al + mulb %bl + addw %ax, %si + addw %ax, %si + popw %ax + movb %cl, %dl +res4: cmpb %ch, %al # Too wide? + jc res5 + + movb %ch, %al # AX=width of src. line +res5: movb $0, %cl + xchgb %ch, %cl + movw %cx, %bp # BP=width of dest. line + pushw %es + movw video_segment, %es + xorw %di, %di # Move the data + addw %bx, %bx # Convert BX and BP to _bytes_ + addw %bp, %bp +res6: pushw %si + pushw %di + movw %ax, %cx + rep + movsw + popw %di + popw %si + addw %bp, %di + addw %bx, %si + decb %dl + jnz res6 + + popw %es # Done +res1: ret +#endif /* CONFIG_VIDEO_RETAIN */ + +# Write to indexed VGA register (AL=index, AH=data, DX=index reg. port) +outidx: outb %al, %dx + pushw %ax + movb %ah, %al + incw %dx + outb %al, %dx + decw %dx + popw %ax + ret + +# Build the table of video modes (stored after the setup.S code at the +# `modelist' label. Each video mode record looks like: +# .word MODE-ID (our special mode ID (see above)) +# .byte rows (number of rows) +# .byte columns (number of columns) +# Returns address of the end of the table in DI, the end is marked +# with a ASK_VGA ID. +mode_table: + movw mt_end, %di # Already filled? + orw %di, %di + jnz mtab1x + + leaw modelist, %di # Store standard modes: + movl $VIDEO_80x25 + 0x50190000, %eax # The 80x25 mode (ALL) + stosl + movb adapter, %al # CGA/MDA/HGA -- no more modes + orb %al, %al + jz mtabe + + decb %al + jnz mtabv + + movl $VIDEO_8POINT + 0x502b0000, %eax # The 80x43 EGA mode + stosl + jmp mtabe + +mtab1x: jmp mtab1 + +mtabv: leaw vga_modes, %si # All modes for std VGA + movw $vga_modes_end-vga_modes, %cx + rep # I'm unable to use movsw as I don't know how to store a half + movsb # of the expression above to cx without using explicit shr. + + cmpb $0, scanning # Mode scan requested? + jz mscan1 + + call mode_scan +mscan1: + +#ifdef CONFIG_VIDEO_LOCAL + call local_modes +#endif /* CONFIG_VIDEO_LOCAL */ + +#ifdef CONFIG_VIDEO_VESA + call vesa_modes # Detect VESA VGA modes +#endif /* CONFIG_VIDEO_VESA */ + +#ifdef CONFIG_VIDEO_SVGA + cmpb $0, scanning # Bypass when scanning + jnz mscan2 + + call svga_modes # Detect SVGA cards & modes +mscan2: +#endif /* CONFIG_VIDEO_SVGA */ + +mtabe: + +#ifdef CONFIG_VIDEO_COMPACT + leaw modelist, %si + movw %di, %dx + movw %si, %di +cmt1: cmpw %dx, %si # Scan all modes + jz cmt2 + + leaw modelist, %bx # Find in previous entries + movw 2(%si), %cx +cmt3: cmpw %bx, %si + jz cmt4 + + cmpw 2(%bx), %cx # Found => don't copy this entry + jz cmt5 + + addw $4, %bx + jmp cmt3 + +cmt4: movsl # Copy entry + jmp cmt1 + +cmt5: addw $4, %si # Skip entry + jmp cmt1 + +cmt2: +#endif /* CONFIG_VIDEO_COMPACT */ + + movw $ASK_VGA, (%di) # End marker + movw %di, mt_end +mtab1: leaw modelist, %si # SI=mode list, DI=list end +ret0: ret + +# Modes usable on all standard VGAs +vga_modes: + .word VIDEO_8POINT + .word 0x5032 # 80x50 + .word VIDEO_80x43 + .word 0x502b # 80x43 + .word VIDEO_80x28 + .word 0x501c # 80x28 + .word VIDEO_80x30 + .word 0x501e # 80x30 + .word VIDEO_80x34 + .word 0x5022 # 80x34 + .word VIDEO_80x60 + .word 0x503c # 80x60 +#ifdef CONFIG_VIDEO_GFX_HACK + .word VIDEO_GFX_HACK + .word VIDEO_GFX_DUMMY_RESOLUTION +#endif + +vga_modes_end: +# Detect VESA modes. + +#ifdef CONFIG_VIDEO_VESA +vesa_modes: + cmpb $2, adapter # VGA only + jnz ret0 + + movw %di, %bp # BP=original mode table end + addw $0x200, %di # Buffer space + movw $0x4f00, %ax # VESA Get card info call + int $0x10 + movw %bp, %di + cmpw $0x004f, %ax # Successful? + jnz ret0 + + cmpw $0x4556, 0x200(%di) + jnz ret0 + + cmpw $0x4153, 0x202(%di) + jnz ret0 + + movw $vesa_name, card_name # Set name to "VESA VGA" + pushw %gs + lgsw 0x20e(%di), %si # GS:SI=mode list + movw $128, %cx # Iteration limit +vesa1: +# gas version 2.9.1, using BFD version 2.9.1.0.23 buggers the next inst. +# XXX: lodsw %gs:(%si), %ax # Get next mode in the list + gs; lodsw + cmpw $0xffff, %ax # End of the table? + jz vesar + + cmpw $0x0080, %ax # Check validity of mode ID + jc vesa2 + + orb %ah, %ah # Valid IDs: 0x0000-0x007f/0x0100-0x07ff + jz vesan # Certain BIOSes report 0x80-0xff! + + cmpw $0x0800, %ax + jnc vesae + +vesa2: pushw %cx + movw %ax, %cx # Get mode information structure + movw $0x4f01, %ax + int $0x10 + movw %cx, %bx # BX=mode number + addb $VIDEO_FIRST_VESA>>8, %bh + popw %cx + cmpw $0x004f, %ax + jnz vesan # Don't report errors (buggy BIOSES) + + movb (%di), %al # Check capabilities. We require + andb $0x19, %al # a color text mode. + cmpb $0x09, %al + jnz vesan + + cmpw $0xb800, 8(%di) # Standard video memory address required + jnz vesan + + testb $2, (%di) # Mode characteristics supplied? + movw %bx, (%di) # Store mode number + jz vesa3 + + xorw %dx, %dx + movw 0x12(%di), %bx # Width + orb %bh, %bh + jnz vesan + + movb %bl, 0x3(%di) + movw 0x14(%di), %ax # Height + orb %ah, %ah + jnz vesan + + movb %al, 2(%di) + mulb %bl + cmpw $8193, %ax # Small enough for Linux console driver? + jnc vesan + + jmp vesaok + +vesa3: subw $0x8108, %bx # This mode has no detailed info specified, + jc vesan # so it must be a standard VESA mode. + + cmpw $5, %bx + jnc vesan + + movw vesa_text_mode_table(%bx), %ax + movw %ax, 2(%di) +vesaok: addw $4, %di # The mode is valid. Store it. +vesan: loop vesa1 # Next mode. Limit exceeded => error +vesae: leaw vesaer, %si + call prtstr + movw %bp, %di # Discard already found modes. +vesar: popw %gs + ret + +# Dimensions of standard VESA text modes +vesa_text_mode_table: + .byte 60, 80 # 0108 + .byte 25, 132 # 0109 + .byte 43, 132 # 010A + .byte 50, 132 # 010B + .byte 60, 132 # 010C +#endif /* CONFIG_VIDEO_VESA */ + +# Scan for video modes. A bit dirty, but should work. +mode_scan: + movw $0x0100, %cx # Start with mode 0 +scm1: movb $0, %ah # Test the mode + movb %cl, %al + int $0x10 + movb $0x0f, %ah + int $0x10 + cmpb %cl, %al + jnz scm2 # Mode not set + + movw $0x3c0, %dx # Test if it's a text mode + movb $0x10, %al # Mode bits + call inidx + andb $0x03, %al + jnz scm2 + + movb $0xce, %dl # Another set of mode bits + movb $0x06, %al + call inidx + shrb %al + jc scm2 + + movb $0xd4, %dl # Cursor location + movb $0x0f, %al + call inidx + orb %al, %al + jnz scm2 + + movw %cx, %ax # Ok, store the mode + stosw + movb %gs:(0x484), %al # Number of rows + incb %al + stosb + movw %gs:(0x44a), %ax # Number of columns + stosb +scm2: incb %cl + jns scm1 + + movw $0x0003, %ax # Return back to mode 3 + int $0x10 + ret + +tstidx: outw %ax, %dx # OUT DX,AX and inidx +inidx: outb %al, %dx # Read from indexed VGA register + incw %dx # AL=index, DX=index reg port -> AL=data + inb %dx, %al + decw %dx + ret + +# Try to detect type of SVGA card and supply (usually approximate) video +# mode table for it. + +#ifdef CONFIG_VIDEO_SVGA +svga_modes: + leaw svga_table, %si # Test all known SVGA adapters +dosvga: lodsw + movw %ax, %bp # Default mode table + orw %ax, %ax + jz didsv1 + + lodsw # Pointer to test routine + pushw %si + pushw %di + pushw %es + movw $0xc000, %bx + movw %bx, %es + call *%ax # Call test routine + popw %es + popw %di + popw %si + orw %bp, %bp + jz dosvga + + movw %bp, %si # Found, copy the modes + movb svga_prefix, %ah +cpsvga: lodsb + orb %al, %al + jz didsv + + stosw + movsw + jmp cpsvga + +didsv: movw %si, card_name # Store pointer to card name +didsv1: ret + +# Table of all known SVGA cards. For each card, we store a pointer to +# a table of video modes supported by the card and a pointer to a routine +# used for testing of presence of the card. The video mode table is always +# followed by the name of the card or the chipset. +svga_table: + .word ati_md, ati_test + .word oak_md, oak_test + .word paradise_md, paradise_test + .word realtek_md, realtek_test + .word s3_md, s3_test + .word chips_md, chips_test + .word video7_md, video7_test + .word cirrus5_md, cirrus5_test + .word cirrus6_md, cirrus6_test + .word cirrus1_md, cirrus1_test + .word ahead_md, ahead_test + .word everex_md, everex_test + .word genoa_md, genoa_test + .word trident_md, trident_test + .word tseng_md, tseng_test + .word 0 + +# Test routines and mode tables: + +# S3 - The test algorithm was taken from the SuperProbe package +# for XFree86 1.2.1. Report bugs to Christoph.Niemann@linux.org +s3_test: + movw $0x0f35, %cx # we store some constants in cl/ch + movw $0x03d4, %dx + movb $0x38, %al + call inidx + movb %al, %bh # store current CRT-register 0x38 + movw $0x0038, %ax + call outidx # disable writing to special regs + movb %cl, %al # check whether we can write special reg 0x35 + call inidx + movb %al, %bl # save the current value of CRT reg 0x35 + andb $0xf0, %al # clear bits 0-3 + movb %al, %ah + movb %cl, %al # and write it to CRT reg 0x35 + call outidx + call inidx # now read it back + andb %ch, %al # clear the upper 4 bits + jz s3_2 # the first test failed. But we have a + + movb %bl, %ah # second chance + movb %cl, %al + call outidx + jmp s3_1 # do the other tests + +s3_2: movw %cx, %ax # load ah with 0xf and al with 0x35 + orb %bl, %ah # set the upper 4 bits of ah with the orig value + call outidx # write ... + call inidx # ... and reread + andb %cl, %al # turn off the upper 4 bits + pushw %ax + movb %bl, %ah # restore old value in register 0x35 + movb %cl, %al + call outidx + popw %ax + cmpb %ch, %al # setting lower 4 bits was successful => bad + je no_s3 # writing is allowed => this is not an S3 + +s3_1: movw $0x4838, %ax # allow writing to special regs by putting + call outidx # magic number into CRT-register 0x38 + movb %cl, %al # check whether we can write special reg 0x35 + call inidx + movb %al, %bl + andb $0xf0, %al + movb %al, %ah + movb %cl, %al + call outidx + call inidx + andb %ch, %al + jnz no_s3 # no, we can't write => no S3 + + movw %cx, %ax + orb %bl, %ah + call outidx + call inidx + andb %ch, %al + pushw %ax + movb %bl, %ah # restore old value in register 0x35 + movb %cl, %al + call outidx + popw %ax + cmpb %ch, %al + jne no_s31 # writing not possible => no S3 + movb $0x30, %al + call inidx # now get the S3 id ... + leaw idS3, %di + movw $0x10, %cx + repne + scasb + je no_s31 + + movb %bh, %ah + movb $0x38, %al + jmp s3rest + +no_s3: movb $0x35, %al # restore CRT register 0x35 + movb %bl, %ah + call outidx +no_s31: xorw %bp, %bp # Detection failed +s3rest: movb %bh, %ah + movb $0x38, %al # restore old value of CRT register 0x38 + jmp outidx + +idS3: .byte 0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95 + .byte 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0 + +s3_md: .byte 0x54, 0x2b, 0x84 + .byte 0x55, 0x19, 0x84 + .byte 0 + .ascii "S3" + .byte 0 + +# ATI cards. +ati_test: + leaw idati, %si + movw $0x31, %di + movw $0x09, %cx + repe + cmpsb + je atiok + + xorw %bp, %bp +atiok: ret + +idati: .ascii "761295520" + +ati_md: .byte 0x23, 0x19, 0x84 + .byte 0x33, 0x2c, 0x84 + .byte 0x22, 0x1e, 0x64 + .byte 0x21, 0x19, 0x64 + .byte 0x58, 0x21, 0x50 + .byte 0x5b, 0x1e, 0x50 + .byte 0 + .ascii "ATI" + .byte 0 + +# AHEAD +ahead_test: + movw $0x200f, %ax + movw $0x3ce, %dx + outw %ax, %dx + incw %dx + inb %dx, %al + cmpb $0x20, %al + je isahed + + cmpb $0x21, %al + je isahed + + xorw %bp, %bp +isahed: ret + +ahead_md: + .byte 0x22, 0x2c, 0x84 + .byte 0x23, 0x19, 0x84 + .byte 0x24, 0x1c, 0x84 + .byte 0x2f, 0x32, 0xa0 + .byte 0x32, 0x22, 0x50 + .byte 0x34, 0x42, 0x50 + .byte 0 + .ascii "Ahead" + .byte 0 + +# Chips & Tech. +chips_test: + movw $0x3c3, %dx + inb %dx, %al + orb $0x10, %al + outb %al, %dx + movw $0x104, %dx + inb %dx, %al + movb %al, %bl + movw $0x3c3, %dx + inb %dx, %al + andb $0xef, %al + outb %al, %dx + cmpb $0xa5, %bl + je cantok + + xorw %bp, %bp +cantok: ret + +chips_md: + .byte 0x60, 0x19, 0x84 + .byte 0x61, 0x32, 0x84 + .byte 0 + .ascii "Chips & Technologies" + .byte 0 + +# Cirrus Logic 5X0 +cirrus1_test: + movw $0x3d4, %dx + movb $0x0c, %al + outb %al, %dx + incw %dx + inb %dx, %al + movb %al, %bl + xorb %al, %al + outb %al, %dx + decw %dx + movb $0x1f, %al + outb %al, %dx + incw %dx + inb %dx, %al + movb %al, %bh + xorb %ah, %ah + shlb $4, %al + movw %ax, %cx + movb %bh, %al + shrb $4, %al + addw %ax, %cx + shlw $8, %cx + addw $6, %cx + movw %cx, %ax + movw $0x3c4, %dx + outw %ax, %dx + incw %dx + inb %dx, %al + andb %al, %al + jnz nocirr + + movb %bh, %al + outb %al, %dx + inb %dx, %al + cmpb $0x01, %al + je iscirr + +nocirr: xorw %bp, %bp +iscirr: movw $0x3d4, %dx + movb %bl, %al + xorb %ah, %ah + shlw $8, %ax + addw $0x0c, %ax + outw %ax, %dx + ret + +cirrus1_md: + .byte 0x1f, 0x19, 0x84 + .byte 0x20, 0x2c, 0x84 + .byte 0x22, 0x1e, 0x84 + .byte 0x31, 0x25, 0x64 + .byte 0 + .ascii "Cirrus Logic 5X0" + .byte 0 + +# Cirrus Logic 54XX +cirrus5_test: + movw $0x3c4, %dx + movb $6, %al + call inidx + movb %al, %bl # BL=backup + movw $6, %ax + call tstidx + cmpb $0x0f, %al + jne c5fail + + movw $0x1206, %ax + call tstidx + cmpb $0x12, %al + jne c5fail + + movb $0x1e, %al + call inidx + movb %al, %bh + movb %bh, %ah + andb $0xc0, %ah + movb $0x1e, %al + call tstidx + andb $0x3f, %al + jne c5xx + + movb $0x1e, %al + movb %bh, %ah + orb $0x3f, %ah + call tstidx + xorb $0x3f, %al + andb $0x3f, %al +c5xx: pushf + movb $0x1e, %al + movb %bh, %ah + outw %ax, %dx + popf + je c5done + +c5fail: xorw %bp, %bp +c5done: movb $6, %al + movb %bl, %ah + outw %ax, %dx + ret + +cirrus5_md: + .byte 0x14, 0x19, 0x84 + .byte 0x54, 0x2b, 0x84 + .byte 0 + .ascii "Cirrus Logic 54XX" + .byte 0 + +# Cirrus Logic 64XX -- no known extra modes, but must be identified, because +# it's misidentified by the Ahead test. +cirrus6_test: + movw $0x3ce, %dx + movb $0x0a, %al + call inidx + movb %al, %bl # BL=backup + movw $0xce0a, %ax + call tstidx + orb %al, %al + jne c2fail + + movw $0xec0a, %ax + call tstidx + cmpb $0x01, %al + jne c2fail + + movb $0xaa, %al + call inidx # 4X, 5X, 7X and 8X are valid 64XX chip ID's. + shrb $4, %al + subb $4, %al + jz c6done + + decb %al + jz c6done + + subb $2, %al + jz c6done + + decb %al + jz c6done + +c2fail: xorw %bp, %bp +c6done: movb $0x0a, %al + movb %bl, %ah + outw %ax, %dx + ret + +cirrus6_md: + .byte 0 + .ascii "Cirrus Logic 64XX" + .byte 0 + +# Everex / Trident +everex_test: + movw $0x7000, %ax + xorw %bx, %bx + int $0x10 + cmpb $0x70, %al + jne noevrx + + shrw $4, %dx + cmpw $0x678, %dx + je evtrid + + cmpw $0x236, %dx + jne evrxok + +evtrid: leaw trident_md, %bp +evrxok: ret + +noevrx: xorw %bp, %bp + ret + +everex_md: + .byte 0x03, 0x22, 0x50 + .byte 0x04, 0x3c, 0x50 + .byte 0x07, 0x2b, 0x64 + .byte 0x08, 0x4b, 0x64 + .byte 0x0a, 0x19, 0x84 + .byte 0x0b, 0x2c, 0x84 + .byte 0x16, 0x1e, 0x50 + .byte 0x18, 0x1b, 0x64 + .byte 0x21, 0x40, 0xa0 + .byte 0x40, 0x1e, 0x84 + .byte 0 + .ascii "Everex/Trident" + .byte 0 + +# Genoa. +genoa_test: + leaw idgenoa, %si # Check Genoa 'clues' + xorw %ax, %ax + movb %es:(0x37), %al + movw %ax, %di + movw $0x04, %cx + decw %si + decw %di +l1: incw %si + incw %di + movb (%si), %al + testb %al, %al + jz l2 + + cmpb %es:(%di), %al +l2: loope l1 + orw %cx, %cx + je isgen + + xorw %bp, %bp +isgen: ret + +idgenoa: .byte 0x77, 0x00, 0x99, 0x66 + +genoa_md: + .byte 0x58, 0x20, 0x50 + .byte 0x5a, 0x2a, 0x64 + .byte 0x60, 0x19, 0x84 + .byte 0x61, 0x1d, 0x84 + .byte 0x62, 0x20, 0x84 + .byte 0x63, 0x2c, 0x84 + .byte 0x64, 0x3c, 0x84 + .byte 0x6b, 0x4f, 0x64 + .byte 0x72, 0x3c, 0x50 + .byte 0x74, 0x42, 0x50 + .byte 0x78, 0x4b, 0x64 + .byte 0 + .ascii "Genoa" + .byte 0 + +# OAK +oak_test: + leaw idoakvga, %si + movw $0x08, %di + movw $0x08, %cx + repe + cmpsb + je isoak + + xorw %bp, %bp +isoak: ret + +idoakvga: .ascii "OAK VGA " + +oak_md: .byte 0x4e, 0x3c, 0x50 + .byte 0x4f, 0x3c, 0x84 + .byte 0x50, 0x19, 0x84 + .byte 0x51, 0x2b, 0x84 + .byte 0 + .ascii "OAK" + .byte 0 + +# WD Paradise. +paradise_test: + leaw idparadise, %si + movw $0x7d, %di + movw $0x04, %cx + repe + cmpsb + je ispara + + xorw %bp, %bp +ispara: ret + +idparadise: .ascii "VGA=" + +paradise_md: + .byte 0x41, 0x22, 0x50 + .byte 0x47, 0x1c, 0x84 + .byte 0x55, 0x19, 0x84 + .byte 0x54, 0x2c, 0x84 + .byte 0 + .ascii "Paradise" + .byte 0 + +# Trident. +trident_test: + movw $0x3c4, %dx + movb $0x0e, %al + outb %al, %dx + incw %dx + inb %dx, %al + xchgb %al, %ah + xorb %al, %al + outb %al, %dx + inb %dx, %al + xchgb %ah, %al + movb %al, %bl # Strange thing ... in the book this wasn't + andb $0x02, %bl # necessary but it worked on my card which + jz setb2 # is a trident. Without it the screen goes + # blurred ... + andb $0xfd, %al + jmp clrb2 + +setb2: orb $0x02, %al +clrb2: outb %al, %dx + andb $0x0f, %ah + cmpb $0x02, %ah + je istrid + + xorw %bp, %bp +istrid: ret + +trident_md: + .byte 0x50, 0x1e, 0x50 + .byte 0x51, 0x2b, 0x50 + .byte 0x52, 0x3c, 0x50 + .byte 0x57, 0x19, 0x84 + .byte 0x58, 0x1e, 0x84 + .byte 0x59, 0x2b, 0x84 + .byte 0x5a, 0x3c, 0x84 + .byte 0 + .ascii "Trident" + .byte 0 + +# Tseng. +tseng_test: + movw $0x3cd, %dx + inb %dx, %al # Could things be this simple ! :-) + movb %al, %bl + movb $0x55, %al + outb %al, %dx + inb %dx, %al + movb %al, %ah + movb %bl, %al + outb %al, %dx + cmpb $0x55, %ah + je istsen + +isnot: xorw %bp, %bp +istsen: ret + +tseng_md: + .byte 0x26, 0x3c, 0x50 + .byte 0x2a, 0x28, 0x64 + .byte 0x23, 0x19, 0x84 + .byte 0x24, 0x1c, 0x84 + .byte 0x22, 0x2c, 0x84 + .byte 0x21, 0x3c, 0x84 + .byte 0 + .ascii "Tseng" + .byte 0 + +# Video7. +video7_test: + movw $0x3cc, %dx + inb %dx, %al + movw $0x3b4, %dx + andb $0x01, %al + jz even7 + + movw $0x3d4, %dx +even7: movb $0x0c, %al + outb %al, %dx + incw %dx + inb %dx, %al + movb %al, %bl + movb $0x55, %al + outb %al, %dx + inb %dx, %al + decw %dx + movb $0x1f, %al + outb %al, %dx + incw %dx + inb %dx, %al + movb %al, %bh + decw %dx + movb $0x0c, %al + outb %al, %dx + incw %dx + movb %bl, %al + outb %al, %dx + movb $0x55, %al + xorb $0xea, %al + cmpb %bh, %al + jne isnot + + movb $VIDEO_FIRST_V7>>8, svga_prefix # Use special mode switching + ret + +video7_md: + .byte 0x40, 0x2b, 0x50 + .byte 0x43, 0x3c, 0x50 + .byte 0x44, 0x3c, 0x64 + .byte 0x41, 0x19, 0x84 + .byte 0x42, 0x2c, 0x84 + .byte 0x45, 0x1c, 0x84 + .byte 0 + .ascii "Video 7" + .byte 0 + +# Realtek VGA +realtek_test: + leaw idrtvga, %si + movw $0x45, %di + movw $0x0b, %cx + repe + cmpsb + je isrt + + xorw %bp, %bp +isrt: ret + +idrtvga: .ascii "REALTEK VGA" + +realtek_md: + .byte 0x1a, 0x3c, 0x50 + .byte 0x1b, 0x19, 0x84 + .byte 0x1c, 0x1e, 0x84 + .byte 0x1d, 0x2b, 0x84 + .byte 0x1e, 0x3c, 0x84 + .byte 0 + .ascii "REALTEK" + .byte 0 + +#endif /* CONFIG_VIDEO_SVGA */ + +# User-defined local mode table (VGA only) +#ifdef CONFIG_VIDEO_LOCAL +local_modes: + leaw local_mode_table, %si +locm1: lodsw + orw %ax, %ax + jz locm2 + + stosw + movsw + jmp locm1 + +locm2: ret + +# This is the table of local video modes which can be supplied manually +# by the user. Each entry consists of mode ID (word) and dimensions +# (byte for column count and another byte for row count). These modes +# are placed before all SVGA and VESA modes and override them if table +# compacting is enabled. The table must end with a zero word followed +# by NUL-terminated video adapter name. +local_mode_table: + .word 0x0100 # Example: 40x25 + .byte 25,40 + .word 0 + .ascii "Local" + .byte 0 +#endif /* CONFIG_VIDEO_LOCAL */ + +# Read a key and return the ASCII code in al, scan code in ah +getkey: xorb %ah, %ah + int $0x16 + ret + +# Read a key with a timeout of 30 seconds. +# The hardware clock is used to get the time. +getkt: call gettime + addb $30, %al # Wait 30 seconds + cmpb $60, %al + jl lminute + + subb $60, %al +lminute: + movb %al, %cl +again: movb $0x01, %ah + int $0x16 + jnz getkey # key pressed, so get it + + call gettime + cmpb %cl, %al + jne again + + movb $0x20, %al # timeout, return `space' + ret + +# Flush the keyboard buffer +flush: movb $0x01, %ah + int $0x16 + jz empty + + xorb %ah, %ah + int $0x16 + jmp flush + +empty: ret + +# Print hexadecimal number. +prthw: pushw %ax + movb %ah, %al + call prthb + popw %ax +prthb: pushw %ax + shrb $4, %al + call prthn + popw %ax + andb $0x0f, %al +prthn: cmpb $0x0a, %al + jc prth1 + + addb $0x07, %al +prth1: addb $0x30, %al + jmp prtchr + +# Print decimal number in al +prtdec: pushw %ax + pushw %cx + xorb %ah, %ah + movb $0x0a, %cl + idivb %cl + cmpb $0x09, %al + jbe lt100 + + call prtdec + jmp skip10 + +lt100: addb $0x30, %al + call prtchr +skip10: movb %ah, %al + addb $0x30, %al + call prtchr + popw %cx + popw %ax + ret + +# VIDEO_SELECT-only variables +mt_end: .word 0 # End of video mode table if built +edit_buf: .space 6 # Line editor buffer +card_name: .word 0 # Pointer to adapter name +scanning: .byte 0 # Performing mode scan +do_restore: .byte 0 # Screen contents altered during mode change +svga_prefix: .byte VIDEO_FIRST_BIOS>>8 # Default prefix for BIOS modes +graphic_mode: .byte 0 # Graphic mode with a linear frame buffer + +# Status messages +keymsg: .ascii "Press to see video modes available, " + .ascii " to continue or wait 30 secs" + .byte 0x0d, 0x0a, 0 + +listhdr: .byte 0x0d, 0x0a + .ascii "Mode: COLSxROWS:" + +crlft: .byte 0x0d, 0x0a, 0 + +prompt: .byte 0x0d, 0x0a + .asciz "Enter mode number or `scan': " + +unknt: .asciz "Unknown mode ID. Try again." + +badmdt: .ascii "You passed an undefined mode number." + .byte 0x0d, 0x0a, 0 + +vesaer: .ascii "Error: Scanning of VESA modes failed. Please " + .ascii "report to ." + .byte 0x0d, 0x0a, 0 + +old_name: .asciz "CGA/MDA/HGA" + +ega_name: .asciz "EGA" + +svga_name: .ascii " " + +vga_name: .asciz "VGA" + +vesa_name: .asciz "VESA" + +name_bann: .asciz "Video adapter: " +#endif /* CONFIG_VIDEO_SELECT */ + +# Other variables: +adapter: .byte 0 # Video adapter: 0=CGA/MDA/HGA,1=EGA,2=VGA +video_segment: .word 0xb800 # Video memory segment +force_size: .word 0 # Use this size instead of the one in BIOS vars diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/config.in linux.ac/arch/x86_64/config.in --- linux.vanilla/arch/x86_64/config.in Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/config.in Wed Oct 10 01:47:38 2001 @@ -0,0 +1,256 @@ +# +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/config-language.txt. +# +mainmenu_name "Linux Kernel Configuration" + +define_bool CONFIG_X86_64 y + +define_bool CONFIG_X86 y +define_bool CONFIG_ISA y +define_bool CONFIG_SBUS n + +define_bool CONFIG_UID16 y +define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n +define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y +define_bool CONFIG_GENERIC_BUST_SPINLOCK n + +mainmenu_option next_comment +comment 'Code maturity level options' +bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL +endmenu + +mainmenu_option next_comment +comment 'Loadable module support' +bool 'Enable loadable module support' CONFIG_MODULES +if [ "$CONFIG_MODULES" = "y" ]; then + bool ' Set version information on all module symbols' CONFIG_MODVERSIONS + bool ' Kernel module loader' CONFIG_KMOD +fi +endmenu + +mainmenu_option next_comment +comment 'Processor type and features' +choice 'Processor family' \ + "Sledgehammer CONFIG_MK8" Sledgehammer + +# +# Define implied options from the CPU selection here +# +define_int CONFIG_X86_L1_CACHE_BYTES 64 +define_int CONFIG_X86_L1_CACHE_SHIFT 6 +define_bool CONFIG_X86_TSC y +define_bool CONFIG_X86_GOOD_APIC y + +tristate '/dev/cpu/*/msr - Model-specific register support' CONFIG_X86_MSR +tristate '/dev/cpu/*/cpuid - CPU information support' CONFIG_X86_CPUID + +define_bool CONFIG_MATH_EMULATION n +define_bool CONFIG_MCA n +define_bool CONFIG_EISA n + +bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR +bool 'Symmetric multi-processing support' CONFIG_SMP +if [ "$CONFIG_SMP" != "y" ]; then + bool 'APIC support on uniprocessors' CONFIG_X86_UP_APIC + dep_bool 'IO-APIC support on uniprocessors' CONFIG_X86_UP_IOAPIC $CONFIG_X86_UP_APIC + if [ "$CONFIG_X86_UP_APIC" = "y" ]; then + define_bool CONFIG_X86_LOCAL_APIC y + fi + if [ "$CONFIG_X86_UP_IOAPIC" = "y" ]; then + define_bool CONFIG_X86_LOCAL_APIC y + fi +fi +if [ "$CONFIG_SMP" = "y" -a "$CONFIG_X86_CMPXCHG" = "y" ]; then + define_bool CONFIG_HAVE_DEC_LOCK y +fi +endmenu + +mainmenu_option next_comment +comment 'General setup' + +bool 'Networking support' CONFIG_NET +if [ "$CONFIG_SMP" = "y" ]; then + define_bool CONFIG_X86_IO_APIC y + define_bool CONFIG_X86_LOCAL_APIC y +fi +bool 'PCI support' CONFIG_PCI +if [ "$CONFIG_PCI" = "y" ]; then +# x86-64 doesn't support PCI BIOS access from long mode so always go direct. + define_bool CONFIG_PCI_DIRECT y +fi + +source drivers/pci/Config.in + +bool 'Support for hot-pluggable devices' CONFIG_HOTPLUG + +if [ "$CONFIG_HOTPLUG" = "y" ] ; then + source drivers/pcmcia/Config.in +else + define_bool CONFIG_PCMCIA n +fi + +bool 'System V IPC' CONFIG_SYSVIPC +bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT +bool 'Sysctl support' CONFIG_SYSCTL +if [ "$CONFIG_PROC_FS" = "y" ]; then + define_bool CONFIG_KCORE_ELF y +fi +# We probably are not going to support a.out, are we? Or should we support a.out in i386 compatibility mode? +#tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT +tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC + +bool 'Power Management support' CONFIG_PM + +bool 'IA32 Emulation' CONFIG_IA32_EMULATION + +#ACPI is not ported yet +#if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then +# dep_bool ' ACPI support' CONFIG_ACPI $CONFIG_PM +# if [ "$CONFIG_ACPI" != "n" ]; then +# source drivers/acpi/Config.in +# fi +#fi + +endmenu + +source drivers/mtd/Config.in + +source drivers/parport/Config.in + +source drivers/pnp/Config.in + +source drivers/block/Config.in + +source drivers/md/Config.in + +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in +fi + +source drivers/telephony/Config.in + +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 + +mainmenu_option next_comment +comment 'SCSI support' + +tristate 'SCSI support' CONFIG_SCSI + +if [ "$CONFIG_SCSI" != "n" ]; then + source drivers/scsi/Config.in +fi +endmenu + +source drivers/message/fusion/Config.in + +source drivers/ieee1394/Config.in + +#Does not seem to be 64bit safe. +#source drivers/message/i2o/Config.in + +if [ "$CONFIG_NET" = "y" ]; then + 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 +fi + +source net/ax25/Config.in + +source net/irda/Config.in + +mainmenu_option next_comment +comment 'ISDN subsystem' +if [ "$CONFIG_NET" != "n" ]; then + tristate 'ISDN support' CONFIG_ISDN + if [ "$CONFIG_ISDN" != "n" ]; then + source drivers/isdn/Config.in + fi +fi +endmenu + +mainmenu_option next_comment +comment 'Old CD-ROM drivers (not SCSI, not IDE)' + +bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI +if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then + source drivers/cdrom/Config.in +fi +endmenu + +# +# input before char - char/joystick depends on it. As does USB. +# +source drivers/input/Config.in +source drivers/char/Config.in + +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + source net/bluetooth/Config.in +fi + +source drivers/misc/Config.in + +source drivers/media/Config.in + +source fs/Config.in + +if [ "$CONFIG_VT" = "y" ]; then + mainmenu_option next_comment + comment 'Console drivers' + bool 'VGA text console' CONFIG_VGA_CONSOLE + bool 'Video mode selection support' CONFIG_VIDEO_SELECT + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'MDA text console (dual-headed) (EXPERIMENTAL)' CONFIG_MDA_CONSOLE + source drivers/video/Config.in + fi + endmenu +fi + +mainmenu_option next_comment +comment 'Sound' + +tristate 'Sound card support' CONFIG_SOUND +if [ "$CONFIG_SOUND" != "n" ]; then + source drivers/sound/Config.in +fi +endmenu + +source drivers/usb/Config.in + +mainmenu_option next_comment +comment 'Kernel hacking' + +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC +bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +bool 'Simnow environment (disables time-consuming things)' CONFIG_SIMNOW +bool 'Early printk' CONFIG_EARLY_PRINTK +tristate 'Simics host fs' CONFIG_HOSTFS +if [ "$CONFIG_HOSTFS" != "n" ]; then + bool ' hostfs read/write' CONFIG_HOSTFS_RW +fi +#if [ "$CONFIG_SERIAL_CONSOLE" = "y" ]; then +# bool 'Early serial console (ttyS0)' CONFIG_EARLY_SERIAL_CONSOLE +#fi +bool 'Additional run-time checks' CONFIG_CHECKING +bool 'User level oops' CONFIG_USER_LEVEL_OOPS +endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/defconfig linux.ac/arch/x86_64/defconfig --- linux.vanilla/arch/x86_64/defconfig Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/defconfig Wed Oct 10 01:47:38 2001 @@ -0,0 +1,428 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_X86_64=y +CONFIG_X86=y +CONFIG_ISA=y +# CONFIG_SBUS is not set +CONFIG_UID16=y +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +# CONFIG_MODULES is not set + +# +# Processor type and features +# +CONFIG_MK8=y +CONFIG_X86_L1_CACHE_BYTES=64 +CONFIG_X86_L1_CACHE_SHIFT=6 +CONFIG_X86_TSC=y +CONFIG_X86_GOOD_APIC=y +CONFIG_X86_MSR=y +CONFIG_X86_CPUID=y +# CONFIG_MATH_EMULATION is not set +# CONFIG_MCA is not set +# CONFIG_EISA is not set +CONFIG_MTRR=y +# CONFIG_SMP is not set +CONFIG_X86_UP_IOAPIC=y +CONFIG_X86_IO_APIC=y +CONFIG_X86_LOCAL_APIC=y + +# +# General setup +# +CONFIG_NET=y +CONFIG_PCI=y +CONFIG_PCI_DIRECT=y +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PM is not set +CONFIG_IA32_EMULATION=y +# CONFIG_ACPI is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# 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 +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set +# CONFIG_PHONE_IXJ is not set + +# +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set +# CONFIG_BLK_DEV_IDEDISK_IBM is not set +# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set +# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set +# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set +# CONFIG_BLK_DEV_IDEDISK_WD is not set +# CONFIG_BLK_DEV_COMMERIAL is not set +# CONFIG_BLK_DEV_TIVO is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set + +# +# IDE chipset support/bugfixes +# +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_IDEPCI is not set +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_DMA_NONPCI is not set +# CONFIG_BLK_DEV_IDE_MODES is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set +# CONFIG_FUSION_BOOT is not set +# CONFIG_FUSION_ISENSE is not set +# CONFIG_FUSION_CTL is not set +# CONFIG_FUSION_LAN is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_PCI is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Network device support +# +# CONFIG_NETDEVICES is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Input core support +# +# CONFIG_INPUT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +CONFIG_MOUSE=y +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set + +# +# Joysticks +# +# CONFIG_JOYSTICK is not set + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_TMPFS is not set +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFS_V3 is not set +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Console drivers +# +CONFIG_VGA_CONSOLE=y +# CONFIG_VIDEO_SELECT is not set +# CONFIG_MDA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +CONFIG_SIMNOW=y +# CONFIG_EARLY_PRINTK is not set +CONFIG_HOSTFS=y +CONFIG_HOSTFS_RW=y +# CONFIG_CHECKING is not set +# CONFIG_USER_LEVEL_OOPS is not set diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/hostfs/Makefile linux.ac/arch/x86_64/hostfs/Makefile --- linux.vanilla/arch/x86_64/hostfs/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/hostfs/Makefile Wed Oct 10 01:47:38 2001 @@ -0,0 +1,9 @@ +# +# Makefile for Simics hostfs + +O_TARGET := hostfs.o + +obj-$(CONFIG_HOSTFS) := hostfs_inode.o hostfs_super.o hostfs_dir.o hostfs_file.o \ + hostfs_host.o + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/hostfs/hostfs.h linux.ac/arch/x86_64/hostfs/hostfs.h --- linux.vanilla/arch/x86_64/hostfs/hostfs.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/hostfs/hostfs.h Wed Oct 10 01:47:38 2001 @@ -0,0 +1,276 @@ +/* + hostfs for Linux + Copyright 2001 Virtutech AB + + 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, GOOD TITLE or + NON INFRINGEMENT. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _HOST_FS_H +#define _HOST_FS_H + +#define HOSTFS_VERSION 1 + +#if defined(SERENGETI) +#define HOSTFS_DEV (0x7fff07ffff0) +#else +#define HOSTFS_DEV (0xffe81000) +#endif + +#define HOSTFS_MAGIC 0x66685456 /* "VThf" */ + +#define DATA_BUF_LEN (1040) +#define HOSTFS_FILENAME_LEN (512) +#define DEBUG_LEN (1024) +#define XFER_DATA_WORDS (256) /* 1024/4 */ + +#define SET_HI_BITS(b, a) { unsigned long long tmp = (a); tmp <<= 16; tmp <<= 16; b = tmp ; } +#define GET_HI_BITS(b, a) { unsigned long long tmp = (a); tmp >>= 16; tmp >>= 16; (b) = tmp ; } + + +/* used on target */ +#define DPRINTF_0 printk +#define DPRINTF_1 if (hostfs_debug >= 1) printf +#define DPRINTF_2 if (hostfs_debug >= 2) printf + +enum { + HF_FREAD = 0x01, + HF_FWRITE = 0x02, + HF_FNDELAY = 0x04, + HF_FAPPEND = 0x08, + HF_FSYNC = 0x10 +}; + +/* identifies a host file for the filesystem */ +typedef uint host_node_t; + +/* only in target part */ +extern dev_t hostfs_dev; +extern int hostfs_debug; + +#if defined(TARGETOS_SOLARIS) +typedef struct hf_node { + struct vnode vnode; + host_node_t hnode; + struct hf_node *next; + long long write_size; + int num_maps; +} hf_node_t; +#elif defined(TARGETOS_LINUX) +struct hf_node; +typedef struct hf_node hf_node_t; +#endif + +typedef enum host_func { + hf_VfsStat, + hf_Root, + hf_GetAttr, + hf_SetAttr, + hf_Open, + hf_Close, + hf_Readdir, + hf_Lookup, + hf_Seek, + hf_Read, + hf_Write, + hf_Create, + hf_Remove, + hf_MkDir, + hf_Parent, + hf_Rename, + hf_Link, + hf_Symlink, + hf_Readlink, + hf_Debug, + hf_Handshake, + hf_Unmount, + hf_Functions +} host_func_t; + +typedef struct host_funcs { + host_func_t func; + const char *name; + int out; + int in; + int ie_out; + int ie_in; +} host_funcs_t; + +/********* + * + *IMPORTANT: + * If you change the parameters make sure the ie_out and ie_in are still + * correct. They signal the first entry to do endian conversion on. + * -1 is no conversion at all. + * + */ + +struct hf_common_data { + uint sys_error; +}; + +struct hf_hnode_data { + uint sys_error; + uint hnode; + uint type; +}; + +struct hf_vfsstat_data { + uint bsize; + uint frsize; + uint blocks; + uint bfree; + uint bavail; + uint files; + uint ffree; + uint favail; +}; + +struct hf_getattr_data { + uint sys_error; + uint mode; /* access mode */ + uint uid; /* user id */ + uint gid; /* group id */ + uint link; /* number of references */ + uint size_hi; /* file size */ + uint size_lo; + uint atime_hi; /* access time */ + uint atime_lo; + uint mtime_hi; /* modification time */ + uint mtime_lo; + uint ctime_hi; /* creation time */ + uint ctime_lo; + uint blksize; /* fundamental block size */ + uint blocks_hi; /* blocks allocated */ + uint blocks_lo; +}; + +struct hf_setattr_data { + uint set_atime; + uint set_mtime; + uint set_mode; + uint set_uid; + uint set_gid; + uint atime_hi; /* access time */ + uint atime_lo; + uint mtime_hi; /* modification time */ + uint mtime_lo; + uint mode; + uint uid; + uint gid; + uint set_size; + uint size_hi; + uint size_lo; +}; + +struct hf_setattr_data_v0 { + uint set_atime; + uint set_mtime; + uint set_mode; + uint set_uid; + uint set_gid; + uint atime_hi; /* access time */ + uint atime_lo; + uint mtime_hi; /* modification time */ + uint mtime_lo; + uint mode; + uint uid; + uint gid; +}; + +struct hf_readdir_data { + uint sys_error; + host_node_t hnode; + char filename[HOSTFS_FILENAME_LEN]; +}; + +struct hf_readlink_data { + uint sys_error; + char filename[HOSTFS_FILENAME_LEN]; +}; + +struct hf_lookup_data { + char filename[HOSTFS_FILENAME_LEN]; +}; + +struct hf_remove_data { + char filename[HOSTFS_FILENAME_LEN]; +}; + +struct hf_rename_data { + uint new_hnode; + char old_name[HOSTFS_FILENAME_LEN]; + char new_name[HOSTFS_FILENAME_LEN]; +}; + +struct hf_create_data { + uint excl; + uint trunc; + uint mode; + char filename[HOSTFS_FILENAME_LEN]; +}; + +struct hf_mkdir_data { + uint mode; + char dirname[HOSTFS_FILENAME_LEN]; +}; + +struct hf_seek_data { + uint off_hi; + uint off_lo; +}; + +struct hf_read_data { + uint sys_error; + uint size; + uint data[XFER_DATA_WORDS]; /* max 480 bytes of data */ +}; + +struct hf_write_data { + uint size; + uint append; + uint data[XFER_DATA_WORDS]; /* max 480 bytes of data */ +}; + +struct hf_link_data { + uint hnode; + char link[HOSTFS_FILENAME_LEN]; +}; + +struct hf_symlink_data { + char target[HOSTFS_FILENAME_LEN]; + char link[HOSTFS_FILENAME_LEN]; +}; + +struct hf_debug_data { + char string[DEBUG_LEN]; +}; + +struct hf_handshake_data { + uint version; +}; + +struct hf_handshake_reply_data { + uint sys_error; + uint magic; +}; + +#if !defined(HOSTFS_HOST) && defined(TARGETOS_SOLARIS) +int get_host_data(host_func_t func, host_node_t hnode, void *in_buf, void *out_buf); +hf_node_t *hf_getnode(hf_node_t *parent, host_node_t hnode, int type); +void hf_freenode(hf_node_t *hf_node); +void hf_exitstatus(); +#endif + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/hostfs/hostfs_data.h linux.ac/arch/x86_64/hostfs/hostfs_data.h --- linux.vanilla/arch/x86_64/hostfs/hostfs_data.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/hostfs/hostfs_data.h Wed Oct 10 01:47:38 2001 @@ -0,0 +1,90 @@ +/* + hostfs for Linux + Copyright 2001 Virtutech AB + + 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, GOOD TITLE or + NON INFRINGEMENT. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _HOST_FS_DATA_H +#define _HOST_FS_DATA_H + +/* WARNING: Only include once. Contains definitions */ + +/* when transferring data the following protocol is used: + * + * to host: 4 byte function + * to host: 4 bytes hnode + * to host: X bytes out data (defined by fkn_len[fkn].out) + * from host: X bytes in data (defined by fkn_len[fkn].in) ( X > 0 !!! ) + * + */ + +static host_funcs_t host_funcs[] = { + { hf_VfsStat, "VfsStat", 0, sizeof(struct hf_vfsstat_data) >> 2, -1, -1 }, + { hf_Root, "Root", 0, sizeof(struct hf_hnode_data) >> 2, -1, -1 }, + { hf_GetAttr, "GetAttr", 0, sizeof(struct hf_getattr_data) >> 2, -1, -1 }, + { hf_SetAttr, "SetAttr", sizeof(struct hf_setattr_data) >> 2, sizeof(struct hf_common_data) >> 2, -1, -1 }, + { hf_Open, "Open", 1, sizeof(struct hf_common_data) >> 2, -1, -1 }, + { hf_Close, "Close", 0, 1, -1, -1 }, + { hf_Readdir, "Readdir", 1, sizeof(struct hf_readdir_data) >> 2, -1, 2 }, + { hf_Lookup, "Lookup", sizeof(struct hf_lookup_data), sizeof(struct hf_hnode_data) >> 2, 0, -1 }, + { hf_Seek, "Seek", sizeof(struct hf_seek_data) >> 2, sizeof(struct hf_common_data) >> 2, -1, -1 }, + { hf_Read, "Read", 1, sizeof(struct hf_read_data) >> 2, -1, 2 }, + { hf_Write, "Write", sizeof(struct hf_write_data) >> 2, sizeof(struct hf_common_data) >> 2, 2, -1 }, + { hf_Create, "Create", sizeof(struct hf_create_data) >> 2, sizeof(struct hf_hnode_data) >> 2, 3, -1 }, + { hf_Remove, "Remove", sizeof(struct hf_remove_data) >> 2, sizeof(struct hf_common_data) >> 2, 0, -1 }, + { hf_MkDir, "MkDir", sizeof(struct hf_mkdir_data) >> 2, sizeof(struct hf_hnode_data) >> 2, 1, -1 }, + { hf_Parent, "Parent", 0, sizeof(struct hf_hnode_data) >> 2, -1, -1 }, + { hf_Rename, "Rename", sizeof(struct hf_rename_data) >> 2, sizeof(struct hf_common_data) >> 2, 1, -1 }, + { hf_Link, "Link", sizeof(struct hf_link_data) >> 2, sizeof(struct hf_common_data) >> 2, 1, -1 }, + { hf_Symlink, "Symlink", sizeof(struct hf_symlink_data) >> 2, sizeof(struct hf_common_data) >> 2, 0, -1 }, + { hf_Readlink, "Readlink", 0, sizeof(struct hf_readlink_data) >> 2, -1, 1}, + { hf_Debug, "Debug", sizeof(struct hf_debug_data) >> 2, 1, 0, -1 }, + { hf_Handshake, "Handshake", sizeof(struct hf_handshake_data) >> 2, sizeof (struct hf_handshake_reply_data) >> 2, -1, -1 }, + { hf_Unmount, "Unmount", 0, sizeof (struct hf_common_data) >> 2, -1, -1 } +}; + +typedef int check_enough_host_funcs[sizeof host_funcs / sizeof host_funcs[0] == hf_Functions ? 1 : -1]; + +#if defined(HOSTFS_HOST) +static host_funcs_t host_funcs_v0[] = { + { hf_VfsStat, "VfsStat", 0, sizeof(struct hf_vfsstat_data) >> 2, -1, -1 }, + { hf_Root, "Root", 0, sizeof(struct hf_hnode_data) >> 2, -1, -1 }, + { hf_GetAttr, "GetAttr", 0, sizeof(struct hf_getattr_data) >> 2, -1, -1 }, + { hf_SetAttr, "SetAttr", sizeof(struct hf_setattr_data_v0) >> 2, sizeof(struct hf_common_data) >> 2, -1, -1 }, + { hf_Open, "Open", 1, sizeof(struct hf_common_data) >> 2, -1, -1 }, + { hf_Close, "Close", 0, 1, -1, -1 }, + { hf_Readdir, "Readdir", 1, sizeof(struct hf_readdir_data) >> 2, -1, 2 }, + { hf_Lookup, "Lookup", sizeof(struct hf_lookup_data), sizeof(struct hf_hnode_data) >> 2, 0, -1 }, + { hf_Seek, "Seek", sizeof(struct hf_seek_data) >> 2, sizeof(struct hf_common_data) >> 2, -1, -1 }, + { hf_Read, "Read", 1, sizeof(struct hf_read_data) >> 2, -1, 2 }, + { hf_Write, "Write", sizeof(struct hf_write_data) >> 2, sizeof(struct hf_common_data) >> 2, 2, -1 }, + { hf_Create, "Create", sizeof(struct hf_create_data) >> 2, sizeof(struct hf_hnode_data) >> 2, 3, -1 }, + { hf_Remove, "Remove", sizeof(struct hf_remove_data) >> 2, sizeof(struct hf_common_data) >> 2, 0, -1 }, + { hf_MkDir, "MkDir", sizeof(struct hf_mkdir_data) >> 2, sizeof(struct hf_hnode_data) >> 2, 1, -1 }, + { hf_Parent, "Parent", 0, sizeof(struct hf_hnode_data) >> 2, -1, -1 }, + { hf_Rename, "Rename", sizeof(struct hf_rename_data) >> 2, sizeof(struct hf_common_data) >> 2, 1, -1 }, + { hf_Link, "Link", sizeof(struct hf_link_data) >> 2, sizeof(struct hf_common_data) >> 2, 1, -1 }, + { hf_Symlink, "Symlink", sizeof(struct hf_symlink_data) >> 2, sizeof(struct hf_common_data) >> 2, 0, -1 }, + { hf_Readlink, "Readlink", 0, sizeof(struct hf_readlink_data) >> 2, -1, 1}, + { hf_Debug, "Debug", sizeof(struct hf_debug_data) >> 2, 1, 0, -1 }, + { hf_Handshake, "Handshake", sizeof(struct hf_handshake_data) >> 2, sizeof (struct hf_handshake_reply_data) >> 2, -1, -1 }, + { hf_Unmount, "Unmount", 0, 0, -1, -1 } +}; + +typedef int check_enough_host_funcs_v0[sizeof host_funcs_v0 / sizeof host_funcs_v0[0] == hf_Functions ? 1 : -1]; +#endif + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/hostfs/hostfs_dir.c linux.ac/arch/x86_64/hostfs/hostfs_dir.c --- linux.vanilla/arch/x86_64/hostfs/hostfs_dir.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/hostfs/hostfs_dir.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,77 @@ +/* + hostfs for Linux + Copyright 2001 Virtutech AB + Copyright 2001 SuSE + + 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, GOOD TITLE or + NON INFRINGEMENT. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Written by Gustav Hĺllberg. The author may be reached as + gustav@virtutech.com. + + Ported by Andi Kleen and Karsten Keil to Linux 2.4. + + $Id: hostfs_dir.c,v 1.1 2001/07/02 20:17:51 ak Exp $ +*/ + +#include "hostfs_linux.h" + +static int +hostfs_fo_readdir(struct file *file, void *buf, filldir_t fill) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + struct hf_readdir_data data; + uint offset = file->f_pos; + +/* DPRINT1(DEVICE_NAME " hostfs_fo_readdir("); */ +/* DPRINTFILE1(file); */ +/* DPRINT1(", %p, %p)\n", buf, fill); */ +/* DPRINT1(" file->fpos == %ld\n", (long)file->f_pos); */ + + get_host_data(hf_Readdir, inode->i_ino, &offset, &data); + while (data.hnode) { + int res; + NTOHSWAP(data.filename); + if ((res = fill(buf, data.filename, strlen(data.filename), offset, data.hnode, DT_UNKNOWN))) { + return 0; + } + file->f_pos = ++offset; + get_host_data(hf_Readdir, inode->i_ino, &offset, &data); + } + return 0; +} + +static ssize_t +hostfs_fo_dir_read(struct file *file, char *buf, size_t len, loff_t *off) +{ +/* DPRINT1(DEVICE_NAME " hostfs_fo_dir_read()\n"); */ + return -EISDIR; +} + + +static int +hostfs_dir_release(struct inode *inode, struct file *file) +{ + uint dummy; + get_host_data(hf_Close, inode->i_ino, NULL, &dummy); + return 0; +} + + +struct file_operations hostfs_file_dirops = { + read: hostfs_fo_dir_read, + readdir: hostfs_fo_readdir, + release: hostfs_dir_release, +}; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/hostfs/hostfs_file.c linux.ac/arch/x86_64/hostfs/hostfs_file.c --- linux.vanilla/arch/x86_64/hostfs/hostfs_file.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/hostfs/hostfs_file.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,113 @@ + +/* + hostfs for Linux + Copyright 2001 Virtutech AB + Copyright 2001 SuSE + + 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, GOOD TITLE or + NON INFRINGEMENT. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Written by Gustav Hĺllberg. The author may be reached as + gustav@virtutech.com. + + Ported by Andi Kleen and Karsten Keil to Linux 2.4. + + $Id: hostfs_file.c,v 1.1 2001/07/02 20:17:51 ak Exp $ +*/ + +#include "hostfs_linux.h" + + + +static ssize_t +hostfs_fo_read(struct file *file, char *buf, size_t len, loff_t *off) +{ +/* DPRINT1(DEVICE_NAME " hostfs_fo_read("); */ +/* DPRINTFILE1(file); */ +/* DPRINT1(", %p, %ld, %p)\n", buf, (long)len, off); */ + return generic_file_read(file, buf, len, off); +} + +static int +hostfs_fo_open(struct inode *inode, struct file *file) +{ + struct hf_common_data idata; + uint oflag = 0, flag = file->f_flags; + + DPRINT1("hostfs_fo_open %ld flags %x\n", inode->i_ino, file->f_flags); + + switch (flag & O_ACCMODE) { + case O_RDONLY: + oflag = HF_FREAD; + break; +#if defined(HF_RW) + case O_RDWR: + oflag = HF_FREAD; + /* fallthrough */ + case O_WRONLY: + oflag |= HF_FWRITE; + break; +#else + case O_WRONLY: + case O_RDWR: + return -EACCES; +#endif + default: + return -EINVAL; + } + + get_host_data(hf_Open, inode->i_ino, &oflag, &idata); + + return 0; +} + + +static ssize_t +hostfs_fo_write(struct file *file, const char *buf, size_t len, loff_t *off) +{ + DPRINT1(DEVICE_NAME " hostfs_fo_write("); + DPRINTFILE1(file); + DPRINT1(", %p, %ld, %p)\n", buf, (long)len, off); + + return generic_file_write(file, buf, len, off); +} + + +static int +hostfs_fo_release(struct inode *inode, struct file *file) +{ + uint dummy; + + DPRINT1("hostfs_fo_release %ld fcount %d icount %d\n", + (long)inode->i_ino, atomic_read(&file->f_count), + atomic_read(&inode->i_count)); + get_host_data(hf_Close, inode->i_ino, NULL, &dummy); + return 0; +} + +static int +hostfs_fo_mmap(struct file *file, struct vm_area_struct *area) +{ +/* DPRINT1("hostfs_fo_mmap()\n"); */ + return generic_file_mmap(file, area); +} + + +struct file_operations hostfs_file_fops = { + read: hostfs_fo_read, + write: RW_FUNC(hostfs_fo_write), + mmap: hostfs_fo_mmap, + open: hostfs_fo_open, + release: hostfs_fo_release, +}; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/hostfs/hostfs_host.c linux.ac/arch/x86_64/hostfs/hostfs_host.c --- linux.vanilla/arch/x86_64/hostfs/hostfs_host.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/hostfs/hostfs_host.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,101 @@ +/* + hostfs for Linux + Copyright 2001 Virtutech AB + Copyright 2001 SuSE + + 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, GOOD TITLE or + NON INFRINGEMENT. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Written by Gustav Hĺllberg. The author may be reached as + gustav@virtutech.com. + + Ported by Andi Kleen and Karsten Keil to Linux 2.4. + + $Id: hostfs_host.c,v 1.1 2001/07/02 20:17:51 ak Exp $ +*/ + + +#include "hostfs_linux.h" + +static volatile void *hostfs_dev_data; + +static void +data_to_host(uint *data, int len) +{ + int i; + + for (i = 0; i < len; i++) + *(uint *)hostfs_dev_data = data[i]; +} + +static void +data_from_host(uint *data, int len) +{ + int i; + + for (i = 0; i < len; i++) + data[i] = *(uint *)hostfs_dev_data; +} + +spinlock_t get_host_data_lock = SPIN_LOCK_UNLOCKED; +spinlock_t hostfs_position_lock = SPIN_LOCK_UNLOCKED; + + +int +get_host_data(host_func_t func, host_node_t hnode, void *in_buf, void *out_buf) +{ + uint data; + + if (func != host_funcs[func].func) + printk(DEVICE_NAME " INTERNAL ERROR: host_funcs_t != func\n"); + + spin_lock(&get_host_data_lock); + + data = func; + data_to_host(&data, 1); + data_to_host(&hnode, 1); + data_to_host(in_buf, host_funcs[func].out); + data_from_host(out_buf, host_funcs[func].in); + + spin_unlock(&get_host_data_lock); + + return 0; +} + + +/* internal function */ +int hf_do_seek(ino_t inode, loff_t off) +{ + struct hf_seek_data odata; + struct hf_common_data idata; + + SET_HI_LO(odata.off, off); + get_host_data(hf_Seek, inode, &odata, &idata); + return idata.sys_error; +} + + + +int init_host_fs(void) +{ +#if defined(__sparc_v9__) + hostfs_dev_data = (void *)HOSTFS_DEV; +#elif defined(__alpha) + hostfs_dev_data = (void *)phys_to_virt(HOSTFS_DEV); +#elif defined(__i386) || defined(__x86_64__) + hostfs_dev_data = (void *)ioremap(HOSTFS_DEV, 16); +#else +#error "No device mapping for this architecture" +#endif +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/hostfs/hostfs_inode.c linux.ac/arch/x86_64/hostfs/hostfs_inode.c --- linux.vanilla/arch/x86_64/hostfs/hostfs_inode.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/hostfs/hostfs_inode.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,488 @@ +/* + hostfs for Linux + Copyright 2001 Virtutech AB + Copyright 2001 SuSE + + 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, GOOD TITLE or + NON INFRINGEMENT. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Written by Gustav Hĺllberg. The author may be reached as + gustav@virtutech.com. + + Ported by Andi Kleen and Karsten Keil to Linux 2.4. + + $Id: hostfs_inode.c,v 1.5 2001/07/05 18:56:32 ak Exp $ +*/ + +#include "hostfs_linux.h" + + +static struct address_space_operations hostfs_file_aops; +static struct inode_operations hostfs_symlink_iops, hostfs_dir_iops; + +static struct inode_operations hostfs_file_iops; + +void hostfs_read_inode(struct inode *inode) +{ + struct hf_getattr_data data; + uint mask; + + get_host_data(hf_GetAttr, inode->i_ino, &mask, &data); + if (data.sys_error) { + printk(DEVICE_NAME "get_host_data(hf_GetAttr, %ld, ...) had an error\n", + inode->i_ino); + return; + } + +/* DPRINT1("hostfs_read_inode(%ld) flags %lx\n", inode->i_ino, (long)inode->i_flags); */ + + inode->i_flags = 0; + inode->i_mode = data.mode; + inode->i_uid = data.uid; + inode->i_gid = data.gid; + inode->i_nlink = data.link; + GET_HI_LO(inode->i_size, data.size); + GET_HI_LO(inode->i_atime, data.atime); + GET_HI_LO(inode->i_mtime, data.mtime); + GET_HI_LO(inode->i_ctime, data.ctime); + inode->i_blksize = data.blksize; + GET_HI_LO(inode->i_blocks, data.blocks); + + if (S_ISLNK(inode->i_mode)) { + inode->i_op = &hostfs_symlink_iops; + } else if (S_ISDIR(inode->i_mode)) { + inode->i_op = &hostfs_dir_iops; + inode->i_fop = &hostfs_file_dirops; + } else { + inode->i_fop = &hostfs_file_fops; + inode->i_op = &hostfs_file_iops; + inode->i_mapping->a_ops = &hostfs_file_aops; + } +} + +#if defined(HF_RW) +void hostfs_write_inode(struct inode *inode, int sync) +{ + /* + * it might be pointless to implement this function since + * supposedly all changes will have been updated by + * notify_change anyway, but I'm sure it can't hurt + */ + + struct hf_setattr_data odata; + struct hf_common_data idata; + + DPRINT1(DEVICE_NAME " hostfs_write_inode(%ld)\n", inode->i_ino); + + memset(&odata, 0, sizeof odata); + odata.set_atime = 1; + SET_HI_LO(odata.atime, inode->i_atime); + odata.set_mtime = 1; + SET_HI_LO(odata.mtime, inode->i_mtime); + odata.set_mode = 1; + odata.mode = inode->i_mode; + odata.set_uid = 1; + odata.uid = inode->i_uid; + odata.set_gid = 1; + odata.gid = inode->i_gid; + get_host_data(hf_SetAttr, inode->i_ino, &odata, &idata); +} + +static int +hostfs_setattr(struct dentry *dentry, struct iattr *iattr) +{ + struct hf_setattr_data odata; + struct hf_common_data idata; + struct inode *inode = dentry->d_inode; + int error; + + DPRINT1(DEVICE_NAME " hostfs_notify_change(%ld, 0x%lx)\n", inode->i_ino, (long)iattr->ia_valid); + + if ((error = inode_change_ok(inode, iattr))) + return error; + + memset(&odata, 0, sizeof odata); + if (iattr->ia_valid & ATTR_ATIME) { + odata.set_atime = 1; + SET_HI_LO(odata.atime, iattr->ia_atime); + } + if (iattr->ia_valid & ATTR_MTIME) { + odata.set_mtime = 1; + SET_HI_LO(odata.mtime, iattr->ia_mtime); + } + if (iattr->ia_valid & ATTR_MODE) { + odata.set_mode = 1; + odata.mode = iattr->ia_mode; + } + if (iattr->ia_valid & ATTR_UID) { + odata.set_uid = 1; + odata.uid = iattr->ia_uid; + } + if (iattr->ia_valid & ATTR_GID) { + odata.set_gid = 1; + odata.gid = iattr->ia_gid; + } + get_host_data(hf_SetAttr, inode->i_ino, &odata, &idata); + + DPRINT1(" hf_SetAttr returned %d\n", idata.sys_error); + + if (idata.sys_error) + return -idata.sys_error; + + inode_setattr(inode, iattr); + return 0; +} +#endif + +#if defined(HF_RW) +static int hostfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) +{ + kmap(page); + return 0; +} + +static int hostfs_do_writepage(struct inode *inode, struct page *page, + unsigned from, unsigned to) +{ + int offset = (page->index << PAGE_CACHE_SHIFT)+from; + int bytes = to-from; + unsigned long int address = (unsigned long) page_address(page)+from; + uint ino = inode->i_ino; + int res; + + if (page->index > (0x7fffffff/PAGE_CACHE_SIZE)-1) + return -EFBIG; + + spin_lock(&hostfs_position_lock); + + DPRINT1("hostfs_do_writepage: offset:%u bytes:%u address:%lx\n",offset,bytes,address); + + hf_do_seek(ino, offset); + + res = 0; + while (bytes > 0) { + struct hf_write_data odata; + struct hf_common_data idata; + uint cnt = MIN(bytes, XFER_DATA_WORDS * 4); + odata.append = 0; + odata.size = cnt; + memcpy(odata.data, (char *)address, cnt); + HTONSWAP(odata.data); + get_host_data(hf_Write, ino, &odata, &idata); + if (idata.sys_error) { + res = -idata.sys_error; + DPRINT1(DEVICE_NAME " failed with errcode %d\n", res); + ClearPageUptodate(page); + goto got_error; + } + address += cnt; + bytes -= cnt; + res += cnt; + } + DPRINT1(DEVICE_NAME "wrote %d bytes\n", (int)res); + + SetPageUptodate(page); + + got_error: + spin_unlock(&hostfs_position_lock); + + return res; + +} + +static int hostfs_writepage(struct page *page) +{ + struct inode *inode = page->mapping->host; + int err; + int bytes = PAGE_CACHE_SIZE; + if (page->index >= (inode->i_size >> PAGE_CACHE_SHIFT)) + bytes = inode->i_size & (PAGE_CACHE_SIZE-1); + kmap(page); + err = hostfs_do_writepage(inode, page, 0, bytes); + kunmap(page); + return err; +} + +static int +hostfs_commit_write(struct file *file, struct page *page, unsigned from, unsigned to) +{ + struct inode *inode = page->mapping->host; + int err; + + err = hostfs_do_writepage(inode, page, from, to); + kunmap(page); + if (err < 0) + return err; + { + loff_t s = ((page->index) << PAGE_CACHE_SHIFT) + to; + if (s > inode->i_size) + inode->i_size = s; + } + return 0; +} +#endif + +static int +hostfs_inode_readpage(struct file *file, struct page *page) +{ + unsigned long address = (unsigned long) page_address(page); + struct inode *inode = file->f_dentry->d_inode; + uint count = PAGE_CACHE_SIZE; + int err; + + if (page->index > (0xffffffff/PAGE_CACHE_SIZE)-1) + return -EFBIG; + ClearPageError(page); + count = PAGE_SIZE; + +/* DPRINT1("readpage seek %ld\n", (long)page->offset); */ + + kmap(page); + + spin_lock(&hostfs_position_lock); + + if ((err = hf_do_seek(inode->i_ino, page->index << PAGE_CACHE_SHIFT))) { + err = -err; + goto out_error; + } + + while (count > 0) { + struct hf_read_data data; + uint to_read = MIN(count, XFER_DATA_WORDS * 4); + + get_host_data(hf_Read, inode->i_ino, &to_read, &data); + if (data.sys_error) { + err = -data.sys_error; + goto out_error; + } + +/* DPRINT1("readpage count %d out of %d\n", data.size, count); */ + + if (!data.size) { + memset((char*)address,0,count); + break; + } + + NTOHSWAP(data.data); + + memcpy((char *)address, data.data, data.size); + + + address += data.size; + count -= data.size; + } + + err = 0; + spin_unlock(&hostfs_position_lock); + + SetPageUptodate(page); + + out_error: + + kunmap(page); + +/* DPRINT1("postuse %ld\n", (long)atomic_read(&page->count)); */ + + return err; +} + +static int +hostfs_inode_readlink(struct dentry *dentry, char *buf, int size) +{ + struct hf_readlink_data data; + + get_host_data(hf_Readlink, dentry->d_inode->i_ino, NULL, &data); + if (data.sys_error) + return -data.sys_error; + return vfs_readlink(dentry, buf, size, data.filename); +} + +static int +hostfs_inode_follow_link(struct dentry *dentry, struct nameidata *nd) +{ +/* DPRINT1("hostfs_inode_follow_link ino %ld %ld %d\n", base->d_inode->i_ino, dentry->d_inode->i_ino, follow); */ + struct hf_readlink_data data; + + get_host_data(hf_Readlink, dentry->d_inode->i_ino, NULL, &data); + if (data.sys_error) + return -data.sys_error; + return vfs_follow_link(nd, data.filename); +} + +static struct dentry * +hostfs_inode_lookup(struct inode *dir, struct dentry *dentry) +{ + struct hf_lookup_data odata; + struct hf_hnode_data idata; + struct inode *inode; + +/* DPRINT1("hostfs_inode_lookup(%ld, dent %p = %s)\n", dir->i_ino, dentry, dentry->d_name.name); */ + + /* protect lots of stupid strcpy()s */ + if (dentry->d_name.len > HOSTFS_FILENAME_LEN-1) + return ERR_PTR(-ENAMETOOLONG); + + memcpy(odata.filename, dentry->d_name.name, dentry->d_name.len); + odata.filename[dentry->d_name.len] = 0; + HTONSWAP(odata.filename); + get_host_data(hf_Lookup, dir->i_ino, &odata, &idata); + + if (idata.sys_error) { + if (idata.sys_error != ENOENT) + return ERR_PTR(-idata.sys_error); + inode = NULL; + } else + inode = iget(dir->i_sb, idata.hnode); + + d_add(dentry, inode); + + return ERR_PTR(0); +} + +#if defined(HF_RW) +static int +hostfs_inode_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + struct hf_mkdir_data odata; + struct hf_hnode_data idata; + struct inode *inode; + + DPRINT1("hostfs_inode_mkdir()\n"); + + odata.mode = mode; + strcpy(odata.dirname, dentry->d_name.name); + HTONSWAP(odata.dirname); + + get_host_data(hf_MkDir, dir->i_ino, &odata, &idata); + if (idata.sys_error) + return -idata.sys_error;; + + inode = iget(dir->i_sb, idata.hnode); + hostfs_read_inode(inode); + mark_inode_dirty(inode); + d_instantiate(dentry, inode); + + return 0; +} + +static int +hostfs_inode_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct hf_remove_data odata; + struct hf_common_data idata; + + DPRINT1("hostfs_inode_rmdir()\n"); + + strcpy(odata.filename, dentry->d_name.name); + HTONSWAP(odata.filename); + get_host_data(hf_Remove, dir->i_ino, &odata, &idata); + mark_inode_dirty(dir); + + return -idata.sys_error; +} +#endif + +#if defined(HF_RW) +static int +hostfs_inode_create(struct inode *dir, struct dentry *dentry, int mode) +{ + struct hf_create_data odata; + struct hf_hnode_data idata; + struct inode *inode; + + DPRINT1("hostfs_inode_create(%ld, mode = %d)\n", dir->i_ino, mode); + + odata.mode = mode & S_IALLUGO; + odata.excl = 0; + odata.trunc = 0; + strncpy(odata.filename, dentry->d_name.name, sizeof(odata.filename)); + HTONSWAP(odata.filename); + + get_host_data(hf_Create, dir->i_ino, &odata, &idata); + + if (idata.sys_error) { + return -idata.sys_error; + } + + inode = iget(dir->i_sb, idata.hnode); + hostfs_read_inode(inode); + DPRINT1(" read inode mode 0%o uid %d\n", inode->i_mode, inode->i_uid); + mark_inode_dirty(inode); + d_instantiate(dentry, inode); + + return 0; +} + +static void +hostfs_inode_truncate(struct inode *inode) +{ + struct hf_setattr_data odata; + struct hf_common_data idata; + + DPRINT1("hostfs_inode_truncate(inode = %ld)\n", inode->i_ino); + + memset(&odata, 0, sizeof odata); + odata.set_size = 1; + SET_HI_LO(odata.size, inode->i_size); + get_host_data(hf_SetAttr, inode->i_ino, &odata, &idata); + + if (idata.sys_error) { + printk("[hostfs] call to truncate() failed with error %d!\n", idata.sys_error); + } +} + +static int +hostfs_unlink(struct inode *dir, struct dentry *dentry) +{ + struct hf_remove_data odata; + struct hf_common_data idata; + + DPRINT1("hostfs_unlink(dir = %ld, dentry = %s)\n", dir->i_ino, dentry->d_name.name); + + strcpy(odata.filename, dentry->d_name.name); + HTONSWAP(odata.filename); + get_host_data(hf_Remove, dir->i_ino, &odata, &idata); + + return -idata.sys_error; +} + +#endif + + + + +static struct inode_operations hostfs_dir_iops = { + create: RW_FUNC(hostfs_inode_create), + lookup: hostfs_inode_lookup, + unlink: RW_FUNC(hostfs_unlink), + mkdir: RW_FUNC(hostfs_inode_mkdir), + rmdir: RW_FUNC(hostfs_inode_rmdir), +}; + +static struct inode_operations hostfs_symlink_iops = { + readlink: hostfs_inode_readlink, + follow_link: hostfs_inode_follow_link, +}; + +static struct inode_operations hostfs_file_iops = { + truncate: RW_FUNC(hostfs_inode_truncate), + setattr: RW_FUNC(hostfs_setattr) +}; + +static struct address_space_operations hostfs_file_aops = { + readpage: hostfs_inode_readpage, + prepare_write: RW_FUNC(hostfs_prepare_write), + commit_write: RW_FUNC(hostfs_commit_write), + writepage: RW_FUNC(hostfs_writepage), +}; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/hostfs/hostfs_linux.h linux.ac/arch/x86_64/hostfs/hostfs_linux.h --- linux.vanilla/arch/x86_64/hostfs/hostfs_linux.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/hostfs/hostfs_linux.h Wed Oct 10 01:47:38 2001 @@ -0,0 +1,136 @@ +/* + hostfs for Linux + Copyright 2001 Virtutech AB + Copyright 2001 SuSE + + 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, GOOD TITLE or + NON INFRINGEMENT. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Written by Gustav Hĺllberg. The author may be reached as + gustav@virtutech.com. + + Ported by Andi Kleen and Karsten Keil to Linux 2.4. + + $Id: hostfs_linux.h,v 1.2 2001/07/05 18:56:32 ak Exp $ +*/ + + +#include + +#define DRIVER_VERSION "0.2" +#define DEVICE_NAME "[hostfs]" + + +#if CONFIG_SMP==1 +#define __SMP__ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define TARGETOS_LINUX +#include "hostfs.h" +#include "hostfs_data.h" + +#ifdef CONFIG_HOSTFS_RW +#undef HF_RW +#define HF_RW 1 +#else +#undef HF_RW +#endif + +#if defined(HF_RW) +# define RW_FUNC(func) func +#else +# define RW_FUNC(func) NULL +#endif + +#define GET_HI_LO(tgt, src) \ +do { \ + typedef int check_tgt_size[sizeof (tgt) == 8 || sizeof (tgt) == 4 ? 1 : -1]; \ + if (sizeof (tgt) == 4) \ + tgt = src ## _lo; \ + else if (sizeof (tgt) == 8) { \ + tgt = src ## _hi; \ + tgt <<= 16; \ + tgt <<= 16; \ + tgt |= src ## _lo; \ + } else \ + printk("hostfs GET_HI_LO panic, sizeof (" #tgt ") in %s:%d == %dn", \ + __FILE__, __LINE__, (int)sizeof (tgt)); \ +} while (0) + +#define SET_HI_LO(tgt, src) \ +do { \ + typedef int check_src_size[sizeof (src) == 8 || sizeof (src) == 4 ? 1 : -1]; \ + if (sizeof (src) == 4) { \ + tgt ## _lo = src; \ + tgt ## _hi = 0; \ + } else if (sizeof (src) == 8) { \ + unsigned long long tmp = src; \ + tgt ## _lo = (uint)src; \ + tgt ## _hi = tmp >> 32; \ + } \ +} while (0) + +#define DEBUG_LEVEL 2 +#define HOSTFS_BLOCK_BITS 10 +#define HOSTFS_BLOCK_SIZE (1 << HOSTFS_BLOCK_BITS) + +#define HOSTFS_ROOT_INO 1 + +#define DPRINT1 if (DEBUG_LEVEL >= 1) printk +#define DPRINTINODE1(inode) do { if (DEBUG_LEVEL >= 1) print_inode(inode); } while (0) +#define DPRINTFILE1(file) do { } while (0) + +#define ALIGN(n, a) (((n) + (a) - 1) & ~(a)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#define NTOHSWAP(field) \ +do { \ + int __i; \ + for (__i = 0; __i < (sizeof (field) + 3) / 4; ++__i) { \ + ((uint *)&field)[__i] = ntohl(((uint *)&field)[__i]); \ + } \ +} while(0) + +#define HTONSWAP(field) NTOHSWAP(field) + +extern struct file_operations hostfs_file_fops; +extern struct file_operations hostfs_file_dirops; + +void hostfs_read_inode(struct inode *inode); +void hostfs_write_inode(struct inode *inode, int sync); + +extern spinlock_t get_host_data_lock; +extern spinlock_t hostfs_position_lock; + + + +/* host interface */ +int get_host_data(host_func_t func, host_node_t hnode, void *in_buf, + void *out_buf); +int hf_do_seek(ino_t inode, loff_t off); +int init_host_fs(void); + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/hostfs/hostfs_super.c linux.ac/arch/x86_64/hostfs/hostfs_super.c --- linux.vanilla/arch/x86_64/hostfs/hostfs_super.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/hostfs/hostfs_super.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,135 @@ +/* + hostfs for Linux + Copyright 2001 Virtutech AB + Copyright 2001 SuSE + + 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, GOOD TITLE or + NON INFRINGEMENT. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Written by Gustav Hĺllberg. The author may be reached as + gustav@virtutech.com. + + Ported by Andi Kleen and Karsten Keil to Linux 2.4. + + $Id: hostfs_super.c,v 1.3 2001/07/05 18:56:32 ak Exp $ +*/ + + +#include "hostfs_linux.h" + +static struct super_operations hostfs_super_ops; + + +static int +hostfs_statfs(struct super_block *sb, struct statfs *buf) +{ + struct hf_vfsstat_data data; + + get_host_data(hf_VfsStat, 0, NULL, &data); + + memset(buf, 0, sizeof *buf); + buf->f_type = HOSTFS_MAGIC; + buf->f_bsize = data.bsize; + buf->f_blocks = data.blocks; + buf->f_bfree = data.bfree; + buf->f_bavail = data.bavail; + buf->f_files = data.files; + buf->f_ffree = data.ffree; + buf->f_namelen = HOSTFS_FILENAME_LEN; + + return 0; +} + + +static struct super_block * +hostfs_read_super(struct super_block *sb, void *data, int silent) +{ + struct inode *root_inode; + struct hf_handshake_data odata; + struct hf_handshake_reply_data idata; + + odata.version = HOSTFS_VERSION; + get_host_data(hf_Handshake, 0, &odata, &idata); + + if (idata.sys_error || idata.magic != HOSTFS_MAGIC) { + printk(DEVICE_NAME " Handshake with Simics module failed!\n"); + goto out_fail; + } + + printk(DEVICE_NAME " mounted\n"); + + sb->s_op = &hostfs_super_ops; + sb->s_magic = HOSTFS_MAGIC; + sb->s_blocksize = HOSTFS_BLOCK_SIZE; + sb->s_blocksize_bits = HOSTFS_BLOCK_BITS; +#if !defined(HF_RW) + sb->s_flags |= MS_RDONLY; +#endif + root_inode = new_inode(sb); + if (!root_inode) + goto out_fail; + root_inode->i_ino = HOSTFS_ROOT_INO; + hostfs_read_inode(root_inode); + sb->s_root = d_alloc_root(root_inode); + if (!sb->s_root) + goto out_no_root; + + return sb; + + out_no_root: + printk(DEVICE_NAME " get root inode failed\n"); + iput(root_inode); + + out_fail: + return NULL; +} + + +static void +hostfs_put_super(struct super_block *sb) +{ + struct hf_common_data idata; + get_host_data(hf_Unmount, 0, NULL, &idata); + printk(DEVICE_NAME " unmounted\n"); +} + + +static struct super_operations hostfs_super_ops = { + read_inode: hostfs_read_inode, + write_inode: RW_FUNC(hostfs_write_inode), + put_super: hostfs_put_super, + statfs: hostfs_statfs, +}; + +static DECLARE_FSTYPE(host_fs_type, "simicsfs", hostfs_read_super, 0); + +static int +__init init_hostfs(void) +{ + printk(DEVICE_NAME " loaded\n"); + init_host_fs(); + return register_filesystem(&host_fs_type); +} + +static void +__exit cleanup_hostfs(void) +{ + unregister_filesystem(&host_fs_type); + printk(DEVICE_NAME " unloaded\n"); +} + +EXPORT_NO_SYMBOLS; + +module_init(init_hostfs) +module_exit(cleanup_hostfs) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/ia32/Makefile linux.ac/arch/x86_64/ia32/Makefile --- linux.vanilla/arch/x86_64/ia32/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/ia32/Makefile Wed Oct 10 01:47:38 2001 @@ -0,0 +1,18 @@ +# +# Makefile for the ia32 kernel emulation subsystem. +# + +.S.s: + $(CPP) $(AFLAGS) -o $*.s $< +.S.o: + $(CC) $(AFLAGS) -c -o $*.o $< + +all: ia32.o + +O_TARGET := ia32.o +obj-$(CONFIG_IA32_EMULATION) := ia32entry.o sys_ia32.o ia32_ioctl.o ia32_signal.o ia32_binfmt.o \ + socket32.o ptrace32.o + +clean:: + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/ia32/ia32_binfmt.c linux.ac/arch/x86_64/ia32/ia32_binfmt.c --- linux.vanilla/arch/x86_64/ia32/ia32_binfmt.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/ia32/ia32_binfmt.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,164 @@ +/* + * Written 2000 by Andi Kleen. + * + * Losely based on the sparc64 and IA64 32bit emulation loaders. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define IA32_EMULATOR 1 + +#define IA32_PAGE_OFFSET 0xE0000000 +#define IA32_STACK_TOP IA32_PAGE_OFFSET +#define ELF_ET_DYN_BASE (IA32_PAGE_OFFSET/3 + 0x1000000) + +#undef ELF_ARCH +#define ELF_ARCH EM_386 + +#undef ELF_CLASS +#define ELF_CLASS ELFCLASS32 + +#define ELF_DATA ELFDATA2LSB +//#define USE_ELF_CORE_DUMP + +#define __ASM_X86_64_ELF_H 1 +#include +#include + +typedef __u32 elf_greg_t; + +typedef elf_greg_t elf_gregset_t[8]; + +/* FIXME -- wrong */ +typedef struct user_i387_ia32_struct elf_fpregset_t; +typedef struct user_i387_struct elf_fpxregset_t; + +#undef elf_check_arch +#define elf_check_arch(x) \ + ((x)->e_machine == EM_386) + +#define ELF_EXEC_PAGESIZE PAGE_SIZE +#define ELF_HWCAP (boot_cpu_data.x86_capability[0]) +#define ELF_PLATFORM ("i686") +#define SET_PERSONALITY(ex, ibcs2) \ +do { \ + set_personality((ibcs2)?PER_SVR4:current->personality); \ + current->thread.flags |= THREAD_IA32; \ +} while (0) + +/* Override some function names */ +#define elf_format elf32_format + +#define init_elf_binfmt init_elf32_binfmt +#define exit_elf_binfmt exit_elf32_binfmt + +#define load_elf_binary load_elf32_binary + +#undef CONFIG_BINFMT_ELF +#ifdef CONFIG_BINFMT_ELF32 +# define CONFIG_BINFMT_ELF CONFIG_BINFMT_ELF32 +#endif + +#undef CONFIG_BINFMT_ELF_MODULE +#ifdef CONFIG_BINFMT_ELF32_MODULE +# define CONFIG_BINFMT_ELF_MODULE CONFIG_BINFMT_ELF32_MODULE +#endif + +#define ELF_PLAT_INIT(r) elf32_init(r) +#define setup_arg_pages(bprm) ia32_setup_arg_pages(bprm) + +#undef start_thread +#define start_thread(regs,new_rip,new_rsp) do { \ + __asm__("movl %0,%%fs": :"r" (0)); \ + __asm__("movl %0,%%es; movl %0,%%ds": :"r" (__USER32_DS)); \ + wrmsrl(MSR_KERNEL_GS_BASE, 0); \ + (regs)->rip = (new_rip); \ + (regs)->rsp = (new_rsp); \ + (regs)->eflags = 0x200; \ + (regs)->cs = __USER32_CS; \ + (regs)->ss = __USER32_DS; \ + set_fs(USER_DS); \ +} while(0) + + +MODULE_DESCRIPTION("Binary format loader for compatibility with IA32 ELF binaries."); +MODULE_AUTHOR("Eric Youngdale, Andi Kleen"); + +#undef MODULE_DESCRIPTION +#undef MODULE_AUTHOR + +#define elf_addr_t __u32 +#define elf_caddr_t __u32 + +static void elf32_init(struct pt_regs *); + +#include "../../../fs/binfmt_elf.c" + +static void elf32_init(struct pt_regs *regs) +{ + regs->rdi = 0; + regs->rsi = 0; + regs->rdx = 0; + regs->rcx = 0; + regs->rax = 0; + current->thread.fs = 0; current->thread.gs = 0; + current->thread.fsindex = 0; current->thread.gsindex = 0; + current->thread.ds = __USER_DS; current->thread.es == __USER_DS; +} + +extern void put_dirty_page(struct task_struct * tsk, struct page *page, unsigned long address); + + +int ia32_setup_arg_pages(struct linux_binprm *bprm) +{ + unsigned long stack_base; + struct vm_area_struct *mpnt; + int i; + + stack_base = IA32_STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE; + + bprm->p += stack_base; + if (bprm->loader) + bprm->loader += stack_base; + bprm->exec += stack_base; + + mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + if (!mpnt) + return -ENOMEM; + + down_write(¤t->mm->mmap_sem); + { + mpnt->vm_mm = current->mm; + mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; + mpnt->vm_end = IA32_STACK_TOP; + mpnt->vm_page_prot = PAGE_COPY; + mpnt->vm_flags = VM_STACK_FLAGS; + mpnt->vm_ops = NULL; + mpnt->vm_pgoff = 0; + mpnt->vm_file = NULL; + mpnt->vm_private_data = (void *) 0; + insert_vm_struct(current->mm, mpnt); + current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; + } + + for (i = 0 ; i < MAX_ARG_PAGES ; i++) { + struct page *page = bprm->page[i]; + if (page) { + bprm->page[i] = NULL; + current->mm->rss++; + put_dirty_page(current,page,stack_base); + } + stack_base += PAGE_SIZE; + } + up_write(¤t->mm->mmap_sem); + + return 0; +} + +/* TOADD: set thread->flags */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/ia32/ia32_ioctl.c linux.ac/arch/x86_64/ia32/ia32_ioctl.c --- linux.vanilla/arch/x86_64/ia32/ia32_ioctl.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/ia32/ia32_ioctl.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,3843 @@ +/* $Id: ia32_ioctl.c,v 1.2 2001/07/05 06:28:42 ak Exp $ + * ioctl32.c: Conversion between 32bit and 64bit native ioctls. + * + * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 2001 Andi Kleen, SuSE Labs + * + * These routines maintain argument size conversion between 32bit and 64bit + * ioctls. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +/* Ugh. This header really is not clean */ +#define min min +#define max max +#include +#endif /* LVM */ + +#include +/* Ugly hack. */ +#undef __KERNEL__ +#include +#define __KERNEL__ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define A(__x) ((void *)(unsigned long)(__x)) +#define AA(__x) A(__x) + +/* Aiee. Someone does not find a difference between int and long */ +#define EXT2_IOC32_GETFLAGS _IOR('f', 1, int) +#define EXT2_IOC32_SETFLAGS _IOW('f', 2, int) +#define EXT2_IOC32_GETVERSION _IOR('v', 1, int) +#define EXT2_IOC32_SETVERSION _IOW('v', 2, int) + +extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); + +static int w_long(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + int err; + unsigned long val; + + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&val); + set_fs (old_fs); + if (!err && put_user(val, (u32 *)arg)) + return -EFAULT; + return err; +} + +static int rw_long(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + int err; + unsigned long val; + + if(get_user(val, (u32 *)arg)) + return -EFAULT; + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&val); + set_fs (old_fs); + if (!err && put_user(val, (u32 *)arg)) + return -EFAULT; + return err; +} + +static int do_ext2_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + /* These are just misnamed, they actually get/put from/to user an int */ + switch (cmd) { + case EXT2_IOC32_GETFLAGS: cmd = EXT2_IOC_GETFLAGS; break; + case EXT2_IOC32_SETFLAGS: cmd = EXT2_IOC_SETFLAGS; break; + case EXT2_IOC32_GETVERSION: cmd = EXT2_IOC_GETVERSION; break; + case EXT2_IOC32_SETVERSION: cmd = EXT2_IOC_SETVERSION; break; + } + return sys_ioctl(fd, cmd, arg); +} + +struct video_tuner32 { + s32 tuner; + u8 name[32]; + u32 rangelow, rangehigh; + u32 flags; + u16 mode, signal; +}; + +static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 *up) +{ + int i; + + if(get_user(kp->tuner, &up->tuner)) + return -EFAULT; + for(i = 0; i < 32; i++) + __get_user(kp->name[i], &up->name[i]); + __get_user(kp->rangelow, &up->rangelow); + __get_user(kp->rangehigh, &up->rangehigh); + __get_user(kp->flags, &up->flags); + __get_user(kp->mode, &up->mode); + __get_user(kp->signal, &up->signal); + return 0; +} + +static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 *up) +{ + int i; + + if(put_user(kp->tuner, &up->tuner)) + return -EFAULT; + for(i = 0; i < 32; i++) + __put_user(kp->name[i], &up->name[i]); + __put_user(kp->rangelow, &up->rangelow); + __put_user(kp->rangehigh, &up->rangehigh); + __put_user(kp->flags, &up->flags); + __put_user(kp->mode, &up->mode); + __put_user(kp->signal, &up->signal); + return 0; +} + +struct video_buffer32 { + /* void * */ u32 base; + s32 height, width, depth, bytesperline; +}; + +static int get_video_buffer32(struct video_buffer *kp, struct video_buffer32 *up) +{ + u32 tmp; + + if(get_user(tmp, &up->base)) + return -EFAULT; + kp->base = (void *) ((unsigned long)tmp); + __get_user(kp->height, &up->height); + __get_user(kp->width, &up->width); + __get_user(kp->depth, &up->depth); + __get_user(kp->bytesperline, &up->bytesperline); + return 0; +} + +static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 *up) +{ + u32 tmp = (u32)((unsigned long)kp->base); + + if(put_user(tmp, &up->base)) + return -EFAULT; + __put_user(kp->height, &up->height); + __put_user(kp->width, &up->width); + __put_user(kp->depth, &up->depth); + __put_user(kp->bytesperline, &up->bytesperline); + return 0; +} + +struct video_clip32 { + s32 x, y, width, height; + /* struct video_clip32 * */ u32 next; +}; + +struct video_window32 { + u32 x, y, width, height, chromakey, flags; + /* struct video_clip32 * */ u32 clips; + s32 clipcount; +}; + +static void free_kvideo_clips(struct video_window *kp) +{ + struct video_clip *cp; + + cp = kp->clips; + if(cp != NULL) + kfree(cp); +} + +static int get_video_window32(struct video_window *kp, struct video_window32 *up) +{ + struct video_clip32 *ucp; + struct video_clip *kcp; + int nclips, err, i; + u32 tmp; + + if(get_user(kp->x, &up->x)) + return -EFAULT; + __get_user(kp->y, &up->y); + __get_user(kp->width, &up->width); + __get_user(kp->height, &up->height); + __get_user(kp->chromakey, &up->chromakey); + __get_user(kp->flags, &up->flags); + __get_user(kp->clipcount, &up->clipcount); + __get_user(tmp, &up->clips); + ucp = (struct video_clip32 *)A(tmp); + kp->clips = NULL; + + nclips = kp->clipcount; + if(nclips == 0) + return 0; + + if(ucp == 0) + return -EINVAL; + + /* Peculiar interface... */ + if(nclips < 0) + nclips = VIDEO_CLIPMAP_SIZE; + + kcp = kmalloc(nclips * sizeof(struct video_clip), GFP_KERNEL); + err = -ENOMEM; + if(kcp == NULL) + goto cleanup_and_err; + + kp->clips = kcp; + for(i = 0; i < nclips; i++) { + __get_user(kcp[i].x, &ucp[i].x); + __get_user(kcp[i].y, &ucp[i].y); + __get_user(kcp[i].width, &ucp[i].width); + __get_user(kcp[i].height, &ucp[i].height); + kcp[nclips].next = NULL; + } + + return 0; + +cleanup_and_err: + free_kvideo_clips(kp); + return err; +} + +/* You get back everything except the clips... */ +static int put_video_window32(struct video_window *kp, struct video_window32 *up) +{ + if(put_user(kp->x, &up->x)) + return -EFAULT; + __put_user(kp->y, &up->y); + __put_user(kp->width, &up->width); + __put_user(kp->height, &up->height); + __put_user(kp->chromakey, &up->chromakey); + __put_user(kp->flags, &up->flags); + __put_user(kp->clipcount, &up->clipcount); + return 0; +} + +#define VIDIOCGTUNER32 _IOWR('v',4, struct video_tuner32) +#define VIDIOCSTUNER32 _IOW('v',5, struct video_tuner32) +#define VIDIOCGWIN32 _IOR('v',9, struct video_window32) +#define VIDIOCSWIN32 _IOW('v',10, struct video_window32) +#define VIDIOCGFBUF32 _IOR('v',11, struct video_buffer32) +#define VIDIOCSFBUF32 _IOW('v',12, struct video_buffer32) +#define VIDIOCGFREQ32 _IOR('v',14, u32) +#define VIDIOCSFREQ32 _IOW('v',15, u32) + +static int do_video_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + union { + struct video_tuner vt; + struct video_buffer vb; + struct video_window vw; + unsigned long vx; + } karg; + mm_segment_t old_fs = get_fs(); + void *up = (void *)arg; + int err = 0; + + /* First, convert the command. */ + switch(cmd) { + case VIDIOCGTUNER32: cmd = VIDIOCGTUNER; break; + case VIDIOCSTUNER32: cmd = VIDIOCSTUNER; break; + case VIDIOCGWIN32: cmd = VIDIOCGWIN; break; + case VIDIOCSWIN32: cmd = VIDIOCSWIN; break; + case VIDIOCGFBUF32: cmd = VIDIOCGFBUF; break; + case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break; + case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break; + case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break; + }; + + switch(cmd) { + case VIDIOCSTUNER: + case VIDIOCGTUNER: + err = get_video_tuner32(&karg.vt, up); + break; + + case VIDIOCSWIN: + err = get_video_window32(&karg.vw, up); + break; + + case VIDIOCSFBUF: + err = get_video_buffer32(&karg.vb, up); + break; + + case VIDIOCSFREQ: + err = get_user(karg.vx, (u32 *)up); + break; + }; + if(err) + goto out; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&karg); + set_fs(old_fs); + + if(cmd == VIDIOCSWIN) + free_kvideo_clips(&karg.vw); + + if(err == 0) { + switch(cmd) { + case VIDIOCGTUNER: + err = put_video_tuner32(&karg.vt, up); + break; + + case VIDIOCGWIN: + err = put_video_window32(&karg.vw, up); + break; + + case VIDIOCGFBUF: + err = put_video_buffer32(&karg.vb, up); + break; + + case VIDIOCGFREQ: + err = put_user(((u32)karg.vx), (u32 *)up); + break; + }; + } +out: + return err; +} + +struct timeval32 { + int tv_sec; + int tv_usec; +}; + +static int do_siocgstamp(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct timeval32 *up = (struct timeval32 *)arg; + struct timeval ktv; + mm_segment_t old_fs = get_fs(); + int err; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&ktv); + set_fs(old_fs); + if(!err) { + err = put_user(ktv.tv_sec, &up->tv_sec); + err |= __put_user(ktv.tv_usec, &up->tv_usec); + } + return err; +} + +struct ifmap32 { + u32 mem_start; + u32 mem_end; + unsigned short base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; +}; + +struct ifreq32 { +#define IFHWADDRLEN 6 +#define IFNAMSIZ 16 + union { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + } ifr_ifrn; + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_ivalue; + int ifru_mtu; + struct ifmap32 ifru_map; + char ifru_slave[IFNAMSIZ]; /* Just fits the size */ + char ifru_newname[IFNAMSIZ]; + __kernel_caddr_t32 ifru_data; + } ifr_ifru; +}; + +struct ifconf32 { + int ifc_len; /* size of buffer */ + __kernel_caddr_t32 ifcbuf; +}; + +static int dev_ifname32(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct net_device *dev; + struct ifreq32 ifr32; + int err; + + if (copy_from_user(&ifr32, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + + dev = dev_get_by_index(ifr32.ifr_ifindex); + if (!dev) + return -ENODEV; + + strncpy(ifr32.ifr_name, dev->name, sizeof(ifr32.ifr_name)-1); + ifr32.ifr_name[sizeof(ifr32.ifr_name)-1] = 0; + + err = copy_to_user((struct ifreq32 *)arg, &ifr32, sizeof(struct ifreq32)); + return (err ? -EFAULT : 0); +} + +static int dev_ifconf(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifconf32 ifc32; + struct ifconf ifc; + struct ifreq32 *ifr32; + struct ifreq *ifr; + mm_segment_t old_fs; + unsigned int i, j; + int err; + + if (copy_from_user(&ifc32, (struct ifconf32 *)arg, sizeof(struct ifconf32))) + return -EFAULT; + + if(ifc32.ifcbuf == 0) { + ifc32.ifc_len = 0; + ifc.ifc_len = 0; + ifc.ifc_buf = NULL; + } else { + ifc.ifc_len = ((ifc32.ifc_len / sizeof (struct ifreq32)) + 1) * + sizeof (struct ifreq); + ifc.ifc_buf = kmalloc (ifc.ifc_len, GFP_KERNEL); + if (!ifc.ifc_buf) + return -ENOMEM; + } + ifr = ifc.ifc_req; + ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf); + for (i = 0; i < ifc32.ifc_len; i += sizeof (struct ifreq32)) { + if (copy_from_user(ifr, ifr32, sizeof (struct ifreq32))) { + kfree (ifc.ifc_buf); + return -EFAULT; + } + ifr++; + ifr32++; + } + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, SIOCGIFCONF, (unsigned long)&ifc); + set_fs (old_fs); + if (!err) { + ifr = ifc.ifc_req; + ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf); + for (i = 0, j = 0; i < ifc32.ifc_len && j < ifc.ifc_len; + i += sizeof (struct ifreq32), j += sizeof (struct ifreq)) { + int k = copy_to_user(ifr32, ifr, sizeof (struct ifreq32)); + ifr32++; + ifr++; + if (k) { + err = -EFAULT; + break; + } + + } + if (!err) { + if (ifc32.ifcbuf == 0) { + /* Translate from 64-bit structure multiple to + * a 32-bit one. + */ + i = ifc.ifc_len; + i = ((i / sizeof(struct ifreq)) * sizeof(struct ifreq32)); + ifc32.ifc_len = i; + } else { + if (i <= ifc32.ifc_len) + ifc32.ifc_len = i; + else + ifc32.ifc_len = i - sizeof (struct ifreq32); + } + if (copy_to_user((struct ifconf32 *)arg, &ifc32, sizeof(struct ifconf32))) + err = -EFAULT; + } + } + if(ifc.ifc_buf != NULL) + kfree (ifc.ifc_buf); + return err; +} + +static int ethtool_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifreq ifr; + mm_segment_t old_fs; + int err, len; + u32 data, ethcmd; + + if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL); + if (!ifr.ifr_data) + return -EAGAIN; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + + if (get_user(ethcmd, (u32 *)A(data))) { + err = -EFAULT; + goto out; + } + switch (ethcmd) { + case ETHTOOL_GDRVINFO: len = sizeof(struct ethtool_drvinfo); break; + case ETHTOOL_GSET: + case ETHTOOL_SSET: + default: len = sizeof(struct ethtool_cmd); break; + } + + if (copy_from_user(ifr.ifr_data, (char *)A(data), len)) { + err = -EFAULT; + goto out; + } + + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&ifr); + set_fs (old_fs); + if (!err) { + u32 data; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + len = copy_to_user((char *)A(data), ifr.ifr_data, len); + if (len) + err = -EFAULT; + } + +out: + free_page((unsigned long)ifr.ifr_data); + return err; +} + +static int dev_ifsioc(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifreq ifr; + mm_segment_t old_fs; + int err; + + switch (cmd) { + case SIOCSIFMAP: + err = copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(ifr.ifr_name)); + err |= __get_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_start)); + err |= __get_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_end)); + err |= __get_user(ifr.ifr_map.base_addr, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.base_addr)); + err |= __get_user(ifr.ifr_map.irq, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.irq)); + err |= __get_user(ifr.ifr_map.dma, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.dma)); + err |= __get_user(ifr.ifr_map.port, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.port)); + if (err) + return -EFAULT; + break; + case SIOCGPPPSTATS: + case SIOCGPPPCSTATS: + case SIOCGPPPVER: + if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL); + if (!ifr.ifr_data) + return -EAGAIN; + break; + default: + if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + break; + } + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&ifr); + set_fs (old_fs); + if (!err) { + switch (cmd) { + case SIOCGIFFLAGS: + case SIOCGIFMETRIC: + case SIOCGIFMTU: + case SIOCGIFMEM: + case SIOCGIFHWADDR: + case SIOCGIFINDEX: + case SIOCGIFADDR: + case SIOCGIFBRDADDR: + case SIOCGIFDSTADDR: + case SIOCGIFNETMASK: + case SIOCGIFTXQLEN: + if (copy_to_user((struct ifreq32 *)arg, &ifr, sizeof(struct ifreq32))) + return -EFAULT; + break; + case SIOCGPPPSTATS: + case SIOCGPPPCSTATS: + case SIOCGPPPVER: + { + u32 data; + int len; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + if(cmd == SIOCGPPPVER) + len = strlen((char *)ifr.ifr_data) + 1; + else if(cmd == SIOCGPPPCSTATS) + len = sizeof(struct ppp_comp_stats); + else + len = sizeof(struct ppp_stats); + + len = copy_to_user((char *)A(data), ifr.ifr_data, len); + free_page((unsigned long)ifr.ifr_data); + if(len) + return -EFAULT; + break; + } + case SIOCGIFMAP: + err = copy_to_user((struct ifreq32 *)arg, &ifr, sizeof(ifr.ifr_name)); + err |= __put_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_start)); + err |= __put_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_end)); + err |= __put_user(ifr.ifr_map.base_addr, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.base_addr)); + err |= __put_user(ifr.ifr_map.irq, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.irq)); + err |= __put_user(ifr.ifr_map.dma, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.dma)); + err |= __put_user(ifr.ifr_map.port, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.port)); + if (err) + err = -EFAULT; + break; + } + } else { + switch (cmd) { + case SIOCGPPPSTATS: + case SIOCGPPPCSTATS: + case SIOCGPPPVER: + free_page((unsigned long)ifr.ifr_data); + break; + } + } + return err; +} + +struct rtentry32 { + u32 rt_pad1; + struct sockaddr rt_dst; /* target address */ + struct sockaddr rt_gateway; /* gateway addr (RTF_GATEWAY) */ + struct sockaddr rt_genmask; /* target network mask (IP) */ + unsigned short rt_flags; + short rt_pad2; + u32 rt_pad3; + unsigned char rt_tos; + unsigned char rt_class; + short rt_pad4; + short rt_metric; /* +1 for binary compatibility! */ + /* char * */ u32 rt_dev; /* forcing the device at add */ + u32 rt_mtu; /* per route MTU/Window */ + u32 rt_window; /* Window clamping */ + unsigned short rt_irtt; /* Initial RTT */ + +}; + +struct in6_rtmsg32 { + struct in6_addr rtmsg_dst; + struct in6_addr rtmsg_src; + struct in6_addr rtmsg_gateway; + u32 rtmsg_type; + u16 rtmsg_dst_len; + u16 rtmsg_src_len; + u32 rtmsg_metric; + u32 rtmsg_info; + u32 rtmsg_flags; + s32 rtmsg_ifindex; +}; + +extern struct socket *sockfd_lookup(int fd, int *err); + +static int routing_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + int ret; + void *r = NULL; + struct in6_rtmsg r6; + struct rtentry r4; + char devname[16]; + u32 rtdev; + mm_segment_t old_fs = get_fs(); + + struct socket *mysock = sockfd_lookup(fd, &ret); + + if (mysock && mysock->sk && mysock->sk->family == AF_INET6) { /* ipv6 */ + ret = copy_from_user (&r6.rtmsg_dst, &(((struct in6_rtmsg32 *)arg)->rtmsg_dst), + 3 * sizeof(struct in6_addr)); + ret |= __get_user (r6.rtmsg_type, &(((struct in6_rtmsg32 *)arg)->rtmsg_type)); + ret |= __get_user (r6.rtmsg_dst_len, &(((struct in6_rtmsg32 *)arg)->rtmsg_dst_len)); + ret |= __get_user (r6.rtmsg_src_len, &(((struct in6_rtmsg32 *)arg)->rtmsg_src_len)); + ret |= __get_user (r6.rtmsg_metric, &(((struct in6_rtmsg32 *)arg)->rtmsg_metric)); + ret |= __get_user (r6.rtmsg_info, &(((struct in6_rtmsg32 *)arg)->rtmsg_info)); + ret |= __get_user (r6.rtmsg_flags, &(((struct in6_rtmsg32 *)arg)->rtmsg_flags)); + ret |= __get_user (r6.rtmsg_ifindex, &(((struct in6_rtmsg32 *)arg)->rtmsg_ifindex)); + + r = (void *) &r6; + } else { /* ipv4 */ + ret = copy_from_user (&r4.rt_dst, &(((struct rtentry32 *)arg)->rt_dst), 3 * sizeof(struct sockaddr)); + ret |= __get_user (r4.rt_flags, &(((struct rtentry32 *)arg)->rt_flags)); + ret |= __get_user (r4.rt_metric, &(((struct rtentry32 *)arg)->rt_metric)); + ret |= __get_user (r4.rt_mtu, &(((struct rtentry32 *)arg)->rt_mtu)); + ret |= __get_user (r4.rt_window, &(((struct rtentry32 *)arg)->rt_window)); + ret |= __get_user (r4.rt_irtt, &(((struct rtentry32 *)arg)->rt_irtt)); + ret |= __get_user (rtdev, &(((struct rtentry32 *)arg)->rt_dev)); + if (rtdev) { + ret |= copy_from_user (devname, (char *)A(rtdev), 15); + r4.rt_dev = devname; devname[15] = 0; + } else + r4.rt_dev = 0; + + r = (void *) &r4; + } + + if (ret) + return -EFAULT; + + set_fs (KERNEL_DS); + ret = sys_ioctl (fd, cmd, (long) r); + set_fs (old_fs); + + return ret; +} + +struct hd_geometry32 { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + u32 start; +}; + +static int hdio_getgeo(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct hd_geometry geo; + int err; + + set_fs (KERNEL_DS); + err = sys_ioctl(fd, HDIO_GETGEO, (unsigned long)&geo); + set_fs (old_fs); + if (!err) { + err = copy_to_user ((struct hd_geometry32 *)arg, &geo, 4); + err |= __put_user (geo.start, &(((struct hd_geometry32 *)arg)->start)); + } + return err ? -EFAULT : 0; +} + +struct fbcmap32 { + int index; /* first element (0 origin) */ + int count; + u32 red; + u32 green; + u32 blue; +}; + +struct fb_fix_screeninfo32 { + char id[16]; + __kernel_caddr_t32 smem_start; + __u32 smem_len; + __u32 type; + __u32 type_aux; + __u32 visual; + __u16 xpanstep; + __u16 ypanstep; + __u16 ywrapstep; + __u32 line_length; + __kernel_caddr_t32 mmio_start; + __u32 mmio_len; + __u32 accel; + __u16 reserved[3]; +}; + +struct fb_cmap32 { + __u32 start; + __u32 len; + __kernel_caddr_t32 red; + __kernel_caddr_t32 green; + __kernel_caddr_t32 blue; + __kernel_caddr_t32 transp; +}; + +static int fb_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + u32 red = 0, green = 0, blue = 0, transp = 0; + struct fb_fix_screeninfo fix; + struct fb_cmap cmap; + void *karg; + int err = 0; + + memset(&cmap, 0, sizeof(cmap)); + switch (cmd) { + case FBIOGET_FSCREENINFO: + karg = &fix; + break; + case FBIOGETCMAP: + case FBIOPUTCMAP: + karg = &cmap; + err = __get_user(cmap.start, &((struct fb_cmap32 *)arg)->start); + err |= __get_user(cmap.len, &((struct fb_cmap32 *)arg)->len); + err |= __get_user(red, &((struct fb_cmap32 *)arg)->red); + err |= __get_user(green, &((struct fb_cmap32 *)arg)->green); + err |= __get_user(blue, &((struct fb_cmap32 *)arg)->blue); + err |= __get_user(transp, &((struct fb_cmap32 *)arg)->transp); + if (err) { + err = -EFAULT; + goto out; + } + err = -ENOMEM; + cmap.red = kmalloc(cmap.len * sizeof(__u16), GFP_KERNEL); + if (!cmap.red) + goto out; + cmap.green = kmalloc(cmap.len * sizeof(__u16), GFP_KERNEL); + if (!cmap.green) + goto out; + cmap.blue = kmalloc(cmap.len * sizeof(__u16), GFP_KERNEL); + if (!cmap.blue) + goto out; + if (transp) { + cmap.transp = kmalloc(cmap.len * sizeof(__u16), GFP_KERNEL); + if (!cmap.transp) + goto out; + } + + if (cmd == FBIOGETCMAP) + break; + + err = __copy_from_user(cmap.red, (char *)A(red), cmap.len * sizeof(__u16)); + err |= __copy_from_user(cmap.green, (char *)A(green), cmap.len * sizeof(__u16)); + err |= __copy_from_user(cmap.blue, (char *)A(blue), cmap.len * sizeof(__u16)); + if (cmap.transp) err |= __copy_from_user(cmap.transp, (char *)A(transp), cmap.len * sizeof(__u16)); + if (err) { + err = -EFAULT; + goto out; + } + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("%s: Unknown fb ioctl cmd fd(%d) " + "cmd(%08x) arg(%08lx)\n", + __FUNCTION__, fd, cmd, arg); + } while(0); + return -ENOSYS; + } + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)karg); + set_fs(old_fs); + if (err) + goto out; + switch (cmd) { + case FBIOGET_FSCREENINFO: + err = __copy_to_user((char *)((struct fb_fix_screeninfo32 *)arg)->id, (char *)fix.id, sizeof(fix.id)); + err |= __put_user((__u32)(unsigned long)fix.smem_start, &((struct fb_fix_screeninfo32 *)arg)->smem_start); + err |= __put_user(fix.smem_len, &((struct fb_fix_screeninfo32 *)arg)->smem_len); + err |= __put_user(fix.type, &((struct fb_fix_screeninfo32 *)arg)->type); + err |= __put_user(fix.type_aux, &((struct fb_fix_screeninfo32 *)arg)->type_aux); + err |= __put_user(fix.visual, &((struct fb_fix_screeninfo32 *)arg)->visual); + err |= __put_user(fix.xpanstep, &((struct fb_fix_screeninfo32 *)arg)->xpanstep); + err |= __put_user(fix.ypanstep, &((struct fb_fix_screeninfo32 *)arg)->ypanstep); + err |= __put_user(fix.ywrapstep, &((struct fb_fix_screeninfo32 *)arg)->ywrapstep); + err |= __put_user(fix.line_length, &((struct fb_fix_screeninfo32 *)arg)->line_length); + err |= __put_user((__u32)(unsigned long)fix.mmio_start, &((struct fb_fix_screeninfo32 *)arg)->mmio_start); + err |= __put_user(fix.mmio_len, &((struct fb_fix_screeninfo32 *)arg)->mmio_len); + err |= __put_user(fix.accel, &((struct fb_fix_screeninfo32 *)arg)->accel); + err |= __copy_to_user((char *)((struct fb_fix_screeninfo32 *)arg)->reserved, (char *)fix.reserved, sizeof(fix.reserved)); + break; + case FBIOGETCMAP: + err = __copy_to_user((char *)A(red), cmap.red, cmap.len * sizeof(__u16)); + err |= __copy_to_user((char *)A(green), cmap.blue, cmap.len * sizeof(__u16)); + err |= __copy_to_user((char *)A(blue), cmap.blue, cmap.len * sizeof(__u16)); + if (cmap.transp) + err |= __copy_to_user((char *)A(transp), cmap.transp, cmap.len * sizeof(__u16)); + break; + case FBIOPUTCMAP: + break; + } + if (err) + err = -EFAULT; + +out: if (cmap.red) kfree(cmap.red); + if (cmap.green) kfree(cmap.green); + if (cmap.blue) kfree(cmap.blue); + if (cmap.transp) kfree(cmap.transp); + return err; +} + +static int hdio_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + unsigned long kval; + unsigned int *uvp; + int error; + + set_fs(KERNEL_DS); + error = sys_ioctl(fd, cmd, (long)&kval); + set_fs(old_fs); + + if(error == 0) { + uvp = (unsigned int *)arg; + if(put_user(kval, uvp)) + error = -EFAULT; + } + return error; +} + +struct floppy_struct32 { + unsigned int size; + unsigned int sect; + unsigned int head; + unsigned int track; + unsigned int stretch; + unsigned char gap; + unsigned char rate; + unsigned char spec1; + unsigned char fmt_gap; + const __kernel_caddr_t32 name; +}; + +struct floppy_drive_params32 { + char cmos; + u32 max_dtr; + u32 hlt; + u32 hut; + u32 srt; + u32 spinup; + u32 spindown; + unsigned char spindown_offset; + unsigned char select_delay; + unsigned char rps; + unsigned char tracks; + u32 timeout; + unsigned char interleave_sect; + struct floppy_max_errors max_errors; + char flags; + char read_track; + short autodetect[8]; + int checkfreq; + int native_format; +}; + +struct floppy_drive_struct32 { + signed char flags; + u32 spinup_date; + u32 select_date; + u32 first_read_date; + short probed_format; + short track; + short maxblock; + short maxtrack; + int generation; + int keep_data; + int fd_ref; + int fd_device; + int last_checked; + __kernel_caddr_t32 dmabuf; + int bufblocks; +}; + +struct floppy_fdc_state32 { + int spec1; + int spec2; + int dtr; + unsigned char version; + unsigned char dor; + u32 address; + unsigned int rawcmd:2; + unsigned int reset:1; + unsigned int need_configure:1; + unsigned int perp_mode:2; + unsigned int has_fifo:1; + unsigned int driver_version; + unsigned char track[4]; +}; + +struct floppy_write_errors32 { + unsigned int write_errors; + u32 first_error_sector; + int first_error_generation; + u32 last_error_sector; + int last_error_generation; + unsigned int badness; +}; + +#define FDSETPRM32 _IOW(2, 0x42, struct floppy_struct32) +#define FDDEFPRM32 _IOW(2, 0x43, struct floppy_struct32) +#define FDGETPRM32 _IOR(2, 0x04, struct floppy_struct32) +#define FDSETDRVPRM32 _IOW(2, 0x90, struct floppy_drive_params32) +#define FDGETDRVPRM32 _IOR(2, 0x11, struct floppy_drive_params32) +#define FDGETDRVSTAT32 _IOR(2, 0x12, struct floppy_drive_struct32) +#define FDPOLLDRVSTAT32 _IOR(2, 0x13, struct floppy_drive_struct32) +#define FDGETFDCSTAT32 _IOR(2, 0x15, struct floppy_fdc_state32) +#define FDWERRORGET32 _IOR(2, 0x17, struct floppy_write_errors32) + +static struct { + unsigned int cmd32; + unsigned int cmd; +} fd_ioctl_trans_table[] = { + { FDSETPRM32, FDSETPRM }, + { FDDEFPRM32, FDDEFPRM }, + { FDGETPRM32, FDGETPRM }, + { FDSETDRVPRM32, FDSETDRVPRM }, + { FDGETDRVPRM32, FDGETDRVPRM }, + { FDGETDRVSTAT32, FDGETDRVSTAT }, + { FDPOLLDRVSTAT32, FDPOLLDRVSTAT }, + { FDGETFDCSTAT32, FDGETFDCSTAT }, + { FDWERRORGET32, FDWERRORGET } +}; + +#define NR_FD_IOCTL_TRANS (sizeof(fd_ioctl_trans_table)/sizeof(fd_ioctl_trans_table[0])) + +static int fd_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + void *karg = NULL; + unsigned int kcmd = 0; + int i, err; + + for (i = 0; i < NR_FD_IOCTL_TRANS; i++) + if (cmd == fd_ioctl_trans_table[i].cmd32) { + kcmd = fd_ioctl_trans_table[i].cmd; + break; + } + if (!kcmd) + return -EINVAL; + + switch (cmd) { + case FDSETPRM32: + case FDDEFPRM32: + case FDGETPRM32: + { + struct floppy_struct *f; + + f = karg = kmalloc(sizeof(struct floppy_struct), GFP_KERNEL); + if (!karg) + return -ENOMEM; + if (cmd == FDGETPRM32) + break; + err = __get_user(f->size, &((struct floppy_struct32 *)arg)->size); + err |= __get_user(f->sect, &((struct floppy_struct32 *)arg)->sect); + err |= __get_user(f->head, &((struct floppy_struct32 *)arg)->head); + err |= __get_user(f->track, &((struct floppy_struct32 *)arg)->track); + err |= __get_user(f->stretch, &((struct floppy_struct32 *)arg)->stretch); + err |= __get_user(f->gap, &((struct floppy_struct32 *)arg)->gap); + err |= __get_user(f->rate, &((struct floppy_struct32 *)arg)->rate); + err |= __get_user(f->spec1, &((struct floppy_struct32 *)arg)->spec1); + err |= __get_user(f->fmt_gap, &((struct floppy_struct32 *)arg)->fmt_gap); + err |= __get_user((u64)f->name, &((struct floppy_struct32 *)arg)->name); + if (err) { + err = -EFAULT; + goto out; + } + break; + } + case FDSETDRVPRM32: + case FDGETDRVPRM32: + { + struct floppy_drive_params *f; + + f = karg = kmalloc(sizeof(struct floppy_drive_params), GFP_KERNEL); + if (!karg) + return -ENOMEM; + if (cmd == FDGETDRVPRM32) + break; + err = __get_user(f->cmos, &((struct floppy_drive_params32 *)arg)->cmos); + err |= __get_user(f->max_dtr, &((struct floppy_drive_params32 *)arg)->max_dtr); + err |= __get_user(f->hlt, &((struct floppy_drive_params32 *)arg)->hlt); + err |= __get_user(f->hut, &((struct floppy_drive_params32 *)arg)->hut); + err |= __get_user(f->srt, &((struct floppy_drive_params32 *)arg)->srt); + err |= __get_user(f->spinup, &((struct floppy_drive_params32 *)arg)->spinup); + err |= __get_user(f->spindown, &((struct floppy_drive_params32 *)arg)->spindown); + err |= __get_user(f->spindown_offset, &((struct floppy_drive_params32 *)arg)->spindown_offset); + err |= __get_user(f->select_delay, &((struct floppy_drive_params32 *)arg)->select_delay); + err |= __get_user(f->rps, &((struct floppy_drive_params32 *)arg)->rps); + err |= __get_user(f->tracks, &((struct floppy_drive_params32 *)arg)->tracks); + err |= __get_user(f->timeout, &((struct floppy_drive_params32 *)arg)->timeout); + err |= __get_user(f->interleave_sect, &((struct floppy_drive_params32 *)arg)->interleave_sect); + err |= __copy_from_user(&f->max_errors, &((struct floppy_drive_params32 *)arg)->max_errors, sizeof(f->max_errors)); + err |= __get_user(f->flags, &((struct floppy_drive_params32 *)arg)->flags); + err |= __get_user(f->read_track, &((struct floppy_drive_params32 *)arg)->read_track); + err |= __copy_from_user(f->autodetect, ((struct floppy_drive_params32 *)arg)->autodetect, sizeof(f->autodetect)); + err |= __get_user(f->checkfreq, &((struct floppy_drive_params32 *)arg)->checkfreq); + err |= __get_user(f->native_format, &((struct floppy_drive_params32 *)arg)->native_format); + if (err) { + err = -EFAULT; + goto out; + } + break; + } + case FDGETDRVSTAT32: + case FDPOLLDRVSTAT32: + karg = kmalloc(sizeof(struct floppy_drive_struct), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + case FDGETFDCSTAT32: + karg = kmalloc(sizeof(struct floppy_fdc_state), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + case FDWERRORGET32: + karg = kmalloc(sizeof(struct floppy_write_errors), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + default: + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + goto out; + switch (cmd) { + case FDGETPRM32: + { + struct floppy_struct *f = karg; + + err = __put_user(f->size, &((struct floppy_struct32 *)arg)->size); + err |= __put_user(f->sect, &((struct floppy_struct32 *)arg)->sect); + err |= __put_user(f->head, &((struct floppy_struct32 *)arg)->head); + err |= __put_user(f->track, &((struct floppy_struct32 *)arg)->track); + err |= __put_user(f->stretch, &((struct floppy_struct32 *)arg)->stretch); + err |= __put_user(f->gap, &((struct floppy_struct32 *)arg)->gap); + err |= __put_user(f->rate, &((struct floppy_struct32 *)arg)->rate); + err |= __put_user(f->spec1, &((struct floppy_struct32 *)arg)->spec1); + err |= __put_user(f->fmt_gap, &((struct floppy_struct32 *)arg)->fmt_gap); + err |= __put_user((u64)f->name, &((struct floppy_struct32 *)arg)->name); + break; + } + case FDGETDRVPRM32: + { + struct floppy_drive_params *f = karg; + + err = __put_user(f->cmos, &((struct floppy_drive_params32 *)arg)->cmos); + err |= __put_user(f->max_dtr, &((struct floppy_drive_params32 *)arg)->max_dtr); + err |= __put_user(f->hlt, &((struct floppy_drive_params32 *)arg)->hlt); + err |= __put_user(f->hut, &((struct floppy_drive_params32 *)arg)->hut); + err |= __put_user(f->srt, &((struct floppy_drive_params32 *)arg)->srt); + err |= __put_user(f->spinup, &((struct floppy_drive_params32 *)arg)->spinup); + err |= __put_user(f->spindown, &((struct floppy_drive_params32 *)arg)->spindown); + err |= __put_user(f->spindown_offset, &((struct floppy_drive_params32 *)arg)->spindown_offset); + err |= __put_user(f->select_delay, &((struct floppy_drive_params32 *)arg)->select_delay); + err |= __put_user(f->rps, &((struct floppy_drive_params32 *)arg)->rps); + err |= __put_user(f->tracks, &((struct floppy_drive_params32 *)arg)->tracks); + err |= __put_user(f->timeout, &((struct floppy_drive_params32 *)arg)->timeout); + err |= __put_user(f->interleave_sect, &((struct floppy_drive_params32 *)arg)->interleave_sect); + err |= __copy_to_user(&((struct floppy_drive_params32 *)arg)->max_errors, &f->max_errors, sizeof(f->max_errors)); + err |= __put_user(f->flags, &((struct floppy_drive_params32 *)arg)->flags); + err |= __put_user(f->read_track, &((struct floppy_drive_params32 *)arg)->read_track); + err |= __copy_to_user(((struct floppy_drive_params32 *)arg)->autodetect, f->autodetect, sizeof(f->autodetect)); + err |= __put_user(f->checkfreq, &((struct floppy_drive_params32 *)arg)->checkfreq); + err |= __put_user(f->native_format, &((struct floppy_drive_params32 *)arg)->native_format); + break; + } + case FDGETDRVSTAT32: + case FDPOLLDRVSTAT32: + { + struct floppy_drive_struct *f = karg; + + err = __put_user(f->flags, &((struct floppy_drive_struct32 *)arg)->flags); + err |= __put_user(f->spinup_date, &((struct floppy_drive_struct32 *)arg)->spinup_date); + err |= __put_user(f->select_date, &((struct floppy_drive_struct32 *)arg)->select_date); + err |= __put_user(f->first_read_date, &((struct floppy_drive_struct32 *)arg)->first_read_date); + err |= __put_user(f->probed_format, &((struct floppy_drive_struct32 *)arg)->probed_format); + err |= __put_user(f->track, &((struct floppy_drive_struct32 *)arg)->track); + err |= __put_user(f->maxblock, &((struct floppy_drive_struct32 *)arg)->maxblock); + err |= __put_user(f->maxtrack, &((struct floppy_drive_struct32 *)arg)->maxtrack); + err |= __put_user(f->generation, &((struct floppy_drive_struct32 *)arg)->generation); + err |= __put_user(f->keep_data, &((struct floppy_drive_struct32 *)arg)->keep_data); + err |= __put_user(f->fd_ref, &((struct floppy_drive_struct32 *)arg)->fd_ref); + err |= __put_user(f->fd_device, &((struct floppy_drive_struct32 *)arg)->fd_device); + err |= __put_user(f->last_checked, &((struct floppy_drive_struct32 *)arg)->last_checked); + err |= __put_user((u64)f->dmabuf, &((struct floppy_drive_struct32 *)arg)->dmabuf); + err |= __put_user((u64)f->bufblocks, &((struct floppy_drive_struct32 *)arg)->bufblocks); + break; + } + case FDGETFDCSTAT32: + { + struct floppy_fdc_state *f = karg; + + err = __put_user(f->spec1, &((struct floppy_fdc_state32 *)arg)->spec1); + err |= __put_user(f->spec2, &((struct floppy_fdc_state32 *)arg)->spec2); + err |= __put_user(f->dtr, &((struct floppy_fdc_state32 *)arg)->dtr); + err |= __put_user(f->version, &((struct floppy_fdc_state32 *)arg)->version); + err |= __put_user(f->dor, &((struct floppy_fdc_state32 *)arg)->dor); + err |= __put_user(f->address, &((struct floppy_fdc_state32 *)arg)->address); + err |= __copy_to_user((char *)&((struct floppy_fdc_state32 *)arg)->address + + sizeof(((struct floppy_fdc_state32 *)arg)->address), + (char *)&f->address + sizeof(f->address), sizeof(int)); + err |= __put_user(f->driver_version, &((struct floppy_fdc_state32 *)arg)->driver_version); + err |= __copy_to_user(((struct floppy_fdc_state32 *)arg)->track, f->track, sizeof(f->track)); + break; + } + case FDWERRORGET32: + { + struct floppy_write_errors *f = karg; + + err = __put_user(f->write_errors, &((struct floppy_write_errors32 *)arg)->write_errors); + err |= __put_user(f->first_error_sector, &((struct floppy_write_errors32 *)arg)->first_error_sector); + err |= __put_user(f->first_error_generation, &((struct floppy_write_errors32 *)arg)->first_error_generation); + err |= __put_user(f->last_error_sector, &((struct floppy_write_errors32 *)arg)->last_error_sector); + err |= __put_user(f->last_error_generation, &((struct floppy_write_errors32 *)arg)->last_error_generation); + err |= __put_user(f->badness, &((struct floppy_write_errors32 *)arg)->badness); + break; + } + default: + break; + } + if (err) + err = -EFAULT; + +out: if (karg) kfree(karg); + return err; +} + +struct ppp_option_data32 { + __kernel_caddr_t32 ptr; + __u32 length; + int transmit; +}; +#define PPPIOCSCOMPRESS32 _IOW('t', 77, struct ppp_option_data32) + +struct ppp_idle32 { + __kernel_time_t32 xmit_idle; + __kernel_time_t32 recv_idle; +}; +#define PPPIOCGIDLE32 _IOR('t', 63, struct ppp_idle32) + +static int ppp_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct ppp_option_data32 data32; + struct ppp_option_data data; + struct ppp_idle32 idle32; + struct ppp_idle idle; + unsigned int kcmd; + void *karg; + int err = 0; + + switch (cmd) { + case PPPIOCGIDLE32: + kcmd = PPPIOCGIDLE; + karg = &idle; + break; + case PPPIOCSCOMPRESS32: + if (copy_from_user(&data32, (struct ppp_option_data32 *)arg, sizeof(struct ppp_option_data32))) + return -EFAULT; + data.ptr = kmalloc (data32.length, GFP_KERNEL); + if (!data.ptr) + return -ENOMEM; + if (copy_from_user(data.ptr, (__u8 *)A(data32.ptr), data32.length)) { + kfree(data.ptr); + return -EFAULT; + } + data.length = data32.length; + data.transmit = data32.transmit; + kcmd = PPPIOCSCOMPRESS; + karg = &data; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("ppp_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while(0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + switch (cmd) { + case PPPIOCGIDLE32: + if (err) + return err; + idle32.xmit_idle = idle.xmit_idle; + idle32.recv_idle = idle.recv_idle; + if (copy_to_user((struct ppp_idle32 *)arg, &idle32, sizeof(struct ppp_idle32))) + return -EFAULT; + break; + case PPPIOCSCOMPRESS32: + kfree(data.ptr); + break; + default: + break; + } + return err; +} + + +struct mtget32 { + __u32 mt_type; + __u32 mt_resid; + __u32 mt_dsreg; + __u32 mt_gstat; + __u32 mt_erreg; + __kernel_daddr_t32 mt_fileno; + __kernel_daddr_t32 mt_blkno; +}; +#define MTIOCGET32 _IOR('m', 2, struct mtget32) + +struct mtpos32 { + __u32 mt_blkno; +}; +#define MTIOCPOS32 _IOR('m', 3, struct mtpos32) + +struct mtconfiginfo32 { + __u32 mt_type; + __u32 ifc_type; + __u16 irqnr; + __u16 dmanr; + __u16 port; + __u32 debug; + __u32 have_dens:1; + __u32 have_bsf:1; + __u32 have_fsr:1; + __u32 have_bsr:1; + __u32 have_eod:1; + __u32 have_seek:1; + __u32 have_tell:1; + __u32 have_ras1:1; + __u32 have_ras2:1; + __u32 have_ras3:1; + __u32 have_qfa:1; + __u32 pad1:5; + char reserved[10]; +}; +#define MTIOCGETCONFIG32 _IOR('m', 4, struct mtconfiginfo32) +#define MTIOCSETCONFIG32 _IOW('m', 5, struct mtconfiginfo32) + +static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct mtconfiginfo info; + struct mtget get; + struct mtpos pos; + unsigned long kcmd; + void *karg; + int err = 0; + + switch(cmd) { + case MTIOCPOS32: + kcmd = MTIOCPOS; + karg = &pos; + break; + case MTIOCGET32: + kcmd = MTIOCGET; + karg = &get; + break; + case MTIOCGETCONFIG32: + kcmd = MTIOCGETCONFIG; + karg = &info; + break; + case MTIOCSETCONFIG32: + kcmd = MTIOCSETCONFIG; + karg = &info; + err = __get_user(info.mt_type, &((struct mtconfiginfo32 *)arg)->mt_type); + err |= __get_user(info.ifc_type, &((struct mtconfiginfo32 *)arg)->ifc_type); + err |= __get_user(info.irqnr, &((struct mtconfiginfo32 *)arg)->irqnr); + err |= __get_user(info.dmanr, &((struct mtconfiginfo32 *)arg)->dmanr); + err |= __get_user(info.port, &((struct mtconfiginfo32 *)arg)->port); + err |= __get_user(info.debug, &((struct mtconfiginfo32 *)arg)->debug); + err |= __copy_from_user((char *)&info.debug + sizeof(info.debug), + (char *)&((struct mtconfiginfo32 *)arg)->debug + + sizeof(((struct mtconfiginfo32 *)arg)->debug), sizeof(__u32)); + if (err) + return -EFAULT; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("mt_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while(0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + return err; + switch (cmd) { + case MTIOCPOS32: + err = __put_user(pos.mt_blkno, &((struct mtpos32 *)arg)->mt_blkno); + break; + case MTIOCGET32: + err = __put_user(get.mt_type, &((struct mtget32 *)arg)->mt_type); + err |= __put_user(get.mt_resid, &((struct mtget32 *)arg)->mt_resid); + err |= __put_user(get.mt_dsreg, &((struct mtget32 *)arg)->mt_dsreg); + err |= __put_user(get.mt_gstat, &((struct mtget32 *)arg)->mt_gstat); + err |= __put_user(get.mt_erreg, &((struct mtget32 *)arg)->mt_erreg); + err |= __put_user(get.mt_fileno, &((struct mtget32 *)arg)->mt_fileno); + err |= __put_user(get.mt_blkno, &((struct mtget32 *)arg)->mt_blkno); + break; + case MTIOCGETCONFIG32: + err = __put_user(info.mt_type, &((struct mtconfiginfo32 *)arg)->mt_type); + err |= __put_user(info.ifc_type, &((struct mtconfiginfo32 *)arg)->ifc_type); + err |= __put_user(info.irqnr, &((struct mtconfiginfo32 *)arg)->irqnr); + err |= __put_user(info.dmanr, &((struct mtconfiginfo32 *)arg)->dmanr); + err |= __put_user(info.port, &((struct mtconfiginfo32 *)arg)->port); + err |= __put_user(info.debug, &((struct mtconfiginfo32 *)arg)->debug); + err |= __copy_to_user((char *)&((struct mtconfiginfo32 *)arg)->debug + + sizeof(((struct mtconfiginfo32 *)arg)->debug), + (char *)&info.debug + sizeof(info.debug), sizeof(__u32)); + break; + case MTIOCSETCONFIG32: + break; + } + return err ? -EFAULT: 0; +} + +struct cdrom_read32 { + int cdread_lba; + __kernel_caddr_t32 cdread_bufaddr; + int cdread_buflen; +}; + +struct cdrom_read_audio32 { + union cdrom_addr addr; + u_char addr_format; + int nframes; + __kernel_caddr_t32 buf; +}; + +struct cdrom_generic_command32 { + unsigned char cmd[CDROM_PACKET_SIZE]; + __kernel_caddr_t32 buffer; + unsigned int buflen; + int stat; + __kernel_caddr_t32 sense; + __kernel_caddr_t32 reserved[3]; +}; + +static int cdrom_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct cdrom_read cdread; + struct cdrom_read_audio cdreadaudio; + struct cdrom_generic_command cgc; + __kernel_caddr_t32 addr; + char *data = 0; + void *karg; + int err = 0; + + switch(cmd) { + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + karg = &cdread; + err = __get_user(cdread.cdread_lba, &((struct cdrom_read32 *)arg)->cdread_lba); + err |= __get_user(addr, &((struct cdrom_read32 *)arg)->cdread_bufaddr); + err |= __get_user(cdread.cdread_buflen, &((struct cdrom_read32 *)arg)->cdread_buflen); + if (err) + return -EFAULT; + data = kmalloc(cdread.cdread_buflen, GFP_KERNEL); + if (!data) + return -ENOMEM; + cdread.cdread_bufaddr = data; + break; + case CDROMREADAUDIO: + karg = &cdreadaudio; + err = copy_from_user(&cdreadaudio.addr, &((struct cdrom_read_audio32 *)arg)->addr, sizeof(cdreadaudio.addr)); + err |= __get_user(cdreadaudio.addr_format, &((struct cdrom_read_audio32 *)arg)->addr_format); + err |= __get_user(cdreadaudio.nframes, &((struct cdrom_read_audio32 *)arg)->nframes); + err |= __get_user(addr, &((struct cdrom_read_audio32 *)arg)->buf); + if (err) + return -EFAULT; + data = kmalloc(cdreadaudio.nframes * 2352, GFP_KERNEL); + if (!data) + return -ENOMEM; + cdreadaudio.buf = data; + break; + case CDROM_SEND_PACKET: + karg = &cgc; + err = copy_from_user(cgc.cmd, &((struct cdrom_generic_command32 *)arg)->cmd, sizeof(cgc.cmd)); + err |= __get_user(addr, &((struct cdrom_generic_command32 *)arg)->buffer); + err |= __get_user(cgc.buflen, &((struct cdrom_generic_command32 *)arg)->buflen); + if (err) + return -EFAULT; + if ((data = kmalloc(cgc.buflen, GFP_KERNEL)) == NULL) + return -ENOMEM; + cgc.buffer = data; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("cdrom_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while(0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + goto out; + switch (cmd) { + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + err = copy_to_user((char *)A(addr), data, cdread.cdread_buflen); + break; + case CDROMREADAUDIO: + err = copy_to_user((char *)A(addr), data, cdreadaudio.nframes * 2352); + break; + case CDROM_SEND_PACKET: + err = copy_to_user((char *)A(addr), data, cgc.buflen); + break; + default: + break; + } +out: if (data) + kfree(data); + return err ? -EFAULT : 0; +} + +struct loop_info32 { + int lo_number; /* ioctl r/o */ + __kernel_dev_t32 lo_device; /* ioctl r/o */ + unsigned int lo_inode; /* ioctl r/o */ + __kernel_dev_t32 lo_rdevice; /* ioctl r/o */ + int lo_offset; + int lo_encrypt_type; + int lo_encrypt_key_size; /* ioctl w/o */ + int lo_flags; /* ioctl r/o */ + char lo_name[LO_NAME_SIZE]; + unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ + unsigned int lo_init[2]; + char reserved[4]; +}; + +static int loop_status(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct loop_info l; + int err = -EINVAL; + + switch(cmd) { + case LOOP_SET_STATUS: + err = get_user(l.lo_number, &((struct loop_info32 *)arg)->lo_number); + err |= __get_user(l.lo_device, &((struct loop_info32 *)arg)->lo_device); + err |= __get_user(l.lo_inode, &((struct loop_info32 *)arg)->lo_inode); + err |= __get_user(l.lo_rdevice, &((struct loop_info32 *)arg)->lo_rdevice); + err |= __copy_from_user((char *)&l.lo_offset, (char *)&((struct loop_info32 *)arg)->lo_offset, + 8 + (unsigned long)l.lo_init - (unsigned long)&l.lo_offset); + if (err) { + err = -EFAULT; + } else { + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&l); + set_fs (old_fs); + } + break; + case LOOP_GET_STATUS: + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&l); + set_fs (old_fs); + if (!err) { + err = put_user(l.lo_number, &((struct loop_info32 *)arg)->lo_number); + err |= __put_user(l.lo_device, &((struct loop_info32 *)arg)->lo_device); + err |= __put_user(l.lo_inode, &((struct loop_info32 *)arg)->lo_inode); + err |= __put_user(l.lo_rdevice, &((struct loop_info32 *)arg)->lo_rdevice); + err |= __copy_to_user((char *)&((struct loop_info32 *)arg)->lo_offset, + (char *)&l.lo_offset, (unsigned long)l.lo_init - (unsigned long)&l.lo_offset); + if (err) + err = -EFAULT; + } + break; + default: { + static int count = 0; + if (++count <= 20) + printk("%s: Unknown loop ioctl cmd, fd(%d) " + "cmd(%08x) arg(%08lx)\n", + __FUNCTION__, fd, cmd, arg); + } + } + return err; +} + +extern int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg); + +static int vt_check(struct file *file) +{ + struct tty_struct *tty; + struct inode *inode = file->f_dentry->d_inode; + + if (file->f_op->ioctl != tty_ioctl) + return -EINVAL; + + tty = (struct tty_struct *)file->private_data; + if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl")) + return -EINVAL; + + if (tty->driver.ioctl != vt_ioctl) + return -EINVAL; + + /* + * To have permissions to do most of the vt ioctls, we either have + * to be the owner of the tty, or super-user. + */ + if (current->tty == tty || suser()) + return 1; + return 0; +} + +struct consolefontdesc32 { + unsigned short charcount; /* characters in font (256 or 512) */ + unsigned short charheight; /* scan lines per character (1-32) */ + u32 chardata; /* font data in expanded form */ +}; + +static int do_fontx_ioctl(unsigned int fd, int cmd, struct consolefontdesc32 *user_cfd, struct file *file) +{ + struct consolefontdesc cfdarg; + struct console_font_op op; + int i, perm; + + perm = vt_check(file); + if (perm < 0) return perm; + + if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc32))) + return -EFAULT; + + cfdarg.chardata = (unsigned char *)A(((struct consolefontdesc32 *)&cfdarg)->chardata); + + switch (cmd) { + case PIO_FONTX: + if (!perm) + return -EPERM; + op.op = KD_FONT_OP_SET; + op.flags = 0; + op.width = 8; + op.height = cfdarg.charheight; + op.charcount = cfdarg.charcount; + op.data = cfdarg.chardata; + return con_font_op(fg_console, &op); + case GIO_FONTX: + if (!cfdarg.chardata) + return 0; + op.op = KD_FONT_OP_GET; + op.flags = 0; + op.width = 8; + op.height = cfdarg.charheight; + op.charcount = cfdarg.charcount; + op.data = cfdarg.chardata; + i = con_font_op(fg_console, &op); + if (i) + return i; + cfdarg.charheight = op.height; + cfdarg.charcount = op.charcount; + ((struct consolefontdesc32 *)&cfdarg)->chardata = (unsigned long)cfdarg.chardata; + if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc32))) + return -EFAULT; + return 0; + } + return -EINVAL; +} + +struct console_font_op32 { + unsigned int op; /* operation code KD_FONT_OP_* */ + unsigned int flags; /* KD_FONT_FLAG_* */ + unsigned int width, height; /* font size */ + unsigned int charcount; + u32 data; /* font data with height fixed to 32 */ +}; + +static int do_kdfontop_ioctl(unsigned int fd, unsigned int cmd, struct console_font_op32 *fontop, struct file *file) +{ + struct console_font_op op; + int perm = vt_check(file), i; + struct vt_struct *vt; + + if (perm < 0) return perm; + + if (copy_from_user(&op, (void *) fontop, sizeof(struct console_font_op32))) + return -EFAULT; + if (!perm && op.op != KD_FONT_OP_GET) + return -EPERM; + op.data = (unsigned char *)A(((struct console_font_op32 *)&op)->data); + op.flags |= KD_FONT_FLAG_OLD; + vt = (struct vt_struct *)((struct tty_struct *)file->private_data)->driver_data; + i = con_font_op(vt->vc_num, &op); + if (i) return i; + ((struct console_font_op32 *)&op)->data = (unsigned long)op.data; + if (copy_to_user((void *) fontop, &op, sizeof(struct console_font_op32))) + return -EFAULT; + return 0; +} + +struct unimapdesc32 { + unsigned short entry_ct; + u32 entries; +}; + +static int do_unimap_ioctl(unsigned int fd, unsigned int cmd, struct unimapdesc32 *user_ud, struct file *file) +{ + struct unimapdesc32 tmp; + int perm = vt_check(file); + + if (perm < 0) return perm; + if (copy_from_user(&tmp, user_ud, sizeof tmp)) + return -EFAULT; + switch (cmd) { + case PIO_UNIMAP: + if (!perm) return -EPERM; + return con_set_unimap(fg_console, tmp.entry_ct, (struct unipair *)A(tmp.entries)); + case GIO_UNIMAP: + return con_get_unimap(fg_console, tmp.entry_ct, &(user_ud->entry_ct), (struct unipair *)A(tmp.entries)); + } + return 0; +} + +static int do_smb_getmountuid(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + __kernel_uid_t kuid; + int err; + + cmd = SMB_IOC_GETMOUNTUID; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&kuid); + set_fs(old_fs); + + if (err >= 0) + err = put_user(kuid, (__kernel_uid_t32 *)arg); + + return err; +} + +struct atmif_sioc32 { + int number; + int length; + __kernel_caddr_t32 arg; +}; + +struct atm_iobuf32 { + int length; + __kernel_caddr_t32 buffer; +}; + +#define ATM_GETLINKRATE32 _IOW('a', ATMIOC_ITF+1, struct atmif_sioc32) +#define ATM_GETNAMES32 _IOW('a', ATMIOC_ITF+3, struct atm_iobuf32) +#define ATM_GETTYPE32 _IOW('a', ATMIOC_ITF+4, struct atmif_sioc32) +#define ATM_GETESI32 _IOW('a', ATMIOC_ITF+5, struct atmif_sioc32) +#define ATM_GETADDR32 _IOW('a', ATMIOC_ITF+6, struct atmif_sioc32) +#define ATM_RSTADDR32 _IOW('a', ATMIOC_ITF+7, struct atmif_sioc32) +#define ATM_ADDADDR32 _IOW('a', ATMIOC_ITF+8, struct atmif_sioc32) +#define ATM_DELADDR32 _IOW('a', ATMIOC_ITF+9, struct atmif_sioc32) +#define ATM_GETCIRANGE32 _IOW('a', ATMIOC_ITF+10, struct atmif_sioc32) +#define ATM_SETCIRANGE32 _IOW('a', ATMIOC_ITF+11, struct atmif_sioc32) +#define ATM_SETESI32 _IOW('a', ATMIOC_ITF+12, struct atmif_sioc32) +#define ATM_SETESIF32 _IOW('a', ATMIOC_ITF+13, struct atmif_sioc32) +#define ATM_GETSTAT32 _IOW('a', ATMIOC_SARCOM+0, struct atmif_sioc32) +#define ATM_GETSTATZ32 _IOW('a', ATMIOC_SARCOM+1, struct atmif_sioc32) +#define ATM_GETLOOP32 _IOW('a', ATMIOC_SARCOM+2, struct atmif_sioc32) +#define ATM_SETLOOP32 _IOW('a', ATMIOC_SARCOM+3, struct atmif_sioc32) +#define ATM_QUERYLOOP32 _IOW('a', ATMIOC_SARCOM+4, struct atmif_sioc32) + +static struct { + unsigned int cmd32; + unsigned int cmd; +} atm_ioctl_map[] = { + { ATM_GETLINKRATE32, ATM_GETLINKRATE }, + { ATM_GETNAMES32, ATM_GETNAMES }, + { ATM_GETTYPE32, ATM_GETTYPE }, + { ATM_GETESI32, ATM_GETESI }, + { ATM_GETADDR32, ATM_GETADDR }, + { ATM_RSTADDR32, ATM_RSTADDR }, + { ATM_ADDADDR32, ATM_ADDADDR }, + { ATM_DELADDR32, ATM_DELADDR }, + { ATM_GETCIRANGE32, ATM_GETCIRANGE }, + { ATM_SETCIRANGE32, ATM_SETCIRANGE }, + { ATM_SETESI32, ATM_SETESI }, + { ATM_SETESIF32, ATM_SETESIF }, + { ATM_GETSTAT32, ATM_GETSTAT }, + { ATM_GETSTATZ32, ATM_GETSTATZ }, + { ATM_GETLOOP32, ATM_GETLOOP }, + { ATM_SETLOOP32, ATM_SETLOOP }, + { ATM_QUERYLOOP32, ATM_QUERYLOOP } +}; + +#define NR_ATM_IOCTL (sizeof(atm_ioctl_map)/sizeof(atm_ioctl_map[0])) + + +static int do_atm_iobuf(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct atm_iobuf32 iobuf32; + struct atm_iobuf iobuf = { 0, NULL }; + mm_segment_t old_fs; + int err; + + err = copy_from_user(&iobuf32, (struct atm_iobuf32*)arg, + sizeof(struct atm_iobuf32)); + if (err) + return -EFAULT; + + iobuf.length = iobuf32.length; + + if (iobuf32.buffer == (__kernel_caddr_t32) NULL || iobuf32.length == 0) { + iobuf.buffer = (void*)(unsigned long)iobuf32.buffer; + } else { + iobuf.buffer = kmalloc(iobuf.length, GFP_KERNEL); + if (iobuf.buffer == NULL) { + err = -ENOMEM; + goto out; + } + + err = copy_from_user(iobuf.buffer, A(iobuf32.buffer), iobuf.length); + if (err) { + err = -EFAULT; + goto out; + } + } + + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&iobuf); + set_fs (old_fs); + if(err) + goto out; + + if(iobuf.buffer && iobuf.length > 0) { + err = copy_to_user(A(iobuf32.buffer), iobuf.buffer, iobuf.length); + if (err) { + err = -EFAULT; + goto out; + } + } + err = __put_user(iobuf.length, &(((struct atm_iobuf32*)arg)->length)); + + out: + if(iobuf32.buffer && iobuf32.length > 0) + kfree(iobuf.buffer); + + return err; +} + + +static int do_atmif_sioc(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct atmif_sioc32 sioc32; + struct atmif_sioc sioc = { 0, 0, NULL }; + mm_segment_t old_fs; + int err; + + err = copy_from_user(&sioc32, (struct atmif_sioc32*)arg, + sizeof(struct atmif_sioc32)); + if (err) + return -EFAULT; + + sioc.number = sioc32.number; + sioc.length = sioc32.length; + + if (sioc32.arg == (__kernel_caddr_t32) NULL || sioc32.length == 0) { + sioc.arg = (void*)(unsigned long)sioc32.arg; + } else { + sioc.arg = kmalloc(sioc.length, GFP_KERNEL); + if (sioc.arg == NULL) { + err = -ENOMEM; + goto out; + } + + err = copy_from_user(sioc.arg, A(sioc32.arg), sioc32.length); + if (err) { + err = -EFAULT; + goto out; + } + } + + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&sioc); + set_fs (old_fs); + if(err) { + goto out; + } + + if(sioc.arg && sioc.length > 0) { + err = copy_to_user(A(sioc32.arg), sioc.arg, sioc.length); + if (err) { + err = -EFAULT; + goto out; + } + } + err = __put_user(sioc.length, &(((struct atmif_sioc32*)arg)->length)); + + out: + if(sioc32.arg && sioc32.length > 0) + kfree(sioc.arg); + + return err; +} + + +static int do_atm_ioctl(unsigned int fd, unsigned int cmd32, unsigned long arg) +{ + int i; + unsigned int cmd = 0; + + switch (cmd32) { + case SONET_GETSTAT: + case SONET_GETSTATZ: + case SONET_GETDIAG: + case SONET_SETDIAG: + case SONET_CLRDIAG: + case SONET_SETFRAMING: + case SONET_GETFRAMING: + case SONET_GETFRSENSE: + return do_atmif_sioc(fd, cmd32, arg); + } + + for (i = 0; i < NR_ATM_IOCTL; i++) { + if (cmd32 == atm_ioctl_map[i].cmd32) { + cmd = atm_ioctl_map[i].cmd; + break; + } + } + if (i == NR_ATM_IOCTL) { + return -EINVAL; + } + + switch (cmd) { + case ATM_GETNAMES: + return do_atm_iobuf(fd, cmd, arg); + + case ATM_GETLINKRATE: + case ATM_GETTYPE: + case ATM_GETESI: + case ATM_GETADDR: + case ATM_RSTADDR: + case ATM_ADDADDR: + case ATM_DELADDR: + case ATM_GETCIRANGE: + case ATM_SETCIRANGE: + case ATM_SETESI: + case ATM_SETESIF: + case ATM_GETSTAT: + case ATM_GETSTATZ: + case ATM_GETLOOP: + case ATM_SETLOOP: + case ATM_QUERYLOOP: + return do_atmif_sioc(fd, cmd, arg); + } + + return -EINVAL; +} + +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +/* Ugh, LVM. Pitty it was not cleaned up before accepted :((. */ +typedef struct { + uint8_t vg_name[NAME_LEN]; + uint32_t vg_number; + uint32_t vg_access; + uint32_t vg_status; + uint32_t lv_max; + uint32_t lv_cur; + uint32_t lv_open; + uint32_t pv_max; + uint32_t pv_cur; + uint32_t pv_act; + uint32_t dummy; + uint32_t vgda; + uint32_t pe_size; + uint32_t pe_total; + uint32_t pe_allocated; + uint32_t pvg_total; + u32 proc; + u32 pv[ABS_MAX_PV + 1]; + u32 lv[ABS_MAX_LV + 1]; + uint8_t vg_uuid[UUID_LEN+1]; /* volume group UUID */ +} vg32_t; + +typedef struct { + uint8_t id[2]; + uint16_t version; + lvm_disk_data_t pv_on_disk; + lvm_disk_data_t vg_on_disk; + lvm_disk_data_t pv_namelist_on_disk; + lvm_disk_data_t lv_on_disk; + lvm_disk_data_t pe_on_disk; + uint8_t pv_name[NAME_LEN]; + uint8_t vg_name[NAME_LEN]; + uint8_t system_id[NAME_LEN]; + kdev_t pv_dev; + uint32_t pv_number; + uint32_t pv_status; + uint32_t pv_allocatable; + uint32_t pv_size; + uint32_t lv_cur; + uint32_t pe_size; + uint32_t pe_total; + uint32_t pe_allocated; + uint32_t pe_stale; + u32 pe; + u32 inode; + uint8_t pv_uuid[UUID_LEN+1]; +} pv32_t; + +typedef struct { + char lv_name[NAME_LEN]; + u32 lv; +} lv_req32_t; + +typedef struct { + u32 lv_index; + u32 lv; + /* Transfer size because user space and kernel space differ */ + uint16_t size; +} lv_status_byindex_req32_t; + +typedef struct { + dev_t dev; + u32 lv; +} lv_status_bydev_req32_t; + +typedef struct { + uint8_t lv_name[NAME_LEN]; + kdev_t old_dev; + kdev_t new_dev; + u32 old_pe; + u32 new_pe; +} le_remap_req32_t; + +typedef struct { + char pv_name[NAME_LEN]; + u32 pv; +} pv_status_req32_t; + +typedef struct { + uint8_t lv_name[NAME_LEN]; + uint8_t vg_name[NAME_LEN]; + uint32_t lv_access; + uint32_t lv_status; + uint32_t lv_open; + kdev_t lv_dev; + uint32_t lv_number; + uint32_t lv_mirror_copies; + uint32_t lv_recovery; + uint32_t lv_schedule; + uint32_t lv_size; + u32 lv_current_pe; + uint32_t lv_current_le; + uint32_t lv_allocated_le; + uint32_t lv_stripes; + uint32_t lv_stripesize; + uint32_t lv_badblock; + uint32_t lv_allocation; + uint32_t lv_io_timeout; + uint32_t lv_read_ahead; + /* delta to version 1 starts here */ + u32 lv_snapshot_org; + u32 lv_snapshot_prev; + u32 lv_snapshot_next; + u32 lv_block_exception; + uint32_t lv_remap_ptr; + uint32_t lv_remap_end; + uint32_t lv_chunk_size; + uint32_t lv_snapshot_minor; + char dummy[200]; +} lv32_t; + +typedef struct { + u32 hash[2]; + u32 rsector_org; + kdev_t rdev_org; + u32 rsector_new; + kdev_t rdev_new; +} lv_block_exception32_t; + +static void put_lv_t(lv_t *l) +{ + if (l->lv_current_pe) vfree(l->lv_current_pe); + if (l->lv_block_exception) vfree(l->lv_block_exception); + kfree(l); +} + +static lv_t *get_lv_t(u32 p, int *errp) +{ + int err, i; + u32 ptr1, ptr2; + size_t size; + lv_block_exception32_t *lbe32; + lv_block_exception_t *lbe; + lv32_t *ul = (lv32_t *)A(p); + lv_t *l = (lv_t *)kmalloc(sizeof(lv_t), GFP_KERNEL); + if (!l) { + *errp = -ENOMEM; + return NULL; + } + memset(l, 0, sizeof(lv_t)); + err = copy_from_user(l, ul, (long)&((lv32_t *)0)->lv_current_pe); + err |= __copy_from_user(&l->lv_current_le, &ul->lv_current_le, + ((long)&ul->lv_snapshot_org) - ((long)&ul->lv_current_le)); + err |= __copy_from_user(&l->lv_remap_ptr, &ul->lv_remap_ptr, + ((long)&ul->dummy[0]) - ((long)&ul->lv_remap_ptr)); + err |= __get_user(ptr1, &ul->lv_current_pe); + err |= __get_user(ptr2, &ul->lv_block_exception); + if (err) { + kfree(l); + *errp = -EFAULT; + return NULL; + } + if (ptr1) { + size = l->lv_allocated_le * sizeof(pe_t); + l->lv_current_pe = vmalloc(size); + if (l->lv_current_pe) + err = copy_from_user(l->lv_current_pe, (void *)A(ptr1), size); + } + if (!err && ptr2) { + size = l->lv_remap_end * sizeof(lv_block_exception_t); + l->lv_block_exception = lbe = vmalloc(size); + if (l->lv_block_exception) { + lbe32 = (lv_block_exception32_t *)A(ptr2); + memset(lbe, 0, size); + for (i = 0; i < l->lv_remap_end; i++, lbe++, lbe32++) { + err |= get_user(lbe->rsector_org, &lbe32->rsector_org); + err |= __get_user(lbe->rdev_org, &lbe32->rdev_org); + err |= __get_user(lbe->rsector_new, &lbe32->rsector_new); + err |= __get_user(lbe->rdev_new, &lbe32->rdev_new); + + } + } + } + if (err || (ptr1 && !l->lv_current_pe) || (ptr2 && !l->lv_block_exception)) { + if (!err) + *errp = -ENOMEM; + else + *errp = -EFAULT; + put_lv_t(l); + return NULL; + } + return l; +} + +static int copy_lv_t(u32 ptr, lv_t *l) +{ + int err; + lv32_t *ul = (lv32_t *)A(ptr); + u32 ptr1; + size_t size; + + err = get_user(ptr1, &ul->lv_current_pe); + if (err) + return -EFAULT; + err = copy_to_user(ul, l, (long)&((lv32_t *)0)->lv_current_pe); + err |= __copy_to_user(&ul->lv_current_le, &l->lv_current_le, + ((long)&ul->lv_snapshot_org) - ((long)&ul->lv_current_le)); + err |= __copy_to_user(&ul->lv_remap_ptr, &l->lv_remap_ptr, + ((long)&ul->dummy[0]) - ((long)&ul->lv_remap_ptr)); + size = l->lv_allocated_le * sizeof(pe_t); + if (ptr1) + err |= __copy_to_user((void *)A(ptr1), l->lv_current_pe, size); + return err ? -EFAULT : 0; +} + +static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + vg_t *v; + union { + lv_req_t lv_req; + le_remap_req_t le_remap; + lv_status_byindex_req_t lv_byindex; + lv_status_bydev_req_t lv_bydev; + pv_status_req_t pv_status; + } u; + pv_t p; + int err; + u32 ptr = 0; + int i; + mm_segment_t old_fs; + void *karg = &u; + + switch (cmd) { + case VG_STATUS: + v = kmalloc(sizeof(vg_t), GFP_KERNEL); + if (!v) return -ENOMEM; + karg = v; + break; + case VG_CREATE: + v = kmalloc(sizeof(vg_t), GFP_KERNEL); + if (!v) return -ENOMEM; + if (copy_from_user(v, (void *)arg, (long)&((vg32_t *)0)->proc) || + __get_user(v->proc, &((vg32_t *)arg)->proc)) { + kfree(v); + return -EFAULT; + } + if (copy_from_user(v->vg_uuid, ((vg32_t *)arg)->vg_uuid, UUID_LEN+1)) { + kfree(v); + return -EFAULT; + } + + karg = v; + memset(v->pv, 0, sizeof(v->pv) + sizeof(v->lv)); + if (v->pv_max > ABS_MAX_PV || v->lv_max > ABS_MAX_LV) + return -EPERM; + for (i = 0; i < v->pv_max; i++) { + err = __get_user(ptr, &((vg32_t *)arg)->pv[i]); + if (err) break; + if (ptr) { + v->pv[i] = kmalloc(sizeof(pv_t), GFP_KERNEL); + if (!v->pv[i]) { + err = -ENOMEM; + break; + } + err = copy_from_user(v->pv[i], (void *)A(ptr), sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) { + err = -EFAULT; + break; + } + err = copy_from_user(v->pv[i]->pv_uuid, ((pv32_t *)A(ptr))->pv_uuid, UUID_LEN+1); + if (err) { + err = -EFAULT; + break; + } + + + v->pv[i]->pe = NULL; v->pv[i]->inode = NULL; + } + } + if (!err) { + for (i = 0; i < v->lv_max; i++) { + err = __get_user(ptr, &((vg32_t *)arg)->lv[i]); + if (err) break; + if (ptr) { + v->lv[i] = get_lv_t(ptr, &err); + if (err) break; + } + } + } + break; + case LV_CREATE: + case LV_EXTEND: + case LV_REDUCE: + case LV_REMOVE: + case LV_RENAME: + case LV_STATUS_BYNAME: + err = copy_from_user(&u.pv_status, arg, sizeof(u.pv_status.pv_name)); + if (err) return -EFAULT; + if (cmd != LV_REMOVE) { + err = __get_user(ptr, &((lv_req32_t *)arg)->lv); + if (err) return err; + u.lv_req.lv = get_lv_t(ptr, &err); + } else + u.lv_req.lv = NULL; + break; + + + case LV_STATUS_BYINDEX: + err = get_user(u.lv_byindex.lv_index, &((lv_status_byindex_req32_t *)arg)->lv_index); + err |= __get_user(ptr, &((lv_status_byindex_req32_t *)arg)->lv); + if (err) return err; + u.lv_byindex.lv = get_lv_t(ptr, &err); + break; + case LV_STATUS_BYDEV: + err = get_user(u.lv_bydev.dev, &((lv_status_bydev_req32_t *)arg)->dev); + u.lv_bydev.lv = get_lv_t(ptr, &err); + if (err) return err; + u.lv_bydev.lv = &p; + p.pe = NULL; p.inode = NULL; + break; + case VG_EXTEND: + err = copy_from_user(&p, (void *)arg, sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) return -EFAULT; + err = copy_from_user(p.pv_uuid, ((pv32_t *)arg)->pv_uuid, UUID_LEN+1); + if (err) return -EFAULT; + p.pe = NULL; p.inode = NULL; + karg = &p; + break; + case PV_CHANGE: + case PV_STATUS: + err = copy_from_user(&u.pv_status, arg, sizeof(u.lv_req.lv_name)); + if (err) return -EFAULT; + err = __get_user(ptr, &((pv_status_req32_t *)arg)->pv); + if (err) return err; + u.pv_status.pv = &p; + if (cmd == PV_CHANGE) { + err = copy_from_user(&p, (void *)A(ptr), sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) return -EFAULT; + p.pe = NULL; p.inode = NULL; + } + break; + } + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)karg); + set_fs (old_fs); + switch (cmd) { + case VG_STATUS: + if (!err) { + if (copy_to_user((void *)arg, v, (long)&((vg32_t *)0)->proc) || + clear_user(&((vg32_t *)arg)->proc, sizeof(vg32_t) - (long)&((vg32_t *)0)->proc)) + err = -EFAULT; + } + if (copy_to_user(((vg32_t *)arg)->vg_uuid, v->vg_uuid, UUID_LEN+1)) { + err = -EFAULT; + } + kfree(v); + break; + case VG_CREATE: + for (i = 0; i < v->pv_max; i++) + if (v->pv[i]) kfree(v->pv[i]); + for (i = 0; i < v->lv_max; i++) + if (v->lv[i]) put_lv_t(v->lv[i]); + kfree(v); + break; + case LV_STATUS_BYNAME: + if (!err && u.lv_req.lv) err = copy_lv_t(ptr, u.lv_req.lv); + /* Fall through */ + case LV_CREATE: + case LV_EXTEND: + case LV_REDUCE: + if (u.lv_req.lv) put_lv_t(u.lv_req.lv); + break; + case LV_STATUS_BYINDEX: + if (u.lv_byindex.lv) { + if (!err) err = copy_lv_t(ptr, u.lv_byindex.lv); + put_lv_t(u.lv_byindex.lv); + } + break; + case PV_STATUS: + if (!err) { + err = copy_to_user((void *)A(ptr), &p, sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) return -EFAULT; + err = copy_to_user(((pv_t *)A(ptr))->pv_uuid, p.pv_uuid, UUID_LEN + 1); + if (err) return -EFAULT; + } + break; + case LV_STATUS_BYDEV: + if (!err) { + if (!err) err = copy_lv_t(ptr, u.lv_bydev.lv); + put_lv_t(u.lv_byindex.lv); + } + break; + } + return err; +} +#endif + +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +/* This really belongs in include/linux/drm.h -DaveM */ +#include "../../../drivers/char/drm/drm.h" + +typedef struct drm32_version { + int version_major; /* Major version */ + int version_minor; /* Minor version */ + int version_patchlevel;/* Patch level */ + int name_len; /* Length of name buffer */ + u32 name; /* Name of driver */ + int date_len; /* Length of date buffer */ + u32 date; /* User-space buffer to hold date */ + int desc_len; /* Length of desc buffer */ + u32 desc; /* User-space buffer to hold desc */ +} drm32_version_t; +#define DRM32_IOCTL_VERSION DRM_IOWR(0x00, drm32_version_t) + +static int drm32_version(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_version_t *uversion = (drm32_version_t *)arg; + char *name_ptr, *date_ptr, *desc_ptr; + u32 tmp1, tmp2, tmp3; + drm_version_t kversion; + mm_segment_t old_fs; + int ret; + + memset(&kversion, 0, sizeof(kversion)); + if (get_user(kversion.name_len, &uversion->name_len) || + get_user(kversion.date_len, &uversion->date_len) || + get_user(kversion.desc_len, &uversion->desc_len) || + get_user(tmp1, &uversion->name) || + get_user(tmp2, &uversion->date) || + get_user(tmp3, &uversion->desc)) + return -EFAULT; + + name_ptr = (char *) A(tmp1); + date_ptr = (char *) A(tmp2); + desc_ptr = (char *) A(tmp3); + + ret = -ENOMEM; + if (kversion.name_len && name_ptr) { + kversion.name = kmalloc(kversion.name_len, GFP_KERNEL); + if (!kversion.name) + goto out; + } + if (kversion.date_len && date_ptr) { + kversion.date = kmalloc(kversion.date_len, GFP_KERNEL); + if (!kversion.date) + goto out; + } + if (kversion.desc_len && desc_ptr) { + kversion.desc = kmalloc(kversion.desc_len, GFP_KERNEL); + if (!kversion.desc) + goto out; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl (fd, DRM_IOCTL_VERSION, (unsigned long)&kversion); + set_fs(old_fs); + + if (!ret) { + if ((kversion.name && + copy_to_user(name_ptr, kversion.name, kversion.name_len)) || + (kversion.date && + copy_to_user(date_ptr, kversion.date, kversion.date_len)) || + (kversion.desc && + copy_to_user(desc_ptr, kversion.desc, kversion.desc_len))) + ret = -EFAULT; + if (put_user(kversion.version_major, &uversion->version_major) || + put_user(kversion.version_minor, &uversion->version_minor) || + put_user(kversion.version_patchlevel, &uversion->version_patchlevel) || + put_user(kversion.name_len, &uversion->name_len) || + put_user(kversion.date_len, &uversion->date_len) || + put_user(kversion.desc_len, &uversion->desc_len)) + ret = -EFAULT; + } + +out: + if (kversion.name) + kfree(kversion.name); + if (kversion.date) + kfree(kversion.date); + if (kversion.desc) + kfree(kversion.desc); + return ret; +} + +typedef struct drm32_unique { + int unique_len; /* Length of unique */ + u32 unique; /* Unique name for driver instantiation */ +} drm32_unique_t; +#define DRM32_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm32_unique_t) +#define DRM32_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm32_unique_t) + +static int drm32_getsetunique(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_unique_t *uarg = (drm32_unique_t *)arg; + drm_unique_t karg; + mm_segment_t old_fs; + char *uptr; + u32 tmp; + int ret; + + if (get_user(karg.unique_len, &uarg->unique_len)) + return -EFAULT; + karg.unique = NULL; + + if (get_user(tmp, &uarg->unique)) + return -EFAULT; + + uptr = (char *) A(tmp); + + if (uptr) { + karg.unique = kmalloc(karg.unique_len, GFP_KERNEL); + if (!karg.unique) + return -ENOMEM; + if (cmd == DRM32_IOCTL_SET_UNIQUE && + copy_from_user(karg.unique, uptr, karg.unique_len)) { + kfree(karg.unique); + return -EFAULT; + } + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + if (cmd == DRM32_IOCTL_GET_UNIQUE) + ret = sys_ioctl (fd, DRM_IOCTL_GET_UNIQUE, (unsigned long)&karg); + else + ret = sys_ioctl (fd, DRM_IOCTL_SET_UNIQUE, (unsigned long)&karg); + set_fs(old_fs); + + if (!ret) { + if (cmd == DRM32_IOCTL_GET_UNIQUE && + uptr != NULL && + copy_to_user(uptr, karg.unique, karg.unique_len)) + ret = -EFAULT; + if (put_user(karg.unique_len, &uarg->unique_len)) + ret = -EFAULT; + } + + if (karg.unique != NULL) + kfree(karg.unique); + + return ret; +} + +typedef struct drm32_map { + u32 offset; /* Requested physical address (0 for SAREA)*/ + u32 size; /* Requested physical size (bytes) */ + drm_map_type_t type; /* Type of memory to map */ + drm_map_flags_t flags; /* Flags */ + u32 handle; /* User-space: "Handle" to pass to mmap */ + /* Kernel-space: kernel-virtual address */ + int mtrr; /* MTRR slot used */ + /* Private data */ +} drm32_map_t; +#define DRM32_IOCTL_ADD_MAP DRM_IOWR(0x15, drm32_map_t) + +static int drm32_addmap(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_map_t *uarg = (drm32_map_t *) arg; + drm_map_t karg; + mm_segment_t old_fs; + u32 tmp; + int ret; + + ret = get_user(karg.offset, &uarg->offset); + ret |= get_user(karg.size, &uarg->size); + ret |= get_user(karg.type, &uarg->type); + ret |= get_user(karg.flags, &uarg->flags); + ret |= get_user(tmp, &uarg->handle); + ret |= get_user(karg.mtrr, &uarg->mtrr); + if (ret) + return -EFAULT; + + karg.handle = (void *) A(tmp); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_ADD_MAP, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + ret = put_user(karg.offset, &uarg->offset); + ret |= put_user(karg.size, &uarg->size); + ret |= put_user(karg.type, &uarg->type); + ret |= put_user(karg.flags, &uarg->flags); + tmp = (u32) (long)karg.handle; + ret |= put_user(tmp, &uarg->handle); + ret |= put_user(karg.mtrr, &uarg->mtrr); + if (ret) + ret = -EFAULT; + } + + return ret; +} + +typedef struct drm32_buf_info { + int count; /* Entries in list */ + u32 list; /* (drm_buf_desc_t *) */ +} drm32_buf_info_t; +#define DRM32_IOCTL_INFO_BUFS DRM_IOWR(0x18, drm32_buf_info_t) + +static int drm32_info_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_info_t *uarg = (drm32_buf_info_t *)arg; + drm_buf_desc_t *ulist; + drm_buf_info_t karg; + mm_segment_t old_fs; + int orig_count, ret; + u32 tmp; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->list)) + return -EFAULT; + + ulist = (drm_buf_desc_t *) A(tmp); + + orig_count = karg.count; + + karg.list = kmalloc(karg.count * sizeof(drm_buf_desc_t), GFP_KERNEL); + if (!karg.list) + return -EFAULT; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_INFO_BUFS, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (karg.count <= orig_count && + (copy_to_user(ulist, karg.list, + karg.count * sizeof(drm_buf_desc_t)))) + ret = -EFAULT; + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + + kfree(karg.list); + + return ret; +} + +typedef struct drm32_buf_free { + int count; + u32 list; /* (int *) */ +} drm32_buf_free_t; +#define DRM32_IOCTL_FREE_BUFS DRM_IOW( 0x1a, drm32_buf_free_t) + +static int drm32_free_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_free_t *uarg = (drm32_buf_free_t *)arg; + drm_buf_free_t karg; + mm_segment_t old_fs; + int *ulist; + int ret; + u32 tmp; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->list)) + return -EFAULT; + + ulist = (int *) A(tmp); + + karg.list = kmalloc(karg.count * sizeof(int), GFP_KERNEL); + if (!karg.list) + return -ENOMEM; + + ret = -EFAULT; + if (copy_from_user(karg.list, ulist, (karg.count * sizeof(int)))) + goto out; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_FREE_BUFS, (unsigned long) &karg); + set_fs(old_fs); + +out: + kfree(karg.list); + + return ret; +} + +typedef struct drm32_buf_pub { + int idx; /* Index into master buflist */ + int total; /* Buffer size */ + int used; /* Amount of buffer in use (for DMA) */ + u32 address; /* Address of buffer (void *) */ +} drm32_buf_pub_t; + +typedef struct drm32_buf_map { + int count; /* Length of buflist */ + u32 virtual; /* Mmaped area in user-virtual (void *) */ + u32 list; /* Buffer information (drm_buf_pub_t *) */ +} drm32_buf_map_t; +#define DRM32_IOCTL_MAP_BUFS DRM_IOWR(0x19, drm32_buf_map_t) + +static int drm32_map_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_map_t *uarg = (drm32_buf_map_t *)arg; + drm32_buf_pub_t *ulist; + drm_buf_map_t karg; + mm_segment_t old_fs; + int orig_count, ret, i; + u32 tmp1, tmp2; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp1, &uarg->virtual) || + get_user(tmp2, &uarg->list)) + return -EFAULT; + + karg.virtual = (void *) A(tmp1); + ulist = (drm32_buf_pub_t *) A(tmp2); + + orig_count = karg.count; + + karg.list = kmalloc(karg.count * sizeof(drm_buf_pub_t), GFP_KERNEL); + if (!karg.list) + return -ENOMEM; + + ret = -EFAULT; + for (i = 0; i < karg.count; i++) { + if (get_user(karg.list[i].idx, &ulist[i].idx) || + get_user(karg.list[i].total, &ulist[i].total) || + get_user(karg.list[i].used, &ulist[i].used) || + get_user(tmp1, &ulist[i].address)) + goto out; + + karg.list[i].address = (void *) A(tmp1); + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_MAP_BUFS, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + for (i = 0; i < orig_count; i++) { + tmp1 = (u32) (long) karg.list[i].address; + if (put_user(karg.list[i].idx, &ulist[i].idx) || + put_user(karg.list[i].total, &ulist[i].total) || + put_user(karg.list[i].used, &ulist[i].used) || + put_user(tmp1, &ulist[i].address)) { + ret = -EFAULT; + goto out; + } + } + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + +out: + kfree(karg.list); + return ret; +} + +typedef struct drm32_dma { + /* Indices here refer to the offset into + buflist in drm_buf_get_t. */ + int context; /* Context handle */ + int send_count; /* Number of buffers to send */ + u32 send_indices; /* List of handles to buffers (int *) */ + u32 send_sizes; /* Lengths of data to send (int *) */ + drm_dma_flags_t flags; /* Flags */ + int request_count; /* Number of buffers requested */ + int request_size; /* Desired size for buffers */ + u32 request_indices; /* Buffer information (int *) */ + u32 request_sizes; /* (int *) */ + int granted_count; /* Number of buffers granted */ +} drm32_dma_t; +#define DRM32_IOCTL_DMA DRM_IOWR(0x29, drm32_dma_t) + +/* RED PEN The DRM layer blindly dereferences the send/request + * indice/size arrays even though they are userland + * pointers. -DaveM + */ +static int drm32_dma(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_dma_t *uarg = (drm32_dma_t *) arg; + int *u_si, *u_ss, *u_ri, *u_rs; + drm_dma_t karg; + mm_segment_t old_fs; + int ret; + u32 tmp1, tmp2, tmp3, tmp4; + + karg.send_indices = karg.send_sizes = NULL; + karg.request_indices = karg.request_sizes = NULL; + + if (get_user(karg.context, &uarg->context) || + get_user(karg.send_count, &uarg->send_count) || + get_user(tmp1, &uarg->send_indices) || + get_user(tmp2, &uarg->send_sizes) || + get_user(karg.flags, &uarg->flags) || + get_user(karg.request_count, &uarg->request_count) || + get_user(karg.request_size, &uarg->request_size) || + get_user(tmp3, &uarg->request_indices) || + get_user(tmp4, &uarg->request_sizes) || + get_user(karg.granted_count, &uarg->granted_count)) + return -EFAULT; + + u_si = (int *) A(tmp1); + u_ss = (int *) A(tmp2); + u_ri = (int *) A(tmp3); + u_rs = (int *) A(tmp4); + + if (karg.send_count) { + karg.send_indices = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL); + karg.send_sizes = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL); + + ret = -ENOMEM; + if (!karg.send_indices || !karg.send_sizes) + goto out; + + ret = -EFAULT; + if (copy_from_user(karg.send_indices, u_si, + (karg.send_count * sizeof(int))) || + copy_from_user(karg.send_sizes, u_ss, + (karg.send_count * sizeof(int)))) + goto out; + } + + if (karg.request_count) { + karg.request_indices = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL); + karg.request_sizes = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL); + + ret = -ENOMEM; + if (!karg.request_indices || !karg.request_sizes) + goto out; + + ret = -EFAULT; + if (copy_from_user(karg.request_indices, u_ri, + (karg.request_count * sizeof(int))) || + copy_from_user(karg.request_sizes, u_rs, + (karg.request_count * sizeof(int)))) + goto out; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_DMA, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (put_user(karg.context, &uarg->context) || + put_user(karg.send_count, &uarg->send_count) || + put_user(karg.flags, &uarg->flags) || + put_user(karg.request_count, &uarg->request_count) || + put_user(karg.request_size, &uarg->request_size) || + put_user(karg.granted_count, &uarg->granted_count)) + ret = -EFAULT; + + if (karg.send_count) { + if (copy_to_user(u_si, karg.send_indices, + (karg.send_count * sizeof(int))) || + copy_to_user(u_ss, karg.send_sizes, + (karg.send_count * sizeof(int)))) + ret = -EFAULT; + } + if (karg.request_count) { + if (copy_to_user(u_ri, karg.request_indices, + (karg.request_count * sizeof(int))) || + copy_to_user(u_rs, karg.request_sizes, + (karg.request_count * sizeof(int)))) + ret = -EFAULT; + } + } + +out: + if (karg.send_indices) + kfree(karg.send_indices); + if (karg.send_sizes) + kfree(karg.send_sizes); + if (karg.request_indices) + kfree(karg.request_indices); + if (karg.request_sizes) + kfree(karg.request_sizes); + + return ret; +} + +typedef struct drm32_ctx_res { + int count; + u32 contexts; /* (drm_ctx_t *) */ +} drm32_ctx_res_t; +#define DRM32_IOCTL_RES_CTX DRM_IOWR(0x26, drm32_ctx_res_t) + +static int drm32_res_ctx(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_ctx_res_t *uarg = (drm32_ctx_res_t *) arg; + drm_ctx_t *ulist; + drm_ctx_res_t karg; + mm_segment_t old_fs; + int orig_count, ret; + u32 tmp; + + karg.contexts = NULL; + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->contexts)) + return -EFAULT; + + ulist = (drm_ctx_t *) A(tmp); + + orig_count = karg.count; + if (karg.count && ulist) { + karg.contexts = kmalloc((karg.count * sizeof(drm_ctx_t)), GFP_KERNEL); + if (!karg.contexts) + return -ENOMEM; + if (copy_from_user(karg.contexts, ulist, + (karg.count * sizeof(drm_ctx_t)))) { + kfree(karg.contexts); + return -EFAULT; + } + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_RES_CTX, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (orig_count) { + if (copy_to_user(ulist, karg.contexts, + (orig_count * sizeof(drm_ctx_t)))) + ret = -EFAULT; + } + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + + if (karg.contexts) + kfree(karg.contexts); + + return ret; +} + +#endif + +static int ret_einval(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + return -EINVAL; +} + +static int broken_blkgetsize(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + /* The mkswap binary hard codes it to Intel value :-((( */ + return w_long(fd, BLKGETSIZE, arg); +} + +struct blkpg_ioctl_arg32 { + int op; + int flags; + int datalen; + u32 data; +}; + +static int blkpg_ioctl_trans(unsigned int fd, unsigned int cmd, struct blkpg_ioctl_arg32 *arg) +{ + struct blkpg_ioctl_arg a; + struct blkpg_partition p; + int err; + mm_segment_t old_fs = get_fs(); + + err = get_user(a.op, &arg->op); + err |= __get_user(a.flags, &arg->flags); + err |= __get_user(a.datalen, &arg->datalen); + err |= __get_user((long)a.data, &arg->data); + if (err) return err; + switch (a.op) { + case BLKPG_ADD_PARTITION: + case BLKPG_DEL_PARTITION: + if (a.datalen < sizeof(struct blkpg_partition)) + return -EINVAL; + if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) + return -EFAULT; + a.data = &p; + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&a); + set_fs (old_fs); + default: + return -EINVAL; + } + return err; +} + +static int ioc_settimeout(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + return rw_long(fd, AUTOFS_IOC_SETTIMEOUT, arg); +} + +struct ioctl_trans { + unsigned long cmd; + unsigned long handler; + struct ioctl_trans *next; +}; + +#define REF_SYMBOL(handler) if (0) (void)handler; +#define HANDLE_IOCTL2(cmd,handler) REF_SYMBOL(handler); asm volatile(".quad %c0, " #handler ",0"::"i" (cmd)); +#define HANDLE_IOCTL(cmd,handler) HANDLE_IOCTL2(cmd,handler) +#define COMPATIBLE_IOCTL(cmd) HANDLE_IOCTL(cmd,sys_ioctl) +#define IOCTL_TABLE_START void ioctl_dummy(void) { asm volatile("\nioctl_start:\n\t" ); +#define IOCTL_TABLE_END asm volatile("\nioctl_end:"); } + +IOCTL_TABLE_START +/* List here exlicitly which ioctl's are known to have + * compatable types passed or none at all... + */ +/* Big T */ +COMPATIBLE_IOCTL(TCGETA) +COMPATIBLE_IOCTL(TCSETA) +COMPATIBLE_IOCTL(TCSETAW) +COMPATIBLE_IOCTL(TCSETAF) +COMPATIBLE_IOCTL(TCSBRK) +COMPATIBLE_IOCTL(TCXONC) +COMPATIBLE_IOCTL(TCFLSH) +COMPATIBLE_IOCTL(TCGETS) +COMPATIBLE_IOCTL(TCSETS) +COMPATIBLE_IOCTL(TCSETSW) +COMPATIBLE_IOCTL(TCSETSF) +COMPATIBLE_IOCTL(TIOCLINUX) +/* Little t */ +COMPATIBLE_IOCTL(TIOCGETD) +COMPATIBLE_IOCTL(TIOCSETD) +COMPATIBLE_IOCTL(TIOCEXCL) +COMPATIBLE_IOCTL(TIOCNXCL) +COMPATIBLE_IOCTL(TIOCCONS) +COMPATIBLE_IOCTL(TIOCGSOFTCAR) +COMPATIBLE_IOCTL(TIOCSSOFTCAR) +COMPATIBLE_IOCTL(TIOCSWINSZ) +COMPATIBLE_IOCTL(TIOCGWINSZ) +COMPATIBLE_IOCTL(TIOCMGET) +COMPATIBLE_IOCTL(TIOCMBIC) +COMPATIBLE_IOCTL(TIOCMBIS) +COMPATIBLE_IOCTL(TIOCMSET) +COMPATIBLE_IOCTL(TIOCPKT) +COMPATIBLE_IOCTL(TIOCNOTTY) +COMPATIBLE_IOCTL(TIOCSTI) +COMPATIBLE_IOCTL(TIOCOUTQ) +COMPATIBLE_IOCTL(TIOCSPGRP) +COMPATIBLE_IOCTL(TIOCGPGRP) +COMPATIBLE_IOCTL(TIOCSCTTY) +COMPATIBLE_IOCTL(TIOCGPTN) +COMPATIBLE_IOCTL(TIOCSPTLCK) +COMPATIBLE_IOCTL(TIOCGSERIAL) +COMPATIBLE_IOCTL(TIOCSSERIAL) +COMPATIBLE_IOCTL(TIOCSERGETLSR) +COMPATIBLE_IOCTL(FBIOGET_VSCREENINFO) +COMPATIBLE_IOCTL(FBIOPUT_VSCREENINFO) +COMPATIBLE_IOCTL(FBIOPAN_DISPLAY) +COMPATIBLE_IOCTL(FBIOGET_FCURSORINFO) +COMPATIBLE_IOCTL(FBIOGET_VCURSORINFO) +COMPATIBLE_IOCTL(FBIOPUT_VCURSORINFO) +COMPATIBLE_IOCTL(FBIOGET_CURSORSTATE) +COMPATIBLE_IOCTL(FBIOPUT_CURSORSTATE) +COMPATIBLE_IOCTL(FBIOGET_CON2FBMAP) +COMPATIBLE_IOCTL(FBIOPUT_CON2FBMAP) +/* Little f */ +COMPATIBLE_IOCTL(FIOCLEX) +COMPATIBLE_IOCTL(FIONCLEX) +COMPATIBLE_IOCTL(FIOASYNC) +COMPATIBLE_IOCTL(FIONBIO) +COMPATIBLE_IOCTL(FIONREAD) /* This is also TIOCINQ */ +/* 0x00 */ +COMPATIBLE_IOCTL(FIBMAP) +COMPATIBLE_IOCTL(FIGETBSZ) +/* 0x03 -- HD/IDE ioctl's used by hdparm and friends. + * Some need translations, these do not. + */ +COMPATIBLE_IOCTL(HDIO_GET_IDENTITY) +COMPATIBLE_IOCTL(HDIO_SET_DMA) +COMPATIBLE_IOCTL(HDIO_SET_KEEPSETTINGS) +COMPATIBLE_IOCTL(HDIO_SET_UNMASKINTR) +COMPATIBLE_IOCTL(HDIO_SET_NOWERR) +COMPATIBLE_IOCTL(HDIO_SET_32BIT) +COMPATIBLE_IOCTL(HDIO_SET_MULTCOUNT) +COMPATIBLE_IOCTL(HDIO_DRIVE_CMD) +COMPATIBLE_IOCTL(HDIO_SET_PIO_MODE) +COMPATIBLE_IOCTL(HDIO_SCAN_HWIF) +COMPATIBLE_IOCTL(HDIO_SET_NICE) +/* 0x02 -- Floppy ioctls */ +COMPATIBLE_IOCTL(FDMSGON) +COMPATIBLE_IOCTL(FDMSGOFF) +COMPATIBLE_IOCTL(FDSETEMSGTRESH) +COMPATIBLE_IOCTL(FDFLUSH) +COMPATIBLE_IOCTL(FDWERRORCLR) +COMPATIBLE_IOCTL(FDSETMAXERRS) +COMPATIBLE_IOCTL(FDGETMAXERRS) +COMPATIBLE_IOCTL(FDGETDRVTYP) +COMPATIBLE_IOCTL(FDEJECT) +COMPATIBLE_IOCTL(FDCLRPRM) +COMPATIBLE_IOCTL(FDFMTBEG) +COMPATIBLE_IOCTL(FDFMTEND) +COMPATIBLE_IOCTL(FDRESET) +COMPATIBLE_IOCTL(FDTWADDLE) +COMPATIBLE_IOCTL(FDFMTTRK) +COMPATIBLE_IOCTL(FDRAWCMD) +/* 0x12 */ +COMPATIBLE_IOCTL(BLKROSET) +COMPATIBLE_IOCTL(BLKROGET) +COMPATIBLE_IOCTL(BLKRRPART) +COMPATIBLE_IOCTL(BLKFLSBUF) +COMPATIBLE_IOCTL(BLKRASET) +COMPATIBLE_IOCTL(BLKFRASET) +COMPATIBLE_IOCTL(BLKSECTSET) +COMPATIBLE_IOCTL(BLKSSZGET) + +/* RAID */ +COMPATIBLE_IOCTL(RAID_VERSION) +COMPATIBLE_IOCTL(GET_ARRAY_INFO) +COMPATIBLE_IOCTL(GET_DISK_INFO) +COMPATIBLE_IOCTL(PRINT_RAID_DEBUG) +COMPATIBLE_IOCTL(CLEAR_ARRAY) +COMPATIBLE_IOCTL(ADD_NEW_DISK) +COMPATIBLE_IOCTL(HOT_REMOVE_DISK) +COMPATIBLE_IOCTL(SET_ARRAY_INFO) +COMPATIBLE_IOCTL(SET_DISK_INFO) +COMPATIBLE_IOCTL(WRITE_RAID_INFO) +COMPATIBLE_IOCTL(UNPROTECT_ARRAY) +COMPATIBLE_IOCTL(PROTECT_ARRAY) +COMPATIBLE_IOCTL(HOT_ADD_DISK) +COMPATIBLE_IOCTL(SET_DISK_FAULTY) +COMPATIBLE_IOCTL(RUN_ARRAY) +COMPATIBLE_IOCTL(START_ARRAY) +COMPATIBLE_IOCTL(STOP_ARRAY) +COMPATIBLE_IOCTL(STOP_ARRAY_RO) +COMPATIBLE_IOCTL(RESTART_ARRAY_RW) + +/* Big K */ +COMPATIBLE_IOCTL(PIO_FONT) +COMPATIBLE_IOCTL(GIO_FONT) +COMPATIBLE_IOCTL(KDSIGACCEPT) +COMPATIBLE_IOCTL(KDGETKEYCODE) +COMPATIBLE_IOCTL(KDSETKEYCODE) +COMPATIBLE_IOCTL(KIOCSOUND) +COMPATIBLE_IOCTL(KDMKTONE) +COMPATIBLE_IOCTL(KDGKBTYPE) +COMPATIBLE_IOCTL(KDSETMODE) +COMPATIBLE_IOCTL(KDGETMODE) +COMPATIBLE_IOCTL(KDSKBMODE) +COMPATIBLE_IOCTL(KDGKBMODE) +COMPATIBLE_IOCTL(KDSKBMETA) +COMPATIBLE_IOCTL(KDGKBMETA) +COMPATIBLE_IOCTL(KDGKBENT) +COMPATIBLE_IOCTL(KDSKBENT) +COMPATIBLE_IOCTL(KDGKBSENT) +COMPATIBLE_IOCTL(KDSKBSENT) +COMPATIBLE_IOCTL(KDGKBDIACR) +COMPATIBLE_IOCTL(KDSKBDIACR) +COMPATIBLE_IOCTL(KDGKBLED) +COMPATIBLE_IOCTL(KDSKBLED) +COMPATIBLE_IOCTL(KDGETLED) +COMPATIBLE_IOCTL(KDSETLED) +COMPATIBLE_IOCTL(GIO_SCRNMAP) +COMPATIBLE_IOCTL(PIO_SCRNMAP) +COMPATIBLE_IOCTL(GIO_UNISCRNMAP) +COMPATIBLE_IOCTL(PIO_UNISCRNMAP) +COMPATIBLE_IOCTL(PIO_FONTRESET) +COMPATIBLE_IOCTL(PIO_UNIMAPCLR) +/* Big S */ +COMPATIBLE_IOCTL(SCSI_IOCTL_GET_IDLUN) +COMPATIBLE_IOCTL(SCSI_IOCTL_DOORLOCK) +COMPATIBLE_IOCTL(SCSI_IOCTL_DOORUNLOCK) +COMPATIBLE_IOCTL(SCSI_IOCTL_TEST_UNIT_READY) +COMPATIBLE_IOCTL(SCSI_IOCTL_TAGGED_ENABLE) +COMPATIBLE_IOCTL(SCSI_IOCTL_TAGGED_DISABLE) +COMPATIBLE_IOCTL(SCSI_IOCTL_GET_BUS_NUMBER) +COMPATIBLE_IOCTL(SCSI_IOCTL_SEND_COMMAND) +/* Big V */ +COMPATIBLE_IOCTL(VT_SETMODE) +COMPATIBLE_IOCTL(VT_GETMODE) +COMPATIBLE_IOCTL(VT_GETSTATE) +COMPATIBLE_IOCTL(VT_OPENQRY) +COMPATIBLE_IOCTL(VT_ACTIVATE) +COMPATIBLE_IOCTL(VT_WAITACTIVE) +COMPATIBLE_IOCTL(VT_RELDISP) +COMPATIBLE_IOCTL(VT_DISALLOCATE) +COMPATIBLE_IOCTL(VT_RESIZE) +COMPATIBLE_IOCTL(VT_RESIZEX) +COMPATIBLE_IOCTL(VT_LOCKSWITCH) +COMPATIBLE_IOCTL(VT_UNLOCKSWITCH) +/* Little v, the video4linux ioctls */ +COMPATIBLE_IOCTL(VIDIOCGCAP) +COMPATIBLE_IOCTL(VIDIOCGCHAN) +COMPATIBLE_IOCTL(VIDIOCSCHAN) +COMPATIBLE_IOCTL(VIDIOCGPICT) +COMPATIBLE_IOCTL(VIDIOCSPICT) +COMPATIBLE_IOCTL(VIDIOCCAPTURE) +COMPATIBLE_IOCTL(VIDIOCKEY) +COMPATIBLE_IOCTL(VIDIOCGAUDIO) +COMPATIBLE_IOCTL(VIDIOCSAUDIO) +COMPATIBLE_IOCTL(VIDIOCSYNC) +COMPATIBLE_IOCTL(VIDIOCMCAPTURE) +COMPATIBLE_IOCTL(VIDIOCGMBUF) +COMPATIBLE_IOCTL(VIDIOCGUNIT) +COMPATIBLE_IOCTL(VIDIOCGCAPTURE) +COMPATIBLE_IOCTL(VIDIOCSCAPTURE) +/* BTTV specific... */ +COMPATIBLE_IOCTL(_IOW('v', BASE_VIDIOCPRIVATE+0, char [256])) +COMPATIBLE_IOCTL(_IOR('v', BASE_VIDIOCPRIVATE+1, char [256])) +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int)) +COMPATIBLE_IOCTL(_IOW('v' , BASE_VIDIOCPRIVATE+3, char [16])) /* struct bttv_pll_info */ +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+4, int)) +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+5, int)) +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+6, int)) +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+7, int)) +/* Little p (/dev/rtc, /dev/envctrl, etc.) */ +#if 0 +COMPATIBLE_IOCTL(_IOR('p', 20, int[7])) /* RTCGET */ +COMPATIBLE_IOCTL(_IOW('p', 21, int[7])) /* RTCSET */ +#endif +COMPATIBLE_IOCTL(RTC_AIE_ON) +COMPATIBLE_IOCTL(RTC_AIE_OFF) +COMPATIBLE_IOCTL(RTC_UIE_ON) +COMPATIBLE_IOCTL(RTC_UIE_OFF) +COMPATIBLE_IOCTL(RTC_PIE_ON) +COMPATIBLE_IOCTL(RTC_PIE_OFF) +COMPATIBLE_IOCTL(RTC_WIE_ON) +COMPATIBLE_IOCTL(RTC_WIE_OFF) +COMPATIBLE_IOCTL(RTC_ALM_SET) +COMPATIBLE_IOCTL(RTC_ALM_READ) +COMPATIBLE_IOCTL(RTC_RD_TIME) +COMPATIBLE_IOCTL(RTC_SET_TIME) +COMPATIBLE_IOCTL(RTC_WKALM_SET) +COMPATIBLE_IOCTL(RTC_WKALM_RD) +COMPATIBLE_IOCTL(RTC_IRQP_READ) +COMPATIBLE_IOCTL(RTC_IRQP_SET) +COMPATIBLE_IOCTL(RTC_EPOCH_READ) +COMPATIBLE_IOCTL(RTC_EPOCH_SET) +/* Little m */ +COMPATIBLE_IOCTL(MTIOCTOP) +/* Socket level stuff */ +COMPATIBLE_IOCTL(FIOSETOWN) +COMPATIBLE_IOCTL(SIOCSPGRP) +COMPATIBLE_IOCTL(FIOGETOWN) +COMPATIBLE_IOCTL(SIOCGPGRP) +COMPATIBLE_IOCTL(SIOCATMARK) +COMPATIBLE_IOCTL(SIOCSIFLINK) +COMPATIBLE_IOCTL(SIOCSIFENCAP) +COMPATIBLE_IOCTL(SIOCGIFENCAP) +COMPATIBLE_IOCTL(SIOCSIFBR) +COMPATIBLE_IOCTL(SIOCGIFBR) +COMPATIBLE_IOCTL(SIOCSARP) +COMPATIBLE_IOCTL(SIOCGARP) +COMPATIBLE_IOCTL(SIOCDARP) +COMPATIBLE_IOCTL(SIOCSRARP) +COMPATIBLE_IOCTL(SIOCGRARP) +COMPATIBLE_IOCTL(SIOCDRARP) +COMPATIBLE_IOCTL(SIOCADDDLCI) +COMPATIBLE_IOCTL(SIOCDELDLCI) +/* SG stuff */ +COMPATIBLE_IOCTL(SG_SET_TIMEOUT) +COMPATIBLE_IOCTL(SG_GET_TIMEOUT) +COMPATIBLE_IOCTL(SG_EMULATED_HOST) +COMPATIBLE_IOCTL(SG_SET_TRANSFORM) +COMPATIBLE_IOCTL(SG_GET_TRANSFORM) +COMPATIBLE_IOCTL(SG_SET_RESERVED_SIZE) +COMPATIBLE_IOCTL(SG_GET_RESERVED_SIZE) +COMPATIBLE_IOCTL(SG_GET_SCSI_ID) +COMPATIBLE_IOCTL(SG_SET_FORCE_LOW_DMA) +COMPATIBLE_IOCTL(SG_GET_LOW_DMA) +COMPATIBLE_IOCTL(SG_SET_FORCE_PACK_ID) +COMPATIBLE_IOCTL(SG_GET_PACK_ID) +COMPATIBLE_IOCTL(SG_GET_NUM_WAITING) +COMPATIBLE_IOCTL(SG_SET_DEBUG) +COMPATIBLE_IOCTL(SG_GET_SG_TABLESIZE) +COMPATIBLE_IOCTL(SG_GET_COMMAND_Q) +COMPATIBLE_IOCTL(SG_SET_COMMAND_Q) +COMPATIBLE_IOCTL(SG_GET_VERSION_NUM) +COMPATIBLE_IOCTL(SG_NEXT_CMD_LEN) +COMPATIBLE_IOCTL(SG_SCSI_RESET) +COMPATIBLE_IOCTL(SG_IO) +COMPATIBLE_IOCTL(SG_GET_REQUEST_TABLE) +COMPATIBLE_IOCTL(SG_SET_KEEP_ORPHAN) +COMPATIBLE_IOCTL(SG_GET_KEEP_ORPHAN) +/* PPP stuff */ +COMPATIBLE_IOCTL(PPPIOCGFLAGS) +COMPATIBLE_IOCTL(PPPIOCSFLAGS) +COMPATIBLE_IOCTL(PPPIOCGASYNCMAP) +COMPATIBLE_IOCTL(PPPIOCSASYNCMAP) +COMPATIBLE_IOCTL(PPPIOCGUNIT) +COMPATIBLE_IOCTL(PPPIOCGRASYNCMAP) +COMPATIBLE_IOCTL(PPPIOCSRASYNCMAP) +COMPATIBLE_IOCTL(PPPIOCGMRU) +COMPATIBLE_IOCTL(PPPIOCSMRU) +COMPATIBLE_IOCTL(PPPIOCSMAXCID) +COMPATIBLE_IOCTL(PPPIOCGXASYNCMAP) +COMPATIBLE_IOCTL(PPPIOCSXASYNCMAP) +COMPATIBLE_IOCTL(PPPIOCXFERUNIT) +COMPATIBLE_IOCTL(PPPIOCGNPMODE) +COMPATIBLE_IOCTL(PPPIOCSNPMODE) +COMPATIBLE_IOCTL(PPPIOCGDEBUG) +COMPATIBLE_IOCTL(PPPIOCSDEBUG) +COMPATIBLE_IOCTL(PPPIOCNEWUNIT) +COMPATIBLE_IOCTL(PPPIOCATTACH) +COMPATIBLE_IOCTL(PPPIOCDETACH) +COMPATIBLE_IOCTL(PPPIOCSMRRU) +COMPATIBLE_IOCTL(PPPIOCCONNECT) +COMPATIBLE_IOCTL(PPPIOCDISCONN) +COMPATIBLE_IOCTL(PPPIOCATTCHAN) +COMPATIBLE_IOCTL(PPPIOCGCHAN) +/* PPPOX */ +COMPATIBLE_IOCTL(PPPOEIOCSFWD); +COMPATIBLE_IOCTL(PPPOEIOCDFWD); +/* CDROM stuff */ +COMPATIBLE_IOCTL(CDROMPAUSE) +COMPATIBLE_IOCTL(CDROMRESUME) +COMPATIBLE_IOCTL(CDROMPLAYMSF) +COMPATIBLE_IOCTL(CDROMPLAYTRKIND) +COMPATIBLE_IOCTL(CDROMREADTOCHDR) +COMPATIBLE_IOCTL(CDROMREADTOCENTRY) +COMPATIBLE_IOCTL(CDROMSTOP) +COMPATIBLE_IOCTL(CDROMSTART) +COMPATIBLE_IOCTL(CDROMEJECT) +COMPATIBLE_IOCTL(CDROMVOLCTRL) +COMPATIBLE_IOCTL(CDROMSUBCHNL) +COMPATIBLE_IOCTL(CDROMEJECT_SW) +COMPATIBLE_IOCTL(CDROMMULTISESSION) +COMPATIBLE_IOCTL(CDROM_GET_MCN) +COMPATIBLE_IOCTL(CDROMRESET) +COMPATIBLE_IOCTL(CDROMVOLREAD) +COMPATIBLE_IOCTL(CDROMSEEK) +COMPATIBLE_IOCTL(CDROMPLAYBLK) +COMPATIBLE_IOCTL(CDROMCLOSETRAY) +COMPATIBLE_IOCTL(CDROM_SET_OPTIONS) +COMPATIBLE_IOCTL(CDROM_CLEAR_OPTIONS) +COMPATIBLE_IOCTL(CDROM_SELECT_SPEED) +COMPATIBLE_IOCTL(CDROM_SELECT_DISC) +COMPATIBLE_IOCTL(CDROM_MEDIA_CHANGED) +COMPATIBLE_IOCTL(CDROM_DRIVE_STATUS) +COMPATIBLE_IOCTL(CDROM_DISC_STATUS) +COMPATIBLE_IOCTL(CDROM_CHANGER_NSLOTS) +COMPATIBLE_IOCTL(CDROM_LOCKDOOR) +COMPATIBLE_IOCTL(CDROM_DEBUG) +COMPATIBLE_IOCTL(CDROM_GET_CAPABILITY) +/* Big L */ +COMPATIBLE_IOCTL(LOOP_SET_FD) +COMPATIBLE_IOCTL(LOOP_CLR_FD) +/* Big Q for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_SEQ_RESET) +COMPATIBLE_IOCTL(SNDCTL_SEQ_SYNC) +COMPATIBLE_IOCTL(SNDCTL_SYNTH_INFO) +COMPATIBLE_IOCTL(SNDCTL_SEQ_CTRLRATE) +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETOUTCOUNT) +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETINCOUNT) +COMPATIBLE_IOCTL(SNDCTL_SEQ_PERCMODE) +COMPATIBLE_IOCTL(SNDCTL_FM_LOAD_INSTR) +COMPATIBLE_IOCTL(SNDCTL_SEQ_TESTMIDI) +COMPATIBLE_IOCTL(SNDCTL_SEQ_RESETSAMPLES) +COMPATIBLE_IOCTL(SNDCTL_SEQ_NRSYNTHS) +COMPATIBLE_IOCTL(SNDCTL_SEQ_NRMIDIS) +COMPATIBLE_IOCTL(SNDCTL_MIDI_INFO) +COMPATIBLE_IOCTL(SNDCTL_SEQ_THRESHOLD) +COMPATIBLE_IOCTL(SNDCTL_SYNTH_MEMAVL) +COMPATIBLE_IOCTL(SNDCTL_FM_4OP_ENABLE) +COMPATIBLE_IOCTL(SNDCTL_SEQ_PANIC) +COMPATIBLE_IOCTL(SNDCTL_SEQ_OUTOFBAND) +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETTIME) +COMPATIBLE_IOCTL(SNDCTL_SYNTH_ID) +COMPATIBLE_IOCTL(SNDCTL_SYNTH_CONTROL) +COMPATIBLE_IOCTL(SNDCTL_SYNTH_REMOVESAMPLE) +/* Big T for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_TMR_TIMEBASE) +COMPATIBLE_IOCTL(SNDCTL_TMR_START) +COMPATIBLE_IOCTL(SNDCTL_TMR_STOP) +COMPATIBLE_IOCTL(SNDCTL_TMR_CONTINUE) +COMPATIBLE_IOCTL(SNDCTL_TMR_TEMPO) +COMPATIBLE_IOCTL(SNDCTL_TMR_SOURCE) +COMPATIBLE_IOCTL(SNDCTL_TMR_METRONOME) +COMPATIBLE_IOCTL(SNDCTL_TMR_SELECT) +/* Little m for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_MIDI_PRETIME) +COMPATIBLE_IOCTL(SNDCTL_MIDI_MPUMODE) +COMPATIBLE_IOCTL(SNDCTL_MIDI_MPUCMD) +/* Big P for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_DSP_RESET) +COMPATIBLE_IOCTL(SNDCTL_DSP_SYNC) +COMPATIBLE_IOCTL(SNDCTL_DSP_SPEED) +COMPATIBLE_IOCTL(SNDCTL_DSP_STEREO) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETBLKSIZE) +COMPATIBLE_IOCTL(SNDCTL_DSP_CHANNELS) +COMPATIBLE_IOCTL(SOUND_PCM_WRITE_FILTER) +COMPATIBLE_IOCTL(SNDCTL_DSP_POST) +COMPATIBLE_IOCTL(SNDCTL_DSP_SUBDIVIDE) +COMPATIBLE_IOCTL(SNDCTL_DSP_SETFRAGMENT) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETFMTS) +COMPATIBLE_IOCTL(SNDCTL_DSP_SETFMT) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETOSPACE) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETISPACE) +COMPATIBLE_IOCTL(SNDCTL_DSP_NONBLOCK) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETCAPS) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETTRIGGER) +COMPATIBLE_IOCTL(SNDCTL_DSP_SETTRIGGER) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETIPTR) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETOPTR) +/* SNDCTL_DSP_MAPINBUF, XXX needs translation */ +/* SNDCTL_DSP_MAPOUTBUF, XXX needs translation */ +COMPATIBLE_IOCTL(SNDCTL_DSP_SETSYNCRO) +COMPATIBLE_IOCTL(SNDCTL_DSP_SETDUPLEX) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETODELAY) +COMPATIBLE_IOCTL(SNDCTL_DSP_PROFILE) +COMPATIBLE_IOCTL(SOUND_PCM_READ_RATE) +COMPATIBLE_IOCTL(SOUND_PCM_READ_CHANNELS) +COMPATIBLE_IOCTL(SOUND_PCM_READ_BITS) +COMPATIBLE_IOCTL(SOUND_PCM_READ_FILTER) +/* Big C for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_COPR_RESET) +COMPATIBLE_IOCTL(SNDCTL_COPR_LOAD) +COMPATIBLE_IOCTL(SNDCTL_COPR_RDATA) +COMPATIBLE_IOCTL(SNDCTL_COPR_RCODE) +COMPATIBLE_IOCTL(SNDCTL_COPR_WDATA) +COMPATIBLE_IOCTL(SNDCTL_COPR_WCODE) +COMPATIBLE_IOCTL(SNDCTL_COPR_RUN) +COMPATIBLE_IOCTL(SNDCTL_COPR_HALT) +COMPATIBLE_IOCTL(SNDCTL_COPR_SENDMSG) +COMPATIBLE_IOCTL(SNDCTL_COPR_RCVMSG) +/* Big M for sound/OSS */ +COMPATIBLE_IOCTL(SOUND_MIXER_READ_VOLUME) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_BASS) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_TREBLE) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_SYNTH) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_PCM) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_SPEAKER) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_MIC) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_CD) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_IMIX) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_ALTPCM) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECLEV) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_IGAIN) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_OGAIN) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE1) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE2) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE3) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL1)) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL2)) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL3)) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_PHONEIN)) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_PHONEOUT)) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_VIDEO)) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_RADIO)) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_MONITOR)) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_MUTE) +/* SOUND_MIXER_READ_ENHANCE, same value as READ_MUTE */ +/* SOUND_MIXER_READ_LOUD, same value as READ_MUTE */ +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECSRC) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_DEVMASK) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECMASK) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_STEREODEVS) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_CAPS) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_VOLUME) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_BASS) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_TREBLE) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_SYNTH) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_PCM) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_SPEAKER) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_MIC) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_CD) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_IMIX) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_ALTPCM) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_RECLEV) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_IGAIN) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_OGAIN) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE1) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE2) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE3) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL1)) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL2)) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL3)) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_PHONEIN)) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_PHONEOUT)) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_VIDEO)) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_RADIO)) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_MONITOR)) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_MUTE) +/* SOUND_MIXER_WRITE_ENHANCE, same value as WRITE_MUTE */ +/* SOUND_MIXER_WRITE_LOUD, same value as WRITE_MUTE */ +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_RECSRC) +COMPATIBLE_IOCTL(SOUND_MIXER_INFO) +COMPATIBLE_IOCTL(SOUND_OLD_MIXER_INFO) +COMPATIBLE_IOCTL(SOUND_MIXER_ACCESS) +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE1) +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE2) +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE3) +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE4) +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE5) +COMPATIBLE_IOCTL(SOUND_MIXER_GETLEVELS) +COMPATIBLE_IOCTL(SOUND_MIXER_SETLEVELS) +COMPATIBLE_IOCTL(OSS_GETVERSION) +/* AUTOFS */ +COMPATIBLE_IOCTL(AUTOFS_IOC_READY) +COMPATIBLE_IOCTL(AUTOFS_IOC_FAIL) +COMPATIBLE_IOCTL(AUTOFS_IOC_CATATONIC) +COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) +COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) +/* DEVFS */ +COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV) +COMPATIBLE_IOCTL(DEVFSDIOC_SET_EVENT_MASK) +COMPATIBLE_IOCTL(DEVFSDIOC_RELEASE_EVENT_QUEUE) +COMPATIBLE_IOCTL(DEVFSDIOC_SET_DEBUG_MASK) +/* Raw devices */ +COMPATIBLE_IOCTL(RAW_SETBIND) +COMPATIBLE_IOCTL(RAW_GETBIND) +/* SMB ioctls which do not need any translations */ +COMPATIBLE_IOCTL(SMB_IOC_NEWCONN) +/* Little a */ +COMPATIBLE_IOCTL(ATMSIGD_CTRL) +COMPATIBLE_IOCTL(ATMARPD_CTRL) +COMPATIBLE_IOCTL(ATMLEC_CTRL) +COMPATIBLE_IOCTL(ATMLEC_MCAST) +COMPATIBLE_IOCTL(ATMLEC_DATA) +COMPATIBLE_IOCTL(ATM_SETSC) +COMPATIBLE_IOCTL(SIOCSIFATMTCP) +COMPATIBLE_IOCTL(SIOCMKCLIP) +COMPATIBLE_IOCTL(ATMARP_MKIP) +COMPATIBLE_IOCTL(ATMARP_SETENTRY) +COMPATIBLE_IOCTL(ATMARP_ENCAP) +COMPATIBLE_IOCTL(ATMTCP_CREATE) +COMPATIBLE_IOCTL(ATMTCP_REMOVE) +COMPATIBLE_IOCTL(ATMMPC_CTRL) +COMPATIBLE_IOCTL(ATMMPC_DATA) +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +/* 0xfe - lvm */ +COMPATIBLE_IOCTL(VG_SET_EXTENDABLE) +COMPATIBLE_IOCTL(VG_STATUS_GET_COUNT) +COMPATIBLE_IOCTL(VG_STATUS_GET_NAMELIST) +COMPATIBLE_IOCTL(VG_REMOVE) +COMPATIBLE_IOCTL(VG_RENAME) +COMPATIBLE_IOCTL(VG_REDUCE) +COMPATIBLE_IOCTL(PE_LOCK_UNLOCK) +COMPATIBLE_IOCTL(PV_FLUSH) +COMPATIBLE_IOCTL(LVM_LOCK_LVM) +COMPATIBLE_IOCTL(LVM_GET_IOP_VERSION) +#ifdef LVM_TOTAL_RESET +COMPATIBLE_IOCTL(LVM_RESET) +#endif +COMPATIBLE_IOCTL(LV_SET_ACCESS) +COMPATIBLE_IOCTL(LV_SET_STATUS) +COMPATIBLE_IOCTL(LV_SET_ALLOCATION) +COMPATIBLE_IOCTL(LE_REMAP) +COMPATIBLE_IOCTL(LV_BMAP) +COMPATIBLE_IOCTL(LV_SNAPSHOT_USE_RATE) +#endif /* LVM */ +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +COMPATIBLE_IOCTL(DRM_IOCTL_GET_MAGIC) +COMPATIBLE_IOCTL(DRM_IOCTL_IRQ_BUSID) +COMPATIBLE_IOCTL(DRM_IOCTL_AUTH_MAGIC) +COMPATIBLE_IOCTL(DRM_IOCTL_BLOCK) +COMPATIBLE_IOCTL(DRM_IOCTL_UNBLOCK) +COMPATIBLE_IOCTL(DRM_IOCTL_CONTROL) +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_BUFS) +COMPATIBLE_IOCTL(DRM_IOCTL_MARK_BUFS) +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_RM_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_MOD_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_GET_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_SWITCH_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_NEW_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_DRAW) +COMPATIBLE_IOCTL(DRM_IOCTL_RM_DRAW) +COMPATIBLE_IOCTL(DRM_IOCTL_LOCK) +COMPATIBLE_IOCTL(DRM_IOCTL_UNLOCK) +COMPATIBLE_IOCTL(DRM_IOCTL_FINISH) +#endif /* DRM */ +/* elevator */ +COMPATIBLE_IOCTL(BLKELVGET) +COMPATIBLE_IOCTL(BLKELVSET) +/* Misc. */ +COMPATIBLE_IOCTL(0x41545900) /* ATYIO_CLKR */ +COMPATIBLE_IOCTL(0x41545901) /* ATYIO_CLKW */ +COMPATIBLE_IOCTL(PCIIOC_CONTROLLER) +COMPATIBLE_IOCTL(PCIIOC_MMAP_IS_IO) +COMPATIBLE_IOCTL(PCIIOC_MMAP_IS_MEM) +COMPATIBLE_IOCTL(PCIIOC_WRITE_COMBINE) +/* And these ioctls need translation */ +HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32) +HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf) +HANDLE_IOCTL(SIOCGIFFLAGS, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFFLAGS, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFMETRIC, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFMETRIC, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFMTU, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFMTU, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFMEM, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFMEM, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFHWADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFHWADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCADDMULTI, dev_ifsioc) +HANDLE_IOCTL(SIOCDELMULTI, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFINDEX, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFMAP, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFMAP, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFBRDADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFBRDADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFDSTADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFDSTADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFNETMASK, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFNETMASK, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFPFLAGS, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFPFLAGS, dev_ifsioc) +HANDLE_IOCTL(SIOCGPPPSTATS, dev_ifsioc) +HANDLE_IOCTL(SIOCGPPPCSTATS, dev_ifsioc) +HANDLE_IOCTL(SIOCGPPPVER, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFTXQLEN, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFTXQLEN, dev_ifsioc) +HANDLE_IOCTL(SIOCETHTOOL, ethtool_ioctl) +HANDLE_IOCTL(SIOCADDRT, routing_ioctl) +HANDLE_IOCTL(SIOCDELRT, routing_ioctl) +/* Note SIOCRTMSG is no longer, so this is safe and * the user would have seen just an -EINVAL anyways. */ +HANDLE_IOCTL(SIOCRTMSG, ret_einval) +HANDLE_IOCTL(SIOCGSTAMP, do_siocgstamp) +HANDLE_IOCTL(HDIO_GETGEO, hdio_getgeo) +HANDLE_IOCTL(BLKRAGET, w_long) +HANDLE_IOCTL(BLKGETSIZE, w_long) +HANDLE_IOCTL(0x1260, broken_blkgetsize) +HANDLE_IOCTL(BLKFRAGET, w_long) +HANDLE_IOCTL(BLKSECTGET, w_long) +HANDLE_IOCTL(BLKPG, blkpg_ioctl_trans) +HANDLE_IOCTL(FBIOGETCMAP, fb_ioctl_trans) +HANDLE_IOCTL(FBIOPUTCMAP, fb_ioctl_trans) +HANDLE_IOCTL(HDIO_GET_KEEPSETTINGS, hdio_ioctl_trans) +HANDLE_IOCTL(HDIO_GET_UNMASKINTR, hdio_ioctl_trans) +HANDLE_IOCTL(HDIO_GET_DMA, hdio_ioctl_trans) +HANDLE_IOCTL(HDIO_GET_32BIT, hdio_ioctl_trans) +HANDLE_IOCTL(HDIO_GET_MULTCOUNT, hdio_ioctl_trans) +HANDLE_IOCTL(HDIO_GET_NOWERR, hdio_ioctl_trans) +HANDLE_IOCTL(HDIO_GET_NICE, hdio_ioctl_trans) +HANDLE_IOCTL(FDSETPRM32, fd_ioctl_trans) +HANDLE_IOCTL(FDDEFPRM32, fd_ioctl_trans) +HANDLE_IOCTL(FDGETPRM32, fd_ioctl_trans) +HANDLE_IOCTL(FDSETDRVPRM32, fd_ioctl_trans) +HANDLE_IOCTL(FDGETDRVPRM32, fd_ioctl_trans) +HANDLE_IOCTL(FDGETDRVSTAT32, fd_ioctl_trans) +HANDLE_IOCTL(FDPOLLDRVSTAT32, fd_ioctl_trans) +HANDLE_IOCTL(FDGETFDCSTAT32, fd_ioctl_trans) +HANDLE_IOCTL(FDWERRORGET32, fd_ioctl_trans) +HANDLE_IOCTL(PPPIOCGIDLE32, ppp_ioctl_trans) +HANDLE_IOCTL(PPPIOCSCOMPRESS32, ppp_ioctl_trans) +HANDLE_IOCTL(MTIOCGET32, mt_ioctl_trans) +HANDLE_IOCTL(MTIOCPOS32, mt_ioctl_trans) +HANDLE_IOCTL(MTIOCGETCONFIG32, mt_ioctl_trans) +HANDLE_IOCTL(MTIOCSETCONFIG32, mt_ioctl_trans) +HANDLE_IOCTL(CDROMREADMODE2, cdrom_ioctl_trans) +HANDLE_IOCTL(CDROMREADMODE1, cdrom_ioctl_trans) +HANDLE_IOCTL(CDROMREADRAW, cdrom_ioctl_trans) +HANDLE_IOCTL(CDROMREADCOOKED, cdrom_ioctl_trans) +HANDLE_IOCTL(CDROMREADAUDIO, cdrom_ioctl_trans) +HANDLE_IOCTL(CDROMREADALL, cdrom_ioctl_trans) +HANDLE_IOCTL(CDROM_SEND_PACKET, cdrom_ioctl_trans) +HANDLE_IOCTL(LOOP_SET_STATUS, loop_status) +HANDLE_IOCTL(LOOP_GET_STATUS, loop_status) +#define AUTOFS_IOC_SETTIMEOUT32 _IOWR(0x93,0x64,unsigned int) +HANDLE_IOCTL(AUTOFS_IOC_SETTIMEOUT32, ioc_settimeout) +HANDLE_IOCTL(PIO_FONTX, do_fontx_ioctl) +HANDLE_IOCTL(GIO_FONTX, do_fontx_ioctl) +HANDLE_IOCTL(PIO_UNIMAP, do_unimap_ioctl) +HANDLE_IOCTL(GIO_UNIMAP, do_unimap_ioctl) +HANDLE_IOCTL(KDFONTOP, do_kdfontop_ioctl) +HANDLE_IOCTL(EXT2_IOC32_GETFLAGS, do_ext2_ioctl) +HANDLE_IOCTL(EXT2_IOC32_SETFLAGS, do_ext2_ioctl) +HANDLE_IOCTL(EXT2_IOC32_GETVERSION, do_ext2_ioctl) +HANDLE_IOCTL(EXT2_IOC32_SETVERSION, do_ext2_ioctl) +HANDLE_IOCTL(VIDIOCGTUNER32, do_video_ioctl) +HANDLE_IOCTL(VIDIOCSTUNER32, do_video_ioctl) +HANDLE_IOCTL(VIDIOCGWIN32, do_video_ioctl) +HANDLE_IOCTL(VIDIOCSWIN32, do_video_ioctl) +HANDLE_IOCTL(VIDIOCGFBUF32, do_video_ioctl) +HANDLE_IOCTL(VIDIOCSFBUF32, do_video_ioctl) +HANDLE_IOCTL(VIDIOCGFREQ32, do_video_ioctl) +HANDLE_IOCTL(VIDIOCSFREQ32, do_video_ioctl) +/* One SMB ioctl needs translations. */ +#define SMB_IOC_GETMOUNTUID_32 _IOR('u', 1, __kernel_uid_t32) +HANDLE_IOCTL(SMB_IOC_GETMOUNTUID_32, do_smb_getmountuid) +HANDLE_IOCTL(ATM_GETLINKRATE32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETNAMES32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETTYPE32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETESI32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETADDR32, do_atm_ioctl) +HANDLE_IOCTL(ATM_RSTADDR32, do_atm_ioctl) +HANDLE_IOCTL(ATM_ADDADDR32, do_atm_ioctl) +HANDLE_IOCTL(ATM_DELADDR32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETCIRANGE32, do_atm_ioctl) +HANDLE_IOCTL(ATM_SETCIRANGE32, do_atm_ioctl) +HANDLE_IOCTL(ATM_SETESI32, do_atm_ioctl) +HANDLE_IOCTL(ATM_SETESIF32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETSTAT32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETSTATZ32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETLOOP32, do_atm_ioctl) +HANDLE_IOCTL(ATM_SETLOOP32, do_atm_ioctl) +HANDLE_IOCTL(ATM_QUERYLOOP32, do_atm_ioctl) +HANDLE_IOCTL(SONET_GETSTAT, do_atm_ioctl) +HANDLE_IOCTL(SONET_GETSTATZ, do_atm_ioctl) +HANDLE_IOCTL(SONET_GETDIAG, do_atm_ioctl) +HANDLE_IOCTL(SONET_SETDIAG, do_atm_ioctl) +HANDLE_IOCTL(SONET_CLRDIAG, do_atm_ioctl) +HANDLE_IOCTL(SONET_SETFRAMING, do_atm_ioctl) +HANDLE_IOCTL(SONET_GETFRAMING, do_atm_ioctl) +HANDLE_IOCTL(SONET_GETFRSENSE, do_atm_ioctl) +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +HANDLE_IOCTL(VG_STATUS, do_lvm_ioctl) +HANDLE_IOCTL(VG_CREATE, do_lvm_ioctl) +HANDLE_IOCTL(VG_EXTEND, do_lvm_ioctl) +HANDLE_IOCTL(LV_CREATE, do_lvm_ioctl) +HANDLE_IOCTL(LV_REMOVE, do_lvm_ioctl) +HANDLE_IOCTL(LV_EXTEND, do_lvm_ioctl) +HANDLE_IOCTL(LV_REDUCE, do_lvm_ioctl) +HANDLE_IOCTL(LV_RENAME, do_lvm_ioctl) +HANDLE_IOCTL(LV_STATUS_BYNAME, do_lvm_ioctl) +HANDLE_IOCTL(LV_STATUS_BYINDEX, do_lvm_ioctl) +HANDLE_IOCTL(PV_CHANGE, do_lvm_ioctl) +HANDLE_IOCTL(PV_STATUS, do_lvm_ioctl) +#endif /* LVM */ +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +HANDLE_IOCTL(DRM32_IOCTL_VERSION, drm32_version); +HANDLE_IOCTL(DRM32_IOCTL_GET_UNIQUE, drm32_getsetunique); +HANDLE_IOCTL(DRM32_IOCTL_SET_UNIQUE, drm32_getsetunique); +HANDLE_IOCTL(DRM32_IOCTL_ADD_MAP, drm32_addmap); +HANDLE_IOCTL(DRM32_IOCTL_INFO_BUFS, drm32_info_bufs); +HANDLE_IOCTL(DRM32_IOCTL_FREE_BUFS, drm32_free_bufs); +HANDLE_IOCTL(DRM32_IOCTL_MAP_BUFS, drm32_map_bufs); +HANDLE_IOCTL(DRM32_IOCTL_DMA, drm32_dma); +HANDLE_IOCTL(DRM32_IOCTL_RES_CTX, drm32_res_ctx); +#endif /* DRM */ +IOCTL_TABLE_END + +#define IOCTL_HASHSIZE 256 +struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE]; + +static inline unsigned long ioctl32_hash(unsigned long cmd) +{ + return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE; +} + +static void ioctl32_insert_translation(struct ioctl_trans *trans) +{ + unsigned long hash; + struct ioctl_trans *t; + + hash = ioctl32_hash (trans->cmd); + if (!ioctl32_hash_table[hash]) + ioctl32_hash_table[hash] = trans; + else { + t = ioctl32_hash_table[hash]; + while (t->next) + t = t->next; + trans->next = 0; + t->next = trans; + } +} + +static int __init init_sys32_ioctl(void) +{ + int i; + extern struct ioctl_trans ioctl_start[], ioctl_end[]; + + for (i = 0; &ioctl_start[i] < &ioctl_end[0]; i++) { + if (ioctl_start[i].next != 0) { + printk("ioctl translation %d bad\n",i); + return -1; + } + + ioctl32_insert_translation(&ioctl_start[i]); + } + return 0; +} + +__initcall(init_sys32_ioctl); + +static struct ioctl_trans *additional_ioctls; + +/* Always call these with kernel lock held! */ + +int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)) +{ + int i; + if (!additional_ioctls) { + additional_ioctls = module_map(PAGE_SIZE); + if (!additional_ioctls) + return -ENOMEM; + memset(additional_ioctls, 0, PAGE_SIZE); + } + for (i = 0; i < PAGE_SIZE/sizeof(struct ioctl_trans); i++) + if (!additional_ioctls[i].cmd) + break; + if (i == PAGE_SIZE/sizeof(struct ioctl_trans)) + return -ENOMEM; + additional_ioctls[i].cmd = cmd; + if (!handler) + additional_ioctls[i].handler = (u32)(long)sys_ioctl; + else + additional_ioctls[i].handler = (u32)(long)handler; + ioctl32_insert_translation(&additional_ioctls[i]); + return 0; +} + +int unregister_ioctl32_conversion(unsigned int cmd) +{ + unsigned long hash = ioctl32_hash(cmd); + struct ioctl_trans *t, *t1; + + t = (struct ioctl_trans *)(long)ioctl32_hash_table[hash]; + if (!t) return -EINVAL; + if (t->cmd == cmd && t >= additional_ioctls && + (unsigned long)t < ((unsigned long)additional_ioctls) + PAGE_SIZE) { + ioctl32_hash_table[hash] = t->next; + t->cmd = 0; + return 0; + } else while (t->next) { + t1 = (struct ioctl_trans *)(long)t->next; + if (t1->cmd == cmd && t1 >= additional_ioctls && + (unsigned long)t1 < ((unsigned long)additional_ioctls) + PAGE_SIZE) { + t1->cmd = 0; + t->next = t1->next; + return 0; + } + t = t1; + } + return -EINVAL; +} + +asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct file * filp; + int error = -EBADF; + int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp); + struct ioctl_trans *t; + + filp = fget(fd); + if(!filp) + goto out2; + + if (!filp->f_op || !filp->f_op->ioctl) { + error = sys_ioctl (fd, cmd, arg); + goto out; + } + + t = (struct ioctl_trans *)(long)ioctl32_hash_table [ioctl32_hash (cmd)]; + + while (t && t->cmd != cmd) + t = (struct ioctl_trans *)(long)t->next; + if (t) { + handler = (void *)(long)t->handler; + error = handler(fd, cmd, arg, filp); + } else { + static int count = 0; + if (++count <= 50) + printk("sys32_ioctl(%s:%d): Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + current->comm, current->pid, + (int)fd, (unsigned int)cmd, (unsigned int)arg); + error = -EINVAL; + } +out: + fput(filp); +out2: + return error; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/ia32/ia32_signal.c linux.ac/arch/x86_64/ia32/ia32_signal.c --- linux.vanilla/arch/x86_64/ia32/ia32_signal.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/ia32/ia32_signal.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,486 @@ +/* + * linux/arch/x86_64/ia32/ia32_signal.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + * 2000-06-20 Pentium III FXSR, SSE support by Gareth Hughes + * 2000-12-* x86-64 compatibility mode signal handling by Andi Kleen + * + * $Id: ia32_signal.c,v 1.13 2001/08/15 05:38:38 ak Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ptr_to_u32(x) ((u32)(u64)(x)) /* avoid gcc warning */ + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset); + +static int ia32_copy_siginfo_to_user(siginfo_t32 *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + +asmlinkage int +sys32_sigsuspend(int history0, int history1, old_sigset_t mask, struct pt_regs regs) +{ + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs.rax = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(®s, &saveset)) + return -EINTR; + } +} + +asmlinkage int +sys32_sigaltstack(const stack_ia32_t *uss_ptr, stack_ia32_t *uoss_ptr, + struct pt_regs regs) +{ + stack_t uss,uoss; + int ret; + mm_segment_t seg; + if (!access_ok(VERIFY_READ,uss_ptr,sizeof(stack_ia32_t)) || + __get_user(ptr_to_u32(uss.ss_sp), &uss_ptr->ss_sp) || + __get_user((u32)uss.ss_flags, &uss_ptr->ss_flags) || + __get_user((u32)uss.ss_size, &uss_ptr->ss_size)) + return -EFAULT; + seg = get_fs(); + set_fs(KERNEL_DS); + ret = do_sigaltstack(&uss, &uoss, regs.rsp); + set_fs(seg); + if (ret >= 0 && uoss_ptr) { + if (!access_ok(VERIFY_WRITE,uss_ptr,sizeof(stack_ia32_t)) || + __put_user(ptr_to_u32(uss.ss_sp), &uss_ptr->ss_sp) || + __put_user((u32)uss.ss_flags, &uss_ptr->ss_flags) || + __put_user((u32)uss.ss_size, &uss_ptr->ss_size)) + ret = -EFAULT; + } + return ret; +} + +/* + * Do a signal return; undo the signal stack. + */ + +struct sigframe +{ + u32 pretcode; + int sig; + struct sigcontext_ia32 sc; + struct _fpstate_ia32 fpstate; + unsigned int extramask[_IA32_NSIG_WORDS-1]; + char retcode[8]; +}; + +struct rt_sigframe +{ + u32 pretcode; + int sig; + u32 pinfo; + u32 puc; + struct siginfo32 info; + struct ucontext_ia32 uc; + struct _fpstate_ia32 fpstate; + char retcode[8]; +}; + +static int +restore_sigcontext(struct pt_regs *regs, struct sigcontext_ia32 *sc, unsigned int *peax) +{ + unsigned int err = 0; + +#if DEBUG_SIG + printk("SIG restore_sigcontext: sc=%p err(%x) eip(%x) cs(%x) flg(%x)\n", + sc, sc->err, sc->eip, sc->cs, sc->eflags); +#endif +#define COPY(x) { \ + unsigned int reg; \ + err |= __get_user(reg, &sc->e ##x); \ + regs->r ## x = reg; \ +} + +#define RELOAD_SEG(seg) \ + { unsigned int cur; \ + unsigned short pre; \ + err |= __get_user(pre, &sc->seg); \ + asm volatile("movl %%" #seg ",%0" : "=r" (cur)); \ + if (pre != cur) loadsegment(seg,pre); } + + /* Reload fs and gs if they have changed in the signal handler. + This does not handle long fs/gs base changes in the handler, but does not clobber + them at least in the normal case. */ + RELOAD_SEG(gs); + RELOAD_SEG(fs); + + COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx); + COPY(dx); COPY(cx); COPY(ip); + /* Don't touch extended registers */ + + { + unsigned int tmpflags; + err |= __get_user(tmpflags, &sc->eflags); + regs->eflags = (regs->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); + regs->orig_rax = -1; /* disable syscall checks */ + } + + { + u32 tmp; + struct _fpstate * buf; + err |= __get_user(tmp, &sc->fpstate); + buf = (struct _fpstate *) (u64)tmp; + if (buf) { + if (verify_area(VERIFY_READ, buf, sizeof(*buf))) + goto badframe; + err |= restore_i387(buf); + } + } + + { + u32 tmp; + err |= __get_user(tmp, &sc->eax); + *peax = tmp; + } + return err; + +badframe: + return 1; +} + +asmlinkage int sys32_sigreturn(struct pt_regs regs) +{ + struct sigframe *frame = (struct sigframe *)(regs.rsp - 8); + sigset_t set; + unsigned int eax; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__get_user(set.sig[0], &frame->sc.oldmask) + || (_IA32_NSIG_WORDS > 1 + && __copy_from_user((((char *) &set.sig) + 4), &frame->extramask, + sizeof(frame->extramask)))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigcontext(®s, &frame->sc, &eax)) + goto badframe; + return eax; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage int sys32_rt_sigreturn(struct pt_regs regs) +{ + struct rt_sigframe *frame = (struct rt_sigframe *)(regs.rsp - 4); + sigset_t set; + stack_t st; + unsigned int eax; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigcontext(®s, &frame->uc.uc_mcontext, &eax)) + goto badframe; + + if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) + goto badframe; + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + { + mm_segment_t oldds = get_fs(); + set_fs(KERNEL_DS); + do_sigaltstack(&st, NULL, regs.rsp); + set_fs(oldds); + } + + return eax; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Set up a signal frame. + */ + +static int +setup_sigcontext(struct sigcontext_ia32 *sc, struct _fpstate *fpstate, + struct pt_regs *regs, unsigned int mask) +{ + int tmp, err = 0; + + tmp = 0; + __asm__("movl %%gs,%0" : "=r"(tmp): "0"(tmp)); + err |= __put_user(tmp, (unsigned int *)&sc->gs); + __asm__("movl %%fs,%0" : "=r"(tmp): "0"(tmp)); + err |= __put_user(tmp, (unsigned int *)&sc->fs); + + err |= __put_user((u32)regs->rdi, &sc->edi); + err |= __put_user((u32)regs->rsi, &sc->esi); + err |= __put_user((u32)regs->rbp, &sc->ebp); + err |= __put_user((u32)regs->rsp, &sc->esp); + err |= __put_user((u32)regs->rbx, &sc->ebx); + err |= __put_user((u32)regs->rdx, &sc->edx); + err |= __put_user((u32)regs->rcx, &sc->ecx); + err |= __put_user((u32)regs->rax, &sc->eax); + err |= __put_user(current->thread.trap_no, &sc->trapno); + err |= __put_user(current->thread.error_code, &sc->err); + err |= __put_user((u32)regs->rip, &sc->eip); + err |= __put_user((u32)regs->eflags, &sc->eflags); + err |= __put_user((u32)regs->rsp, &sc->esp_at_signal); + + tmp = save_i387(fpstate); + if (tmp < 0) + err = -EFAULT; + else + err |= __put_user((u32)(u64)(tmp ? fpstate : NULL), &sc->fpstate); + + /* non-iBCS2 extensions.. */ + err |= __put_user(mask, &sc->oldmask); + err |= __put_user(current->thread.cr2, &sc->cr2); + + return err; +} + +/* + * Determine which stack to use.. + */ +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) +{ + unsigned long rsp; + + /* Default to using normal stack */ + rsp = regs->rsp; + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (! on_sig_stack(rsp)) + rsp = current->sas_ss_sp + current->sas_ss_size; + } + + /* This is the legacy signal stack switching. */ + else if ((regs->ss & 0xffff) != __USER_DS && + !(ka->sa.sa_flags & SA_RESTORER) && + ka->sa.sa_restorer) { + rsp = (unsigned long) ka->sa.sa_restorer; + } + + return (void *)((rsp - frame_size) & -8UL); +} + +void ia32_setup_frame(int sig, struct k_sigaction *ka, + sigset32_t *set, struct pt_regs * regs) +{ + struct sigframe *frame; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + err |= __put_user((current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? current->exec_domain->signal_invmap[sig] + : sig), + &frame->sig); + if (err) + goto give_sigsegv; + + err |= setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]); + if (err) + goto give_sigsegv; + + if (_IA32_NSIG_WORDS > 1) { + err |= __copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + } + if (err) + goto give_sigsegv; + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + err |= __put_user((u32)(u64)ka->sa.sa_restorer, &frame->pretcode); + } else { + err |= __put_user((u32)(u64)frame->retcode, &frame->pretcode); + /* This is popl %eax ; movl $,%eax ; int $0x80 */ + err |= __put_user((u16)0xb858, (short *)(frame->retcode+0)); + err |= __put_user((u32)__NR_ia32_sigreturn, (int *)(frame->retcode+2)); + err |= __put_user((u16)0x80cd, (short *)(frame->retcode+6)); + } + + if (err) + goto give_sigsegv; + + /* Set up registers for signal handler */ + regs->rsp = (unsigned long) frame; + regs->rip = (unsigned long) ka->sa.sa_handler; + + set_fs(USER_DS); + // XXX: cs + regs->eflags &= ~TF_MASK; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->rip, frame->pretcode); +#endif + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +void ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset32_t *set, struct pt_regs * regs) +{ + struct rt_sigframe *frame; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + err |= __put_user((current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? current->exec_domain->signal_invmap[sig] + : sig), + &frame->sig); + err |= __put_user((u32)(u64)&frame->info, &frame->pinfo); + err |= __put_user((u32)(u64)&frame->uc, &frame->puc); + err |= ia32_copy_siginfo_to_user(&frame->info, info); + if (err) + goto give_sigsegv; + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->rsp), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate, + regs, set->sig[0]); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + goto give_sigsegv; + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + err |= __put_user((u32)(u64)ka->sa.sa_restorer, &frame->pretcode); + } else { + err |= __put_user(ptr_to_u32(frame->retcode), &frame->pretcode); + /* This is movl $,%eax ; int $0x80 */ + err |= __put_user(0xb8, (char *)(frame->retcode+0)); + err |= __put_user((u32)__NR_ia32_rt_sigreturn, (int *)(frame->retcode+1)); + err |= __put_user(0x80cd, (short *)(frame->retcode+5)); + } + + if (err) + goto give_sigsegv; + + /* Set up registers for signal handler */ + regs->rsp = (unsigned long) frame; + regs->rip = (unsigned long) ka->sa.sa_handler; + + set_fs(USER_DS); + // XXX: cs + regs->eflags &= ~TF_MASK; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->rip, frame->pretcode); +#endif + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/ia32/ia32entry.S linux.ac/arch/x86_64/ia32/ia32entry.S --- linux.vanilla/arch/x86_64/ia32/ia32entry.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/ia32/ia32entry.S Wed Oct 10 01:47:38 2001 @@ -0,0 +1,358 @@ +/* + * Compatibility mode system call entry point for x86-64. + * + * Copyright 2000,2001 Andi Kleen, SuSE Labs. + * + * $Id: ia32entry.S,v 1.20 2001/08/15 05:38:38 ak Exp $ + */ + +#include +#include +#include +#include +#include +#include + + .macro IA32_ARG_FIXUP + movl %edi,%r8d + movl %ebp,%r9d + xchg %ecx,%esi + movl %ebx,%edi + .endm + +/* + * 32bit SYSCALL instruction entry. + * It'll probably kill you because it destroys your segments. + * Should coredump here, but the next instruction will likely do + * that anyways. + */ +ENTRY(ia32_cstar_target) + movq $-ENOSYS,%rax + SYSRET32 + +/* + * Emulated IA32 system calls via int 0x80. + * + * Arguments: + * %eax System call number. + * %ebx Arg1 + * %ecx Arg2 + * %edx Arg3 + * %esi Arg4 + * %edi Arg5 + * %ebp Arg6 [note: not saved in the stack frame, should not be touched] + * + * Notes: + * Uses the same stack frame as the x86-64 version. + * All registers except %eax must be saved (but ptrace may violate that) + * Arguments are zero extended. For system calls that want sign extension and + * take long arguments a wrapper is needed. Most calls can just be called + * directly. + * Assumes it is only called from user space and entered with interrups off. + */ + +ENTRY(ia32_syscall) + swapgs + sti + pushq %rax + cld + SAVE_ARGS + cmpl $(IA32_NR_syscalls),%eax + jae ia32_badsys + GET_CURRENT(%r10) + testl $PT_TRACESYS,tsk_ptrace(%r10) + jne ia32_tracesys + IA32_ARG_FIXUP + movl $1,%r10d + call *ia32_sys_call_table(,%rax,8) # xxx: rip relative + movq %rax,RAX-ARGOFFSET(%rsp) + jmp int_ret_from_sys_call + +ia32_tracesys: + SAVE_REST + movq $-ENOSYS,RAX(%rsp) + movq %rsp,%rdi /* &pt_regs -> arg1 */ + call syscall_trace + LOAD_ARGS ARGOFFSET /* reload args from stack in case ptrace changed it */ + addq $ARGOFFSET,%rsp + cmpl $(IA32_NR_syscalls),%eax + jae 1f + IA32_ARG_FIXUP + movl $1,%r10d + call *ia32_sys_call_table(,%rax,8) +ia32_tracesys_end: + movq %rax,RAX-ARGOFFSET(%rsp) +1: SAVE_REST + movq %rsp,%rdi /* &pt_regs -> arg1 */ + call syscall_trace + addq $ARGOFFSET,%rsp + jmp int_ret_from_sys_call + +ia32_badsys: + movq $-ENOSYS,RAX-ARGOFFSET(%rsp) + jmp int_ret_from_sys_call + +ni_syscall: + movq %rax,%rdi + jmp sys32_ni_syscall + + .macro PTREGSCALL label, func + .globl \label +\label: + leaq \func(%rip),%rax + jmp ia32_ptregs_common + .endm + + PTREGSCALL stub32_rt_sigreturn, sys32_rt_sigreturn + PTREGSCALL stub32_sigreturn, sys32_sigreturn + PTREGSCALL stub32_sigaltstack, sys32_sigaltstack + PTREGSCALL stub32_sigsuspend, sys32_sigsuspend + PTREGSCALL stub32_execve, sys32_execve + PTREGSCALL stub32_fork, sys32_fork + PTREGSCALL stub32_clone, sys32_clone + PTREGSCALL stub32_vfork, sys32_vfork + PTREGSCALL stub32_iopl, sys_iopl + PTREGSCALL stub32_rt_sigsuspend, sys_rt_sigsuspend + +ENTRY(ia32_ptregs_common) + popq %r11 + SAVE_REST + movq %r11, %r15 + call *%rax + movq %r15, %r11 + RESTORE_REST + pushq %r11 + ret + + .data + .align 8 +ia32_sys_call_table: + .quad ni_syscall /* 0 - old "setup" system call*/ + .quad sys_exit + .quad stub32_fork + .quad sys_read + .quad sys_write + .quad sys_open /* 5 */ + .quad sys_close + .quad sys32_waitpid + .quad sys_creat + .quad sys_link + .quad sys_unlink /* 10 */ + .quad stub32_execve + .quad sys_chdir + .quad sys32_time + .quad sys_mknod + .quad sys_chmod /* 15 */ + .quad sys_lchown16 + .quad ni_syscall /* old break syscall holder */ + .quad ni_syscall /* (old)stat */ + .quad sys32_lseek + .quad sys_getpid /* 20 */ + .quad sys_mount /* mount */ + .quad sys_oldumount /* old_umount */ + .quad sys_setuid16 + .quad sys_getuid16 + .quad ni_syscall /* stime */ /* 25 */ + .quad sys32_ptrace /* ptrace */ + .quad sys_alarm /* XXX sign extension??? */ + .quad ni_syscall /* (old)fstat */ + .quad sys_pause + .quad sys32_utime /* 30 */ + .quad ni_syscall /* old stty syscall holder */ + .quad ni_syscall /* old gtty syscall holder */ + .quad sys_access + .quad sys_nice + .quad ni_syscall /* 35 */ /* old ftime syscall holder */ + .quad sys_sync + .quad sys32_kill + .quad sys_rename + .quad sys_mkdir + .quad sys_rmdir /* 40 */ + .quad sys_dup + .quad sys32_pipe + .quad sys32_times + .quad ni_syscall /* old prof syscall holder */ + .quad sys_brk /* 45 */ + .quad sys_setgid16 + .quad sys_getgid16 + .quad ni_syscall /* signal */ + .quad sys_geteuid16 + .quad sys_getegid16 /* 50 */ + .quad sys_acct + .quad sys_umount /* new_umount */ + .quad ni_syscall /* old lock syscall holder */ + .quad sys32_ioctl + .quad sys32_fcntl /* 55 */ + .quad ni_syscall /* old mpx syscall holder */ + .quad sys_setpgid + .quad ni_syscall /* old ulimit syscall holder */ + .quad sys32_olduname + .quad sys_umask /* 60 */ + .quad sys_chroot + .quad sys32_ustat + .quad sys_dup2 + .quad sys_getppid + .quad sys_getpgrp /* 65 */ + .quad sys_setsid + .quad sys32_sigaction + .quad sys_sgetmask + .quad sys_ssetmask + .quad sys_setreuid16 /* 70 */ + .quad sys_setregid16 + .quad stub32_sigsuspend + .quad sys32_sigpending + .quad sys_sethostname + .quad sys32_setrlimit /* 75 */ + .quad sys32_old_getrlimit /* old_getrlimit */ + .quad sys32_getrusage + .quad sys32_gettimeofday + .quad sys32_settimeofday + .quad sys_getgroups16 /* 80 */ + .quad sys_setgroups16 + .quad sys32_old_select + .quad sys_symlink + .quad ni_syscall /* (old)lstat */ + .quad sys_readlink /* 85 */ + .quad sys_uselib + .quad sys_swapon + .quad sys_reboot + .quad sys32_oldreaddir + .quad sys32_mmap /* 90 */ + .quad sys_munmap + .quad sys_truncate + .quad sys_ftruncate + .quad sys_fchmod + .quad sys_fchown16 /* 95 */ + .quad sys_getpriority + .quad sys_setpriority + .quad ni_syscall /* old profil syscall holder */ + .quad sys32_statfs + .quad sys32_fstatfs /* 100 */ + .quad sys_ioperm + .quad sys32_socketcall + .quad sys_syslog + .quad sys32_setitimer + .quad sys32_getitimer /* 105 */ + .quad sys32_newstat + .quad sys32_newlstat + .quad sys32_newfstat + .quad sys32_uname + .quad stub32_iopl /* 110 */ + .quad sys_vhangup + .quad ni_syscall /* old "idle" system call */ + .quad ni_syscall /* vm86old */ + .quad sys32_wait4 + .quad sys_swapoff /* 115 */ + .quad sys32_sysinfo + .quad sys32_ipc + .quad sys_fsync + .quad stub32_sigreturn + .quad stub32_clone /* 120 */ + .quad sys_setdomainname + .quad sys_newuname + .quad ni_syscall /* modify_ldt */ + .quad sys32_adjtimex + .quad sys_mprotect /* 125 */ + .quad sys32_sigprocmask + .quad ni_syscall /* query_module */ + .quad ni_syscall /* init_module */ + .quad ni_syscall /* delete module */ + .quad ni_syscall /* 130 get_kernel_syms */ + .quad ni_syscall /* quotactl */ + .quad sys_getpgid + .quad sys_fchdir + .quad ni_syscall /* bdflush */ + .quad sys_sysfs /* 135 */ + .quad sys_personality + .quad ni_syscall /* for afs_syscall */ + .quad sys_setfsuid16 + .quad sys_setfsgid16 + .quad sys_llseek /* 140 */ + .quad sys32_getdents + .quad sys32_select + .quad sys_flock + .quad sys_msync + .quad sys32_readv /* 145 */ + .quad sys32_writev + .quad sys_getsid + .quad sys_fdatasync + .quad sys32_sysctl /* sysctl */ + .quad sys_mlock /* 150 */ + .quad sys_munlock + .quad sys_mlockall + .quad sys_munlockall + .quad sys_sched_setparam + .quad sys_sched_getparam /* 155 */ + .quad sys_sched_setscheduler + .quad sys_sched_getscheduler + .quad sys_sched_yield + .quad sys_sched_get_priority_max + .quad sys_sched_get_priority_min /* 160 */ + .quad sys_sched_rr_get_interval + .quad sys32_nanosleep + .quad sys_mremap + .quad sys_setresuid16 + .quad sys_getresuid16 /* 165 */ + .quad ni_syscall /* vm86 */ + .quad ni_syscall /* query_module */ + .quad sys_poll + .quad ni_syscall /* nfsserverctl */ + .quad sys_setresgid16 /* 170 */ + .quad sys_getresgid16 + .quad sys_prctl + .quad stub32_rt_sigreturn + .quad sys32_rt_sigaction + .quad sys32_rt_sigprocmask /* 175 */ + .quad sys32_rt_sigpending + .quad sys32_rt_sigtimedwait + .quad sys32_rt_sigqueueinfo + .quad stub32_rt_sigsuspend + .quad sys32_pread /* 180 */ + .quad sys32_pwrite + .quad sys_chown16 + .quad sys_getcwd + .quad ni_syscall /* capget */ + .quad ni_syscall /* capset */ + .quad stub32_sigaltstack + .quad sys32_sendfile + .quad ni_syscall /* streams1 */ + .quad ni_syscall /* streams2 */ + .quad stub32_vfork /* 190 */ + .quad sys32_getrlimit + .quad sys32_mmap2 + .quad sys_truncate + .quad sys_ftruncate + .quad sys32_stat64 /* 195 */ + .quad sys32_lstat64 + .quad sys32_fstat64 + .quad sys_lchown + .quad sys_getuid + .quad sys_getgid /* 200 */ + .quad sys_geteuid + .quad sys_getegid + .quad sys32_setreuid + .quad sys32_setregid + .quad sys32_getgroups /* 205 */ + .quad sys32_setgroups + .quad sys_fchown + .quad sys32_setresuid + .quad sys32_getresuid + .quad sys32_setresgid /* 210 */ + .quad sys32_getresgid + .quad sys_chown + .quad sys_setuid + .quad sys_setgid + .quad sys_setfsuid /* 215 */ + .quad sys_setfsgid + .quad sys_pivot_root + .quad sys_mincore + .quad sys_madvise + .quad sys_getdents64 /* 220 getdents64 */ + .quad sys32_fcntl64 + .quad sys_ni_syscall /* tux */ +ia32_syscall_end: + .rept IA32_NR_syscalls-(ia32_syscall_end-ia32_sys_call_table)/8 + .quad ni_syscall + .endr + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/ia32/ptrace32.c linux.ac/arch/x86_64/ia32/ptrace32.c --- linux.vanilla/arch/x86_64/ia32/ptrace32.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/ia32/ptrace32.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,310 @@ +/* + * 32bit ptrace for x86-64. + * + * Copyright 2001 Andi Kleen, SuSE Labs. + * Some parts copied from arch/i386/kernel/ptrace.c. See that file for earlier copyright. + * + * This allows to access 64bit processes too; but there is no way to see the extended + * register contents. + * + * $Id: ptrace32.c,v 1.2 2001/08/15 06:41:13 ak Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define R32(l,q) \ + case offsetof(struct user32, regs.l): stack[offsetof(struct pt_regs, q)/8] = val; break + +static int putreg32(struct task_struct *child, unsigned regno, u32 val) +{ + int i; + __u64 *stack = (__u64 *)(child->thread.rsp0 - sizeof(struct pt_regs)); + + switch (regno) { + case offsetof(struct user32, regs.fs): + child->thread.fs = val; + break; + case offsetof(struct user32, regs.gs): + child->thread.gs = val; + break; + case offsetof(struct user32, regs.ds): + child->thread.ds = val; + break; + case offsetof(struct user32, regs.es): + child->thread.es = val; + break; + + R32(cs, cs); + R32(ss, ss); + R32(ebx, rbx); + R32(ecx, rcx); + R32(edx, rdx); + R32(edi, rdi); + R32(esi, rsi); + R32(ebp, rbp); + R32(eax, rax); + R32(orig_eax, orig_rax); + R32(eip, rip); + R32(esp, rsp); + + case offsetof(struct user32, regs.eflags): + stack[offsetof(struct pt_regs, eflags)/8] = val & 0x44dd5; + break; + + case offsetof(struct user32, u_debugreg[0]) ... offsetof(struct user32, u_debugreg[6]): + child->thread.debugreg[(regno-offsetof(struct user32, u_debugreg[0]))/4] = val; + break; + + case offsetof(struct user32, u_debugreg[7]): + val &= ~DR_CONTROL_RESERVED; + /* You are not expected to understand this ... I don't neither. */ + for(i=0; i<4; i++) + if ((0x5454 >> ((val >> (16 + 4*i)) & 0xf)) & 1) + return -EIO; + child->thread.debugreg[7] = val; + break; + + default: + if (regno > sizeof(struct user32) || (regno & 3)) + return -EIO; + + /* Other dummy fields in the virtual user structure are ignored */ + break; + } + return 0; +} + +#undef R32 + +#define R32(l,q) \ + case offsetof(struct user32, regs.l): *val = stack[offsetof(struct pt_regs, q)/8]; break + +static int getreg32(struct task_struct *child, unsigned regno, u32 *val) +{ + __u64 *stack = (__u64 *)(child->thread.rsp0 - sizeof(struct pt_regs)); + + switch (regno) { + case offsetof(struct user32, regs.fs): + *val = child->thread.fs; + break; + case offsetof(struct user32, regs.gs): + *val = child->thread.gs; + break; + case offsetof(struct user32, regs.ds): + *val = child->thread.ds; + break; + case offsetof(struct user32, regs.es): + *val = child->thread.es; + break; + + R32(cs, cs); + R32(ss, ss); + R32(ebx, rbx); + R32(ecx, rcx); + R32(edx, rdx); + R32(edi, rdi); + R32(esi, rsi); + R32(ebp, rbp); + R32(eax, rax); + R32(orig_eax, orig_rax); + R32(eip, rip); + R32(eflags, eflags); + R32(esp, rsp); + + case offsetof(struct user32, u_debugreg[0]) ... offsetof(struct user32, u_debugreg[7]): + *val = child->thread.debugreg[(regno-offsetof(struct user32, u_debugreg[0]))/4]; + break; + + default: + if (regno > sizeof(struct user32) || (regno & 3)) + return -EIO; + + /* Other dummy fields in the virtual user structure are ignored */ + *val = 0; + break; + } + return 0; +} + +#undef R32 + + +static struct task_struct *find_target(int request, int pid, int *err) +{ + struct task_struct *child; + + *err = -EPERM; + if (pid == 1) + return NULL; + + *err = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (child) { + *err = -ESRCH; + if (!(child->ptrace & PT_PTRACED)) + goto out; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out; + } + if (child->p_pptr != current) + goto out; + + return child; + } + out: + free_task_struct(child); + return NULL; + +} + +extern asmlinkage long sys_ptrace(long request, long pid, unsigned long addr, unsigned long data); + +asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data) +{ + struct task_struct *child; + int ret; + __u32 val; + + switch (request) { + case PTRACE_TRACEME: + case PTRACE_ATTACH: + case PTRACE_SYSCALL: + case PTRACE_CONT: + case PTRACE_KILL: + case PTRACE_SINGLESTEP: + case PTRACE_DETACH: + case PTRACE_SETOPTIONS: + ret = sys_ptrace(request, pid, addr, data); + return ret; + + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: + case PTRACE_POKEDATA: + case PTRACE_POKETEXT: + case PTRACE_POKEUSR: + case PTRACE_PEEKUSR: + case PTRACE_GETREGS: + case PTRACE_SETREGS: + case PTRACE_SETFPREGS: + case PTRACE_GETFPREGS: + break; + + default: + return -EIO; + } + + child = find_target(request, pid, &ret); + if (!child) + return ret; + + switch (request) { + case PTRACE_PEEKDATA: + case PTRACE_PEEKTEXT: + ret = 0; + if (access_process_vm(child, addr, &val, sizeof(u32), 0) != sizeof(u32)) + ret = -EIO; + else + ret = put_user(val, (unsigned int *)(u64)data); + break; + + case PTRACE_POKEDATA: + case PTRACE_POKETEXT: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(u32), 1) != sizeof(u32)) + ret = -EIO; + break; + + case PTRACE_PEEKUSR: + ret = getreg32(child, addr, &val); + if (ret >= 0) + ret = put_user(val, (__u32 *)(unsigned long) data); + break; + + case PTRACE_POKEUSR: + ret = putreg32(child, addr, data); + break; + + case PTRACE_GETREGS: { /* Get all gp regs from the child. */ + int i; + if (!access_ok(VERIFY_WRITE, (unsigned *)(unsigned long)data, FRAME_SIZE)) { + ret = -EIO; + break; + } + ret = 0; + for ( i = 0; i <= 16*4 ; i += sizeof(__u32) ) { + getreg32(child, i, &val); + ret |= __put_user(val,(u32 *) (unsigned long) data); + data += sizeof(u32); + } + break; + } + + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp; + int i; + if (!access_ok(VERIFY_READ, (unsigned *)(unsigned long)data, FRAME_SIZE)) { + ret = -EIO; + break; + } + ret = 0; + for ( i = 0; i <= 16*4; i += sizeof(u32) ) { + ret |= __get_user(tmp, (u32 *) (unsigned long) data); + putreg32(child, i, tmp); + data += sizeof(u32); + } + break; + } + +#if 0 /* to be done. */ + case PTRACE_GETFPREGS: { /* Get the child extended FPU state. */ + if (!access_ok(VERIFY_WRITE, (unsigned *)data, + sizeof(struct user_i387_struct))) { + ret = -EIO; + break; + } + if ( !child->used_math ) { + /* Simulate an empty FPU. */ + set_fpu_cwd(child, 0x037f); + set_fpu_swd(child, 0x0000); + set_fpu_twd(child, 0xffff); + set_fpu_mxcsr(child, 0x1f80); + } + ret = get_fpregs((struct user_i387_struct *)data, child); + break; + } + + case PTRACE_SETFPREGS: { /* Set the child extended FPU state. */ + if (!access_ok(VERIFY_READ, (unsigned *)data, + sizeof(struct user_i387_struct))) { + ret = -EIO; + break; + } + child->used_math = 1; + ret = set_fpregs(child, (struct user_i387_struct *)data); + break; + +#endif + + default: + ret = -EINVAL; + break; + } + + free_task_struct(child); + return ret; +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/ia32/socket32.c linux.ac/arch/x86_64/ia32/socket32.c --- linux.vanilla/arch/x86_64/ia32/socket32.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/ia32/socket32.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,686 @@ +/* + * 32bit Socket syscall emulation. Based on arch/sparc64/kernel/sys_sparc32.c. + * + * Copyright (C) 2000 VA Linux Co + * Copyright (C) 2000 Don Dugger + * Copyright (C) 1999 Arun Sharma + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 2000 Hewlett-Packard Co. + * Copyright (C) 2000 David Mosberger-Tang + * Copyright (C) 2000,2001 Andi Kleen, SuSE Labs + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) ((unsigned long)(__x)) + + +static inline int iov_from_user32_to_kern(struct iovec *kiov, + struct iovec32 *uiov32, + int niov) +{ + int tot_len = 0; + + while(niov > 0) { + u32 len, buf; + + if(get_user(len, &uiov32->iov_len) || + get_user(buf, &uiov32->iov_base)) { + tot_len = -EFAULT; + break; + } + tot_len += len; + kiov->iov_base = (void *)A(buf); + kiov->iov_len = (__kernel_size_t) len; + uiov32++; + kiov++; + niov--; + } + return tot_len; +} + +static inline int msghdr_from_user32_to_kern(struct msghdr *kmsg, + struct msghdr32 *umsg) +{ + u32 tmp1, tmp2, tmp3; + int err; + + err = get_user(tmp1, &umsg->msg_name); + err |= __get_user(tmp2, &umsg->msg_iov); + err |= __get_user(tmp3, &umsg->msg_control); + if (err) + return -EFAULT; + + kmsg->msg_name = (void *)A(tmp1); + kmsg->msg_iov = (struct iovec *)A(tmp2); + kmsg->msg_control = (void *)A(tmp3); + + err = get_user(kmsg->msg_namelen, &umsg->msg_namelen); + err |= get_user(kmsg->msg_iovlen, &umsg->msg_iovlen); + err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen); + err |= get_user(kmsg->msg_flags, &umsg->msg_flags); + + return err; +} + +/* I've named the args so it is easy to tell whose space the pointers are in. */ +static int verify_iovec32(struct msghdr *kern_msg, struct iovec *kern_iov, + char *kern_address, int mode) +{ + int tot_len; + + if(kern_msg->msg_namelen) { + if(mode==VERIFY_READ) { + int err = move_addr_to_kernel(kern_msg->msg_name, + kern_msg->msg_namelen, + kern_address); + if(err < 0) + return err; + } + kern_msg->msg_name = kern_address; + } else + kern_msg->msg_name = NULL; + + if(kern_msg->msg_iovlen > UIO_FASTIOV) { + kern_iov = kmalloc(kern_msg->msg_iovlen * sizeof(struct iovec), + GFP_KERNEL); + if(!kern_iov) + return -ENOMEM; + } + + tot_len = iov_from_user32_to_kern(kern_iov, + (struct iovec32 *)kern_msg->msg_iov, + kern_msg->msg_iovlen); + if(tot_len >= 0) + kern_msg->msg_iov = kern_iov; + else if(kern_msg->msg_iovlen > UIO_FASTIOV) + kfree(kern_iov); + + return tot_len; +} + +/* There is a lot of hair here because the alignment rules (and + * thus placement) of cmsg headers and length are different for + * 32-bit apps. -DaveM + */ +static int cmsghdr_from_user32_to_kern(struct msghdr *kmsg, + unsigned char *stackbuf, int stackbuf_size) +{ + struct cmsghdr32 *ucmsg; + struct cmsghdr *kcmsg, *kcmsg_base; + __kernel_size_t32 ucmlen; + __kernel_size_t kcmlen, tmp; + + kcmlen = 0; + kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + if(get_user(ucmlen, &ucmsg->cmsg_len)) + return -EFAULT; + + /* Catch bogons. */ + if(CMSG32_ALIGN(ucmlen) < + CMSG32_ALIGN(sizeof(struct cmsghdr32))) + return -EINVAL; + if((unsigned long)(((char *)ucmsg - (char *)kmsg->msg_control) + + ucmlen) > kmsg->msg_controllen) + return -EINVAL; + + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmlen += tmp; + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + if(kcmlen == 0) + return -EINVAL; + + /* The kcmlen holds the 64-bit version of the control length. + * It may not be modified as we do not stick it into the kmsg + * until we have successfully copied over all of the data + * from the user. + */ + if(kcmlen > stackbuf_size) + kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); + if(kcmsg == NULL) + return -ENOBUFS; + + /* Now copy them over neatly. */ + memset(kcmsg, 0, kcmlen); + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + __get_user(ucmlen, &ucmsg->cmsg_len); + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmsg->cmsg_len = tmp; + __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); + + /* Copy over the data. */ + if(copy_from_user(CMSG_DATA(kcmsg), + CMSG32_DATA(ucmsg), + (ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))))) + goto out_free_efault; + + /* Advance. */ + kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + + /* Ok, looks like we made it. Hook it up and return success. */ + kmsg->msg_control = kcmsg_base; + kmsg->msg_controllen = kcmlen; + return 0; + +out_free_efault: + if(kcmsg_base != (struct cmsghdr *)stackbuf) + kfree(kcmsg_base); + return -EFAULT; +} + +static void put_cmsg32(struct msghdr *kmsg, int level, int type, + int len, void *data) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + struct cmsghdr32 cmhdr; + int cmlen = CMSG32_LEN(len); + + if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { + kmsg->msg_flags |= MSG_CTRUNC; + return; + } + + if(kmsg->msg_controllen < cmlen) { + kmsg->msg_flags |= MSG_CTRUNC; + cmlen = kmsg->msg_controllen; + } + cmhdr.cmsg_level = level; + cmhdr.cmsg_type = type; + cmhdr.cmsg_len = cmlen; + + if(copy_to_user(cm, &cmhdr, sizeof cmhdr)) + return; + if(copy_to_user(CMSG32_DATA(cm), data, cmlen - sizeof(struct cmsghdr32))) + return; + cmlen = CMSG32_SPACE(len); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; +} + +static void scm_detach_fds32(struct msghdr *kmsg, struct scm_cookie *scm) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + int fdmax = (kmsg->msg_controllen - sizeof(struct cmsghdr32)) / sizeof(int); + int fdnum = scm->fp->count; + struct file **fp = scm->fp->fp; + int *cmfptr; + int err = 0, i; + + if (fdnum < fdmax) + fdmax = fdnum; + + for (i = 0, cmfptr = (int *) CMSG32_DATA(cm); i < fdmax; i++, cmfptr++) { + int new_fd; + err = get_unused_fd(); + if (err < 0) + break; + new_fd = err; + err = put_user(new_fd, cmfptr); + if (err) { + put_unused_fd(new_fd); + break; + } + /* Bump the usage count and install the file. */ + get_file(fp[i]); + fd_install(new_fd, fp[i]); + } + + if (i > 0) { + int cmlen = CMSG32_LEN(i * sizeof(int)); + if (!err) + err = put_user(SOL_SOCKET, &cm->cmsg_level); + if (!err) + err = put_user(SCM_RIGHTS, &cm->cmsg_type); + if (!err) + err = put_user(cmlen, &cm->cmsg_len); + if (!err) { + cmlen = CMSG32_SPACE(i * sizeof(int)); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; + } + } + if (i < fdnum) + kmsg->msg_flags |= MSG_CTRUNC; + + /* + * All of the files that fit in the message have had their + * usage counts incremented, so we just free the list. + */ + __scm_destroy(scm); +} + +/* In these cases we (currently) can just copy to data over verbatim + * because all CMSGs created by the kernel have well defined types which + * have the same layout in both the 32-bit and 64-bit API. One must add + * some special cased conversions here if we start sending control messages + * with incompatible types. + * + * SCM_RIGHTS and SCM_CREDENTIALS are done by hand in recvmsg32 right after + * we do our work. The remaining cases are: + * + * SOL_IP IP_PKTINFO struct in_pktinfo 32-bit clean + * IP_TTL int 32-bit clean + * IP_TOS __u8 32-bit clean + * IP_RECVOPTS variable length 32-bit clean + * IP_RETOPTS variable length 32-bit clean + * (these last two are clean because the types are defined + * by the IPv4 protocol) + * IP_RECVERR struct sock_extended_err + + * struct sockaddr_in 32-bit clean + * SOL_IPV6 IPV6_RECVERR struct sock_extended_err + + * struct sockaddr_in6 32-bit clean + * IPV6_PKTINFO struct in6_pktinfo 32-bit clean + * IPV6_HOPLIMIT int 32-bit clean + * IPV6_FLOWINFO u32 32-bit clean + * IPV6_HOPOPTS ipv6 hop exthdr 32-bit clean + * IPV6_DSTOPTS ipv6 dst exthdr(s) 32-bit clean + * IPV6_RTHDR ipv6 routing exthdr 32-bit clean + * IPV6_AUTHHDR ipv6 auth exthdr 32-bit clean + */ +static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_uptr) +{ + unsigned char *workbuf, *wp; + unsigned long bufsz, space_avail; + struct cmsghdr *ucmsg; + + bufsz = ((unsigned long)kmsg->msg_control) - orig_cmsg_uptr; + space_avail = kmsg->msg_controllen + bufsz; + wp = workbuf = kmalloc(bufsz, GFP_KERNEL); + if(workbuf == NULL) + goto fail; + + /* To make this more sane we assume the kernel sends back properly + * formatted control messages. Because of how the kernel will truncate + * the cmsg_len for MSG_TRUNC cases, we need not check that case either. + */ + ucmsg = (struct cmsghdr *) orig_cmsg_uptr; + while(((unsigned long)ucmsg) <= + (((unsigned long)kmsg->msg_control) - sizeof(struct cmsghdr))) { + struct cmsghdr32 *kcmsg32 = (struct cmsghdr32 *) wp; + int clen64, clen32; + + /* UCMSG is the 64-bit format CMSG entry in user-space. + * KCMSG32 is within the kernel space temporary buffer + * we use to convert into a 32-bit style CMSG. + */ + __get_user(kcmsg32->cmsg_len, &ucmsg->cmsg_len); + __get_user(kcmsg32->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg32->cmsg_type, &ucmsg->cmsg_type); + + clen64 = kcmsg32->cmsg_len; + copy_from_user(CMSG32_DATA(kcmsg32), CMSG_DATA(ucmsg), + clen64 - CMSG_ALIGN(sizeof(*ucmsg))); + clen32 = ((clen64 - CMSG_ALIGN(sizeof(*ucmsg))) + + CMSG32_ALIGN(sizeof(struct cmsghdr32))); + kcmsg32->cmsg_len = clen32; + + ucmsg = (struct cmsghdr *) (((char *)ucmsg) + CMSG_ALIGN(clen64)); + wp = (((char *)kcmsg32) + CMSG32_ALIGN(clen32)); + } + + /* Copy back fixed up data, and adjust pointers. */ + bufsz = (wp - workbuf); + copy_to_user((void *)orig_cmsg_uptr, workbuf, bufsz); + + kmsg->msg_control = (struct cmsghdr *) + (((char *)orig_cmsg_uptr) + bufsz); + kmsg->msg_controllen = space_avail - bufsz; + + kfree(workbuf); + return; + +fail: + /* If we leave the 64-bit format CMSG chunks in there, + * the application could get confused and crash. So to + * ensure greater recovery, we report no CMSGs. + */ + kmsg->msg_controllen += bufsz; + kmsg->msg_control = (void *) orig_cmsg_uptr; +} + +asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags) +{ + struct socket *sock; + char address[MAX_SOCK_ADDR]; + struct iovec iov[UIO_FASTIOV]; + unsigned char ctl[sizeof(struct cmsghdr) + 20]; + unsigned char *ctl_buf = ctl; + struct msghdr kern_msg; + int err, total_len; + + if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ); + if (err < 0) + goto out; + total_len = err; + + if(kern_msg.msg_controllen) { + err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl)); + if(err) + goto out_freeiov; + ctl_buf = kern_msg.msg_control; + } + kern_msg.msg_flags = user_flags; + + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + if (sock->file->f_flags & O_NONBLOCK) + kern_msg.msg_flags |= MSG_DONTWAIT; + err = sock_sendmsg(sock, &kern_msg, total_len); + sockfd_put(sock); + } + + /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */ + if(ctl_buf != ctl) + kfree(ctl_buf); +out_freeiov: + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + return err; +} + +asmlinkage int sys32_recvmsg(int fd, struct msghdr32 *user_msg, unsigned int user_flags) +{ + struct iovec iovstack[UIO_FASTIOV]; + struct msghdr kern_msg; + char addr[MAX_SOCK_ADDR]; + struct socket *sock; + struct iovec *iov = iovstack; + struct sockaddr *uaddr; + int *uaddr_len; + unsigned long cmsg_ptr; + int err, total_len, len = 0; + + if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + + uaddr = kern_msg.msg_name; + uaddr_len = &user_msg->msg_namelen; + err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE); + if (err < 0) + goto out; + total_len = err; + + cmsg_ptr = (unsigned long) kern_msg.msg_control; + kern_msg.msg_flags = 0; + + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + struct scm_cookie scm; + + if (sock->file->f_flags & O_NONBLOCK) + user_flags |= MSG_DONTWAIT; + memset(&scm, 0, sizeof(scm)); + err = sock->ops->recvmsg(sock, &kern_msg, total_len, + user_flags, &scm); + if(err >= 0) { + len = err; + if(!kern_msg.msg_control) { + if(sock->passcred || scm.fp) + kern_msg.msg_flags |= MSG_CTRUNC; + if(scm.fp) + __scm_destroy(&scm); + } else { + /* If recvmsg processing itself placed some + * control messages into user space, it's is + * using 64-bit CMSG processing, so we need + * to fix it up before we tack on more stuff. + */ + if((unsigned long) kern_msg.msg_control != cmsg_ptr) + cmsg32_recvmsg_fixup(&kern_msg, cmsg_ptr); + + /* Wheee... */ + if(sock->passcred) + put_cmsg32(&kern_msg, + SOL_SOCKET, SCM_CREDENTIALS, + sizeof(scm.creds), &scm.creds); + if(scm.fp != NULL) + scm_detach_fds32(&kern_msg, &scm); + } + } + sockfd_put(sock); + } + + if(uaddr != NULL && err >= 0) + err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); + if(cmsg_ptr != 0 && err >= 0) { + unsigned long ucmsg_ptr = ((unsigned long)kern_msg.msg_control); + __kernel_size_t32 uclen = (__kernel_size_t32) (ucmsg_ptr - cmsg_ptr); + err |= __put_user(uclen, &user_msg->msg_controllen); + } + if(err >= 0) + err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags); + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + if(err < 0) + return err; + return len; +} + +extern asmlinkage int sys_setsockopt(int fd, int level, int optname, + char *optval, int optlen); + +static int do_set_attach_filter(int fd, int level, int optname, + char *optval, int optlen) +{ + struct sock_fprog32 { + __u16 len; + __u32 filter; + } *fprog32 = (struct sock_fprog32 *)optval; + struct sock_fprog kfprog; + struct sock_filter *kfilter; + unsigned int fsize; + mm_segment_t old_fs; + __u32 uptr; + int ret; + + if (get_user(kfprog.len, &fprog32->len) || + __get_user(uptr, &fprog32->filter)) + return -EFAULT; + + kfprog.filter = (struct sock_filter *)A(uptr); + fsize = kfprog.len * sizeof(struct sock_filter); + + kfilter = (struct sock_filter *)kmalloc(fsize, GFP_KERNEL); + if (kfilter == NULL) + return -ENOMEM; + + if (copy_from_user(kfilter, kfprog.filter, fsize)) { + kfree(kfilter); + return -EFAULT; + } + + kfprog.filter = kfilter; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_setsockopt(fd, level, optname, + (char *)&kfprog, sizeof(kfprog)); + set_fs(old_fs); + + kfree(kfilter); + + return ret; +} + +static int do_set_icmpv6_filter(int fd, int level, int optname, + char *optval, int optlen) +{ + struct icmp6_filter kfilter; + mm_segment_t old_fs; + int ret, i; + + if (copy_from_user(&kfilter, optval, sizeof(kfilter))) + return -EFAULT; + + + for (i = 0; i < 8; i += 2) { + u32 tmp = kfilter.data[i]; + + kfilter.data[i] = kfilter.data[i + 1]; + kfilter.data[i + 1] = tmp; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_setsockopt(fd, level, optname, + (char *) &kfilter, sizeof(kfilter)); + set_fs(old_fs); + + return ret; +} + +asmlinkage int sys32_setsockopt(int fd, int level, int optname, + char *optval, int optlen) +{ + if (optname == SO_ATTACH_FILTER) + return do_set_attach_filter(fd, level, optname, + optval, optlen); + if (level == SOL_ICMPV6 && optname == ICMPV6_FILTER) + return do_set_icmpv6_filter(fd, level, optname, + optval, optlen); + + return sys_setsockopt(fd, level, optname, optval, optlen); +} + + +/* Argument list sizes for sys_socketcall */ +#define AL(x) ((x) * sizeof(u32)) +static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), + AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), + AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; +#undef AL + +extern asmlinkage long sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); +extern asmlinkage long sys_connect(int fd, struct sockaddr *uservaddr, + int addrlen); +extern asmlinkage long sys_accept(int fd, struct sockaddr *upeer_sockaddr, + int *upeer_addrlen); +extern asmlinkage long sys_getsockname(int fd, struct sockaddr *usockaddr, + int *usockaddr_len); +extern asmlinkage long sys_getpeername(int fd, struct sockaddr *usockaddr, + int *usockaddr_len); +extern asmlinkage long sys_send(int fd, void *buff, size_t len, unsigned flags); +extern asmlinkage long sys_sendto(int fd, u32 buff, __kernel_size_t32 len, + unsigned flags, u32 addr, int addr_len); +extern asmlinkage long sys_recv(int fd, void *ubuf, size_t size, unsigned flags); +extern asmlinkage long sys_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size, + unsigned flags, u32 addr, u32 addr_len); +extern asmlinkage long sys_getsockopt(int fd, int level, int optname, + u32 optval, u32 optlen); + +extern asmlinkage long sys_socket(int family, int type, int protocol); +extern asmlinkage long sys_socketpair(int family, int type, int protocol, + int usockvec[2]); +extern asmlinkage long sys_shutdown(int fd, int how); +extern asmlinkage long sys_listen(int fd, int backlog); + +asmlinkage long sys32_socketcall(int call, u32 *args) +{ + int ret; + u32 a[6]; + u32 a0,a1; + + if (callSYS_RECVMSG) + return -EINVAL; + if (copy_from_user(a, args, nas[call])) + return -EFAULT; + a0=a[0]; + a1=a[1]; + + switch(call) + { + case SYS_SOCKET: + ret = sys_socket(a0, a1, a[2]); + break; + case SYS_BIND: + ret = sys_bind(a0, (struct sockaddr *)A(a1), a[2]); + break; + case SYS_CONNECT: + ret = sys_connect(a0, (struct sockaddr *)A(a1), a[2]); + break; + case SYS_LISTEN: + ret = sys_listen(a0, a1); + break; + case SYS_ACCEPT: + ret = sys_accept(a0, (struct sockaddr *)A(a1), + (int *)A(a[2])); + break; + case SYS_GETSOCKNAME: + ret = sys_getsockname(a0, (struct sockaddr *)A(a1), + (int *)A(a[2])); + break; + case SYS_GETPEERNAME: + ret = sys_getpeername(a0, (struct sockaddr *)A(a1), + (int *)A(a[2])); + break; + case SYS_SOCKETPAIR: + ret = sys_socketpair(a0, a1, a[2], (int *)A(a[3])); + break; + case SYS_SEND: + ret = sys_send(a0, (void *)A(a1), a[2], a[3]); + break; + case SYS_SENDTO: + ret = sys_sendto(a0, a1, a[2], a[3], a[4], a[5]); + break; + case SYS_RECV: + ret = sys_recv(a0, (void *)A(a1), a[2], a[3]); + break; + case SYS_RECVFROM: + ret = sys_recvfrom(a0, a1, a[2], a[3], a[4], a[5]); + break; + case SYS_SHUTDOWN: + ret = sys_shutdown(a0,a1); + break; + case SYS_SETSOCKOPT: + ret = sys_setsockopt(a0, a1, a[2], (char *)A(a[3]), + a[4]); + break; + case SYS_GETSOCKOPT: + ret = sys_getsockopt(a0, a1, a[2], a[3], a[4]); + break; + case SYS_SENDMSG: + ret = sys32_sendmsg(a0, (struct msghdr32 *)A(a1), + a[2]); + break; + case SYS_RECVMSG: + ret = sys32_recvmsg(a0, (struct msghdr32 *)A(a1), + a[2]); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/ia32/sys_ia32.c linux.ac/arch/x86_64/ia32/sys_ia32.c --- linux.vanilla/arch/x86_64/ia32/sys_ia32.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/ia32/sys_ia32.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,2788 @@ +/* + * sys_ia32.c: Conversion between 32bit and 64bit native syscalls. Based on + * sys_sparc32 + * + * Copyright (C) 2000 VA Linux Co + * Copyright (C) 2000 Don Dugger + * Copyright (C) 1999 Arun Sharma + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 2000 Hewlett-Packard Co. + * Copyright (C) 2000 David Mosberger-Tang + * Copyright (C) 2000,2001 Andi Kleen, SuSE Labs (x86-64 port) + * + * These routines maintain argument size conversion between 32bit and 64bit + * environment. In 2.5 most of this should be moved to a generic directory. + * + * This file assumes that there is a hole at the end of user address space. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) ((unsigned long)(__x)) +#define ROUND_UP(x,a) ((__typeof__(x))(((unsigned long)(x) + ((a) - 1)) & ~((a) - 1))) +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) + +static int +putstat(struct stat32 *ubuf, struct stat *kbuf) +{ + if (verify_area(VERIFY_WRITE, ubuf, sizeof(struct stat32)) || + __put_user (kbuf->st_dev, &ubuf->st_dev) || + __put_user (kbuf->st_ino, &ubuf->st_ino) || + __put_user (kbuf->st_mode, &ubuf->st_mode) || + __put_user (kbuf->st_nlink, &ubuf->st_nlink) || + __put_user (kbuf->st_uid, &ubuf->st_uid) || + __put_user (kbuf->st_gid, &ubuf->st_gid) || + __put_user (kbuf->st_rdev, &ubuf->st_rdev) || + __put_user (kbuf->st_size, &ubuf->st_size) || + __put_user (kbuf->st_atime, &ubuf->st_atime) || + __put_user (kbuf->st_mtime, &ubuf->st_mtime) || + __put_user (kbuf->st_ctime, &ubuf->st_ctime) || + __put_user (kbuf->st_blksize, &ubuf->st_blksize) || + __put_user (kbuf->st_blocks, &ubuf->st_blocks)) + return -EFAULT; + return 0; +} + +extern asmlinkage long sys_newstat(char * filename, struct stat * statbuf); + +asmlinkage long +sys32_newstat(char * filename, struct stat32 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newstat(filename, &s); + set_fs (old_fs); + if (putstat (statbuf, &s)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_newlstat(char * filename, struct stat * statbuf); + +asmlinkage long +sys32_newlstat(char * filename, struct stat32 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newlstat(filename, &s); + set_fs (old_fs); + if (putstat (statbuf, &s)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_newfstat(unsigned int fd, struct stat * statbuf); + +asmlinkage long +sys32_newfstat(unsigned int fd, struct stat32 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newfstat(fd, &s); + set_fs (old_fs); + if (putstat (statbuf, &s)) + return -EFAULT; + return ret; +} + +/* Another set for IA32/LFS -- x86_64 struct stat is different due to + support for 64bit inode numbers. */ + +static int +putstat64(struct stat64 *ubuf, struct stat *kbuf) +{ + if (verify_area(VERIFY_WRITE, ubuf, sizeof(struct stat64)) || + __put_user (kbuf->st_dev, &ubuf->st_dev) || + __put_user (kbuf->st_ino, &ubuf->__st_ino) || + __put_user (kbuf->st_ino, &ubuf->st_ino) || + __put_user (kbuf->st_mode, &ubuf->st_mode) || + __put_user (kbuf->st_nlink, &ubuf->st_nlink) || + __put_user (kbuf->st_uid, &ubuf->st_uid) || + __put_user (kbuf->st_gid, &ubuf->st_gid) || + __put_user (kbuf->st_rdev, &ubuf->st_rdev) || + __put_user (kbuf->st_size, &ubuf->st_size) || + __put_user (kbuf->st_atime, &ubuf->st_atime) || + __put_user (kbuf->st_mtime, &ubuf->st_mtime) || + __put_user (kbuf->st_ctime, &ubuf->st_ctime) || + __put_user (kbuf->st_blksize, &ubuf->st_blksize) || + __put_user (kbuf->st_blocks, &ubuf->st_blocks)) + return -EFAULT; + return 0; +} + +asmlinkage long +sys32_stat64(char * filename, struct stat64 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newstat(filename, &s); + set_fs (old_fs); + if (putstat64 (statbuf, &s)) + return -EFAULT; + return ret; +} + +asmlinkage long +sys32_lstat64(char * filename, struct stat64 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newlstat(filename, &s); + set_fs (old_fs); + if (putstat64 (statbuf, &s)) + return -EFAULT; + return ret; +} + +asmlinkage long +sys32_fstat64(unsigned int fd, struct stat64 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newfstat(fd, &s); + set_fs (old_fs); + if (putstat64 (statbuf, &s)) + return -EFAULT; + return ret; +} + + + +/* + * Linux/i386 didn't use to be able to handle more than + * 4 system call parameters, so these system calls used a memory + * block for parameter passing.. + */ + +struct mmap_arg_struct { + unsigned int addr; + unsigned int len; + unsigned int prot; + unsigned int flags; + unsigned int fd; + unsigned int offset; +}; + +asmlinkage __u32 +sys32_mmap(struct mmap_arg_struct *arg) +{ + struct mmap_arg_struct a; + struct file *file = NULL; + unsigned long retval; + struct mm_struct *mm ; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + + if (a.offset & ~PAGE_MASK) + return -EINVAL; + + if (!(a.flags & MAP_ANONYMOUS)) { + file = fget(a.fd); + if (!file) + return -EBADF; + } + + mm = current->mm; + down_write(&mm->mmap_sem); + retval = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, a.offset>>PAGE_SHIFT); + if (file) + fput(file); + + if (retval >= 0xFFFFFFFF) { + do_munmap(mm, retval, a.len); + retval = -ENOMEM; + } + up_write(&mm->mmap_sem); + + + + return retval; +} + +asmlinkage long +sys32_pipe(int *fd) +{ + int retval; + int fds[2]; + + retval = do_pipe(fds); + if (retval) + goto out; + if (copy_to_user(fd, fds, sizeof(fds))) + retval = -EFAULT; + out: + return retval; +} + +asmlinkage long +sys32_rt_sigaction(int sig, struct sigaction32 *act, + struct sigaction32 *oact, unsigned int sigsetsize) +{ + struct k_sigaction new_ka, old_ka; + int ret; + sigset32_t set32; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset32_t)) + return -EINVAL; + + if (act) { + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user((long)new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_flags, &act->sa_flags) || + __get_user((long)new_ka.sa.sa_restorer, &act->sa_restorer)|| + __copy_from_user(&set32, &act->sa_mask, sizeof(sigset32_t))) + return -EFAULT; + + /* FIXME: here we rely on _IA32_NSIG_WORS to be >= than _NSIG_WORDS << 1 */ + switch (_NSIG_WORDS) { + case 4: new_ka.sa.sa_mask.sig[3] = set32.sig[6] + | (((long)set32.sig[7]) << 32); + case 3: new_ka.sa.sa_mask.sig[2] = set32.sig[4] + | (((long)set32.sig[5]) << 32); + case 2: new_ka.sa.sa_mask.sig[1] = set32.sig[2] + | (((long)set32.sig[3]) << 32); + case 1: new_ka.sa.sa_mask.sig[0] = set32.sig[0] + | (((long)set32.sig[1]) << 32); + } + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + /* FIXME: here we rely on _IA32_NSIG_WORS to be >= than _NSIG_WORDS << 1 */ + switch (_NSIG_WORDS) { + case 4: + set32.sig[7] = (old_ka.sa.sa_mask.sig[3] >> 32); + set32.sig[6] = old_ka.sa.sa_mask.sig[3]; + case 3: + set32.sig[5] = (old_ka.sa.sa_mask.sig[2] >> 32); + set32.sig[4] = old_ka.sa.sa_mask.sig[2]; + case 2: + set32.sig[3] = (old_ka.sa.sa_mask.sig[1] >> 32); + set32.sig[2] = old_ka.sa.sa_mask.sig[1]; + case 1: + set32.sig[1] = (old_ka.sa.sa_mask.sig[0] >> 32); + set32.sig[0] = old_ka.sa.sa_mask.sig[0]; + } + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user((long)old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user((long)old_ka.sa.sa_restorer, &oact->sa_restorer) || + __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || + __copy_to_user(&oact->sa_mask, &set32, sizeof(sigset32_t))) + return -EFAULT; + } + + return ret; +} + +asmlinkage long +sys32_sigaction (int sig, struct old_sigaction32 *act, struct old_sigaction32 *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset32_t mask; + + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user((long)new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_flags, &act->sa_flags) || + __get_user((long)new_ka.sa.sa_restorer, &act->sa_restorer) || + __get_user(mask, &act->sa_mask)) + return -EFAULT; + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user((long)old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user((long)old_ka.sa.sa_restorer, &oact->sa_restorer) || + __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) + return -EFAULT; + } + + return ret; +} + +extern asmlinkage long sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, + size_t sigsetsize); + +asmlinkage long +sys32_rt_sigprocmask(int how, sigset32_t *set, sigset32_t *oset, + unsigned int sigsetsize) +{ + sigset_t s; + sigset32_t s32; + int ret; + mm_segment_t old_fs = get_fs(); + + if (set) { + if (copy_from_user (&s32, set, sizeof(sigset32_t))) + return -EFAULT; + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + } + set_fs (KERNEL_DS); + ret = sys_rt_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL, + sigsetsize); + set_fs (old_fs); + if (ret) return ret; + if (oset) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user (oset, &s32, sizeof(sigset32_t))) + return -EFAULT; + } + return 0; +} + +static int +put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) +{ + if (verify_area(VERIFY_WRITE, ubuf, sizeof(struct statfs32)) || + __put_user (kbuf->f_type, &ubuf->f_type) || + __put_user (kbuf->f_bsize, &ubuf->f_bsize) || + __put_user (kbuf->f_blocks, &ubuf->f_blocks) || + __put_user (kbuf->f_bfree, &ubuf->f_bfree) || + __put_user (kbuf->f_bavail, &ubuf->f_bavail) || + __put_user (kbuf->f_files, &ubuf->f_files) || + __put_user (kbuf->f_ffree, &ubuf->f_ffree) || + __put_user (kbuf->f_namelen, &ubuf->f_namelen) || + __put_user (kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) || + __put_user (kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1])) + return -EFAULT; + return 0; +} + +extern asmlinkage long sys_statfs(const char * path, struct statfs * buf); + +asmlinkage long +sys32_statfs(const char * path, struct statfs32 *buf) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_statfs((const char *)path, &s); + set_fs (old_fs); + if (put_statfs(buf, &s)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_fstatfs(unsigned int fd, struct statfs * buf); + +asmlinkage long +sys32_fstatfs(unsigned int fd, struct statfs32 *buf) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_fstatfs(fd, &s); + set_fs (old_fs); + if (put_statfs(buf, &s)) + return -EFAULT; + return ret; +} + +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +struct itimerval32 +{ + struct timeval32 it_interval; + struct timeval32 it_value; +}; + +static inline long +get_tv32(struct timeval *o, struct timeval32 *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + __get_user(o->tv_sec, &i->tv_sec) || + __get_user(o->tv_usec, &i->tv_usec)); + return ENOSYS; +} + +static inline long +put_tv32(struct timeval32 *o, struct timeval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + __put_user(i->tv_sec, &o->tv_sec) || + __put_user(i->tv_usec, &o->tv_usec)); +} + +static inline long +get_it32(struct itimerval *o, struct itimerval32 *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + __get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec) || + __get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec) || + __get_user(o->it_value.tv_sec, &i->it_value.tv_sec) || + __get_user(o->it_value.tv_usec, &i->it_value.tv_usec)); + return ENOSYS; +} + +static inline long +put_it32(struct itimerval32 *o, struct itimerval *i) +{ + return (!access_ok(VERIFY_WRITE, i, sizeof(*i)) || + __put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) || + __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) || + __put_user(i->it_value.tv_sec, &o->it_value.tv_sec) || + __put_user(i->it_value.tv_usec, &o->it_value.tv_usec)); + return ENOSYS; +} + +extern int do_getitimer(int which, struct itimerval *value); + +asmlinkage long +sys32_getitimer(int which, struct itimerval32 *it) +{ + struct itimerval kit; + int error; + + error = do_getitimer(which, &kit); + if (!error && put_it32(it, &kit)) + error = -EFAULT; + + return error; +} + +extern int do_setitimer(int which, struct itimerval *, struct itimerval *); + +asmlinkage long +sys32_setitimer(int which, struct itimerval32 *in, struct itimerval32 *out) +{ + struct itimerval kin, kout; + int error; + + if (in) { + if (get_it32(&kin, in)) + return -EFAULT; + } else + memset(&kin, 0, sizeof(kin)); + + error = do_setitimer(which, &kin, out ? &kout : NULL); + if (error || !out) + return error; + if (put_it32(out, &kout)) + return -EFAULT; + + return 0; + +} +asmlinkage unsigned long +sys32_alarm(unsigned int seconds) +{ + struct itimerval it_new, it_old; + unsigned int oldalarm; + + it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; + it_new.it_value.tv_sec = seconds; + it_new.it_value.tv_usec = 0; + do_setitimer(ITIMER_REAL, &it_new, &it_old); + oldalarm = it_old.it_value.tv_sec; + /* ehhh.. We can't return 0 if we have an alarm pending.. */ + /* And we'd better return too much than too little anyway */ + if (it_old.it_value.tv_usec) + oldalarm++; + return oldalarm; +} + +/* Translations due to time_t size differences. Which affects all + sorts of things, like timeval and itimerval. */ + +struct utimbuf_32 { + int atime; + int mtime; +}; + +extern asmlinkage long sys_utimes(char * filename, struct timeval * utimes); +extern asmlinkage long sys_gettimeofday (struct timeval *tv, struct timezone *tz); + +asmlinkage long +ia32_utime(char * filename, struct utimbuf_32 *times32) +{ + mm_segment_t old_fs = get_fs(); + struct timeval tv[2]; + long ret; + + if (times32) { + get_user(tv[0].tv_sec, ×32->atime); + tv[0].tv_usec = 0; + get_user(tv[1].tv_sec, ×32->mtime); + tv[1].tv_usec = 0; + set_fs (KERNEL_DS); + } else { + set_fs (KERNEL_DS); + ret = sys_gettimeofday(&tv[0], 0); + if (ret < 0) + goto out; + tv[1] = tv[0]; + } + ret = sys_utimes(filename, tv); + out: + set_fs (old_fs); + return ret; +} + +extern struct timezone sys_tz; +extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz); + +asmlinkage long +sys32_gettimeofday(struct timeval32 *tv, struct timezone *tz) +{ + if (tv) { + struct timeval ktv; + do_gettimeofday(&ktv); + if (put_tv32(tv, &ktv)) + return -EFAULT; + } + if (tz) { + if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) + return -EFAULT; + } + return 0; +} + +asmlinkage long +sys32_settimeofday(struct timeval32 *tv, struct timezone *tz) +{ + struct timeval ktv; + struct timezone ktz; + + if (tv) { + if (get_tv32(&ktv, tv)) + return -EFAULT; + } + if (tz) { + if (copy_from_user(&ktz, tz, sizeof(ktz))) + return -EFAULT; + } + + return do_sys_settimeofday(tv ? &ktv : NULL, tz ? &ktz : NULL); +} + +struct linux32_dirent { + u32 d_ino; + u32 d_off; + u16 d_reclen; + char d_name[1]; +}; + +struct old_linux32_dirent { + u32 d_ino; + u32 d_offset; + u16 d_namlen; + char d_name[1]; +}; + +struct getdents32_callback { + struct linux32_dirent * current_dir; + struct linux32_dirent * previous; + int count; + int error; +}; + +struct readdir32_callback { + struct old_linux32_dirent * dirent; + int count; +}; + +static int +filldir32 (void *__buf, const char *name, int namlen, off_t offset, ino_t ino, + unsigned int d_type) +{ + struct linux32_dirent * dirent; + struct getdents32_callback * buf = (struct getdents32_callback *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1, 4); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) + put_user(offset, &dirent->d_off); + dirent = buf->current_dir; + buf->previous = dirent; + put_user(ino, &dirent->d_ino); + put_user(reclen, &dirent->d_reclen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->current_dir = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage long +sys32_getdents (unsigned int fd, void * dirent, unsigned int count) +{ + struct file * file; + struct linux32_dirent * lastdirent; + struct getdents32_callback buf; + int error; + + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + + buf.current_dir = (struct linux32_dirent *) dirent; + buf.previous = NULL; + buf.count = count; + buf.error = 0; + + error = vfs_readdir(file, filldir32, &buf); + if (error < 0) + goto out_putf; + error = buf.error; + lastdirent = buf.previous; + if (lastdirent) { + put_user(file->f_pos, &lastdirent->d_off); + error = count - buf.count; + } + +out_putf: + fput(file); +out: + return error; +} + +static int +fillonedir32 (void * __buf, const char * name, int namlen, off_t offset, ino_t ino, unsigned d_type) +{ + struct readdir32_callback * buf = (struct readdir32_callback *) __buf; + struct old_linux32_dirent * dirent; + + if (buf->count) + return -EINVAL; + buf->count++; + dirent = buf->dirent; + put_user(ino, &dirent->d_ino); + put_user(offset, &dirent->d_offset); + put_user(namlen, &dirent->d_namlen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + return 0; +} + +asmlinkage long +sys32_oldreaddir (unsigned int fd, void * dirent, unsigned int count) +{ + int error; + struct file * file; + struct readdir32_callback buf; + + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + + buf.count = 0; + buf.dirent = dirent; + + error = vfs_readdir(file, fillonedir32, &buf); + if (error >= 0) + error = buf.count; + fput(file); +out: + return error; +} + +/* + * We can actually return ERESTARTSYS instead of EINTR, but I'd + * like to be certain this leads to no problems. So I return + * EINTR just for safety. + * + * Update: ERESTARTSYS breaks at least the xview clock binary, so + * I'm trying ERESTARTNOHAND which restart only when you want to. + */ +#define MAX_SELECT_SECONDS \ + ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) +#define ROUND_UP_TIME(x,y) (((x)+(y)-1)/(y)) + +asmlinkage long +sys32_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval32 *tvp32) +{ + fd_set_bits fds; + char *bits; + long timeout; + int ret, size; + + timeout = MAX_SCHEDULE_TIMEOUT; + if (tvp32) { + time_t sec, usec; + + get_user(sec, &tvp32->tv_sec); + get_user(usec, &tvp32->tv_usec); + + ret = -EINVAL; + if (sec < 0 || usec < 0) + goto out_nofds; + + if ((unsigned long) sec < MAX_SELECT_SECONDS) { + timeout = ROUND_UP_TIME(usec, 1000000/HZ); + timeout += sec * (unsigned long) HZ; + } + } + + ret = -EINVAL; + if (n < 0) + goto out_nofds; + + if (n > current->files->max_fdset) + n = current->files->max_fdset; + + /* + * We need 6 bitmaps (in/out/ex for both incoming and outgoing), + * since we used fdset we need to allocate memory in units of + * long-words. + */ + ret = -ENOMEM; + size = FDS_BYTES(n); + bits = kmalloc(6 * size, GFP_KERNEL); + if (!bits) + goto out_nofds; + fds.in = (unsigned long *) bits; + fds.out = (unsigned long *) (bits + size); + fds.ex = (unsigned long *) (bits + 2*size); + fds.res_in = (unsigned long *) (bits + 3*size); + fds.res_out = (unsigned long *) (bits + 4*size); + fds.res_ex = (unsigned long *) (bits + 5*size); + + if ((ret = get_fd_set(n, inp, fds.in)) || + (ret = get_fd_set(n, outp, fds.out)) || + (ret = get_fd_set(n, exp, fds.ex))) + goto out; + zero_fd_set(n, fds.res_in); + zero_fd_set(n, fds.res_out); + zero_fd_set(n, fds.res_ex); + + ret = do_select(n, &fds, &timeout); + + if (tvp32 && !(current->personality & STICKY_TIMEOUTS)) { + time_t sec = 0, usec = 0; + if (timeout) { + sec = timeout / HZ; + usec = timeout % HZ; + usec *= (1000000/HZ); + } + put_user(sec, (int *)&tvp32->tv_sec); + put_user(usec, (int *)&tvp32->tv_usec); + } + + if (ret < 0) + goto out; + if (!ret) { + ret = -ERESTARTNOHAND; + if (signal_pending(current)) + goto out; + ret = 0; + } + + set_fd_set(n, inp, fds.res_in); + set_fd_set(n, outp, fds.res_out); + set_fd_set(n, exp, fds.res_ex); + +out: + kfree(bits); +out_nofds: + return ret; +} + +struct sel_arg_struct { + unsigned int n; + unsigned int inp; + unsigned int outp; + unsigned int exp; + unsigned int tvp; +}; + +asmlinkage long +sys32_old_select(struct sel_arg_struct *arg) +{ + struct sel_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + return sys32_select(a.n, (fd_set *)A(a.inp), (fd_set *)A(a.outp), (fd_set *)A(a.exp), + (struct timeval32 *)A(a.tvp)); +} + +struct timespec32 { + int tv_sec; + int tv_nsec; +}; + +extern asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp); + +asmlinkage long +sys32_nanosleep(struct timespec32 *rqtp, struct timespec32 *rmtp) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + if (verify_area(VERIFY_READ, rqtp, sizeof(struct timespec32)) || + __get_user (t.tv_sec, &rqtp->tv_sec) || + __get_user (t.tv_nsec, &rqtp->tv_nsec)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_nanosleep(&t, rmtp ? &t : NULL); + set_fs (old_fs); + if (rmtp && ret == -EINTR) { + if (verify_area(VERIFY_WRITE, rmtp, sizeof(struct timespec32)) || + __put_user (t.tv_sec, &rmtp->tv_sec) || + __put_user (t.tv_nsec, &rmtp->tv_nsec)) + return -EFAULT; + } + return ret; +} + +asmlinkage ssize_t sys_readv(unsigned long,const struct iovec *,unsigned long); +asmlinkage ssize_t sys_writev(unsigned long,const struct iovec *,unsigned long); + +struct iovec * +get_iovec32(struct iovec32 *iov32, struct iovec *iov_buf, u32 count, int type) +{ + int i; + u32 buf, len; + struct iovec *ivp, *iov; + + /* Get the "struct iovec" from user memory */ + + if (!count) + return 0; + if(verify_area(VERIFY_READ, iov32, sizeof(struct iovec32)*count)) + return(struct iovec *)0; + if (count > UIO_MAXIOV) + return(struct iovec *)0; + if (count > UIO_FASTIOV) { + iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL); + if (!iov) + return((struct iovec *)0); + } else + iov = iov_buf; + + ivp = iov; + for (i = 0; i < count; i++) { + if (__get_user(len, &iov32->iov_len) || + __get_user(buf, &iov32->iov_base)) { + if (iov != iov_buf) + kfree(iov); + return((struct iovec *)0); + } + if (verify_area(type, (void *)A(buf), len)) { + if (iov != iov_buf) + kfree(iov); + return((struct iovec *)0); + } + ivp->iov_base = (void *)A(buf); + ivp->iov_len = (__kernel_size_t)len; + iov32++; + ivp++; + } + return(iov); +} + +asmlinkage long +sys32_readv(int fd, struct iovec32 *vector, u32 count) +{ + struct iovec iovstack[UIO_FASTIOV]; + struct iovec *iov; + int ret; + mm_segment_t old_fs = get_fs(); + + if ((iov = get_iovec32(vector, iovstack, count, VERIFY_WRITE)) == (struct iovec *)0) + return -EFAULT; + set_fs(KERNEL_DS); + ret = sys_readv(fd, iov, count); + set_fs(old_fs); + if (iov != iovstack) + kfree(iov); + return ret; +} + +asmlinkage long +sys32_writev(int fd, struct iovec32 *vector, u32 count) +{ + struct iovec iovstack[UIO_FASTIOV]; + struct iovec *iov; + int ret; + mm_segment_t old_fs = get_fs(); + + if ((iov = get_iovec32(vector, iovstack, count, VERIFY_READ)) == (struct iovec *)0) + return -EFAULT; + set_fs(KERNEL_DS); + ret = sys_writev(fd, iov, count); + set_fs(old_fs); + if (iov != iovstack) + kfree(iov); + return ret; +} + +#define RLIM_INFINITY32 0xffffffff +#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) + +struct rlimit32 { + int rlim_cur; + int rlim_max; +}; + +extern asmlinkage long sys_getrlimit(unsigned int resource, struct rlimit *rlim); + +asmlinkage long +sys32_getrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_getrlimit(resource, &r); + set_fs(old_fs); + if (!ret) { + if (verify_area(VERIFY_WRITE, rlim, sizeof(struct rlimit32)) || + __put_user(RESOURCE32(r.rlim_cur), &rlim->rlim_cur) || + __put_user(RESOURCE32(r.rlim_max), &rlim->rlim_max)) + ret = -EFAULT; + } + return ret; +} + +extern asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit *rlim); + +asmlinkage long +sys32_old_getrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_old_getrlimit(resource, &r); + set_fs(old_fs); + if (!ret) { + if (verify_area(VERIFY_WRITE, rlim, sizeof(struct rlimit32)) || + __put_user(r.rlim_cur, &rlim->rlim_cur) || + __put_user(r.rlim_max, &rlim->rlim_max)) + ret = -EFAULT; + } + return ret; +} + +extern asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit *rlim); + +asmlinkage long +sys32_setrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs = get_fs (); + + if (resource >= RLIM_NLIMITS) return -EINVAL; + if (verify_area(VERIFY_READ, rlim, sizeof(struct rlimit32)) || + __get_user (r.rlim_cur, &rlim->rlim_cur) || + __get_user (r.rlim_max, &rlim->rlim_max)) + return -EFAULT; + if (r.rlim_cur == RLIM_INFINITY32) + r.rlim_cur = RLIM_INFINITY; + if (r.rlim_max == RLIM_INFINITY32) + r.rlim_max = RLIM_INFINITY; + set_fs (KERNEL_DS); + ret = sys_setrlimit(resource, &r); + set_fs (old_fs); + return ret; +} + +/* + * sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation.. + * + * This is really horribly ugly. + */ + +struct msgbuf32 { s32 mtype; char mtext[1]; }; + +struct ipc_perm32 +{ + key_t key; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_uid_t32 cuid; + __kernel_gid_t32 cgid; + __kernel_mode_t32 mode; + unsigned short seq; +}; + +struct semid_ds32 { + struct ipc_perm32 sem_perm; /* permissions .. see ipc.h */ + __kernel_time_t32 sem_otime; /* last semop time */ + __kernel_time_t32 sem_ctime; /* last change time */ + u32 sem_base; /* ptr to first semaphore in array */ + u32 sem_pending; /* pending operations to be processed */ + u32 sem_pending_last; /* last pending operation */ + u32 undo; /* undo requests on this array */ + unsigned short sem_nsems; /* no. of semaphores in array */ +}; + +struct msqid_ds32 +{ + struct ipc_perm32 msg_perm; + u32 msg_first; + u32 msg_last; + __kernel_time_t32 msg_stime; + __kernel_time_t32 msg_rtime; + __kernel_time_t32 msg_ctime; + u32 wwait; + u32 rwait; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + __kernel_ipc_pid_t32 msg_lspid; + __kernel_ipc_pid_t32 msg_lrpid; +}; + +struct shmid_ds32 { + struct ipc_perm32 shm_perm; + int shm_segsz; + __kernel_time_t32 shm_atime; + __kernel_time_t32 shm_dtime; + __kernel_time_t32 shm_ctime; + __kernel_ipc_pid_t32 shm_cpid; + __kernel_ipc_pid_t32 shm_lpid; + unsigned short shm_nattch; +}; + +#define IPCOP_MASK(__x) (1UL << (__x)) + +static int +do_sys32_semctl(int first, int second, int third, void *uptr) +{ + union semun fourth; + u32 pad; + int err; + struct semid64_ds s; + struct semid_ds32 *usp; + mm_segment_t old_fs; + + if (!uptr) + return -EINVAL; + err = -EFAULT; + if (get_user (pad, (u32 *)uptr)) + return err; + if(third == SETVAL) + fourth.val = (int)pad; + else + fourth.__pad = (void *)A(pad); + switch (third) { + + case IPC_INFO: + case IPC_RMID: + case IPC_SET: + case SEM_INFO: + case GETVAL: + case GETPID: + case GETNCNT: + case GETZCNT: + case GETALL: + case SETVAL: + case SETALL: + err = sys_semctl (first, second, third, fourth); + break; + + case IPC_STAT: + case SEM_STAT: + usp = (struct semid_ds32 *)A(pad); + fourth.__pad = &s; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_semctl (first, second, third, fourth); + set_fs (old_fs); + if (verify_area(VERIFY_WRITE, usp, sizeof(struct semid_ds32)) || + __put_user(s.sem_perm.key, &usp->sem_perm.key) || + __put_user(s.sem_perm.uid, &usp->sem_perm.uid) || + __put_user(s.sem_perm.gid, &usp->sem_perm.gid) || + __put_user(s.sem_perm.cuid, &usp->sem_perm.cuid) || + __put_user (s.sem_perm.cgid, &usp->sem_perm.cgid) || + __put_user (s.sem_perm.mode, &usp->sem_perm.mode) || + __put_user (s.sem_perm.seq, &usp->sem_perm.seq) || + __put_user (s.sem_otime, &usp->sem_otime) || + __put_user (s.sem_ctime, &usp->sem_ctime) || + __put_user (s.sem_nsems, &usp->sem_nsems)) + return -EFAULT; + break; + + } + + return err; +} + +static int +do_sys32_msgsnd (int first, int second, int third, void *uptr) +{ + struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf) + + 4, GFP_USER); + struct msgbuf32 *up = (struct msgbuf32 *)uptr; + mm_segment_t old_fs; + int err; + + if (!p) + return -ENOMEM; + err = verify_area(VERIFY_READ, up, sizeof(struct msgbuf32)); + if (err) + goto out; + err = __get_user (p->mtype, &up->mtype); + err |= __copy_from_user (p->mtext, &up->mtext, second); + if (err) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgsnd (first, p, second, third); + set_fs (old_fs); +out: + kfree (p); + return err; +} + +static int +do_sys32_msgrcv (int first, int second, int msgtyp, int third, + int version, void *uptr) +{ + struct msgbuf32 *up; + struct msgbuf *p; + mm_segment_t old_fs; + int err; + + if (!version) { + struct ipc_kludge *uipck = (struct ipc_kludge *)uptr; + struct ipc_kludge ipck; + + err = -EINVAL; + if (!uptr) + goto out; + err = -EFAULT; + if (copy_from_user (&ipck, uipck, sizeof (struct ipc_kludge))) + goto out; + uptr = (void *)A(ipck.msgp); + msgtyp = ipck.msgtyp; + } + err = -ENOMEM; + p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER); + if (!p) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgrcv (first, p, second + 4, msgtyp, third); + set_fs (old_fs); + if (err < 0) + goto free_then_out; + up = (struct msgbuf32 *)uptr; + if (verify_area(VERIFY_WRITE, up, sizeof(struct msgbuf32)) || + __put_user (p->mtype, &up->mtype) || + __copy_to_user (&up->mtext, p->mtext, err)) + err = -EFAULT; +free_then_out: + kfree (p); +out: + return err; +} + +static int +do_sys32_msgctl (int first, int second, void *uptr) +{ + int err = -EINVAL; + struct msqid_ds m; + struct msqid64_ds m64; + struct msqid_ds32 *up = (struct msqid_ds32 *)uptr; + mm_segment_t old_fs; + + switch (second) { + + case IPC_INFO: + case IPC_RMID: + case MSG_INFO: + err = sys_msgctl (first, second, (struct msqid_ds *)uptr); + break; + + case IPC_SET: + err = verify_area(VERIFY_READ, up, sizeof(struct msqid_ds32)); + if (err) + break; + err = __get_user (m.msg_perm.uid, &up->msg_perm.uid); + err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid); + err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode); + err |= __get_user (m.msg_qbytes, &up->msg_qbytes); + if (err) + break; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, &m); + set_fs (old_fs); + break; + + case IPC_STAT: + case MSG_STAT: + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, (void *) &m64); + set_fs (old_fs); + if (verify_area(VERIFY_WRITE, up, sizeof(struct msqid_ds32)) || + __put_user (m64.msg_perm.key, &up->msg_perm.key) || + __put_user(m64.msg_perm.uid, &up->msg_perm.uid) || + __put_user(m64.msg_perm.gid, &up->msg_perm.gid) || + __put_user(m64.msg_perm.cuid, &up->msg_perm.cuid) || + __put_user(m64.msg_perm.cgid, &up->msg_perm.cgid) || + __put_user(m64.msg_perm.mode, &up->msg_perm.mode) || + __put_user(m64.msg_perm.seq, &up->msg_perm.seq) || + __put_user(m64.msg_stime, &up->msg_stime) || + __put_user(m64.msg_rtime, &up->msg_rtime) || + __put_user(m64.msg_ctime, &up->msg_ctime) || + __put_user(m64.msg_cbytes, &up->msg_cbytes) || + __put_user(m64.msg_qnum, &up->msg_qnum) || + __put_user(m64.msg_qbytes, &up->msg_qbytes) || + __put_user(m64.msg_lspid, &up->msg_lspid) || + __put_user(m64.msg_lrpid, &up->msg_lrpid)) + return -EFAULT; + break; + + } + + return err; +} + +static int +do_sys32_shmat (int first, int second, int third, int version, void *uptr) +{ + unsigned long raddr; + u32 *uaddr = (u32 *)A((u32)third); + int err = -EINVAL; + + if (version == 1) + return err; + err = sys_shmat (first, uptr, second, &raddr); + if (err) + return err; + err = put_user (raddr, uaddr); + return err; +} + +static int +do_sys32_shmctl (int first, int second, void *uptr) +{ + int err = -EFAULT; + struct shmid_ds s; + struct shmid64_ds s64; + struct shmid_ds32 *up = (struct shmid_ds32 *)uptr; + mm_segment_t old_fs; + struct shm_info32 { + int used_ids; + u32 shm_tot, shm_rss, shm_swp; + u32 swap_attempts, swap_successes; + } *uip = (struct shm_info32 *)uptr; + struct shm_info si; + + switch (second) { + + case IPC_INFO: + case IPC_RMID: + case SHM_LOCK: + case SHM_UNLOCK: + err = sys_shmctl (first, second, (struct shmid_ds *)uptr); + break; + case IPC_SET: + err = verify_area(VERIFY_READ, up, sizeof(struct shmid_ds32)); + if (err) + break; + err = __get_user (s.shm_perm.uid, &up->shm_perm.uid); + err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid); + err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode); + if (err) + break; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, &s); + set_fs (old_fs); + break; + + case IPC_STAT: + case SHM_STAT: + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, (void *) &s64); + set_fs (old_fs); + if (err < 0) + break; + if (verify_area(VERIFY_WRITE, up, sizeof(struct shmid_ds32)) || + __put_user (s64.shm_perm.key, &up->shm_perm.key) || + __put_user (s64.shm_perm.uid, &up->shm_perm.uid) || + __put_user (s64.shm_perm.gid, &up->shm_perm.gid) || + __put_user (s64.shm_perm.cuid, &up->shm_perm.cuid) || + __put_user (s64.shm_perm.cgid, &up->shm_perm.cgid) || + __put_user (s64.shm_perm.mode, &up->shm_perm.mode) || + __put_user (s64.shm_perm.seq, &up->shm_perm.seq) || + __put_user (s64.shm_atime, &up->shm_atime) || + __put_user (s64.shm_dtime, &up->shm_dtime) || + __put_user (s64.shm_ctime, &up->shm_ctime) || + __put_user (s64.shm_segsz, &up->shm_segsz) || + __put_user (s64.shm_nattch, &up->shm_nattch) || + __put_user (s64.shm_cpid, &up->shm_cpid) || + __put_user (s64.shm_lpid, &up->shm_lpid)) + return -EFAULT; + break; + + case SHM_INFO: + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, (void *)&si); + set_fs (old_fs); + if (err < 0) + break; + if (verify_area(VERIFY_WRITE, uip, sizeof(struct shm_info32)) || + __put_user (si.used_ids, &uip->used_ids) || + __put_user (si.shm_tot, &uip->shm_tot) || + __put_user (si.shm_rss, &uip->shm_rss) || + __put_user (si.shm_swp, &uip->shm_swp) || + __put_user (si.swap_attempts, &uip->swap_attempts) || + __put_user (si.swap_successes, &uip->swap_successes)) + return -EFAULT; + break; + + } + return err; +} + +asmlinkage long +sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth) +{ + int version, err; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + + case SEMOP: + /* struct sembuf is the same on 32 and 64bit :)) */ + err = sys_semop (first, (struct sembuf *)AA(ptr), + second); + break; + case SEMGET: + err = sys_semget (first, second, third); + break; + case SEMCTL: + err = do_sys32_semctl (first, second, third, + (void *)AA(ptr)); + break; + + case MSGSND: + err = do_sys32_msgsnd (first, second, third, + (void *)AA(ptr)); + break; + case MSGRCV: + err = do_sys32_msgrcv (first, second, fifth, third, + version, (void *)AA(ptr)); + break; + case MSGGET: + err = sys_msgget ((key_t) first, second); + break; + case MSGCTL: + err = do_sys32_msgctl (first, second, (void *)AA(ptr)); + break; + + case SHMAT: + err = do_sys32_shmat (first, second, third, + version, (void *)AA(ptr)); + break; + case SHMDT: + err = sys_shmdt ((char *)AA(ptr)); + break; + case SHMGET: + err = sys_shmget (first, second, third); + break; + case SHMCTL: + err = do_sys32_shmctl (first, second, (void *)AA(ptr)); + break; + default: + err = -EINVAL; + break; + } + + return err; +} + +/* + * sys_time() can be implemented in user-level using + * sys_gettimeofday(). IA64 did this but i386 Linux did not + * so we have to implement this system call here. + */ +asmlinkage long sys32_time(int * tloc) +{ + int i; + + /* SMP: This is fairly trivial. We grab CURRENT_TIME and + stuff it to user space. No side effects */ + i = CURRENT_TIME; + if (tloc) { + if (put_user(i,tloc)) + i = -EFAULT; + } + return i; +} + +struct rusage32 { + struct timeval32 ru_utime; + struct timeval32 ru_stime; + int ru_maxrss; + int ru_ixrss; + int ru_idrss; + int ru_isrss; + int ru_minflt; + int ru_majflt; + int ru_nswap; + int ru_inblock; + int ru_oublock; + int ru_msgsnd; + int ru_msgrcv; + int ru_nsignals; + int ru_nvcsw; + int ru_nivcsw; +}; + +static int +put_rusage (struct rusage32 *ru, struct rusage *r) +{ + if (verify_area(VERIFY_WRITE, ru, sizeof(struct rusage32)) || + __put_user (r->ru_utime.tv_sec, &ru->ru_utime.tv_sec) || + __put_user (r->ru_utime.tv_usec, &ru->ru_utime.tv_usec) || + __put_user (r->ru_stime.tv_sec, &ru->ru_stime.tv_sec) || + __put_user (r->ru_stime.tv_usec, &ru->ru_stime.tv_usec) || + __put_user (r->ru_maxrss, &ru->ru_maxrss) || + __put_user (r->ru_ixrss, &ru->ru_ixrss) || + __put_user (r->ru_idrss, &ru->ru_idrss) || + __put_user (r->ru_isrss, &ru->ru_isrss) || + __put_user (r->ru_minflt, &ru->ru_minflt) || + __put_user (r->ru_majflt, &ru->ru_majflt) || + __put_user (r->ru_nswap, &ru->ru_nswap) || + __put_user (r->ru_inblock, &ru->ru_inblock) || + __put_user (r->ru_oublock, &ru->ru_oublock) || + __put_user (r->ru_msgsnd, &ru->ru_msgsnd) || + __put_user (r->ru_msgrcv, &ru->ru_msgrcv) || + __put_user (r->ru_nsignals, &ru->ru_nsignals) || + __put_user (r->ru_nvcsw, &ru->ru_nvcsw) || + __put_user (r->ru_nivcsw, &ru->ru_nivcsw)) + return -EFAULT; + return 0; +} + +extern asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, + int options, struct rusage * ru); + +asmlinkage long +sys32_wait4(__kernel_pid_t32 pid, unsigned int *stat_addr, int options, + struct rusage32 *ru) +{ + if (!ru) + return sys_wait4(pid, stat_addr, options, NULL); + else { + struct rusage r; + int ret; + unsigned int status; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_wait4(pid, stat_addr ? &status : NULL, options, &r); + set_fs (old_fs); + if (put_rusage (ru, &r)) return -EFAULT; + if (stat_addr && put_user (status, stat_addr)) + return -EFAULT; + return ret; + } +} + +asmlinkage long +sys32_waitpid(__kernel_pid_t32 pid, unsigned int *stat_addr, int options) +{ + return sys32_wait4(pid, stat_addr, options, NULL); +} + + +extern asmlinkage long +sys_getrusage(int who, struct rusage *ru); + +asmlinkage long +sys32_getrusage(int who, struct rusage32 *ru) +{ + struct rusage r; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_getrusage(who, &r); + set_fs (old_fs); + if (put_rusage (ru, &r)) return -EFAULT; + return ret; +} + +struct tms32 { + __kernel_clock_t32 tms_utime; + __kernel_clock_t32 tms_stime; + __kernel_clock_t32 tms_cutime; + __kernel_clock_t32 tms_cstime; +}; + +extern asmlinkage long sys_times(struct tms * tbuf); + +asmlinkage long +sys32_times(struct tms32 *tbuf) +{ + struct tms t; + long ret; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_times(tbuf ? &t : NULL); + set_fs (old_fs); + if (tbuf) { + if (verify_area(VERIFY_WRITE, tbuf, sizeof(struct tms32)) || + __put_user (t.tms_utime, &tbuf->tms_utime) || + __put_user (t.tms_stime, &tbuf->tms_stime) || + __put_user (t.tms_cutime, &tbuf->tms_cutime) || + __put_user (t.tms_cstime, &tbuf->tms_cstime)) + return -EFAULT; + } + return ret; +} + +static inline int +get_flock32(struct flock *kfl, struct flock32 *ufl) +{ + if (verify_area(VERIFY_READ, ufl, sizeof(struct flock32)) || + __get_user(kfl->l_type, &ufl->l_type) || + __get_user(kfl->l_whence, &ufl->l_whence) || + __get_user(kfl->l_start, &ufl->l_start) || + __get_user(kfl->l_len, &ufl->l_len) || + __get_user(kfl->l_pid, &ufl->l_pid)) + return -EFAULT; + return 0; +} + +static inline int +put_flock32(struct flock *kfl, struct flock32 *ufl) +{ + if (verify_area(VERIFY_WRITE, ufl, sizeof(struct flock32)) || + __put_user(kfl->l_type, &ufl->l_type) || + __put_user(kfl->l_whence, &ufl->l_whence) || + __put_user(kfl->l_start, &ufl->l_start) || + __put_user(kfl->l_len, &ufl->l_len) || + __put_user(kfl->l_pid, &ufl->l_pid)) + return -EFAULT; + return 0; +} + +extern asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, + unsigned long arg); + +asmlinkage long +sys32_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct flock f; + mm_segment_t old_fs; + long ret; + + switch (cmd) { + case F_GETLK: + case F_SETLK: + case F_SETLKW: + if(cmd != F_GETLK && get_flock32(&f, (struct flock32 *)((long)arg))) + return -EFAULT; + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_fcntl(fd, cmd, (unsigned long)&f); + set_fs(old_fs); + if(cmd == F_GETLK && put_flock32(&f, (struct flock32 *)((long)arg))) + return -EFAULT; + return ret; + default: + /* + * `sys_fcntl' lies about arg, for the F_SETOWN + * sub-function arg can have a negative value. + */ + return sys_fcntl(fd, cmd, (unsigned long)((long)arg)); + } +} + +static inline int +get_flock64(struct flock *kfl, struct ia32_flock64 *ufl) +{ + if (verify_area(VERIFY_READ, ufl, sizeof(struct ia32_flock64)) || + __get_user(kfl->l_type, &ufl->l_type) || + __get_user(kfl->l_whence, &ufl->l_whence) || + __copy_from_user(&kfl->l_start, &ufl->l_start, 8) || + __copy_from_user(&kfl->l_len, &ufl->l_len, 8) || + __get_user(kfl->l_pid, &ufl->l_pid)) + return -EFAULT; + return 0; +} + +static inline int +put_flock64(struct flock *kfl, struct ia32_flock64 *ufl) +{ + if (verify_area(VERIFY_WRITE, ufl, sizeof(struct ia32_flock64)) || + __put_user(kfl->l_type, &ufl->l_type) || + __put_user(kfl->l_whence, &ufl->l_whence) || + __copy_to_user(&ufl->l_start,&kfl->l_start, 8) || + __copy_to_user(&ufl->l_len,&kfl->l_len, 8) || + __put_user(kfl->l_pid, &ufl->l_pid)) + return -EFAULT; + return 0; +} + +asmlinkage long +sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct flock f; + mm_segment_t old_fs; + long ret; + + /* sys_fcntl() is by default 64 bit and so don't know anything + * about F_xxxx64 commands + */ + switch (cmd) { + case F_GETLK64: + cmd = F_GETLK; + break; + case F_SETLK64: + cmd = F_SETLK; + break; + case F_SETLKW64: + cmd = F_SETLKW; + break; + } + + switch (cmd) { + case F_SETLKW: + case F_SETLK: + if(get_flock64(&f, (struct ia32_flock64 *)arg)) + return -EFAULT; + case F_GETLK: + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_fcntl(fd, cmd, (unsigned long)&f); + set_fs(old_fs); + if(cmd == F_GETLK64 && put_flock64(&f, (struct ia32_flock64 *)((long)arg))) + return -EFAULT; + return ret; + default: + /* + * `sys_fcntl' lies about arg, for the F_SETOWN + * sub-function arg can have a negative value. + */ + return sys_fcntl(fd, cmd, (unsigned long)((long)arg)); + } +} + +int sys32_ni_syscall(int call) +{ + printk(KERN_INFO "IA32 syscall %d from %s not implemented\n", call, + current->comm); + return -ENOSYS; +} + +/* In order to reduce some races, while at the same time doing additional + * checking and hopefully speeding things up, we copy filenames to the + * kernel data space before using them.. + * + * POSIX.1 2.4: an empty pathname is invalid (ENOENT). + */ +static inline int +do_getname32(const char *filename, char *page) +{ + int retval; + + /* 32bit pointer will be always far below TASK_SIZE :)) */ + retval = strncpy_from_user((char *)page, (char *)filename, PAGE_SIZE); + if (retval > 0) { + if (retval < PAGE_SIZE) + return 0; + return -ENAMETOOLONG; + } else if (!retval) + retval = -ENOENT; + return retval; +} + +char * +getname32(const char *filename) +{ + char *tmp, *result; + + result = ERR_PTR(-ENOMEM); + tmp = (char *)__get_free_page(GFP_KERNEL); + if (tmp) { + int retval = do_getname32(filename, tmp); + + result = tmp; + if (retval < 0) { + putname(tmp); + result = ERR_PTR(retval); + } + } + return result; +} + +/* 32-bit timeval and related flotsam. */ + +extern asmlinkage long sys_utime(char * filename, struct utimbuf * times); + +struct utimbuf32 { + __kernel_time_t32 actime, modtime; +}; + +asmlinkage long +sys32_utime(char * filename, struct utimbuf32 *times) +{ + struct utimbuf t; + mm_segment_t old_fs; + int ret; + char *filenam; + + if (!times) + return sys_utime(filename, NULL); + if (verify_area(VERIFY_READ, times, sizeof(struct utimbuf32)) || + __get_user (t.actime, ×->actime) || + __get_user (t.modtime, ×->modtime)) + return -EFAULT; + filenam = getname32 (filename); + ret = PTR_ERR(filenam); + if (!IS_ERR(filenam)) { + old_fs = get_fs(); + set_fs (KERNEL_DS); + ret = sys_utime(filenam, &t); + set_fs (old_fs); + putname (filenam); + } + return ret; +} + +/* + * Ooo, nasty. We need here to frob 32-bit unsigned longs to + * 64-bit unsigned longs. + */ + +static inline int +get_fd_set32(unsigned long n, unsigned long *fdset, u32 *ufdset) +{ + if (ufdset) { + unsigned long odd; + + if (verify_area(VERIFY_READ, ufdset, n*sizeof(u32))) + return -EFAULT; + + odd = n & 1UL; + n &= ~1UL; + while (n) { + unsigned long h, l; + __get_user(l, ufdset); + __get_user(h, ufdset+1); + ufdset += 2; + *fdset++ = h << 32 | l; + n -= 2; + } + if (odd) + __get_user(*fdset, ufdset); + } else { + /* Tricky, must clear full unsigned long in the + * kernel fdset at the end, this makes sure that + * actually happens. + */ + memset(fdset, 0, ((n + 1) & ~1)*sizeof(u32)); + } + return 0; +} + +extern asmlinkage long sys_sysfs(int option, unsigned long arg1, + unsigned long arg2); + +asmlinkage long +sys32_sysfs(int option, u32 arg1, u32 arg2) +{ + return sys_sysfs(option, arg1, arg2); +} + +extern asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, + unsigned long new_flags, void *data); + +static char *badfs[] = { + "smbfs", "ncpfs", NULL +}; + +static int checktype(char *user_type) +{ + int err = 0; + char **s,*kernel_type = getname32(user_type); + if (!kernel_type) + return -EFAULT; + for (s = badfs; *s; ++s) + if (!strcmp(kernel_type, *s)) { + printk(KERN_ERR "mount32: unsupported fs `%s' -- use 64bit mount\n", *s); + err = -EINVAL; + break; + } + putname(user_type); + return err; +} + +asmlinkage long +sys32_mount(char *dev_name, char *dir_name, char *type, + unsigned long new_flags, u32 data) +{ + int err; + if(!capable(CAP_SYS_ADMIN)) + return -EPERM; + err = checktype(type); + if (err) + return err; + return sys_mount(dev_name, dir_name, type, new_flags, (void *)AA(data)); +} + +struct sysinfo32 { + s32 uptime; + u32 loads[3]; + u32 totalram; + u32 freeram; + u32 sharedram; + u32 bufferram; + u32 totalswap; + u32 freeswap; + unsigned short procs; + char _f[22]; +}; + +extern asmlinkage long sys_sysinfo(struct sysinfo *info); + +asmlinkage long +sys32_sysinfo(struct sysinfo32 *info) +{ + struct sysinfo s; + int ret; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_sysinfo(&s); + set_fs (old_fs); + if (verify_area(VERIFY_WRITE, info, sizeof(struct sysinfo32)) || + __put_user (s.uptime, &info->uptime) || + __put_user (s.loads[0], &info->loads[0]) || + __put_user (s.loads[1], &info->loads[1]) || + __put_user (s.loads[2], &info->loads[2]) || + __put_user (s.totalram, &info->totalram) || + __put_user (s.freeram, &info->freeram) || + __put_user (s.sharedram, &info->sharedram) || + __put_user (s.bufferram, &info->bufferram) || + __put_user (s.totalswap, &info->totalswap) || + __put_user (s.freeswap, &info->freeswap) || + __put_user (s.procs, &info->procs)) + return -EFAULT; + return 0; +} + +extern asmlinkage long sys_sched_rr_get_interval(pid_t pid, + struct timespec *interval); + +asmlinkage long +sys32_sched_rr_get_interval(__kernel_pid_t32 pid, struct timespec32 *interval) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_sched_rr_get_interval(pid, &t); + set_fs (old_fs); + if (verify_area(VERIFY_WRITE, interval, sizeof(struct timespec32)) || + __put_user (t.tv_sec, &interval->tv_sec) || + __put_user (t.tv_nsec, &interval->tv_nsec)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_sigprocmask(int how, old_sigset_t *set, + old_sigset_t *oset); + +asmlinkage long +sys32_sigprocmask(int how, old_sigset32_t *set, old_sigset32_t *oset) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); + + if (set && get_user (s, set)) return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL); + set_fs (old_fs); + if (ret) return ret; + if (oset && put_user (s, oset)) return -EFAULT; + return 0; +} + +extern asmlinkage long sys_sigpending(old_sigset_t *set); + +asmlinkage long +sys32_sigpending(old_sigset32_t *set) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_sigpending(&s); + set_fs (old_fs); + if (put_user (s, set)) return -EFAULT; + return ret; +} + +extern asmlinkage long sys_rt_sigpending(sigset_t *set, size_t sigsetsize); + +asmlinkage long +sys32_rt_sigpending(sigset32_t *set, __kernel_size_t32 sigsetsize) +{ + sigset_t s; + sigset32_t s32; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_rt_sigpending(&s, sigsetsize); + set_fs (old_fs); + if (!ret) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user (set, &s32, sizeof(sigset32_t))) + return -EFAULT; + } + return ret; +} + +siginfo_t32 * +siginfo64to32(siginfo_t32 *d, siginfo_t *s) +{ + memset (d, 0, sizeof(siginfo_t32)); + d->si_signo = s->si_signo; + d->si_errno = s->si_errno; + d->si_code = s->si_code; + if (s->si_signo >= SIGRTMIN) { + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + /* XXX: Ouch, how to find this out??? */ + d->si_int = s->si_int; + } else switch (s->si_signo) { + /* XXX: What about POSIX1.b timers */ + case SIGCHLD: + d->si_pid = s->si_pid; + d->si_status = s->si_status; + d->si_utime = s->si_utime; + d->si_stime = s->si_stime; + break; + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + d->si_addr = (long)(s->si_addr); +// d->si_trapno = s->si_trapno; + break; + case SIGPOLL: + d->si_band = s->si_band; + d->si_fd = s->si_fd; + break; + default: + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + break; + } + return d; +} + +siginfo_t * +siginfo32to64(siginfo_t *d, siginfo_t32 *s) +{ + d->si_signo = s->si_signo; + d->si_errno = s->si_errno; + d->si_code = s->si_code; + if (s->si_signo >= SIGRTMIN) { + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + /* XXX: Ouch, how to find this out??? */ + d->si_int = s->si_int; + } else switch (s->si_signo) { + /* XXX: What about POSIX1.b timers */ + case SIGCHLD: + d->si_pid = s->si_pid; + d->si_status = s->si_status; + d->si_utime = s->si_utime; + d->si_stime = s->si_stime; + break; + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + d->si_addr = (void *)A(s->si_addr); +// d->si_trapno = s->si_trapno; + break; + case SIGPOLL: + d->si_band = s->si_band; + d->si_fd = s->si_fd; + break; + default: + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + break; + } + return d; +} + +extern asmlinkage long +sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo, + const struct timespec *uts, size_t sigsetsize); + +asmlinkage long +sys32_rt_sigtimedwait(sigset32_t *uthese, siginfo_t32 *uinfo, + struct timespec32 *uts, __kernel_size_t32 sigsetsize) +{ + sigset_t s; + sigset32_t s32; + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs(); + siginfo_t info; + siginfo_t32 info32; + + if (copy_from_user (&s32, uthese, sizeof(sigset32_t))) + return -EFAULT; + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + if (uts) { + if (verify_area(VERIFY_READ, uts, sizeof(struct timespec32)) || + __get_user (t.tv_sec, &uts->tv_sec) || + __get_user (t.tv_nsec, &uts->tv_nsec)) + return -EFAULT; + } + set_fs (KERNEL_DS); + ret = sys_rt_sigtimedwait(&s, &info, &t, sigsetsize); + set_fs (old_fs); + if (ret >= 0 && uinfo) { + if (copy_to_user (uinfo, siginfo64to32(&info32, &info), + sizeof(siginfo_t32))) + return -EFAULT; + } + return ret; +} + +extern asmlinkage long +sys_rt_sigqueueinfo(int pid, int sig, siginfo_t *uinfo); + +asmlinkage long +sys32_rt_sigqueueinfo(int pid, int sig, siginfo_t32 *uinfo) +{ + siginfo_t info; + siginfo_t32 info32; + int ret; + mm_segment_t old_fs = get_fs(); + + if (copy_from_user (&info32, uinfo, sizeof(siginfo_t32))) + return -EFAULT; + /* XXX: Is this correct? */ + siginfo32to64(&info, &info32); + set_fs (KERNEL_DS); + ret = sys_rt_sigqueueinfo(pid, sig, &info); + set_fs (old_fs); + return ret; +} + +extern asmlinkage long sys_setreuid(uid_t ruid, uid_t euid); + +asmlinkage long sys32_setreuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid) +{ + uid_t sruid, seuid; + + sruid = (ruid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)ruid); + seuid = (euid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)euid); + return sys_setreuid(sruid, seuid); +} + +extern asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid); + +asmlinkage long +sys32_setresuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid, + __kernel_uid_t32 suid) +{ + uid_t sruid, seuid, ssuid; + + sruid = (ruid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)ruid); + seuid = (euid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)euid); + ssuid = (suid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)suid); + return sys_setresuid(sruid, seuid, ssuid); +} + +extern asmlinkage long sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); + +asmlinkage long +sys32_getresuid(__kernel_uid_t32 *ruid, __kernel_uid_t32 *euid, + __kernel_uid_t32 *suid) +{ + uid_t a, b, c; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_getresuid(&a, &b, &c); + set_fs (old_fs); + if (put_user (a, ruid) || put_user (b, euid) || put_user (c, suid)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_setregid(gid_t rgid, gid_t egid); + +asmlinkage long +sys32_setregid(__kernel_gid_t32 rgid, __kernel_gid_t32 egid) +{ + gid_t srgid, segid; + + srgid = (rgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)rgid); + segid = (egid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)egid); + return sys_setregid(srgid, segid); +} + +extern asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid); + +asmlinkage long +sys32_setresgid(__kernel_gid_t32 rgid, __kernel_gid_t32 egid, + __kernel_gid_t32 sgid) +{ + gid_t srgid, segid, ssgid; + + srgid = (rgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)rgid); + segid = (egid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)egid); + ssgid = (sgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)sgid); + return sys_setresgid(srgid, segid, ssgid); +} + +extern asmlinkage long sys_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); + +asmlinkage long +sys32_getresgid(__kernel_gid_t32 *rgid, __kernel_gid_t32 *egid, + __kernel_gid_t32 *sgid) +{ + gid_t a, b, c; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_getresgid(&a, &b, &c); + set_fs (old_fs); + if (!ret) { + ret = put_user (a, rgid); + ret |= put_user (b, egid); + ret |= put_user (c, sgid); + } + return ret; +} + +extern asmlinkage long sys_getgroups(int gidsetsize, gid_t *grouplist); + +asmlinkage long +sys32_getgroups(int gidsetsize, __kernel_gid_t32 *grouplist) +{ + gid_t gl[NGROUPS]; + int ret, i; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_getgroups(gidsetsize, gl); + set_fs (old_fs); + if (gidsetsize && ret > 0 && ret <= NGROUPS) + for (i = 0; i < ret; i++, grouplist++) + if (put_user (gl[i], grouplist)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_setgroups(int gidsetsize, gid_t *grouplist); + +asmlinkage long +sys32_setgroups(int gidsetsize, __kernel_gid_t32 *grouplist) +{ + gid_t gl[NGROUPS]; + int ret, i; + mm_segment_t old_fs = get_fs (); + + if ((unsigned) gidsetsize > NGROUPS) + return -EINVAL; + for (i = 0; i < gidsetsize; i++, grouplist++) + if (get_user (gl[i], grouplist)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_setgroups(gidsetsize, gl); + set_fs (old_fs); + return ret; +} + + +extern void check_pending(int signum); + +asmlinkage long sys_utimes(char *, struct timeval *); + +asmlinkage long +sys32_utimes(char *filename, struct timeval32 *tvs) +{ + char *kfilename; + struct timeval ktvs[2]; + mm_segment_t old_fs; + int ret; + + kfilename = getname32(filename); + ret = PTR_ERR(kfilename); + if (!IS_ERR(kfilename)) { + if (tvs) { + if (get_tv32(&ktvs[0], tvs) || + get_tv32(&ktvs[1], 1+tvs)) + return -EFAULT; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_utimes(kfilename, &ktvs[0]); + set_fs(old_fs); + + putname(kfilename); + } + return ret; +} + +/* These are here just in case some old ia32 binary calls it. */ +asmlinkage long +sys32_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + + +struct sysctl_ia32 { + unsigned int name; + int nlen; + unsigned int oldval; + unsigned int oldlenp; + unsigned int newval; + unsigned int newlen; + unsigned int __unused[4]; +}; + + +asmlinkage long +sys32_sysctl(struct sysctl_ia32 *args32) +{ +#ifndef CONFIG_SYSCTL + return -ENOSYS; +#else + struct sysctl_ia32 a32; + mm_segment_t old_fs = get_fs (); + void *oldvalp, *newvalp; + size_t oldlen; + int *namep; + long ret; + extern int do_sysctl(int *name, int nlen, void *oldval, size_t *oldlenp, + void *newval, size_t newlen); + + + if (copy_from_user(&a32, args32, sizeof (a32))) + return -EFAULT; + + /* + * We need to pre-validate these because we have to disable address checking + * before calling do_sysctl() because of OLDLEN but we can't run the risk of the + * user specifying bad addresses here. Well, since we're dealing with 32 bit + * addresses, we KNOW that access_ok() will always succeed, so this is an + * expensive NOP, but so what... + */ + namep = (int *) A(a32.name); + oldvalp = (void *) A(a32.oldval); + newvalp = (void *) A(a32.newval); + + if ((oldvalp && get_user(oldlen, (int *) A(a32.oldlenp))) + || !access_ok(VERIFY_WRITE, namep, 0) + || !access_ok(VERIFY_WRITE, oldvalp, 0) + || !access_ok(VERIFY_WRITE, newvalp, 0)) + return -EFAULT; + + set_fs(KERNEL_DS); + lock_kernel(); + ret = do_sysctl(namep, a32.nlen, oldvalp, &oldlen, newvalp, (size_t) a32.newlen); + unlock_kernel(); + set_fs(old_fs); + + if (oldvalp && put_user (oldlen, (int *) A(a32.oldlenp))) + return -EFAULT; + + return ret; +#endif +} + +extern asmlinkage long sys_newuname(struct new_utsname * name); + +asmlinkage long +sys32_newuname(struct new_utsname * name) +{ + int ret = sys_newuname(name); + + if (current->personality == PER_LINUX32 && !ret) { + ret = copy_to_user(name->machine, "i386\0\0", 8); + } + return ret; +} + +extern asmlinkage ssize_t sys_pread(unsigned int fd, char * buf, + size_t count, loff_t pos); + +extern asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, + size_t count, loff_t pos); + +typedef __kernel_ssize_t32 ssize_t32; + +asmlinkage ssize_t32 +sys32_pread(unsigned int fd, char *ubuf, __kernel_size_t32 count, + u32 poshi, u32 poslo) +{ + return sys_pread(fd, ubuf, count, + ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + +asmlinkage ssize_t32 +sys32_pwrite(unsigned int fd, char *ubuf, __kernel_size_t32 count, + u32 poshi, u32 poslo) +{ + return sys_pwrite(fd, ubuf, count, + ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + + +extern asmlinkage long sys_personality(unsigned long); + +asmlinkage long +sys32_personality(unsigned long personality) +{ + int ret; + if (current->personality == PER_LINUX32 && personality == PER_LINUX) + personality = PER_LINUX32; + ret = sys_personality(personality); + if (ret == PER_LINUX32) + ret = PER_LINUX; + return ret; +} + +extern asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, + size_t count); + +asmlinkage long +sys32_sendfile(int out_fd, int in_fd, __kernel_off_t32 *offset, s32 count) +{ + mm_segment_t old_fs = get_fs(); + int ret; + off_t of; + + if (offset && get_user(of, offset)) + return -EFAULT; + + set_fs(KERNEL_DS); + ret = sys_sendfile(out_fd, in_fd, offset ? &of : NULL, count); + set_fs(old_fs); + + if (!ret && offset && put_user(of, offset)) + return -EFAULT; + + return ret; +} + +/* Handle adjtimex compatability. */ + +struct timex32 { + u32 modes; + s32 offset, freq, maxerror, esterror; + s32 status, constant, precision, tolerance; + struct timeval32 time; + s32 tick; + s32 ppsfreq, jitter, shift, stabil; + s32 jitcnt, calcnt, errcnt, stbcnt; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; +}; + +extern int do_adjtimex(struct timex *); + +asmlinkage long +sys32_adjtimex(struct timex32 *utp) +{ + struct timex txc; + int ret; + + memset(&txc, 0, sizeof(struct timex)); + + if(verify_area(VERIFY_READ, utp, sizeof(struct timex32)) || + __get_user(txc.modes, &utp->modes) || + __get_user(txc.offset, &utp->offset) || + __get_user(txc.freq, &utp->freq) || + __get_user(txc.maxerror, &utp->maxerror) || + __get_user(txc.esterror, &utp->esterror) || + __get_user(txc.status, &utp->status) || + __get_user(txc.constant, &utp->constant) || + __get_user(txc.precision, &utp->precision) || + __get_user(txc.tolerance, &utp->tolerance) || + __get_user(txc.time.tv_sec, &utp->time.tv_sec) || + __get_user(txc.time.tv_usec, &utp->time.tv_usec) || + __get_user(txc.tick, &utp->tick) || + __get_user(txc.ppsfreq, &utp->ppsfreq) || + __get_user(txc.jitter, &utp->jitter) || + __get_user(txc.shift, &utp->shift) || + __get_user(txc.stabil, &utp->stabil) || + __get_user(txc.jitcnt, &utp->jitcnt) || + __get_user(txc.calcnt, &utp->calcnt) || + __get_user(txc.errcnt, &utp->errcnt) || + __get_user(txc.stbcnt, &utp->stbcnt)) + return -EFAULT; + + ret = do_adjtimex(&txc); + + if(verify_area(VERIFY_WRITE, utp, sizeof(struct timex32)) || + __put_user(txc.modes, &utp->modes) || + __put_user(txc.offset, &utp->offset) || + __put_user(txc.freq, &utp->freq) || + __put_user(txc.maxerror, &utp->maxerror) || + __put_user(txc.esterror, &utp->esterror) || + __put_user(txc.status, &utp->status) || + __put_user(txc.constant, &utp->constant) || + __put_user(txc.precision, &utp->precision) || + __put_user(txc.tolerance, &utp->tolerance) || + __put_user(txc.time.tv_sec, &utp->time.tv_sec) || + __put_user(txc.time.tv_usec, &utp->time.tv_usec) || + __put_user(txc.tick, &utp->tick) || + __put_user(txc.ppsfreq, &utp->ppsfreq) || + __put_user(txc.jitter, &utp->jitter) || + __put_user(txc.shift, &utp->shift) || + __put_user(txc.stabil, &utp->stabil) || + __put_user(txc.jitcnt, &utp->jitcnt) || + __put_user(txc.calcnt, &utp->calcnt) || + __put_user(txc.errcnt, &utp->errcnt) || + __put_user(txc.stbcnt, &utp->stbcnt)) + ret = -EFAULT; + + return ret; +} + + +/* common code for old and new mmaps */ +static inline long do_mmap2( + unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + int error = -EBADF; + struct file * file = NULL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +asmlinkage long sys32_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + return do_mmap2(addr, len, prot, flags, fd, pgoff); +} + + +asmlinkage int sys32_olduname(struct oldold_utsname * name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + down_read(&uts_sem); + + error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); + __put_user(0,name->sysname+__OLD_UTS_LEN); + __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); + __put_user(0,name->nodename+__OLD_UTS_LEN); + __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); + __put_user(0,name->release+__OLD_UTS_LEN); + __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); + __put_user(0,name->version+__OLD_UTS_LEN); + { + char *arch = current->personality == PER_LINUX32 + ? "i386" : "x86_64"; + + __copy_to_user(&name->machine,arch,strlen(arch)+1); + } + + up_read(&uts_sem); + + error = error ? -EFAULT : 0; + + return error; +} + +int sys32_uname(struct old_utsname * name) +{ + int err; + if (!name) + return -EFAULT; + down_read(&uts_sem); + err=copy_to_user(name, &system_utsname, sizeof (*name)); + up_read(&uts_sem); + return err?-EFAULT:0; +} + +extern int sys_ustat(dev_t, struct ustat *); + +int sys32_ustat(dev_t dev, struct ustat32 *u32p) +{ + struct ustat u; + mm_segment_t seg; + int ret; + + seg = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ustat(dev,&u); + set_fs(seg); + if (ret >= 0) { + if (!access_ok(VERIFY_WRITE,u32p,sizeof(struct ustat32)) || + __put_user((__u32) u.f_tfree, &u32p->f_tfree) || + __put_user((__u32) u.f_tinode, &u32p->f_tfree) || + __copy_to_user(&u32p->f_fname, u.f_fname, sizeof(u.f_fname)) || + __copy_to_user(&u32p->f_fpack, u.f_fpack, sizeof(u.f_fpack))) + ret = -EFAULT; + } + return ret; +} + +static int nargs(u32 src, char **dst) +{ + int cnt; + u32 val; + + cnt = 0; + do { + int ret = get_user(val, (__u32 *)(u64)src); + if (ret) { + return ret; + } + if (dst) + dst[cnt] = (char *)(u64)val; + cnt++; + src += 4; + } while(val && cnt < 1023); // XXX: fix limit. + if (dst) + dst[cnt-1] = 0; + return cnt; +} + +int sys32_execve(char *name, u32 argv, u32 envp, struct pt_regs regs) +{ + mm_segment_t oldseg; + char **buf; + int na,ne; + int ret; + + na = nargs(argv, NULL); + if (na < 0) + return -EFAULT; + ne = nargs(envp, NULL); + if (ne < 0) + return -EFAULT; + + buf = kmalloc((na+ne)*sizeof(char*), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = nargs(argv, buf); + if (ret < 0) + goto free; + + ret = nargs(envp, buf + na); + if (ret < 0) + goto free; + + name = getname(name); + ret = PTR_ERR(name); + if (IS_ERR(name)) + goto free; + + oldseg = get_fs(); + set_fs(KERNEL_DS); + ret = do_execve(name, buf, buf+na, ®s); + set_fs(oldseg); + + if (ret == 0) + current->ptrace &= ~PT_DTRACE; + + putname(name); + +free: + kfree(buf); + return ret; +} + +asmlinkage int sys32_fork(struct pt_regs regs) +{ + return do_fork(SIGCHLD, regs.rsp, ®s, 0); +} + +asmlinkage int sys32_clone(unsigned int clone_flags, unsigned int newsp, struct pt_regs regs) +{ + if (!newsp) + newsp = regs.rsp; + return do_fork(clone_flags, newsp, ®s, 0); +} + +/* + * This is trivial, and on the face of it looks like it + * could equally well be done in user mode. + * + * Not so, for quite unobvious reasons - register pressure. + * In user mode vfork() cannot have a stack frame, and if + * done by calling the "clone()" system call directly, you + * do not have enough call-clobbered registers to hold all + * the information you need. + */ +asmlinkage int sys32_vfork(struct pt_regs regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.rsp, ®s, 0); +} + +/* + * Some system calls that need sign extended arguments. This could be done by a generic wrapper. + */ + +extern off_t sys_lseek (unsigned int fd, off_t offset, unsigned int origin); + +int sys32_lseek (unsigned int fd, int offset, unsigned int whence) +{ + return sys_lseek(fd, offset, whence); +} + +extern int sys_kill(pid_t pid, int sig); + +int sys32_kill(int pid, int sig) +{ + return sys_kill(pid, sig); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/Makefile linux.ac/arch/x86_64/kernel/Makefile --- linux.vanilla/arch/x86_64/kernel/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/Makefile Wed Oct 10 01:47:38 2001 @@ -0,0 +1,38 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.S.o: + $(CC) $(AFLAGS) -traditional -c $< -o $*.o + +all: kernel.o head.o head64.o init_task.o + +O_TARGET := kernel.o + + +export-objs := mtrr.o msr.o cpuid.o x8664_ksyms.o + +obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o \ + ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_x86_64.o \ + pci-dma.o x8664_ksyms.o i387.o syscall.o early_printk.o vsyscall.o \ + dmi_scan.o setup64.o bootflag.o + +ifdef CONFIG_PCI +obj-y += pci-x86_64.o +obj-y += pci-pc.o pci-irq.o +endif + +obj-$(CONFIG_MTRR) += mtrr.o +obj-$(CONFIG_X86_MSR) += msr.o +obj-$(CONFIG_X86_CPUID) += cpuid.o +obj-$(CONFIG_SMP) += smp.o smpboot.o trampoline.o +obj-$(CONFIG_X86_LOCAL_APIC) += mpparse.o apic.o nmi.o +obj-$(CONFIG_X86_IO_APIC) += io_apic.o + +include $(TOPDIR)/Rules.make + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/apic.c linux.ac/arch/x86_64/kernel/apic.c --- linux.vanilla/arch/x86_64/kernel/apic.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/apic.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,1107 @@ +/* + * Local APIC handling, local APIC timers + * + * (c) 1999, 2000 Ingo Molnar + * + * Fixes + * Maciej W. Rozycki : Bits for genuine 82489DX APICs; + * thanks to Eric Gilmore + * and Rolf G. Tews + * for testing these extensively. + * Maciej W. Rozycki : Various updates and fixes. + * Mikael Pettersson : Power Management for UP-APIC. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Using APIC to generate smp_local_timer_interrupt? */ +int using_apic_timer = 0; + +int prof_multiplier[NR_CPUS] = { 1, }; +int prof_old_multiplier[NR_CPUS] = { 1, }; +int prof_counter[NR_CPUS] = { 1, }; + +int get_maxlvt(void) +{ + unsigned int v, ver, maxlvt; + + v = apic_read(APIC_LVR); + ver = GET_APIC_VERSION(v); + /* 82489DXs do not report # of LVT entries. */ + maxlvt = APIC_INTEGRATED(ver) ? GET_APIC_MAXLVT(v) : 2; + return maxlvt; +} + +static void clear_local_APIC(void) +{ + int maxlvt; + unsigned long v; + + maxlvt = get_maxlvt(); + + /* + * Careful: we have to set masks only first to deassert + * any level-triggered sources. + */ + v = apic_read(APIC_LVTT); + apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED); + v = apic_read(APIC_LVT0); + apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED); + v = apic_read(APIC_LVT1); + apic_write_around(APIC_LVT1, v | APIC_LVT_MASKED); + if (maxlvt >= 3) { + v = apic_read(APIC_LVTERR); + apic_write_around(APIC_LVTERR, v | APIC_LVT_MASKED); + } + if (maxlvt >= 4) { + v = apic_read(APIC_LVTPC); + apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED); + } + + /* + * Clean APIC state for other OSs: + */ + apic_write_around(APIC_LVTT, APIC_LVT_MASKED); + apic_write_around(APIC_LVT0, APIC_LVT_MASKED); + apic_write_around(APIC_LVT1, APIC_LVT_MASKED); + if (maxlvt >= 3) + apic_write_around(APIC_LVTERR, APIC_LVT_MASKED); + if (maxlvt >= 4) + apic_write_around(APIC_LVTPC, APIC_LVT_MASKED); +} + +void __init connect_bsp_APIC(void) +{ + if (pic_mode) { + /* + * Do not trust the local APIC being empty at bootup. + */ + clear_local_APIC(); + /* + * PIC mode, enable APIC mode in the IMCR, i.e. + * connect BSP's local APIC to INT and NMI lines. + */ + printk("leaving PIC mode, enabling APIC mode.\n"); + outb(0x70, 0x22); + outb(0x01, 0x23); + } +} + +void disconnect_bsp_APIC(void) +{ + if (pic_mode) { + /* + * Put the board back into PIC mode (has an effect + * only on certain older boards). Note that APIC + * interrupts, including IPIs, won't work beyond + * this point! The only exception are INIT IPIs. + */ + printk("disabling APIC mode, entering PIC mode.\n"); + outb(0x70, 0x22); + outb(0x00, 0x23); + } +} + +void disable_local_APIC(void) +{ + unsigned long value; + + clear_local_APIC(); + + /* + * Disable APIC (implies clearing of registers + * for 82489DX!). + */ + value = apic_read(APIC_SPIV); + value &= ~APIC_SPIV_APIC_ENABLED; + apic_write_around(APIC_SPIV, value); +} + +/* + * This is to verify that we're looking at a real local APIC. + * Check these against your board if the CPUs aren't getting + * started for no apparent reason. + */ +int __init verify_local_APIC(void) +{ + unsigned int reg0, reg1; + + /* + * The version register is read-only in a real APIC. + */ + reg0 = apic_read(APIC_LVR); + Dprintk("Getting VERSION: %x\n", reg0); + apic_write(APIC_LVR, reg0 ^ APIC_LVR_MASK); + reg1 = apic_read(APIC_LVR); + Dprintk("Getting VERSION: %x\n", reg1); + + /* + * The two version reads above should print the same + * numbers. If the second one is different, then we + * poke at a non-APIC. + */ + if (reg1 != reg0) + return 0; + + /* + * Check if the version looks reasonably. + */ + reg1 = GET_APIC_VERSION(reg0); + if (reg1 == 0x00 || reg1 == 0xff) + return 0; + reg1 = get_maxlvt(); + if (reg1 < 0x02 || reg1 == 0xff) + return 0; + + /* + * The ID register is read/write in a real APIC. + */ + reg0 = apic_read(APIC_ID); + Dprintk("Getting ID: %x\n", reg0); + apic_write(APIC_ID, reg0 ^ APIC_ID_MASK); + reg1 = apic_read(APIC_ID); + Dprintk("Getting ID: %x\n", reg1); + apic_write(APIC_ID, reg0); + if (reg1 != (reg0 ^ APIC_ID_MASK)) + return 0; + + /* + * The next two are just to see if we have sane values. + * They're only really relevant if we're in Virtual Wire + * compatibility mode, but most boxes are anymore. + */ + reg0 = apic_read(APIC_LVT0); + Dprintk("Getting LVT0: %x\n", reg0); + reg1 = apic_read(APIC_LVT1); + Dprintk("Getting LVT1: %x\n", reg1); + + return 1; +} + +void __init sync_Arb_IDs(void) +{ + /* + * Wait for idle. + */ + apic_wait_icr_idle(); + + Dprintk("Synchronizing Arb IDs.\n"); + apic_write_around(APIC_ICR, APIC_DEST_ALLINC | APIC_INT_LEVELTRIG + | APIC_DM_INIT); +} + +extern void __error_in_apic_c (void); + +/* + * An initial setup of the virtual wire mode. + */ +void __init init_bsp_APIC(void) +{ + unsigned long value, ver; + + /* + * Don't do the setup now if we have a SMP BIOS as the + * through-I/O-APIC virtual wire mode might be active. + */ + if (smp_found_config || !cpu_has_apic) + return; + + value = apic_read(APIC_LVR); + ver = GET_APIC_VERSION(value); + + /* + * Do not trust the local APIC being empty at bootup. + */ + clear_local_APIC(); + + /* + * Enable APIC. + */ + value = apic_read(APIC_SPIV); + value &= ~APIC_VECTOR_MASK; + value |= APIC_SPIV_APIC_ENABLED; + value |= APIC_SPIV_FOCUS_DISABLED; + value |= SPURIOUS_APIC_VECTOR; + apic_write_around(APIC_SPIV, value); + + /* + * Set up the virtual wire mode. + */ + apic_write_around(APIC_LVT0, APIC_DM_EXTINT); + value = APIC_DM_NMI; + if (!APIC_INTEGRATED(ver)) /* 82489DX */ + value |= APIC_LVT_LEVEL_TRIGGER; + apic_write_around(APIC_LVT1, value); +} + +void __init setup_local_APIC (void) +{ + unsigned long value, ver, maxlvt; + + value = apic_read(APIC_LVR); + ver = GET_APIC_VERSION(value); + + if ((SPURIOUS_APIC_VECTOR & 0x0f) != 0x0f) + __error_in_apic_c(); + + /* + * Double-check wether this APIC is really registered. + */ + if (!test_bit(GET_APIC_ID(apic_read(APIC_ID)), &phys_cpu_present_map)) + BUG(); + + /* + * Intel recommends to set DFR, LDR and TPR before enabling + * an APIC. See e.g. "AP-388 82489DX User's Manual" (Intel + * document number 292116). So here it goes... + */ + + /* + * Put the APIC into flat delivery mode. + * Must be "all ones" explicitly for 82489DX. + */ + apic_write_around(APIC_DFR, 0xffffffff); + + /* + * Set up the logical destination ID. + */ + value = apic_read(APIC_LDR); + value &= ~APIC_LDR_MASK; + value |= (1<<(smp_processor_id()+24)); + apic_write_around(APIC_LDR, value); + + /* + * Set Task Priority to 'accept all'. We never change this + * later on. + */ + value = apic_read(APIC_TASKPRI); + value &= ~APIC_TPRI_MASK; + apic_write_around(APIC_TASKPRI, value); + + /* + * Now that we are all set up, enable the APIC + */ + value = apic_read(APIC_SPIV); + value &= ~APIC_VECTOR_MASK; + /* + * Enable APIC + */ + value |= APIC_SPIV_APIC_ENABLED; + + /* + * Some unknown Intel IO/APIC (or APIC) errata is biting us with + * certain networking cards. If high frequency interrupts are + * happening on a particular IOAPIC pin, plus the IOAPIC routing + * entry is masked/unmasked at a high rate as well then sooner or + * later IOAPIC line gets 'stuck', no more interrupts are received + * from the device. If focus CPU is disabled then the hang goes + * away, oh well :-( + * + * [ This bug can be reproduced easily with a level-triggered + * PCI Ne2000 networking cards and PII/PIII processors, dual + * BX chipset. ] + */ + /* + * Actually disabling the focus CPU check just makes the hang less + * frequent as it makes the interrupt distributon model be more + * like LRU than MRU (the short-term load is more even across CPUs). + * See also the comment in end_level_ioapic_irq(). --macro + */ +#if 1 + /* Enable focus processor (bit==0) */ + value &= ~APIC_SPIV_FOCUS_DISABLED; +#else + /* Disable focus processor (bit==1) */ + value |= APIC_SPIV_FOCUS_DISABLED; +#endif + /* + * Set spurious IRQ vector + */ + value |= SPURIOUS_APIC_VECTOR; + apic_write_around(APIC_SPIV, value); + + /* + * Set up LVT0, LVT1: + * + * set up through-local-APIC on the BP's LINT0. This is not + * strictly necessery in pure symmetric-IO mode, but sometimes + * we delegate interrupts to the 8259A. + */ + /* + * TODO: set up through-local-APIC from through-I/O-APIC? --macro + */ + value = apic_read(APIC_LVT0) & APIC_LVT_MASKED; + if (!smp_processor_id() && (pic_mode || !value)) { + value = APIC_DM_EXTINT; + printk("enabled ExtINT on CPU#%d\n", smp_processor_id()); + } else { + value = APIC_DM_EXTINT | APIC_LVT_MASKED; + printk("masked ExtINT on CPU#%d\n", smp_processor_id()); + } + apic_write_around(APIC_LVT0, value); + + /* + * only the BP should see the LINT1 NMI signal, obviously. + */ + if (!smp_processor_id()) + value = APIC_DM_NMI; + else + value = APIC_DM_NMI | APIC_LVT_MASKED; + if (!APIC_INTEGRATED(ver)) /* 82489DX */ + value |= APIC_LVT_LEVEL_TRIGGER; + apic_write_around(APIC_LVT1, value); + + if (APIC_INTEGRATED(ver)) { /* !82489DX */ + maxlvt = get_maxlvt(); + if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ + apic_write(APIC_ESR, 0); + value = apic_read(APIC_ESR); + printk("ESR value before enabling vector: %08lx\n", value); + + value = ERROR_APIC_VECTOR; // enables sending errors + apic_write_around(APIC_LVTERR, value); + /* + * spec says clear errors after enabling vector. + */ + if (maxlvt > 3) + apic_write(APIC_ESR, 0); + value = apic_read(APIC_ESR); + printk("ESR value after enabling vector: %08lx\n", value); + } else + printk("No ESR for 82489DX.\n"); + + if (nmi_watchdog == NMI_LOCAL_APIC) + setup_apic_nmi_watchdog(); +} + +#ifdef CONFIG_PM + +#include +#include + +static struct { + /* 'active' is true if the local APIC was enabled by us and + not the BIOS; this signifies that we are also responsible + for disabling it before entering apm/acpi suspend */ + int active; + /* 'perfctr_pmdev' is here because the current (2.4.1) PM + callback system doesn't handle hierarchical dependencies */ + struct pm_dev *perfctr_pmdev; + /* r/w apic fields */ + unsigned int apic_id; + unsigned int apic_taskpri; + unsigned int apic_ldr; + unsigned int apic_dfr; + unsigned int apic_spiv; + unsigned int apic_lvtt; + unsigned int apic_lvtpc; + unsigned int apic_lvt0; + unsigned int apic_lvt1; + unsigned int apic_lvterr; + unsigned int apic_tmict; + unsigned int apic_tdcr; +} apic_pm_state; + +static void apic_pm_suspend(void *data) +{ + unsigned int l, h; + unsigned long flags; + + if (apic_pm_state.perfctr_pmdev) + pm_send(apic_pm_state.perfctr_pmdev, PM_SUSPEND, data); + apic_pm_state.apic_id = apic_read(APIC_ID); + apic_pm_state.apic_taskpri = apic_read(APIC_TASKPRI); + apic_pm_state.apic_ldr = apic_read(APIC_LDR); + apic_pm_state.apic_dfr = apic_read(APIC_DFR); + apic_pm_state.apic_spiv = apic_read(APIC_SPIV); + apic_pm_state.apic_lvtt = apic_read(APIC_LVTT); + apic_pm_state.apic_lvtpc = apic_read(APIC_LVTPC); + apic_pm_state.apic_lvt0 = apic_read(APIC_LVT0); + apic_pm_state.apic_lvt1 = apic_read(APIC_LVT1); + apic_pm_state.apic_lvterr = apic_read(APIC_LVTERR); + apic_pm_state.apic_tmict = apic_read(APIC_TMICT); + apic_pm_state.apic_tdcr = apic_read(APIC_TDCR); + __save_flags(flags); + __cli(); + disable_local_APIC(); + rdmsr(MSR_IA32_APICBASE, l, h); + l &= ~MSR_IA32_APICBASE_ENABLE; + wrmsr(MSR_IA32_APICBASE, l, h); + __restore_flags(flags); +} + +static void apic_pm_resume(void *data) +{ + unsigned int l, h; + unsigned long flags; + + __save_flags(flags); + __cli(); + rdmsr(MSR_IA32_APICBASE, l, h); + l &= ~MSR_IA32_APICBASE_BASE; + l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE; + wrmsr(MSR_IA32_APICBASE, l, h); + apic_write(APIC_ID, apic_pm_state.apic_id); + apic_write(APIC_DFR, apic_pm_state.apic_dfr); + apic_write(APIC_LDR, apic_pm_state.apic_ldr); + apic_write(APIC_TASKPRI, apic_pm_state.apic_taskpri); + apic_write(APIC_SPIV, apic_pm_state.apic_spiv); + apic_write(APIC_LVT0, apic_pm_state.apic_lvt0); + apic_write(APIC_LVT1, apic_pm_state.apic_lvt1); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + apic_write(APIC_LVTERR, apic_pm_state.apic_lvterr); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + apic_write(APIC_LVTPC, apic_pm_state.apic_lvtpc); + apic_write(APIC_LVTT, apic_pm_state.apic_lvtt); + apic_write(APIC_TDCR, apic_pm_state.apic_tdcr); + apic_write(APIC_TMICT, apic_pm_state.apic_tmict); + __restore_flags(flags); + if (apic_pm_state.perfctr_pmdev) + pm_send(apic_pm_state.perfctr_pmdev, PM_RESUME, data); +} + +static int apic_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + switch (rqst) { + case PM_SUSPEND: + apic_pm_suspend(data); + break; + case PM_RESUME: + apic_pm_resume(data); + break; + } + return 0; +} + +/* perfctr driver should call this instead of pm_register() */ +struct pm_dev *apic_pm_register(pm_dev_t type, + unsigned long id, + pm_callback callback) +{ + struct pm_dev *dev; + + if (!apic_pm_state.active) + return pm_register(type, id, callback); + if (apic_pm_state.perfctr_pmdev) + return NULL; /* we're busy */ + dev = kmalloc(sizeof(struct pm_dev), GFP_KERNEL); + if (dev) { + memset(dev, 0, sizeof(*dev)); + dev->type = type; + dev->id = id; + dev->callback = callback; + apic_pm_state.perfctr_pmdev = dev; + } + return dev; +} + +/* perfctr driver should call this instead of pm_unregister() */ +void apic_pm_unregister(struct pm_dev *dev) +{ + if (!apic_pm_state.active) { + pm_unregister(dev); + } else if (dev == apic_pm_state.perfctr_pmdev) { + apic_pm_state.perfctr_pmdev = NULL; + kfree(dev); + } +} + +static void __init apic_pm_init1(void) +{ + /* can't pm_register() at this early stage in the boot process + (causes an immediate reboot), so just set the flag */ + apic_pm_state.active = 1; +} + +static void __init apic_pm_init2(void) +{ + if (apic_pm_state.active) + pm_register(PM_SYS_DEV, 0, apic_pm_callback); +} + +#else /* CONFIG_PM */ + +static inline void apic_pm_init1(void) { } +static inline void apic_pm_init2(void) { } + +#endif /* CONFIG_PM */ + +/* + * Detect and enable local APICs on non-SMP boards. + * Original code written by Keir Fraser. + */ + +static int __init detect_init_APIC (void) +{ + u32 h, l, features; + int needs_pm = 0; + extern void get_cpu_vendor(struct cpuinfo_x86*); + + /* Workaround for us being called before identify_cpu(). */ + get_cpu_vendor(&boot_cpu_data); + + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model > 1) + break; + goto no_apic; + case X86_VENDOR_INTEL: + if (boot_cpu_data.x86 == 6 || + (boot_cpu_data.x86 == 5 && cpu_has_apic)) + break; + goto no_apic; + default: + goto no_apic; + } + + if (!cpu_has_apic) { + /* + * Some BIOSes disable the local APIC in the + * APIC_BASE MSR. This can only be done in + * software for Intel P6 and AMD K7 (Model > 1). + */ + rdmsr(MSR_IA32_APICBASE, l, h); + if (!(l & MSR_IA32_APICBASE_ENABLE)) { + printk("Local APIC disabled by BIOS -- reenabling.\n"); + l &= ~MSR_IA32_APICBASE_BASE; + l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE; + wrmsr(MSR_IA32_APICBASE, l, h); + needs_pm = 1; + } + } + /* + * The APIC feature bit should now be enabled + * in `cpuid' + */ + features = cpuid_edx(1); + if (!(features & (1 << X86_FEATURE_APIC))) { + printk("Could not enable APIC!\n"); + return -1; + } + set_bit(X86_FEATURE_APIC, &boot_cpu_data.x86_capability); + mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; + boot_cpu_id = 0; + if (nmi_watchdog != NMI_NONE) + nmi_watchdog = NMI_LOCAL_APIC; + + printk("Found and enabled local APIC!\n"); + + if (needs_pm) + apic_pm_init1(); + + return 0; + +no_apic: + printk("No local APIC present or hardware disabled\n"); + return -1; +} + +void __init init_apic_mappings(void) +{ + unsigned long apic_phys; + + /* + * If no local APIC can be found then set up a fake all + * zeroes page to simulate the local APIC and another + * one for the IO-APIC. + */ + if (!smp_found_config && detect_init_APIC()) { + apic_phys = (unsigned long) alloc_bootmem_pages(PAGE_SIZE); + apic_phys = __pa(apic_phys); + } else + apic_phys = mp_lapic_addr; + + set_fixmap_nocache(FIX_APIC_BASE, apic_phys); + Dprintk("mapped APIC to %08lx (%08lx)\n", APIC_BASE, apic_phys); + + /* + * Fetch the APIC ID of the BSP in case we have a + * default configuration (or the MP table is broken). + */ + if (boot_cpu_id == -1U) + boot_cpu_id = GET_APIC_ID(apic_read(APIC_ID)); + +#ifdef CONFIG_X86_IO_APIC + { + unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0; + int i; + + for (i = 0; i < nr_ioapics; i++) { + if (smp_found_config) { + ioapic_phys = mp_ioapics[i].mpc_apicaddr; + } else { + ioapic_phys = (unsigned long) alloc_bootmem_pages(PAGE_SIZE); + ioapic_phys = __pa(ioapic_phys); + } + set_fixmap_nocache(idx, ioapic_phys); + Dprintk("mapped IOAPIC to %08lx (%08lx)\n", + __fix_to_virt(idx), ioapic_phys); + idx++; + } + } +#endif +} + +/* + * This part sets up the APIC 32 bit clock in LVTT1, with HZ interrupts + * per second. We assume that the caller has already set up the local + * APIC. + * + * The APIC timer is not exactly sync with the external timer chip, it + * closely follows bus clocks. + */ + +/* + * The timer chip is already set up at HZ interrupts per second here, + * but we do not accept timer interrupts yet. We only allow the BP + * to calibrate. + */ +static unsigned int __init get_8254_timer_count(void) +{ + extern spinlock_t i8253_lock; + unsigned long flags; + + unsigned int count; + + spin_lock_irqsave(&i8253_lock, flags); + + outb_p(0x00, 0x43); + count = inb_p(0x40); + count |= inb_p(0x40) << 8; + + spin_unlock_irqrestore(&i8253_lock, flags); + + return count; +} + +void __init wait_8254_wraparound(void) +{ + unsigned int curr_count, prev_count=~0; + int delta; + + curr_count = get_8254_timer_count(); + + do { + prev_count = curr_count; + curr_count = get_8254_timer_count(); + delta = curr_count-prev_count; + + /* + * This limit for delta seems arbitrary, but it isn't, it's + * slightly above the level of error a buggy Mercury/Neptune + * chipset timer can cause. + */ + + } while (delta < 300); +} + +/* + * This function sets up the local APIC timer, with a timeout of + * 'clocks' APIC bus clock. During calibration we actually call + * this function twice on the boot CPU, once with a bogus timeout + * value, second time for real. The other (noncalibrating) CPUs + * call this function only once, with the real, calibrated value. + * + * We do reads before writes even if unnecessary, to get around the + * P5 APIC double write bug. + */ + +#define APIC_DIVISOR 16 + +void __setup_APIC_LVTT(unsigned int clocks) +{ + unsigned int lvtt1_value, tmp_value; + + lvtt1_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) | + APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR; + apic_write_around(APIC_LVTT, lvtt1_value); + + /* + * Divide PICLK by 16 + */ + tmp_value = apic_read(APIC_TDCR); + apic_write_around(APIC_TDCR, (tmp_value + & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) + | APIC_TDR_DIV_16); + + apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR); +} + +void setup_APIC_timer(void * data) +{ + unsigned long clocks = (unsigned long) data, slice, t0, t1; + unsigned long flags; + int delta; + + __save_flags(flags); + __sti(); + /* + * ok, Intel has some smart code in their APIC that knows + * if a CPU was in 'hlt' lowpower mode, and this increases + * its APIC arbitration priority. To avoid the external timer + * IRQ APIC event being in synchron with the APIC clock we + * introduce an interrupt skew to spread out timer events. + * + * The number of slices within a 'big' timeslice is smp_num_cpus+1 + */ + + slice = clocks / (smp_num_cpus+1); + printk("cpu: %d, clocks: %d, slice: %ld\n", + smp_processor_id(), clocks, slice); + + /* + * Wait for IRQ0's slice: + */ + wait_8254_wraparound(); + + __setup_APIC_LVTT(clocks); + + t0 = apic_read(APIC_TMICT)*APIC_DIVISOR; + /* Wait till TMCCT gets reloaded from TMICT... */ + do { + t1 = apic_read(APIC_TMCCT)*APIC_DIVISOR; + delta = (int)(t0 - t1 - slice*(smp_processor_id()+1)); + } while (delta >= 0); + /* Now wait for our slice for real. */ + do { + t1 = apic_read(APIC_TMCCT)*APIC_DIVISOR; + delta = (int)(t0 - t1 - slice*(smp_processor_id()+1)); + } while (delta < 0); + + __setup_APIC_LVTT(clocks); + + printk("CPU%d\n", + smp_processor_id(), t0, t1, delta, slice, clocks); + + __restore_flags(flags); +} + +/* + * In this function we calibrate APIC bus clocks to the external + * timer. Unfortunately we cannot use jiffies and the timer irq + * to calibrate, since some later bootup code depends on getting + * the first irq? Ugh. + * + * We want to do the calibration only once since we + * want to have local timer irqs syncron. CPUs connected + * by the same APIC bus have the very same bus frequency. + * And we want to have irqs off anyways, no accidental + * APIC irq that way. + */ + +int __init calibrate_APIC_clock(void) +{ + unsigned long long t1 = 0, t2 = 0; + long tt1, tt2; + long result; + int i; + const int LOOPS = HZ/10; + + printk("calibrating APIC timer ...\n"); + + /* + * Put whatever arbitrary (but long enough) timeout + * value into the APIC clock, we just want to get the + * counter running for calibration. + */ + __setup_APIC_LVTT(1000000000); + + /* + * The timer chip counts down to zero. Let's wait + * for a wraparound to start exact measurement: + * (the current tick might have been already half done) + */ + + wait_8254_wraparound(); + + /* + * We wrapped around just now. Let's start: + */ + if (cpu_has_tsc) + rdtscll(t1); + tt1 = apic_read(APIC_TMCCT); + + /* + * Let's wait LOOPS wraprounds: + */ + for (i = 0; i < LOOPS; i++) + wait_8254_wraparound(); + + tt2 = apic_read(APIC_TMCCT); + if (cpu_has_tsc) + rdtscll(t2); + + /* + * The APIC bus clock counter is 32 bits only, it + * might have overflown, but note that we use signed + * longs, thus no extra care needed. + * + * underflown to be exact, as the timer counts down ;) + */ + + result = (tt1-tt2)*APIC_DIVISOR/LOOPS; + + if (cpu_has_tsc) + printk("..... CPU clock speed is %ld.%04ld MHz.\n", + ((long)(t2-t1)/LOOPS)/(1000000/HZ), + ((long)(t2-t1)/LOOPS)%(1000000/HZ)); + + printk("..... host bus clock speed is %ld.%04ld MHz.\n", + result/(1000000/HZ), + result%(1000000/HZ)); + + return result; +} + +static unsigned long calibration_result; + +void __init setup_APIC_clocks (void) +{ + printk("Using local APIC timer interrupts.\n"); + using_apic_timer = 1; + + __cli(); + + calibration_result = calibrate_APIC_clock(); + /* + * Now set up the timer for real. + */ + setup_APIC_timer((void *)calibration_result); + + __sti(); + + /* and update all other cpus */ + smp_call_function(setup_APIC_timer, (void *)calibration_result, 1, 1); +} + +/* + * the frequency of the profiling timer can be changed + * by writing a multiplier value into /proc/profile. + */ +int setup_profiling_timer(unsigned int multiplier) +{ + int i; + + /* + * Sanity check. [at least 500 APIC cycles should be + * between APIC interrupts as a rule of thumb, to avoid + * irqs flooding us] + */ + if ( (!multiplier) || (calibration_result/multiplier < 500)) + return -EINVAL; + + /* + * Set the new multiplier for each CPU. CPUs don't start using the + * new values until the next timer interrupt in which they do process + * accounting. At that time they also adjust their APIC timers + * accordingly. + */ + for (i = 0; i < NR_CPUS; ++i) + prof_multiplier[i] = multiplier; + + return 0; +} + +#undef APIC_DIVISOR + +/* + * Local timer interrupt handler. It does both profiling and + * process statistics/rescheduling. + * + * We do profiling in every local tick, statistics/rescheduling + * happen only every 'profiling multiplier' ticks. The default + * multiplier is 1 and it can be changed by writing the new multiplier + * value into /proc/profile. + */ + +inline void smp_local_timer_interrupt(struct pt_regs * regs) +{ + int user = user_mode(regs); + int cpu = smp_processor_id(); + + /* + * The profiling function is SMP safe. (nothing can mess + * around with "current", and the profiling counters are + * updated with atomic operations). This is especially + * useful with a profiling multiplier != 1 + */ + if (!user) + x86_do_profile(regs->rip); + + if (--prof_counter[cpu] <= 0) { + /* + * The multiplier may have changed since the last time we got + * to this point as a result of the user writing to + * /proc/profile. In this case we need to adjust the APIC + * timer accordingly. + * + * Interrupts are already masked off at this point. + */ + prof_counter[cpu] = prof_multiplier[cpu]; + if (prof_counter[cpu] != prof_old_multiplier[cpu]) { + __setup_APIC_LVTT(calibration_result/prof_counter[cpu]); + prof_old_multiplier[cpu] = prof_counter[cpu]; + } + +#ifdef CONFIG_SMP + update_process_times(user); +#endif + } + + /* + * We take the 'long' return path, and there every subsystem + * grabs the apropriate locks (kernel lock/ irq lock). + * + * we might want to decouple profiling from the 'long path', + * and do the profiling totally in assembly. + * + * Currently this isn't too much of an issue (performance wise), + * we can take more than 100K local irqs per second on a 100 MHz P5. + */ +} + +/* + * Local APIC timer interrupt. This is the most natural way for doing + * local interrupts, but local timer interrupts can be emulated by + * broadcast interrupts too. [in case the hw doesnt support APIC timers] + * + * [ if a single-CPU system runs an SMP kernel then we call the local + * interrupt as well. Thus we cannot inline the local irq ... ] + */ +unsigned int apic_timer_irqs [NR_CPUS]; + +void smp_apic_timer_interrupt(struct pt_regs * regs) +{ + int cpu = smp_processor_id(); + + /* + * the NMI deadlock-detector uses this. + */ + apic_timer_irqs[cpu]++; + + /* + * NOTE! We'd better ACK the irq immediately, + * because timer handling can be slow. + */ + ack_APIC_irq(); + /* + * update_process_times() expects us to have done irq_enter(). + * Besides, if we don't timer interrupts ignore the global + * interrupt lock, which is the WrongThing (tm) to do. + */ + irq_enter(cpu, 0); + smp_local_timer_interrupt(regs); + irq_exit(cpu, 0); + + if (softirq_pending(cpu)) + do_softirq(); +} + +/* + * This interrupt should _never_ happen with our APIC/SMP architecture + */ +asmlinkage void smp_spurious_interrupt(void) +{ + unsigned long v; + + /* + * Check if this really is a spurious interrupt and ACK it + * if it is a vectored one. Just in case... + * Spurious interrupts should not be ACKed. + */ + v = apic_read(APIC_ISR + ((SPURIOUS_APIC_VECTOR & ~0x1f) >> 1)); + if (v & (1 << (SPURIOUS_APIC_VECTOR & 0x1f))) + ack_APIC_irq(); + + /* see sw-dev-man vol 3, chapter 7.4.13.5 */ + printk(KERN_INFO "spurious APIC interrupt on CPU#%d, should never happen.\n", + smp_processor_id()); +} + +/* + * This interrupt should never happen with our APIC/SMP architecture + */ + +asmlinkage void smp_error_interrupt(void) +{ + unsigned long v, v1; + + /* First tickle the hardware, only then report what went on. -- REW */ + v = apic_read(APIC_ESR); + apic_write(APIC_ESR, 0); + v1 = apic_read(APIC_ESR); + ack_APIC_irq(); + atomic_inc(&irq_err_count); + + /* Here is what the APIC error bits mean: + 0: Send CS error + 1: Receive CS error + 2: Send accept error + 3: Receive accept error + 4: Reserved + 5: Send illegal vector + 6: Received illegal vector + 7: Illegal register address + */ + printk (KERN_ERR "APIC error on CPU%d: %02lx(%02lx)\n", + smp_processor_id(), v , v1); +} + +/* + * This initializes the IO-APIC and APIC hardware if this is + * a UP kernel. + */ +int __init APIC_init_uniprocessor (void) +{ + if (!smp_found_config && !cpu_has_apic) + return -1; + + /* + * Complain if the BIOS pretends there is one. + */ + if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_id])) { + printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n", + boot_cpu_id); + return -1; + } + + verify_local_APIC(); + + connect_bsp_APIC(); + + phys_cpu_present_map = 1; + apic_write_around(APIC_ID, boot_cpu_id); + + apic_pm_init2(); + + setup_local_APIC(); + + if (nmi_watchdog == NMI_LOCAL_APIC) + check_nmi_watchdog(); +#ifdef CONFIG_X86_IO_APIC + if (smp_found_config) + if (!skip_ioapic_setup && nr_ioapics) + setup_IO_APIC(); +#endif + setup_APIC_clocks(); + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/bootflag.c linux.ac/arch/x86_64/kernel/bootflag.c --- linux.vanilla/arch/x86_64/kernel/bootflag.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/bootflag.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,8 @@ +/* + * The submitted file matched the 386 one and this is 16bit code + * so I've marked it this way so we have one copy to debug only + * + * Alan + */ + +#include <../arch/i386/kernel/bootflag.c> diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/cpuid.c linux.ac/arch/x86_64/kernel/cpuid.c --- linux.vanilla/arch/x86_64/kernel/cpuid.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/cpuid.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,8 @@ +/* + * The submitted file matched the 386 one and this is 16bit code + * so I've marked it this way so we have one copy to debug only + * + * Alan + */ + +#include <../arch/i386/kernel/cpuid.c> diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/dmi_scan.c linux.ac/arch/x86_64/kernel/dmi_scan.c --- linux.vanilla/arch/x86_64/kernel/dmi_scan.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/dmi_scan.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,8 @@ +/* + * The submitted file matched the 386 one and this is 16bit code + * so I've marked it this way so we have one copy to debug only + * + * Alan + */ + +#include <../arch/i386/kernel/dmi_scan.c> diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/early_printk.c linux.ac/arch/x86_64/kernel/early_printk.c --- linux.vanilla/arch/x86_64/kernel/early_printk.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/early_printk.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,105 @@ +#include + + +#ifndef MINIKERNEL +#define VGABASE 0xffffffff800b8000ul /* This is "wrong" address to access it, we should access it using 0xffff8000000b8000ul; but 0xffff8000000b8000ul is not available early at boot. */ +#else +#define VGABASE 0xb8000ul +#endif + +#define MAX_YPOS 25 +#define MAX_XPOS 80 + +static int current_ypos = 1, current_xpos = 0; /* We want to print before clearing BSS */ + +#ifndef MINIKERNEL +void +early_clear (void) +{ + int k, i; + for(k = 0; k < MAX_YPOS; k++) + for(i = 0; i < MAX_XPOS; i++) + writew(0, VGABASE + 2*(MAX_XPOS*k + i)); + current_ypos = 0; +} + +void +early_puts (const char *str) +{ + char c; + int i, k, j; + + while ((c = *str++) != '\0') { + if (current_ypos >= MAX_YPOS) { +#if 1 + /* scroll 1 line up */ + for(k = 1, j = 0; k < MAX_YPOS; k++, j++) { + for(i = 0; i < MAX_XPOS; i++) { + writew(readw(VGABASE + 2*(MAX_XPOS*k + i)), + VGABASE + 2*(MAX_XPOS*j + i)); + } + } + for(i = 0; i < MAX_XPOS; i++) { + writew(0x720, VGABASE + 2*(MAX_XPOS*j + i)); + } + current_ypos = MAX_YPOS-1; +#else + /* MUCH faster */ + early_clear(); + current_ypos = 0; +#endif + } + if (c == '\n') { + current_xpos = 0; + current_ypos++; + } else if (c != '\r') { + writew(((0x7 << 8) | (unsigned short) c), + VGABASE + 2*(MAX_XPOS*current_ypos + current_xpos++)); + if (current_xpos >= MAX_XPOS) { + current_xpos = 0; + current_ypos++; + } + } + } +} +#else +#define putc(c, x) writew(((0x7 << 8) | (unsigned short) (c)), \ + VGABASE + (x) * 2); + +void +early_puts (const char *str) +{ + char c; + int i, rest; + + while ((c = *str++) != '\0') { + if (c == '\n' || c == '\r') { + /* blank rest of the line */ + rest = MAX_XPOS - (current_xpos % MAX_XPOS); + for (i=0; i < rest; i++) { + putc(' ', current_xpos++); + } + } else { + putc(c, current_xpos); + current_xpos++; + } + current_xpos %= MAX_XPOS * MAX_YPOS; + } +} +#endif /* MINIKERNEL */ + +static char buf[1024]; + +int early_printk(const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vsprintf(buf, fmt, args); /* hopefully i < sizeof(buf)-4 */ + va_end(args); + + early_puts(buf); + + return i; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/entry.S linux.ac/arch/x86_64/kernel/entry.S --- linux.vanilla/arch/x86_64/kernel/entry.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/entry.S Wed Oct 10 01:47:38 2001 @@ -0,0 +1,579 @@ +/* + * linux/arch/x86_64/entry.S + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 2000, 2001 Andi Kleen SuSE Labs + * Copyright (C) 2000 Pavel Machek + * + * $Id: entry.S,v 1.65 2001/07/06 14:34:22 ak Exp $ + */ + +/* + * entry.S contains the system-call and fault low-level handling routines. + * + * NOTE: This code handles signal-recognition, which happens every time + * after an interrupt and after each system call. + * + * Normal syscalls and interrupts don't save a full stack frame, this is + * only done for PT_TRACESYS, signals or fork/exec et.al. + * + * TODO: + * - schedule it carefully for the final hardware. + * + */ + +#define ASSEMBLY 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RIP_SYMBOL_NAME(x) x(%rip) + + .code64 + +#define PDAREF(field) %gs:field + +/* + * C code is not supposed to know about partial frames. Everytime a C function + * that looks at the pt_regs is called these two macros are executed around it. + * RESTORE_TOP_OF_STACK syncs the syscall state after any possible ptregs + * manipulation. + */ + + /* %rsp:at FRAMEEND */ + .macro FIXUP_TOP_OF_STACK tmp + movq PDAREF(pda_oldrsp),\tmp + movq \tmp,RSP(%rsp) + movq $__USER_DS,SS(%rsp) + movq $__USER_CS,CS(%rsp) + movq RCX(%rsp),\tmp /* get return address */ + movq \tmp,RIP(%rsp) + movq R11(%rsp),\tmp /* get eflags */ + movq \tmp,EFLAGS(%rsp) + .endm + + .macro RESTORE_TOP_OF_STACK tmp,offset=0 + movq RSP-\offset(%rsp),\tmp + movq \tmp,PDAREF(pda_oldrsp) + movq RIP-\offset(%rsp),\tmp + movq \tmp,RCX-\offset(%rsp) + movq EFLAGS-\offset(%rsp),\tmp + movq \tmp,R11-\offset(%rsp) + .endm + + +/* + * A newly forked process directly context switches into this. + */ +ENTRY(ret_from_fork) + movq %rbx, %rdi + call schedule_tail + GET_CURRENT(%rcx) + testb $PT_TRACESYS,tsk_ptrace(%rcx) + jnz 2f +1: + RESTORE_REST + cmpq $__KERNEL_CS,CS-ARGOFFSET(%rsp) # from kernel_thread? + je int_ret_from_sys_call + testl $ASM_THREAD_IA32,tsk_thread+thread_flags(%rcx) + jnz int_ret_from_sys_call + RESTORE_TOP_OF_STACK %rdi,ARGOFFSET + jmp ret_from_sys_call +2: + call syscall_trace + jmp 1b + +/* + * System call entry. Upto 6 arguments in registers are supported. + * + * SYSCALL does not save anything on the stack and does not change the + * stack pointer. Get the per CPU area from the hidden GS MSR and finds the + * current kernel stack. + */ + +/* + * Register setup: + * rax system call number + * rdi arg0 + * rcx return address for syscall/sysret, C arg3 + * rsi arg1 + * rdx arg2 + * r10 arg4 (--> moved to rcx for C, serves as TOS flag afterwards) + * r8 arg5 + * r9 arg6 + * r11 eflags for syscall/sysret, temporary for C + * r12-r15,rbp,rbx saved by C code, not touched. + * + * Interrupts are off on entry. + * Only called from user space. + */ + +ENTRY(system_call) + swapgs + movq %rsp,PDAREF(pda_oldrsp) + movq PDAREF(pda_kernelstack),%rsp + pushq %rax + sti + SAVE_ARGS + cmpq $__NR_syscall_max,%rax + ja badsys + GET_CURRENT(%rcx) + testl $PT_TRACESYS,tsk_ptrace(%rcx) + jne tracesys + movq %r10,%rcx + call *sys_call_table(,%rax,8) # XXX: rip relative + movq %rax,RAX-ARGOFFSET(%rsp) +ENTRY(ret_from_sys_call) +sysret_with_reschedule: + GET_CURRENT(%rcx) + cli + cmpq $0,tsk_need_resched(%rcx) + jne sysret_reschedule + cmpl $0,tsk_sigpending(%rcx) + jne sysret_signal +sysret_restore_args: + RESTORE_ARGS + movq PDAREF(pda_oldrsp),%rsp + swapgs + SYSRET64 + +sysret_signal: + sti + SAVE_REST + FIXUP_TOP_OF_STACK %rax + xorq %rsi,%rsi # oldset + movq %rsp,%rdi # &ptregs + call do_signal + RESTORE_TOP_OF_STACK %rax + RESTORE_REST + jmp sysret_with_reschedule + +sysret_reschedule: + call schedule + jmp sysret_with_reschedule + +tracesys: + SAVE_REST + movq $-ENOSYS,RAX(%rsp) + FIXUP_TOP_OF_STACK %rdi + call syscall_trace + LOAD_ARGS ARGOFFSET /* reload args from stack in case ptrace changed it */ + RESTORE_REST + cmpq $__NR_syscall_max,%rax + ja 1f + movq %r10,%rcx /* fixup for C */ + movl $1,%r10d /* set TOS flag */ + call *sys_call_table(,%rax,8) + movq %rax,RAX-ARGOFFSET(%rsp) +1: SAVE_REST + call syscall_trace + RESTORE_TOP_OF_STACK %rbx + RESTORE_REST + jmp ret_from_sys_call + +badsys: + movq $-ENOSYS,%rax + cli + jmp sysret_restore_args + +ENTRY(int_ret_from_sys_call) +intret_test_kernel: + cmpq $__KERNEL_CS,CS-ARGOFFSET(%rsp) + je int_restore_args +intret_with_reschedule: + GET_CURRENT(%rcx) + cli + cmpq $0,tsk_need_resched(%rcx) + jne int_reschedule + cmpl $0,tsk_sigpending(%rcx) + jne int_signal_return + swapgs +int_restore_args: + RESTORE_ARGS + addq $8,%rsp /* Remove oldrax */ + iretq + +int_reschedule: + sti + call schedule + jmp intret_with_reschedule + +int_signal_return: + sti + SAVE_REST + xorq %rsi,%rsi # oldset -> arg2 + movq %rsp,%rdi # &ptregs -> arg1 + call do_signal + RESTORE_REST + jmp intret_with_reschedule + +/* + * Certain special system calls that need to save a complete stack frame. + */ + + .macro PTREGSCALL label,func + .globl \label +\label: + leaq \func(%rip),%rax + jmp ptregscall_common + .endm + + PTREGSCALL stub_clone, sys_clone + PTREGSCALL stub_fork, sys_fork + PTREGSCALL stub_vfork, sys_vfork + PTREGSCALL stub_rt_sigsuspend, sys_rt_sigsuspend + PTREGSCALL stub_sigaltstack, sys_sigaltstack + PTREGSCALL stub_iopl, sys_iopl + +ENTRY(ptregscall_common) + popq %r11 + SAVE_REST + movq %r11, %r15 + FIXUP_TOP_OF_STACK %r11 + call *%rax + RESTORE_TOP_OF_STACK %r11 + movq %r15, %r11 + RESTORE_REST + pushq %r11 + ret + +ENTRY(stub_execve) + popq %r11 + SAVE_REST + movq %r11, %r15 + FIXUP_TOP_OF_STACK %r11 + call sys_execve + GET_CURRENT(%rcx) + testl $ASM_THREAD_IA32,tsk_thread+thread_flags(%rcx) + jnz exec_32bit + RESTORE_TOP_OF_STACK %r11 + movq %r15, %r11 + RESTORE_REST + push %r11 + ret + +exec_32bit: + movq %rax,RAX(%rsp) + RESTORE_REST + jmp int_ret_from_sys_call + +/* + * sigreturn is special because it needs to restore all registers on return. + * This cannot be done with SYSRET, so use the IRET return path instead. + */ +ENTRY(stub_rt_sigreturn) + addq $8, %rsp + SAVE_REST + FIXUP_TOP_OF_STACK %r11 + call sys_rt_sigreturn + movq %rax,RAX(%rsp) # fixme, this could be done at the higher layer + RESTORE_REST + jmp int_ret_from_sys_call + +/* + * Interrupt entry/exit. + * + * Interrupt entry points save only callee clobbered registers, except + * for signals again. + * + * Entry runs with interrupts off. + */ + +/* 0(%rsp): interrupt number */ +ENTRY(common_interrupt) + cmpq $__KERNEL_CS,16(%rsp) + je 1f + swapgs +1: cld + SAVE_ARGS + leaq -ARGOFFSET(%rsp),%rdi # arg1 for handler + addl $1,PDAREF(pda_irqcount) # XXX: should be merged with irq.c irqcount + movq PDAREF(pda_irqstackptr),%rax + cmoveq %rax,%rsp + pushq %rdi # save old stack + call do_IRQ + /* 0(%rsp): oldrsp-ARGOFFSET */ +ENTRY(ret_from_intr) + cli + popq %rdi + subl $1,PDAREF(pda_irqcount) + leaq ARGOFFSET(%rdi),%rsp + cmpq $__KERNEL_CS,CS(%rdi) + je retint_restore_args + /* Interrupt came from user space */ +retint_with_reschedule: + GET_CURRENT(%rcx) + cmpq $0,tsk_need_resched(%rcx) + jne retint_reschedule + cmpl $0,tsk_sigpending(%rcx) + jne retint_signal + swapgs +retint_restore_args: + RESTORE_ARGS + addq $8,%rsp + iretq + +retint_signal: + sti + SAVE_REST + movq $-1,ORIG_RAX(%rsp) + xorq %rsi,%rsi # oldset + movq %rsp,%rdi # &pt_regs + call do_signal + RESTORE_REST + cli + jmp retint_with_reschedule + +retint_reschedule: + /* schedule does sti */ + call schedule + cli + jmp retint_with_reschedule + +/* + * Exception entry points. + */ + .macro zeroentry sym + pushq $0 /* push error code/oldrax */ + pushq %rax /* push real oldrax to the rdi slot */ + leaq RIP_SYMBOL_NAME(\sym),%rax + jmp error_entry + .endm + + .macro errorentry sym + pushq %rax + leaq RIP_SYMBOL_NAME(\sym),%rax + jmp error_entry + .endm + +/* + * Exception entry point. This expects an error code/orig_rax on the stack + * and the exception handler in %rax. + */ + ALIGN +error_entry: + cmpq $__KERNEL_CS,24(%rsp) + je error_kernelspace + swapgs +error_kernelspace: + sti + /* rdi slot contains rax, oldrax contains error code */ + pushq %rsi + movq 8(%rsp),%rsi /* load rax */ + pushq %rdx + pushq %rcx + pushq %rsi /* store rax */ + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + cld + SAVE_REST + movq %rdi,RDI(%rsp) + movq %rsp,%rdi + movq ORIG_RAX(%rsp),%rsi /* get error code */ + movq $-1,ORIG_RAX(%rsp) + call *%rax +error_exit: +error_after_softirq: + cmpq $__KERNEL_CS,CS(%rsp) + je error_kernel_exit +error_test: + cli + GET_CURRENT(%rcx) + cmpq $0,tsk_need_resched(%rcx) + jne error_reschedule + cmpl $0,tsk_sigpending(%rcx) + jne error_signal + swapgs +error_kernel_exit: + RESTORE_ALL + addq $8,%rsp + iretq + +error_reschedule: + call schedule + jmp error_test + +error_signal: + sti + xorq %rsi,%rsi + movq %rsp,%rdi + call do_signal + jmp error_test + +/* + * Create a kernel thread. + * + * C extern interface: + * extern long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) + * + * asm input arguments: + * rdi: fn, rsi: arg, rdx: flags + */ +ENTRY(kernel_thread) + FAKE_STACK_FRAME $child_rip + SAVE_ALL + + # rdi: flags, rsi: usp, rdx: will be &pt_regs + movq %rdx,%rdi + orq $CLONE_VM, %rdi + + movq $-1, %rsi + + movq %rsp, %rdx + + # clone now + call do_fork + # save retval on the stack so it's popped before `ret` + movq %rax, RAX(%rsp) + + /* + * It isn't worth to check for reschedule here, + * so internally to the x86_64 port you can rely on kernel_thread() + * not to reschedule the child before returning, this avoids the need + * of hacks for example to fork off the per-CPU idle tasks. + * [Hopefully no generic code relies on the reschedule -AK] + */ + RESTORE_ALL + UNFAKE_STACK_FRAME + ret + +child_rip: + /* + * Here we are in the child and the registers are set as they were + * at kernel_thread() invocation in the parent. + */ + movq %rdi, %rax + movq %rsi, %rdi + call *%rax + # exit + xorq %rdi, %rdi + call do_exit + +/* + * execve(). This function needs to use IRET, not SYSRET, to set up all state properly. + * + * C extern interface: + * extern long execve(char *name, char **argv, char **envp) + * + * asm input arguments: + * rdi: name, rsi: argv, rdx: envp + * + * We want to fallback into: + * extern long sys_execve(char *name, char **argv,char **envp, struct pt_regs regs) + * + * do_sys_execve asm fallback arguments: + * rdi: name, rsi: argv, rdx: envp, fake frame on the stack + */ +ENTRY(execve) + FAKE_STACK_FRAME $0 + SAVE_ALL + call sys_execve + movq %rax, RAX(%rsp) + RESTORE_REST + testq %rax,%rax + je int_ret_from_sys_call + RESTORE_ARGS + UNFAKE_STACK_FRAME + ret + +ENTRY(page_fault) + errorentry do_page_fault + +ENTRY(coprocessor_error) + zeroentry do_coprocessor_error + +ENTRY(simd_coprocessor_error) + zeroentry do_simd_coprocessor_error + +ENTRY(device_not_available) + cmpq $0,(%rsp) + jl 1f + swapgs +1: pushq $-1 + SAVE_ALL + movq %cr0,%rax + leaq math_state_restore(%rip),%rcx + leaq math_emulate(%rip),%rbx + testl $0x4,%eax + cmoveq %rcx,%rbx + call *%rbx + jmp error_exit + +ENTRY(debug) + zeroentry do_debug + + /* XXX checkme */ +ENTRY(nmi) + cmpq $0,(%rsp) + jl 1f + swapgs +1: pushq $-1 + SAVE_ALL + movq %rsp,%rdi + call do_nmi + RESTORE_ALL + addq $8,%rsp + cmpq $0,(%rsp) + jl 2f + swapgs +2: iretq + +ENTRY(int3) + zeroentry do_int3 + +ENTRY(overflow) + zeroentry do_overflow + +ENTRY(bounds) + zeroentry do_bounds + +ENTRY(invalid_op) + zeroentry do_invalid_op + +ENTRY(coprocessor_segment_overrun) + zeroentry do_coprocessor_segment_overrun + +ENTRY(reserved) + zeroentry do_reserved + +ENTRY(double_fault) + errorentry do_double_fault + +ENTRY(invalid_TSS) + errorentry do_invalid_TSS + +ENTRY(segment_not_present) + errorentry do_segment_not_present + +ENTRY(stack_segment) + errorentry do_stack_segment + +ENTRY(general_protection) + errorentry do_general_protection + +ENTRY(alignment_check) + errorentry do_alignment_check + +ENTRY(divide_error) + errorentry do_divide_error + +ENTRY(spurious_interrupt_bug) + zeroentry do_spurious_interrupt_bug + +ENTRY(__bad_intr) + pushq $-1 + SAVE_ALL + call bad_intr + RESTORE_ALL + addq $8,%rsp + iretq diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/head.S linux.ac/arch/x86_64/kernel/head.S --- linux.vanilla/arch/x86_64/kernel/head.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/head.S Wed Oct 10 01:47:38 2001 @@ -0,0 +1,337 @@ +/* + * linux/arch/x86_64/kernel/head.S -- start in 32bit and switch to 64bit + * + * Copyright (C) 2000 Andrea Arcangeli SuSE + * Copyright (C) 2000 Pavel Machek + * Copyright (C) 2000 Karsten Keil + * Copyright (C) 2001 Andi Kleen + * + * $Id: head.S,v 1.41 2001/07/05 23:43:45 ak Exp $ + */ +.code64 +.text + +#include +#include +#include +#include +#include + +/* we don't able to switch in one step to final KERNEL ADRESS SPACE + * because we need identity-mapped pages on setup so define __START_KERNEL to + * 0x100000 for this stage + * + */ + + +startup_32: +.code32 + /* + * At this point the CPU runs in 32bit protected mode (CS.D = 1) with + * paging disabled and the point of this file is to switch to 64bit + * long mode with a kernel mapping for kerneland to jump into the + * kernel virtual addresses. + * There is no stack until we set one up. + */ + + /* As first check if extended functions are implemented */ + movl $0x80000000, %eax + cpuid + cmpl $0x80000000, %eax + jbe no_long_mode + /* Check if long mode is implemented */ + mov $0x80000001, %eax + cpuid + btl $29, %edx + jnc no_long_mode + + /* + * Prepare for entering 64bits mode + */ + + /* Enable PAE mode and PGE */ + xorl %eax, %eax + btsl $5, %eax + btsl $7, %eax + movl %eax, %cr4 + + /* Setup early boot stage 4 level pagetables */ + movl $0x101000, %eax + movl %eax, %cr3 + + /* Setup EFER (Extended Feature Enable Register) */ + movl $0xc0000080, %ecx + rdmsr + /* Fool rdmsr and reset %eax to avoid dependences */ + xorl %eax, %eax + /* Enable Long Mode */ + btsl $8, %eax + /* Enable System Call */ + btsl $0, %eax + /* Make changes effective */ + wrmsr + + xorl %eax, %eax + /* Enable paging and in turn activate Long Mode */ + btsl $31, %eax + /* Enable protected mode */ + btsl $0, %eax + /* Enable MP */ + btsl $1, %eax + /* Enable ET */ + btsl $4, %eax + /* Enable NE */ + btsl $5, %eax + /* Enable WP */ + btsl $16, %eax + /* Enable AM */ + btsl $18, %eax + /* Make changes effective */ + movl %eax, %cr0 + jmp reach_compatibility_mode +reach_compatibility_mode: + + /* + * At this point we're in long mode but in 32bit compatibility mode + * with EFER.LME = 1, CS.L = 0, CS.D = 1 (and in turn + * EFER.LMA = 1). Now we want to jump in 64bit mode, to do that we load + * the new gdt/idt that has __KERNEL_CS with CS.L = 1. + */ + + /* Load new GDT with the 64bit segment using 32bit descriptor */ + /* to avoid 32bit relocations we use fixed adresses here */ + movl $0x100F00, %eax + lgdt (%eax) + movl $0x100F10, %eax + /* Finally jump in 64bit mode */ + ljmp *(%eax) + +.code64 +reach_long64: + /* + * Where we're running at 0x0000000000100000, and yes, finally + * in 64bit mode. + */ + .globl init_rsp + + /* Setup the first kernel stack */ + /* FIXME: how can I write this in assembly? */ + .byte 0x48, 0xb8 +init_rsp: + .quad init_task_union+THREAD_SIZE + movq %rax, %rsp + + /* zero EFLAGS after setting rsp */ + pushq $0 + popfq + + /* + * We must switch to a new descriptor in kernel space for the GDT + * because soon the kernel won't have access anymore to the userspace + * addresses where we're currently running on. We have to do that here + * because in 32bit we couldn't load a 64bit linear address. + */ + lgdt pGDT64 + + /* esi is pointer to real mode structure with interesting info. + pass it to C */ + movl %esi, %edi + + /* Finally jump to run C code and to be on real kernel address + * Since we are running on identity-mapped space we have to jump + * to the full 64bit address , this is only possible as indirect + * jump + */ + movq initial_code(%rip),%rax + jmp *%rax + + + /* SMP bootup changes this */ + .globl initial_code +initial_code: + .quad x86_64_start_kernel + +.code32 +ENTRY(no_long_mode) + /* This isn't an x86-64 CPU so hang */ +1: + jmp 1b + +.org 0xf00 +pGDT32: + .word gdt32_end-gdt_table32 + .quad gdt_table32-__START_KERNEL+0x100000 + +.org 0xf10 +ljumpvector: + .long reach_long64-__START_KERNEL+0x100000 + .word __KERNEL_CS + +ENTRY(stext) +ENTRY(_stext) + + /* + * This default setting generates an ident mapping at address 0x100000 + * and a mapping for the kernel that precisely maps virtual address + * 0xffffffff80000000 to physical address 0x000000. (always using + * 2Mbyte large pages provided by PAE mode) + */ +.org 0x1000 +ENTRY(level4_pgt) + .quad 0x0000000000102007 /* -> level3_ident_pgt */ + .fill 255,8,0 + /* __PAGE_OFFSET 0xffff800000000000 */ + .quad 0x000000000010a007 + .fill 254,8,0 + /* (2^48-(2*1024*1024*1024))/(2^39) = 511 */ + .quad 0x0000000000103007 /* -> level3_kernel_pgt */ + +.org 0x2000 +/* Kernel does not "know" about 4-th level of page tables. */ +ENTRY(swapper_pg_dir) +ENTRY(level3_ident_pgt) + .quad 0x0000000000104007 + .fill 511,8,0 + +.org 0x3000 +ENTRY(level3_kernel_pgt) + .fill 510,8,0 + /* (2^48-(2*1024*1024*1024)-((2^39)*511))/(2^30) = 510 */ + .quad 0x0000000000105007 /* -> level2_kernel_pgt */ + .fill 1,8,0 + +.org 0x4000 +ENTRY(level2_ident_pgt) + /* 2 Mbytes are enough, this is necessary only for head.S */ + .quad 0x0000000000000283 + /* .fill 511,8,0 */ + /* Jan needs more than 2Mbytes, so set a 40Mbyte mapping instead */ + .quad 0x0000000000200183 + .quad 0x0000000000400183 + .quad 0x0000000000600183 + .quad 0x0000000000800183 + .quad 0x0000000000A00183 + .quad 0x0000000000C00183 + .quad 0x0000000000E00183 + .quad 0x0000000001000183 + .quad 0x0000000001200183 + .quad 0x0000000001400183 + .quad 0x0000000001600183 + .quad 0x0000000001800183 + .quad 0x0000000001A00183 + .quad 0x0000000001C00183 + .quad 0x0000000001E00183 + .quad 0x0000000002000183 + .quad 0x0000000002200183 + .quad 0x0000000002400183 + .quad 0x0000000002600183 + .fill 492,8,0 + +.org 0x5000 +ENTRY(level2_kernel_pgt) + /* (2^48-(2*1024*1024*1024)-((2^39)*511)-((2^30)*510)) = 0 */ + .quad 0x0000000000000183 + .quad 0x0000000000200183 + .quad 0x0000000000400183 + .quad 0x0000000000600183 + .quad 0x0000000000800183 + .quad 0x0000000000A00183 + .quad 0x0000000000C00183 + .quad 0x0000000000E00183 + .quad 0x0000000001000183 + .quad 0x0000000001200183 + .quad 0x0000000001400183 + .quad 0x0000000001600183 + .quad 0x0000000001800183 + .quad 0x0000000001A00183 + .quad 0x0000000001C00183 + .quad 0x0000000001E00183 + .quad 0x0000000002000183 + .quad 0x0000000002200183 + .quad 0x0000000002400183 + .quad 0x0000000002600183 + /* + * We could go ahead without any downside (except typing programmer + * wise :) but 40Mbyte are just enough for the kernel statically + * linked part (and extending it is trivial). + */ + .fill 492,8,0 + +.org 0x6000 +ENTRY(empty_zero_page) + +.org 0x7000 +ENTRY(empty_bad_page) + +.org 0x8000 +ENTRY(empty_bad_pte_table) + +.org 0x9000 +ENTRY(empty_bad_pmd_table) + +.org 0xa000 +ENTRY(level3_physmem_pgt) + .quad 0x0000000000105007 /* -> level2_kernel_pgt (so that __va works even before pagetable_init) */ + + + +.org 0xb000 + + +.data + +.globl SYMBOL_NAME(gdt) + + .word 0 + .align 16 + .word 0 +pGDT64: + .word gdt_end-gdt_table +SYMBOL_NAME_LABEL(gdt) + .quad gdt_table + + +.align 64 /* cacheline aligned */ +ENTRY(gdt_table32) + .quad 0x0000000000000000 /* This one is magic */ + .quad 0x0000000000000000 /* unused */ + .quad 0x00af9a000000ffff /* __KERNEL_CS */ +gdt32_end: + +/* We need valid kernel segments for data and code in long mode too + * IRET will check the segment types kkeil 2000/10/28 + * Also sysret mandates a special GDT layout + */ + +.align 64 /* cacheline aligned, keep this synchronized with asm/desc.h */ +ENTRY(gdt_table) + .quad 0x0000000000000000 /* This one is magic */ + .quad 0x0000000000000000 /* unused */ + .quad 0x00af9a000000ffff /* __KERNEL_CS */ + .quad 0x00cf92000000ffff /* __KERNEL_DS */ + .quad 0x00cffe000000ffff /* __USER32_CS */ + .quad 0x00cff2000000ffff /* __USER_DS, __USER32_DS */ + .quad 0x00affa000000ffff /* __USER_CS */ + + .globl tss_start +tss_start: + .rept NR_CPUS + .quad 0,0 /* TSS descriptors. filled in later */ + .endr + .globl ldt_start +ldt_start: + .rept NR_CPUS + .quad 0,0 /* LDT descriptors. filled in later */ + .endr +gdt_end: + .globl gdt_end + + .align 64 +ENTRY(idt_table) + .rept 256 + .quad 0 + .quad 0 + .endr + +.section .text.lock +ENTRY(stext_lock) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/head64.c linux.ac/arch/x86_64/kernel/head64.c --- linux.vanilla/arch/x86_64/kernel/head64.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/head64.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,85 @@ +/* + * linux/arch/x86_64/kernel/head64.c -- prepare to run common code + * + * Copyright (C) 2000 Andrea Arcangeli SuSE + * + * $Id: head64.c,v 1.22 2001/07/06 14:28:20 ak Exp $ + */ + +#include +#include +#include +#include +#include + +#include + +static void __init clear_bss(void) +{ + extern char __bss_start[], __bss_end[]; + printk("Clearing %ld bytes of bss...", (unsigned long) __bss_end - (unsigned long) __bss_start); + memset(__bss_start, 0, + (unsigned long) __bss_end - (unsigned long) __bss_start); + printk("ok\n"); +} + +extern char x86_boot_params[2048]; + +#define NEW_CL_POINTER 0x228 /* Relative to real mode data */ +#define OLD_CL_MAGIC_ADDR 0x90020 +#define OLD_CL_MAGIC 0xA33F +#define OLD_CL_BASE_ADDR 0x90000 +#define OLD_CL_OFFSET 0x90022 + +extern char saved_command_line[]; + +static void __init copy_bootdata(char *real_mode_data) +{ + int new_data; + char * command_line; + + memcpy(x86_boot_params, real_mode_data, 2048); + new_data = *(int *) (x86_boot_params + NEW_CL_POINTER); + if (!new_data) { + if (OLD_CL_MAGIC != * (u16 *) OLD_CL_MAGIC_ADDR) { + printk("so old bootloader that it does not support commandline?!\n"); + return; + } + new_data = OLD_CL_BASE_ADDR + * (u16 *) OLD_CL_OFFSET; + printk("old bootloader convention, maybe loadlin?\n"); + } + command_line = (char *) ((u64)(new_data)); + memcpy(saved_command_line, command_line, 2048); + printk("Bootdata ok (command line is %s)\n", saved_command_line); +} + +static void __init setup_boot_cpu_data(void) +{ + int dummy, eax; + + /* get vendor info */ + cpuid(0, &boot_cpu_data.cpuid_level, + (int *)&boot_cpu_data.x86_vendor_id[0], + (int *)&boot_cpu_data.x86_vendor_id[8], + (int *)&boot_cpu_data.x86_vendor_id[4]); + + /* get cpu type */ + cpuid(1, &eax, &dummy, &dummy, &boot_cpu_data.x86_capability[0]); + boot_cpu_data.x86 = (eax >> 8) & 0xf; + boot_cpu_data.x86_model = (eax >> 4) & 0xf; + boot_cpu_data.x86_mask = eax & 0xf; +} + +extern void start_kernel(void), pda_init(int); + +void __init x86_64_start_kernel(char * real_mode_data) +{ + clear_bss(); /* must be the first thing in C and must not depend on .bss to be zero */ + + printk("booting x86_64 kernel... "); + copy_bootdata(real_mode_data); + setup_boot_cpu_data(); + pda_init(0); + + start_kernel(); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/i387.c linux.ac/arch/x86_64/kernel/i387.c --- linux.vanilla/arch/x86_64/kernel/i387.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/i387.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,443 @@ +/* + * linux/arch/i386/kernel/i387.c + * + * Copyright (C) 1994 Linus Torvalds + * + * Pentium III FXSR, SSE support + * General FPU state handling cleanups + * Gareth Hughes , May 2000 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MATH_EMULATION +#define HAVE_HWFP (boot_cpu_data.hard_math) +#else +#define HAVE_HWFP 1 +#endif + +/* + * The _current_ task is using the FPU for the first time + * so initialize it and set the mxcsr to its default + * value at reset if we support XMM instructions and then + * remeber the current task has used the FPU. + */ +void init_fpu(void) +{ + __asm__("fninit"); + if ( cpu_has_xmm ) + load_mxcsr(0x1f80); + + current->used_math = 1; +} + +/* + * FPU lazy state save handling. + */ + +static inline void __save_init_fpu( struct task_struct *tsk ) +{ + if ( cpu_has_fxsr ) { + asm volatile( "fxsave %0 ; fnclex" + : "=m" (tsk->thread.i387.fxsave) ); + } else { + asm volatile( "fnsave %0 ; fwait" + : "=m" (tsk->thread.i387.fsave) ); + } + tsk->flags &= ~PF_USEDFPU; +} + +void save_init_fpu( struct task_struct *tsk ) +{ + __save_init_fpu(tsk); + stts(); +} + +void kernel_fpu_begin(void) +{ + struct task_struct *tsk = current; + + if (tsk->flags & PF_USEDFPU) { + __save_init_fpu(tsk); + return; + } + clts(); +} + +void restore_fpu( struct task_struct *tsk ) +{ + if ( cpu_has_fxsr ) { + asm volatile( "fxrstor %0" + : : "m" (tsk->thread.i387.fxsave) ); + } else { + asm volatile( "frstor %0" + : : "m" (tsk->thread.i387.fsave) ); + } +} + +/* + * FPU tag word conversions. + */ + +static inline unsigned short twd_i387_to_fxsr( unsigned short twd ) +{ + unsigned int tmp; /* to avoid 16 bit prefixes in the code */ + + /* Transform each pair of bits into 01 (valid) or 00 (empty) */ + tmp = ~twd; + tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ + /* and move the valid bits to the lower byte. */ + tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ + tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ + tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ + return tmp; +} + +static inline u32 twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave ) +{ + struct _fpxreg *st = NULL; + u32 twd = (u32) fxsave->twd; + u32 tag; + u32 ret = 0xffff0000; + int i; + +#define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16); + + for ( i = 0 ; i < 8 ; i++ ) { + if ( twd & 0x1 ) { + st = (struct _fpxreg *) FPREG_ADDR( fxsave, i ); + + switch ( st->exponent & 0x7fff ) { + case 0x7fff: + tag = 2; /* Special */ + break; + case 0x0000: + if ( !st->significand[0] && + !st->significand[1] && + !st->significand[2] && + !st->significand[3] ) { + tag = 1; /* Zero */ + } else { + tag = 2; /* Special */ + } + break; + default: + if ( st->significand[3] & 0x8000 ) { + tag = 0; /* Valid */ + } else { + tag = 2; /* Special */ + } + break; + } + } else { + tag = 3; /* Empty */ + } + ret |= (tag << (2 * i)); + twd = twd >> 1; + } + return ret; +} + +/* + * FPU state interaction. + */ + +unsigned short get_fpu_cwd( struct task_struct *tsk ) +{ + if ( cpu_has_fxsr ) { + return tsk->thread.i387.fxsave.cwd; + } else { + return (unsigned short)tsk->thread.i387.fsave.cwd; + } +} + +unsigned short get_fpu_swd( struct task_struct *tsk ) +{ + if ( cpu_has_fxsr ) { + return tsk->thread.i387.fxsave.swd; + } else { + return (unsigned short)tsk->thread.i387.fsave.swd; + } +} + +unsigned short get_fpu_twd( struct task_struct *tsk ) +{ + if ( cpu_has_fxsr ) { + return tsk->thread.i387.fxsave.twd; + } else { + return (unsigned short)tsk->thread.i387.fsave.twd; + } +} + +unsigned short get_fpu_mxcsr( struct task_struct *tsk ) +{ + if ( cpu_has_xmm ) { + return tsk->thread.i387.fxsave.mxcsr; + } else { + return 0x1f80; + } +} + +void set_fpu_cwd( struct task_struct *tsk, unsigned short cwd ) +{ + if ( cpu_has_fxsr ) { + tsk->thread.i387.fxsave.cwd = cwd; + } else { + tsk->thread.i387.fsave.cwd = ((u32)cwd | 0xffff0000); + } +} + +void set_fpu_swd( struct task_struct *tsk, unsigned short swd ) +{ + if ( cpu_has_fxsr ) { + tsk->thread.i387.fxsave.swd = swd; + } else { + tsk->thread.i387.fsave.swd = ((u32)swd | 0xffff0000); + } +} + +void set_fpu_twd( struct task_struct *tsk, unsigned short twd ) +{ + if ( cpu_has_fxsr ) { + tsk->thread.i387.fxsave.twd = twd_i387_to_fxsr(twd); + } else { + tsk->thread.i387.fsave.twd = ((u32)twd | 0xffff0000); + } +} + +void set_fpu_mxcsr( struct task_struct *tsk, unsigned short mxcsr ) +{ + if ( cpu_has_xmm ) { + tsk->thread.i387.fxsave.mxcsr = (mxcsr & 0xffbf); + } +} + +/* + * FXSR floating point environment conversions. + */ + +static inline int convert_fxsr_to_user( struct _fpstate *buf, + struct i387_fxsave_struct *fxsave ) +{ + u32 env[7]; + struct _fpreg *to; + struct _fpxreg *from; + int i; + + env[0] = (u32)fxsave->cwd | 0xffff0000; + env[1] = (u32)fxsave->swd | 0xffff0000; + env[2] = twd_fxsr_to_i387(fxsave); + env[3] = fxsave->fip; + env[4] = fxsave->fcs | ((u32)fxsave->fop << 16); + env[5] = fxsave->foo; + env[6] = fxsave->fos; + + if ( __copy_to_user( buf, env, 7 * sizeof(u32) ) ) + return 1; + + to = &buf->_st[0]; + from = (struct _fpxreg *) &fxsave->st_space[0]; + for ( i = 0 ; i < 8 ; i++, to++, from++ ) { + if ( __copy_to_user( to, from, sizeof(*to) ) ) + return 1; + } + return 0; +} + +static inline int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave, + struct _fpstate *buf ) +{ + u32 env[7]; + struct _fpxreg *to; + struct _fpreg *from; + int i; + + if ( __copy_from_user( env, buf, 7 * sizeof(u32) ) ) + return 1; + + fxsave->cwd = (unsigned short)(env[0] & 0xffff); + fxsave->swd = (unsigned short)(env[1] & 0xffff); + fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff)); + fxsave->fip = env[3]; + fxsave->fop = (unsigned short)((env[4] & 0xffff0000) >> 16); + fxsave->fcs = (env[4] & 0xffff); + fxsave->foo = env[5]; + fxsave->fos = env[6]; + + to = (struct _fpxreg *) &fxsave->st_space[0]; + from = &buf->_st[0]; + for ( i = 0 ; i < 8 ; i++, to++, from++ ) { + if ( __copy_from_user( to, from, sizeof(*from) ) ) + return 1; + } + return 0; +} + +/* + * Signal frame handlers. + */ + +static inline int save_i387_fsave( struct _fpstate *buf ) +{ + struct task_struct *tsk = current; + + unlazy_fpu( tsk ); + tsk->thread.i387.fsave.status = tsk->thread.i387.fsave.swd; + if ( __copy_to_user( buf, &tsk->thread.i387.fsave, + sizeof(struct i387_fsave_struct) ) ) + return -1; + return 1; +} + +static inline int save_i387_fxsave( struct _fpstate *buf ) +{ + struct task_struct *tsk = current; + int err = 0; + + unlazy_fpu( tsk ); + + if ( convert_fxsr_to_user( buf, &tsk->thread.i387.fxsave ) ) + return -1; + + err |= __put_user( tsk->thread.i387.fxsave.swd, &buf->status ); + err |= __put_user( X86_FXSR_MAGIC, &buf->magic ); + if ( err ) + return -1; + + if ( __copy_to_user( &buf->_fxsr_env[0], &tsk->thread.i387.fxsave, + sizeof(struct i387_fxsave_struct) ) ) + return -1; + return 1; +} + +int save_i387( struct _fpstate *buf ) +{ + if ( !current->used_math ) + return 0; + + /* This will cause a "finit" to be triggered by the next + * attempted FPU operation by the 'current' process. + */ + current->used_math = 0; + + if ( HAVE_HWFP ) { + if ( cpu_has_fxsr ) { + return save_i387_fxsave( buf ); + } else { + return save_i387_fsave( buf ); + } + } +} + +static inline int restore_i387_fsave( struct _fpstate *buf ) +{ + struct task_struct *tsk = current; + clear_fpu( tsk ); + return __copy_from_user( &tsk->thread.i387.fsave, buf, + sizeof(struct i387_fsave_struct) ); +} + +static inline int restore_i387_fxsave( struct _fpstate *buf ) +{ + struct task_struct *tsk = current; + clear_fpu( tsk ); + if ( __copy_from_user( &tsk->thread.i387.fxsave, &buf->_fxsr_env[0], + sizeof(struct i387_fxsave_struct) ) ) + return 1; + /* mxcsr bit 6 and 31-16 must be zero for security reasons */ + tsk->thread.i387.fxsave.mxcsr &= 0xffbf; + return convert_fxsr_from_user( &tsk->thread.i387.fxsave, buf ); +} + +int restore_i387( struct _fpstate *buf ) +{ + int err; + + if ( HAVE_HWFP ) { + if ( cpu_has_fxsr ) { + err = restore_i387_fxsave( buf ); + } else { + err = restore_i387_fsave( buf ); + } + } + current->used_math = 1; + return err; +} + +/* + * ptrace request handlers. + */ + +int get_fpregs( struct user_i387_struct *buf, struct task_struct *tsk ) +{ + if ( cpu_has_fxsr ) { + if (__copy_to_user( (void *)buf, &tsk->thread.i387.fxsave, + sizeof(struct user_i387_struct) )) + return -EFAULT; + return 0; + } else { + return -EIO; + } +} + +int set_fpregs( struct task_struct *tsk, struct user_i387_struct *buf ) +{ + if ( cpu_has_fxsr ) { + __copy_from_user( &tsk->thread.i387.fxsave, (void *)buf, + sizeof(struct user_i387_struct) ); + /* mxcsr bit 6 and 31-16 must be zero for security reasons */ + tsk->thread.i387.fxsave.mxcsr &= 0xffbf; + return 0; + } else { + return -EIO; + } +} + +/* + * FPU state for core dumps. + */ + +static inline void copy_fpu_fsave( struct task_struct *tsk, + struct user_i387_struct *fpu ) +{ + memcpy( fpu, &tsk->thread.i387.fsave, + sizeof(struct user_i387_struct) ); +} + +static inline void copy_fpu_fxsave( struct task_struct *tsk, + struct user_i387_struct *fpu ) +{ + unsigned short *to; + unsigned short *from; + int i; + + memcpy( fpu, &tsk->thread.i387.fxsave, 7 * sizeof(u32) ); + + to = (unsigned short *)&fpu->st_space[0]; + from = (unsigned short *)&tsk->thread.i387.fxsave.st_space[0]; + for ( i = 0 ; i < 8 ; i++, to += 5, from += 8 ) { + memcpy( to, from, 5 * sizeof(unsigned short) ); + } +} + +int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu ) +{ + int fpvalid; + struct task_struct *tsk = current; + + fpvalid = tsk->used_math && cpu_has_fxsr; + if ( fpvalid ) { + unlazy_fpu( tsk ); + memcpy( fpu, &tsk->thread.i387.fxsave, + sizeof(struct user_i387_struct) ); + } + + return fpvalid; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/i8259.c linux.ac/arch/x86_64/kernel/i8259.c --- linux.vanilla/arch/x86_64/kernel/i8259.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/i8259.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,508 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* When we have things working, we can switch to always use + IOAPIC. --pavel */ + +/* + * Common place to define all x86 IRQ vectors + * + * This builds up the IRQ handler stubs using some ugly macros in irq.h + * + * These macros create the low-level assembly IRQ routines that save + * register context and call do_IRQ(). do_IRQ() then does all the + * operations that are needed to keep the AT (or SMP IOAPIC) + * interrupt-controller happy. + */ + +BUILD_COMMON_IRQ() + +#define BI(x,y) \ + BUILD_IRQ(x##y) + +#define BUILD_16_IRQS(x) \ + BI(x,0) BI(x,1) BI(x,2) BI(x,3) \ + BI(x,4) BI(x,5) BI(x,6) BI(x,7) \ + BI(x,8) BI(x,9) BI(x,a) BI(x,b) \ + BI(x,c) BI(x,d) BI(x,e) BI(x,f) + +/* + * ISA PIC or low IO-APIC triggered (INTA-cycle or APIC) interrupts: + * (these are usually mapped to vectors 0x20-0x2f) + */ +BUILD_16_IRQS(0x0) + +#ifdef CONFIG_X86_IO_APIC +/* + * The IO-APIC gives us many more interrupt sources. Most of these + * are unused but an SMP system is supposed to have enough memory ... + * sometimes (mostly wrt. hw bugs) we get corrupted vectors all + * across the spectrum, so we really want to be prepared to get all + * of these. Plus, more powerful systems might have more than 64 + * IO-APIC registers. + * + * (these are usually mapped into the 0x30-0xff vector range) + */ + BUILD_16_IRQS(0x1) BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3) +BUILD_16_IRQS(0x4) BUILD_16_IRQS(0x5) BUILD_16_IRQS(0x6) BUILD_16_IRQS(0x7) +BUILD_16_IRQS(0x8) BUILD_16_IRQS(0x9) BUILD_16_IRQS(0xa) BUILD_16_IRQS(0xb) +BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) +#endif + +#undef BUILD_16_IRQS +#undef BI + + +/* + * The following vectors are part of the Linux architecture, there + * is no hardware IRQ pin equivalent for them, they are triggered + * through the ICC by us (IPIs) + */ +#ifdef CONFIG_SMP +BUILD_SMP_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR); +BUILD_SMP_INTERRUPT(invalidate_interrupt,INVALIDATE_TLB_VECTOR); +BUILD_SMP_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR); +#endif + +/* + * every pentium local APIC has two 'local interrupts', with a + * soft-definable vector attached to both interrupts, one of + * which is a timer interrupt, the other one is error counter + * overflow. Linux uses the local APIC timer interrupt to get + * a much simpler SMP time architecture: + */ +#ifdef CONFIG_X86_LOCAL_APIC +BUILD_SMP_TIMER_INTERRUPT(apic_timer_interrupt,LOCAL_TIMER_VECTOR); +BUILD_SMP_INTERRUPT(error_interrupt,ERROR_APIC_VECTOR); +BUILD_SMP_INTERRUPT(spurious_interrupt,SPURIOUS_APIC_VECTOR); +#endif + +#define IRQ(x,y) \ + IRQ##x##y##_interrupt + +#define IRQLIST_16(x) \ + IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), \ + IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), \ + IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \ + IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f) + +void (*interrupt[NR_IRQS])(void) = { + IRQLIST_16(0x0), + +#ifdef CONFIG_X86_IO_APIC + IRQLIST_16(0x1), IRQLIST_16(0x2), IRQLIST_16(0x3), + IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7), + IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb), + IRQLIST_16(0xc), IRQLIST_16(0xd) +#endif +}; + +#undef IRQ +#undef IRQLIST_16 + +/* + * This is the 'legacy' 8259A Programmable Interrupt Controller, + * present in the majority of PC/AT boxes. + * plus some generic x86 specific things if generic specifics makes + * any sense at all. + * this file should become arch/i386/kernel/irq.c when the old irq.c + * moves to arch independent land + */ + +spinlock_t i8259A_lock = SPIN_LOCK_UNLOCKED; + +static void end_8259A_irq (unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_8259A_irq(irq); +} + +#define shutdown_8259A_irq disable_8259A_irq + +void mask_and_ack_8259A(unsigned int); + +static unsigned int startup_8259A_irq(unsigned int irq) +{ + enable_8259A_irq(irq); + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type i8259A_irq_type = { + "XT-PIC", + startup_8259A_irq, + shutdown_8259A_irq, + enable_8259A_irq, + disable_8259A_irq, + mask_and_ack_8259A, + end_8259A_irq, + NULL +}; + +/* + * 8259A PIC functions to handle ISA devices: + */ + +/* + * This contains the irq mask for both 8259A irq controllers, + */ +static unsigned int cached_irq_mask = 0xffff; + +#define __byte(x,y) (((unsigned char *)&(y))[x]) +#define cached_21 (__byte(0,cached_irq_mask)) +#define cached_A1 (__byte(1,cached_irq_mask)) + +/* + * Not all IRQs can be routed through the IO-APIC, eg. on certain (older) + * boards the timer interrupt is not really connected to any IO-APIC pin, + * it's fed to the master 8259A's IR0 line only. + * + * Any '1' bit in this mask means the IRQ is routed through the IO-APIC. + * this 'mixed mode' IRQ handling costs nothing because it's only used + * at IRQ setup time. + */ +unsigned long io_apic_irqs; + +void disable_8259A_irq(unsigned int irq) +{ + unsigned int mask = 1 << irq; + unsigned long flags; + + spin_lock_irqsave(&i8259A_lock, flags); + cached_irq_mask |= mask; + if (irq & 8) + outb(cached_A1,0xA1); + else + outb(cached_21,0x21); + spin_unlock_irqrestore(&i8259A_lock, flags); +} + +void enable_8259A_irq(unsigned int irq) +{ + unsigned int mask = ~(1 << irq); + unsigned long flags; + + spin_lock_irqsave(&i8259A_lock, flags); + cached_irq_mask &= mask; + if (irq & 8) + outb(cached_A1,0xA1); + else + outb(cached_21,0x21); + spin_unlock_irqrestore(&i8259A_lock, flags); +} + +int i8259A_irq_pending(unsigned int irq) +{ + unsigned int mask = 1<> 8); + spin_unlock_irqrestore(&i8259A_lock, flags); + + return ret; +} + +void make_8259A_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + io_apic_irqs &= ~(1<> 8); + outb(0x0A,0xA0); /* back to the IRR register */ + return value; +} + +/* + * Careful! The 8259A is a fragile beast, it pretty + * much _has_ to be done exactly like this (mask it + * first, _then_ send the EOI, and the order of EOI + * to the two 8259s is important! + */ +void mask_and_ack_8259A(unsigned int irq) +{ + unsigned int irqmask = 1 << irq; + unsigned long flags; + + spin_lock_irqsave(&i8259A_lock, flags); + /* + * Lightweight spurious IRQ detection. We do not want + * to overdo spurious IRQ handling - it's usually a sign + * of hardware problems, so we only do the checks we can + * do without slowing down good hardware unnecesserily. + * + * Note that IRQ7 and IRQ15 (the two spurious IRQs + * usually resulting from the 8259A-1|2 PICs) occur + * even if the IRQ is masked in the 8259A. Thus we + * can check spurious 8259A IRQs without doing the + * quite slow i8259A_irq_real() call for every IRQ. + * This does not cover 100% of spurious interrupts, + * but should be enough to warn the user that there + * is something bad going on ... + */ + if (cached_irq_mask & irqmask) + goto spurious_8259A_irq; + cached_irq_mask |= irqmask; + +handle_real_irq: + if (irq & 8) { + inb(0xA1); /* DUMMY - (do we need this?) */ + outb(cached_A1,0xA1); + outb(0x60+(irq&7),0xA0);/* 'Specific EOI' to slave */ + outb(0x62,0x20); /* 'Specific EOI' to master-IRQ2 */ + } else { + inb(0x21); /* DUMMY - (do we need this?) */ + outb(cached_21,0x21); + outb(0x60+irq,0x20); /* 'Specific EOI' to master */ + } + spin_unlock_irqrestore(&i8259A_lock, flags); + return; + +spurious_8259A_irq: + /* + * this is the slow path - should happen rarely. + */ + if (i8259A_irq_real(irq)) + /* + * oops, the IRQ _is_ in service according to the + * 8259A - not spurious, go handle it. + */ + goto handle_real_irq; + + { + static int spurious_irq_mask; + /* + * At this point we can be sure the IRQ is spurious, + * lets ACK and report it. [once per IRQ] + */ + if (!(spurious_irq_mask & irqmask)) { + printk("spurious 8259A interrupt: IRQ%d.\n", irq); + spurious_irq_mask |= irqmask; + } + atomic_inc(&irq_err_count); + /* + * Theoretically we do not have to handle this IRQ, + * but in Linux this does not cause problems and is + * simpler for us. + */ + goto handle_real_irq; + } +} + +void __init init_8259A(int auto_eoi) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259A_lock, flags); + + outb(0xff, 0x21); /* mask all of 8259A-1 */ + outb(0xff, 0xA1); /* mask all of 8259A-2 */ + + /* + * outb_p - this has to work on a wide range of PC hardware. + */ + outb_p(0x11, 0x20); /* ICW1: select 8259A-1 init */ + outb_p(0x20 + 0, 0x21); /* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */ + outb_p(0x04, 0x21); /* 8259A-1 (the master) has a slave on IR2 */ + if (auto_eoi) + outb_p(0x03, 0x21); /* master does Auto EOI */ + else + outb_p(0x01, 0x21); /* master expects normal EOI */ + + outb_p(0x11, 0xA0); /* ICW1: select 8259A-2 init */ + outb_p(0x20 + 8, 0xA1); /* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */ + outb_p(0x02, 0xA1); /* 8259A-2 is a slave on master's IR2 */ + outb_p(0x01, 0xA1); /* (slave's support for AEOI in flat mode + is to be investigated) */ + + if (auto_eoi) + /* + * in AEOI mode we just have to mask the interrupt + * when acking. + */ + i8259A_irq_type.ack = disable_8259A_irq; + else + i8259A_irq_type.ack = mask_and_ack_8259A; + + udelay(100); /* wait for 8259A to initialize */ + + outb(cached_21, 0x21); /* restore master IRQ mask */ + outb(cached_A1, 0xA1); /* restore slave IRQ mask */ + + spin_unlock_irqrestore(&i8259A_lock, flags); +} + +/* + * Note that on a 486, we don't want to do a SIGFPE on an irq13 + * as the irq is unreliable, and exception 16 works correctly + * (ie as explained in the intel literature). On a 386, you + * can't use exception 16 due to bad IBM design, so we have to + * rely on the less exact irq13. + * + * Careful.. Not only is IRQ13 unreliable, but it is also + * leads to races. IBM designers who came up with it should + * be shot. + */ + +static void math_error_irq(int cpl, void *dev_id, struct pt_regs *regs) +{ + extern void math_error(void *); + outb(0,0xF0); + if (ignore_irq13 || !boot_cpu_data.hard_math) + return; + math_error((void *)regs->rip); +} + +/* + * New motherboards sometimes make IRQ 13 be a PCI interrupt, + * so allow interrupt sharing. + */ +static struct irqaction irq13 = { math_error_irq, 0, 0, "fpu", NULL, NULL }; + +/* + * IRQ2 is cascade interrupt to second interrupt controller + */ + +#ifndef CONFIG_VISWS +static struct irqaction irq2 = { no_action, 0, 0, "cascade", NULL, NULL}; +#endif + + +void __init init_ISA_irqs (void) +{ + int i; + +#ifdef CONFIG_X86_LOCAL_APIC + init_bsp_APIC(); +#endif + init_8259A(0); + + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = 0; + irq_desc[i].depth = 1; + + if (i < 16) { + /* + * 16 old-style INTA-cycle interrupts: + */ + irq_desc[i].handler = &i8259A_irq_type; + } else { + /* + * 'high' PCI IRQs filled in on demand + */ + irq_desc[i].handler = &no_irq_type; + } + } +} + +void __init init_IRQ(void) +{ + int i; + +#ifndef CONFIG_X86_VISWS_APIC + init_ISA_irqs(); +#else + init_VISWS_APIC_irqs(); +#endif + /* + * Cover the whole vector space, no vector can escape + * us. (some of these will be overridden and become + * 'special' SMP interrupts) + */ + for (i = 0; i < NR_IRQS; i++) { + int vector = FIRST_EXTERNAL_VECTOR + i; + if (vector != IA32_SYSCALL_VECTOR) + set_intr_gate(vector, interrupt[i]); + } + +#ifdef CONFIG_SMP + /* + * IRQ0 must be given a fixed assignment and initialized, + * because it's used before the IO-APIC is set up. + */ + set_intr_gate(FIRST_DEVICE_VECTOR, interrupt[0]); + + /* + * The reschedule interrupt is a CPU-to-CPU reschedule-helper + * IPI, driven by wakeup. + */ + set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt); + + /* IPI for invalidation */ + set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt); + + /* IPI for generic function call */ + set_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt); +#endif + +#ifdef CONFIG_X86_LOCAL_APIC + /* self generated IPI for local APIC timer */ + set_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt); + + /* IPI vectors for APIC spurious and error interrupts */ + set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); + set_intr_gate(ERROR_APIC_VECTOR, error_interrupt); +#endif + + /* + * Set the clock to HZ Hz, we already have a valid + * vector now: + */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(LATCH & 0xff , 0x40); /* LSB */ + outb(LATCH >> 8 , 0x40); /* MSB */ + +#ifndef CONFIG_VISWS + setup_irq(2, &irq2); +#endif +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/init_task.c linux.ac/arch/x86_64/kernel/init_task.c --- linux.vanilla/arch/x86_64/kernel/init_task.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/init_task.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,34 @@ +#include +#include +#include + +#include +#include +#include + +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); + +/* + * Initial task structure. + * + * We need to make sure that this is 8192-byte aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ +union task_union init_task_union + __attribute__((__section__(".data.init_task"))) = + { INIT_TASK(init_task_union.task) }; + +/* + * per-CPU TSS segments. Threads are completely 'soft' on Linux, + * no more per-task TSS's. The TSS size is kept cacheline-aligned + * so they are allowed to end up in the .data.cacheline_aligned + * section. Since TSS's are completely CPU-local, we want them + * on exact cacheline boundaries, to eliminate cacheline ping-pong. + */ +struct tss_struct init_tss[NR_CPUS] __cacheline_aligned = { [0 ... NR_CPUS-1] = INIT_TSS }; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/io_apic.c linux.ac/arch/x86_64/kernel/io_apic.c --- linux.vanilla/arch/x86_64/kernel/io_apic.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/io_apic.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,1614 @@ +/* + * Intel IO-APIC support for multi-Pentium hosts. + * + * Copyright (C) 1997, 1998, 1999, 2000 Ingo Molnar, Hajnalka Szabo + * + * Many thanks to Stig Venaas for trying out countless experimental + * patches and reporting/debugging problems patiently! + * + * (c) 1999, Multiple IO-APIC support, developed by + * Ken-ichi Yaku and + * Hidemi Kishimoto , + * further tested and cleaned up by Zach Brown + * and Ingo Molnar + * + * Fixes + * Maciej W. Rozycki : Bits for genuine 82489DX APICs; + * thanks to Eric Gilmore + * and Rolf G. Tews + * for testing these extensively + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#undef APIC_LOCKUP_DEBUG + +#define APIC_LOCKUP_DEBUG + +static spinlock_t ioapic_lock = SPIN_LOCK_UNLOCKED; + +/* + * # of IRQ routing registers + */ +int nr_ioapic_registers[MAX_IO_APICS]; + +#if CONFIG_SMP +# define TARGET_CPUS cpu_online_map +#else +# define TARGET_CPUS 0x01 +#endif +/* + * Rough estimation of how many shared IRQs there are, can + * be changed anytime. + */ +#define MAX_PLUS_SHARED_IRQS NR_IRQS +#define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + NR_IRQS) + +/* + * This is performance-critical, we want to do it O(1) + * + * the indexing order of this array favors 1:1 mappings + * between pins and IRQs. + */ + +static struct irq_pin_list { + int apic, pin, next; +} irq_2_pin[PIN_MAP_SIZE]; + +/* + * The common case is 1:1 IRQ<->pin mappings. Sometimes there are + * shared ISA-space IRQs, so we have to support them. We are super + * fast in the common case, and fast for shared ISA-space IRQs. + */ +static void add_pin_to_irq(unsigned int irq, int apic, int pin) +{ + static int first_free_entry = NR_IRQS; + struct irq_pin_list *entry = irq_2_pin + irq; + + while (entry->next) + entry = irq_2_pin + entry->next; + + if (entry->pin != -1) { + entry->next = first_free_entry; + entry = irq_2_pin + entry->next; + if (++first_free_entry >= PIN_MAP_SIZE) + panic("io_apic.c: whoops"); + } + entry->apic = apic; + entry->pin = pin; +} + +#define __DO_ACTION(R, ACTION, FINAL) \ + \ +{ \ + int pin; \ + struct irq_pin_list *entry = irq_2_pin + irq; \ + \ + for (;;) { \ + unsigned int reg; \ + pin = entry->pin; \ + if (pin == -1) \ + break; \ + reg = io_apic_read(entry->apic, 0x10 + R + pin*2); \ + reg ACTION; \ + io_apic_modify(entry->apic, reg); \ + if (!entry->next) \ + break; \ + entry = irq_2_pin + entry->next; \ + } \ + FINAL; \ +} + +#define DO_ACTION(name,R,ACTION, FINAL) \ + \ + static void name##_IO_APIC_irq (unsigned int irq) \ + __DO_ACTION(R, ACTION, FINAL) + +DO_ACTION( __mask, 0, |= 0x00010000, io_apic_sync(entry->apic) ) + /* mask = 1 */ +DO_ACTION( __unmask, 0, &= 0xfffeffff, ) + /* mask = 0 */ +DO_ACTION( __mask_and_edge, 0, = (reg & 0xffff7fff) | 0x00010000, ) + /* mask = 1, trigger = 0 */ +DO_ACTION( __unmask_and_level, 0, = (reg & 0xfffeffff) | 0x00008000, ) + /* mask = 0, trigger = 1 */ + +static void mask_IO_APIC_irq (unsigned int irq) +{ + unsigned long flags; + + spin_lock_irqsave(&ioapic_lock, flags); + __mask_IO_APIC_irq(irq); + spin_unlock_irqrestore(&ioapic_lock, flags); +} + +static void unmask_IO_APIC_irq (unsigned int irq) +{ + unsigned long flags; + + spin_lock_irqsave(&ioapic_lock, flags); + __unmask_IO_APIC_irq(irq); + spin_unlock_irqrestore(&ioapic_lock, flags); +} + +void clear_IO_APIC_pin(unsigned int apic, unsigned int pin) +{ + struct IO_APIC_route_entry entry; + unsigned long flags; + + /* + * Disable it in the IO-APIC irq-routing table: + */ + memset(&entry, 0, sizeof(entry)); + entry.mask = 1; + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry) + 0)); + io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry) + 1)); + spin_unlock_irqrestore(&ioapic_lock, flags); +} + +static void clear_IO_APIC (void) +{ + int apic, pin; + + for (apic = 0; apic < nr_ioapics; apic++) + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) + clear_IO_APIC_pin(apic, pin); +} + +/* + * support for broken MP BIOSs, enables hand-redirection of PIRQ0-7 to + * specific CPU-side IRQs. + */ + +#define MAX_PIRQS 8 +int pirq_entries [MAX_PIRQS]; +int pirqs_enabled; +int skip_ioapic_setup; + +static int __init ioapic_setup(char *str) +{ + skip_ioapic_setup = 1; + return 1; +} + +__setup("noapic", ioapic_setup); + +static int __init ioapic_pirq_setup(char *str) +{ + int i, max; + int ints[MAX_PIRQS+1]; + + get_options(str, ARRAY_SIZE(ints), ints); + + for (i = 0; i < MAX_PIRQS; i++) + pirq_entries[i] = -1; + + pirqs_enabled = 1; + printk(KERN_INFO "PIRQ redirection, working around broken MP-BIOS.\n"); + max = MAX_PIRQS; + if (ints[0] < MAX_PIRQS) + max = ints[0]; + + for (i = 0; i < max; i++) { + printk(KERN_DEBUG "... PIRQ%d -> IRQ %d\n", i, ints[i+1]); + /* + * PIRQs are mapped upside down, usually. + */ + pirq_entries[MAX_PIRQS-i-1] = ints[i+1]; + } + return 1; +} + +__setup("pirq=", ioapic_pirq_setup); + +/* + * Find the IRQ entry number of a certain pin. + */ +static int __init find_irq_entry(int apic, int pin, int type) +{ + int i; + + for (i = 0; i < mp_irq_entries; i++) + if (mp_irqs[i].mpc_irqtype == type && + (mp_irqs[i].mpc_dstapic == mp_ioapics[apic].mpc_apicid || + mp_irqs[i].mpc_dstapic == MP_APIC_ALL) && + mp_irqs[i].mpc_dstirq == pin) + return i; + + return -1; +} + +/* + * Find the pin to which IRQ[irq] (ISA) is connected + */ +static int __init find_isa_irq_pin(int irq, int type) +{ + int i; + + for (i = 0; i < mp_irq_entries; i++) { + int lbus = mp_irqs[i].mpc_srcbus; + + if ((mp_bus_id_to_type[lbus] == MP_BUS_ISA || + mp_bus_id_to_type[lbus] == MP_BUS_EISA || + mp_bus_id_to_type[lbus] == MP_BUS_MCA) && + (mp_irqs[i].mpc_irqtype == type) && + (mp_irqs[i].mpc_srcbusirq == irq)) + + return mp_irqs[i].mpc_dstirq; + } + return -1; +} + +/* + * Find a specific PCI IRQ entry. + * Not an __init, possibly needed by modules + */ +static int pin_2_irq(int idx, int apic, int pin); + +int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin) +{ + int apic, i, best_guess = -1; + + Dprintk("querying PCI -> IRQ mapping bus:%d, slot:%d, pin:%d.\n", + bus, slot, pin); + if (mp_bus_id_to_pci_bus[bus] == -1) { + printk(KERN_WARNING "PCI BIOS passed nonexistent PCI bus %d!\n", bus); + return -1; + } + for (i = 0; i < mp_irq_entries; i++) { + int lbus = mp_irqs[i].mpc_srcbus; + + for (apic = 0; apic < nr_ioapics; apic++) + if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic || + mp_irqs[i].mpc_dstapic == MP_APIC_ALL) + break; + + if ((mp_bus_id_to_type[lbus] == MP_BUS_PCI) && + !mp_irqs[i].mpc_irqtype && + (bus == lbus) && + (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f))) { + int irq = pin_2_irq(i,apic,mp_irqs[i].mpc_dstirq); + + if (!(apic || IO_APIC_IRQ(irq))) + continue; + + if (pin == (mp_irqs[i].mpc_srcbusirq & 3)) + return irq; + /* + * Use the first all-but-pin matching entry as a + * best-guess fuzzy result for broken mptables. + */ + if (best_guess < 0) + best_guess = irq; + } + } + return best_guess; +} + +/* + * EISA Edge/Level control register, ELCR + */ +static int __init EISA_ELCR(unsigned int irq) +{ + if (irq < 16) { + unsigned int port = 0x4d0 + (irq >> 3); + return (inb(port) >> (irq & 7)) & 1; + } + printk(KERN_INFO "Broken MPtable reports ISA irq %d\n", irq); + return 0; +} + +/* EISA interrupts are always polarity zero and can be edge or level + * trigger depending on the ELCR value. If an interrupt is listed as + * EISA conforming in the MP table, that means its trigger type must + * be read in from the ELCR */ + +#define default_EISA_trigger(idx) (EISA_ELCR(mp_irqs[idx].mpc_srcbusirq)) +#define default_EISA_polarity(idx) (0) + +/* ISA interrupts are always polarity zero edge triggered, + * when listed as conforming in the MP table. */ + +#define default_ISA_trigger(idx) (0) +#define default_ISA_polarity(idx) (0) + +/* PCI interrupts are always polarity one level triggered, + * when listed as conforming in the MP table. */ + +#define default_PCI_trigger(idx) (1) +#define default_PCI_polarity(idx) (1) + +/* MCA interrupts are always polarity zero level triggered, + * when listed as conforming in the MP table. */ + +#define default_MCA_trigger(idx) (1) +#define default_MCA_polarity(idx) (0) + +static int __init MPBIOS_polarity(int idx) +{ + int bus = mp_irqs[idx].mpc_srcbus; + int polarity; + + /* + * Determine IRQ line polarity (high active or low active): + */ + switch (mp_irqs[idx].mpc_irqflag & 3) + { + case 0: /* conforms, ie. bus-type dependent polarity */ + { + switch (mp_bus_id_to_type[bus]) + { + case MP_BUS_ISA: /* ISA pin */ + { + polarity = default_ISA_polarity(idx); + break; + } + case MP_BUS_EISA: /* EISA pin */ + { + polarity = default_EISA_polarity(idx); + break; + } + case MP_BUS_PCI: /* PCI pin */ + { + polarity = default_PCI_polarity(idx); + break; + } + case MP_BUS_MCA: /* MCA pin */ + { + polarity = default_MCA_polarity(idx); + break; + } + default: + { + printk(KERN_WARNING "broken BIOS!!\n"); + polarity = 1; + break; + } + } + break; + } + case 1: /* high active */ + { + polarity = 0; + break; + } + case 2: /* reserved */ + { + printk(KERN_WARNING "broken BIOS!!\n"); + polarity = 1; + break; + } + case 3: /* low active */ + { + polarity = 1; + break; + } + default: /* invalid */ + { + printk(KERN_WARNING "broken BIOS!!\n"); + polarity = 1; + break; + } + } + return polarity; +} + +static int __init MPBIOS_trigger(int idx) +{ + int bus = mp_irqs[idx].mpc_srcbus; + int trigger; + + /* + * Determine IRQ trigger mode (edge or level sensitive): + */ + switch ((mp_irqs[idx].mpc_irqflag>>2) & 3) + { + case 0: /* conforms, ie. bus-type dependent */ + { + switch (mp_bus_id_to_type[bus]) + { + case MP_BUS_ISA: /* ISA pin */ + { + trigger = default_ISA_trigger(idx); + break; + } + case MP_BUS_EISA: /* EISA pin */ + { + trigger = default_EISA_trigger(idx); + break; + } + case MP_BUS_PCI: /* PCI pin */ + { + trigger = default_PCI_trigger(idx); + break; + } + case MP_BUS_MCA: /* MCA pin */ + { + trigger = default_MCA_trigger(idx); + break; + } + default: + { + printk(KERN_WARNING "broken BIOS!!\n"); + trigger = 1; + break; + } + } + break; + } + case 1: /* edge */ + { + trigger = 0; + break; + } + case 2: /* reserved */ + { + printk(KERN_WARNING "broken BIOS!!\n"); + trigger = 1; + break; + } + case 3: /* level */ + { + trigger = 1; + break; + } + default: /* invalid */ + { + printk(KERN_WARNING "broken BIOS!!\n"); + trigger = 0; + break; + } + } + return trigger; +} + +static inline int irq_polarity(int idx) +{ + return MPBIOS_polarity(idx); +} + +static inline int irq_trigger(int idx) +{ + return MPBIOS_trigger(idx); +} + +static int pin_2_irq(int idx, int apic, int pin) +{ + int irq, i; + int bus = mp_irqs[idx].mpc_srcbus; + + /* + * Debugging check, we are in big trouble if this message pops up! + */ + if (mp_irqs[idx].mpc_dstirq != pin) + printk(KERN_ERR "broken BIOS or MPTABLE parser, ayiee!!\n"); + + switch (mp_bus_id_to_type[bus]) + { + case MP_BUS_ISA: /* ISA pin */ + case MP_BUS_EISA: + case MP_BUS_MCA: + { + irq = mp_irqs[idx].mpc_srcbusirq; + break; + } + case MP_BUS_PCI: /* PCI pin */ + { + /* + * PCI IRQs are mapped in order + */ + i = irq = 0; + while (i < apic) + irq += nr_ioapic_registers[i++]; + irq += pin; + break; + } + default: + { + printk(KERN_ERR "unknown bus type %d.\n",bus); + irq = 0; + break; + } + } + + /* + * PCI IRQ command line redirection. Yes, limits are hardcoded. + */ + if ((pin >= 16) && (pin <= 23)) { + if (pirq_entries[pin-16] != -1) { + if (!pirq_entries[pin-16]) { + printk(KERN_DEBUG "disabling PIRQ%d\n", pin-16); + } else { + irq = pirq_entries[pin-16]; + printk(KERN_DEBUG "using PIRQ%d -> IRQ %d\n", + pin-16, irq); + } + } + } + return irq; +} + +static inline int IO_APIC_irq_trigger(int irq) +{ + int apic, idx, pin; + + for (apic = 0; apic < nr_ioapics; apic++) { + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { + idx = find_irq_entry(apic,pin,mp_INT); + if ((idx != -1) && (irq == pin_2_irq(idx,apic,pin))) + return irq_trigger(idx); + } + } + /* + * nonexistent IRQs are edge default + */ + return 0; +} + +int irq_vector[NR_IRQS] = { FIRST_DEVICE_VECTOR , 0 }; + +static int __init assign_irq_vector(int irq) +{ + static int current_vector = FIRST_DEVICE_VECTOR, offset = 0; + if (IO_APIC_VECTOR(irq) > 0) + return IO_APIC_VECTOR(irq); +next: + current_vector += 8; + if (current_vector == IA32_SYSCALL_VECTOR) + goto next; + + if (current_vector > FIRST_SYSTEM_VECTOR) { + offset++; + current_vector = FIRST_DEVICE_VECTOR + offset; + } + + if (current_vector == FIRST_SYSTEM_VECTOR) + panic("ran out of interrupt sources!"); + + IO_APIC_VECTOR(irq) = current_vector; + return current_vector; +} + +extern void (*interrupt[NR_IRQS])(void); +static struct hw_interrupt_type ioapic_level_irq_type; +static struct hw_interrupt_type ioapic_edge_irq_type; + +void __init setup_IO_APIC_irqs(void) +{ + struct IO_APIC_route_entry entry; + int apic, pin, idx, irq, first_notcon = 1, vector; + unsigned long flags; + + printk(KERN_DEBUG "init IO_APIC IRQs\n"); + + for (apic = 0; apic < nr_ioapics; apic++) { + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { + + /* + * add it to the IO-APIC irq-routing table: + */ + memset(&entry,0,sizeof(entry)); + + entry.delivery_mode = dest_LowestPrio; + entry.dest_mode = 1; /* logical delivery */ + entry.mask = 0; /* enable IRQ */ + entry.dest.logical.logical_dest = TARGET_CPUS; + + idx = find_irq_entry(apic,pin,mp_INT); + if (idx == -1) { + if (first_notcon) { + printk(KERN_DEBUG " IO-APIC (apicid-pin) %d-%d", mp_ioapics[apic].mpc_apicid, pin); + first_notcon = 0; + } else + printk(", %d-%d", mp_ioapics[apic].mpc_apicid, pin); + continue; + } + + entry.trigger = irq_trigger(idx); + entry.polarity = irq_polarity(idx); + + if (irq_trigger(idx)) { + entry.trigger = 1; + entry.mask = 1; + entry.dest.logical.logical_dest = TARGET_CPUS; + } + + irq = pin_2_irq(idx, apic, pin); + add_pin_to_irq(irq, apic, pin); + + if (!apic && !IO_APIC_IRQ(irq)) + continue; + + if (IO_APIC_IRQ(irq)) { + vector = assign_irq_vector(irq); + entry.vector = vector; + + if (IO_APIC_irq_trigger(irq)) + irq_desc[irq].handler = &ioapic_level_irq_type; + else + irq_desc[irq].handler = &ioapic_edge_irq_type; + + set_intr_gate(vector, interrupt[irq]); + + if (!apic && (irq < 16)) + disable_8259A_irq(irq); + } + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1)); + io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0)); + spin_unlock_irqrestore(&ioapic_lock, flags); + } + } + + if (!first_notcon) + printk(" not connected.\n"); +} + +/* + * Set up the 8259A-master output pin as broadcast to all + * CPUs. + */ +void __init setup_ExtINT_IRQ0_pin(unsigned int pin, int vector) +{ + struct IO_APIC_route_entry entry; + unsigned long flags; + + memset(&entry,0,sizeof(entry)); + + disable_8259A_irq(0); + + /* mask LVT0 */ + apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); + + /* + * We use logical delivery to get the timer IRQ + * to the first CPU. + */ + entry.dest_mode = 1; /* logical delivery */ + entry.mask = 0; /* unmask IRQ now */ + entry.dest.logical.logical_dest = TARGET_CPUS; + entry.delivery_mode = dest_LowestPrio; + entry.polarity = 0; + entry.trigger = 0; + entry.vector = vector; + + /* + * The timer IRQ doesnt have to know that behind the + * scene we have a 8259A-master in AEOI mode ... + */ + irq_desc[0].handler = &ioapic_edge_irq_type; + + /* + * Add it to the IO-APIC irq-routing table: + */ + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(0, 0x11+2*pin, *(((int *)&entry)+1)); + io_apic_write(0, 0x10+2*pin, *(((int *)&entry)+0)); + spin_unlock_irqrestore(&ioapic_lock, flags); + + enable_8259A_irq(0); +} + +void __init UNEXPECTED_IO_APIC(void) +{ + printk(KERN_WARNING " WARNING: unexpected IO-APIC, please mail\n"); + printk(KERN_WARNING " to linux-smp@vger.kernel.org\n"); +} + +void __init print_IO_APIC(void) +{ + int apic, i; + struct IO_APIC_reg_00 reg_00; + struct IO_APIC_reg_01 reg_01; + struct IO_APIC_reg_02 reg_02; + unsigned long flags; + + printk(KERN_DEBUG "number of MP IRQ sources: %d.\n", mp_irq_entries); + for (i = 0; i < nr_ioapics; i++) + printk(KERN_DEBUG "number of IO-APIC #%d registers: %d.\n", + mp_ioapics[i].mpc_apicid, nr_ioapic_registers[i]); + + /* + * We are a bit conservative about what we expect. We have to + * know about every hardware change ASAP. + */ + printk(KERN_INFO "testing the IO APIC.......................\n"); + + for (apic = 0; apic < nr_ioapics; apic++) { + + spin_lock_irqsave(&ioapic_lock, flags); + *(int *)®_00 = io_apic_read(apic, 0); + *(int *)®_01 = io_apic_read(apic, 1); + if (reg_01.version >= 0x10) + *(int *)®_02 = io_apic_read(apic, 2); + spin_unlock_irqrestore(&ioapic_lock, flags); + + printk("\n"); + printk(KERN_DEBUG "IO APIC #%d......\n", mp_ioapics[apic].mpc_apicid); + printk(KERN_DEBUG ".... register #00: %08X\n", *(int *)®_00); + printk(KERN_DEBUG "....... : physical APIC id: %02X\n", reg_00.ID); + if (reg_00.__reserved_1 || reg_00.__reserved_2) + UNEXPECTED_IO_APIC(); + + printk(KERN_DEBUG ".... register #01: %08X\n", *(int *)®_01); + printk(KERN_DEBUG "....... : max redirection entries: %04X\n", reg_01.entries); + if ( (reg_01.entries != 0x0f) && /* older (Neptune) boards */ + (reg_01.entries != 0x17) && /* typical ISA+PCI boards */ + (reg_01.entries != 0x1b) && /* Compaq Proliant boards */ + (reg_01.entries != 0x1f) && /* dual Xeon boards */ + (reg_01.entries != 0x22) && /* bigger Xeon boards */ + (reg_01.entries != 0x2E) && + (reg_01.entries != 0x3F) + ) + UNEXPECTED_IO_APIC(); + + printk(KERN_DEBUG "....... : PRQ implemented: %X\n", reg_01.PRQ); + printk(KERN_DEBUG "....... : IO APIC version: %04X\n", reg_01.version); + if ( (reg_01.version != 0x01) && /* 82489DX IO-APICs */ + (reg_01.version != 0x10) && /* oldest IO-APICs */ + (reg_01.version != 0x11) && /* Pentium/Pro IO-APICs */ + (reg_01.version != 0x13) && /* Xeon IO-APICs */ + (reg_01.entries != 0x20) /* Intel P64H (82806 AA) */ + ) + UNEXPECTED_IO_APIC(); + if (reg_01.__reserved_1 || reg_01.__reserved_2) + UNEXPECTED_IO_APIC(); + + if (reg_01.version >= 0x10) { + printk(KERN_DEBUG ".... register #02: %08X\n", *(int *)®_02); + printk(KERN_DEBUG "....... : arbitration: %02X\n", reg_02.arbitration); + if (reg_02.__reserved_1 || reg_02.__reserved_2) + UNEXPECTED_IO_APIC(); + } + + printk(KERN_DEBUG ".... IRQ redirection table:\n"); + + printk(KERN_DEBUG " NR Log Phy Mask Trig IRR Pol" + " Stat Dest Deli Vect: \n"); + + for (i = 0; i <= reg_01.entries; i++) { + struct IO_APIC_route_entry entry; + + spin_lock_irqsave(&ioapic_lock, flags); + *(((int *)&entry)+0) = io_apic_read(apic, 0x10+i*2); + *(((int *)&entry)+1) = io_apic_read(apic, 0x11+i*2); + spin_unlock_irqrestore(&ioapic_lock, flags); + + printk(KERN_DEBUG " %02x %03X %02X ", + i, + entry.dest.logical.logical_dest, + entry.dest.physical.physical_dest + ); + + printk("%1d %1d %1d %1d %1d %1d %1d %02X\n", + entry.mask, + entry.trigger, + entry.irr, + entry.polarity, + entry.delivery_status, + entry.dest_mode, + entry.delivery_mode, + entry.vector + ); + } + } + printk(KERN_DEBUG "IRQ to pin mappings:\n"); + for (i = 0; i < NR_IRQS; i++) { + struct irq_pin_list *entry = irq_2_pin + i; + if (entry->pin < 0) + continue; + printk(KERN_DEBUG "IRQ%d ", i); + for (;;) { + printk("-> %d:%d", entry->apic, entry->pin); + if (!entry->next) + break; + entry = irq_2_pin + entry->next; + } + printk("\n"); + } + + printk(KERN_INFO ".................................... done.\n"); + + return; +} + +static void print_APIC_bitfield (int base) +{ + unsigned int v; + int i, j; + + printk(KERN_DEBUG "0123456789abcdef0123456789abcdef\n" KERN_DEBUG); + for (i = 0; i < 8; i++) { + v = apic_read(base + i*0x10); + for (j = 0; j < 32; j++) { + if (v & (1< 3) /* Due to the Pentium erratum 3AP. */ + apic_write(APIC_ESR, 0); + v = apic_read(APIC_ESR); + printk(KERN_DEBUG "... APIC ESR: %08x\n", v); + } + + v = apic_read(APIC_ICR); + printk(KERN_DEBUG "... APIC ICR: %08x\n", v); + v = apic_read(APIC_ICR2); + printk(KERN_DEBUG "... APIC ICR2: %08x\n", v); + + v = apic_read(APIC_LVTT); + printk(KERN_DEBUG "... APIC LVTT: %08x\n", v); + + if (maxlvt > 3) { /* PC is LVT#4. */ + v = apic_read(APIC_LVTPC); + printk(KERN_DEBUG "... APIC LVTPC: %08x\n", v); + } + v = apic_read(APIC_LVT0); + printk(KERN_DEBUG "... APIC LVT0: %08x\n", v); + v = apic_read(APIC_LVT1); + printk(KERN_DEBUG "... APIC LVT1: %08x\n", v); + + if (maxlvt > 2) { /* ERR is LVT#3. */ + v = apic_read(APIC_LVTERR); + printk(KERN_DEBUG "... APIC LVTERR: %08x\n", v); + } + + v = apic_read(APIC_TMICT); + printk(KERN_DEBUG "... APIC TMICT: %08x\n", v); + v = apic_read(APIC_TMCCT); + printk(KERN_DEBUG "... APIC TMCCT: %08x\n", v); + v = apic_read(APIC_TDCR); + printk(KERN_DEBUG "... APIC TDCR: %08x\n", v); + printk("\n"); +} + +void print_all_local_APICs (void) +{ + smp_call_function(print_local_APIC, NULL, 1, 1); + print_local_APIC(NULL); +} + +void /*__init*/ print_PIC(void) +{ + extern spinlock_t i8259A_lock; + unsigned int v; + unsigned long flags; + + printk(KERN_DEBUG "\nprinting PIC contents\n"); + + spin_lock_irqsave(&i8259A_lock, flags); + + v = inb(0xa1) << 8 | inb(0x21); + printk(KERN_DEBUG "... PIC IMR: %04x\n", v); + + v = inb(0xa0) << 8 | inb(0x20); + printk(KERN_DEBUG "... PIC IRR: %04x\n", v); + + outb(0x0b,0xa0); + outb(0x0b,0x20); + v = inb(0xa0) << 8 | inb(0x20); + outb(0x0a,0xa0); + outb(0x0a,0x20); + + spin_unlock_irqrestore(&i8259A_lock, flags); + + printk(KERN_DEBUG "... PIC ISR: %04x\n", v); + + v = inb(0x4d1) << 8 | inb(0x4d0); + printk(KERN_DEBUG "... PIC ELCR: %04x\n", v); +} + +static void __init enable_IO_APIC(void) +{ + struct IO_APIC_reg_01 reg_01; + int i; + unsigned long flags; + + for (i = 0; i < PIN_MAP_SIZE; i++) { + irq_2_pin[i].pin = -1; + irq_2_pin[i].next = 0; + } + if (!pirqs_enabled) + for (i = 0; i < MAX_PIRQS; i++) + pirq_entries[i] = -1; + + /* + * The number of IO-APIC IRQ registers (== #pins): + */ + for (i = 0; i < nr_ioapics; i++) { + spin_lock_irqsave(&ioapic_lock, flags); + *(int *)®_01 = io_apic_read(i, 1); + spin_unlock_irqrestore(&ioapic_lock, flags); + nr_ioapic_registers[i] = reg_01.entries+1; + } + + /* + * Do not trust the IO-APIC being empty at bootup + */ + clear_IO_APIC(); +} + +/* + * Not an __init, needed by the reboot code + */ +void disable_IO_APIC(void) +{ + /* + * Clear the IO-APIC before rebooting: + */ + clear_IO_APIC(); + + disconnect_bsp_APIC(); +} + +/* + * function to set the IO-APIC physical IDs based on the + * values stored in the MPC table. + * + * by Matt Domsch Tue Dec 21 12:25:05 CST 1999 + */ + +static void __init setup_ioapic_ids_from_mpc (void) +{ + struct IO_APIC_reg_00 reg_00; + unsigned long phys_id_present_map = phys_cpu_present_map; + int apic; + int i; + unsigned char old_id; + unsigned long flags; + + /* + * Set the IOAPIC ID to the value stored in the MPC table. + */ + for (apic = 0; apic < nr_ioapics; apic++) { + + /* Read the register 0 value */ + spin_lock_irqsave(&ioapic_lock, flags); + *(int *)®_00 = io_apic_read(apic, 0); + spin_unlock_irqrestore(&ioapic_lock, flags); + + old_id = mp_ioapics[apic].mpc_apicid; + + if (mp_ioapics[apic].mpc_apicid >= 0xf) { + printk(KERN_ERR "BIOS bug, IO-APIC#%d ID is %d in the MPC table!...\n", + apic, mp_ioapics[apic].mpc_apicid); + printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n", + reg_00.ID); + mp_ioapics[apic].mpc_apicid = reg_00.ID; + } + + /* + * Sanity check, is the ID really free? Every APIC in a + * system must have a unique ID or we get lots of nice + * 'stuck on smp_invalidate_needed IPI wait' messages. + */ + if (phys_id_present_map & (1 << mp_ioapics[apic].mpc_apicid)) { + printk(KERN_ERR "BIOS bug, IO-APIC#%d ID %d is already used!...\n", + apic, mp_ioapics[apic].mpc_apicid); + for (i = 0; i < 0xf; i++) + if (!(phys_id_present_map & (1 << i))) + break; + if (i >= 0xf) + panic("Max APIC ID exceeded!\n"); + printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n", + i); + phys_id_present_map |= 1 << i; + mp_ioapics[apic].mpc_apicid = i; + } + + /* + * We need to adjust the IRQ routing table + * if the ID changed. + */ + if (old_id != mp_ioapics[apic].mpc_apicid) + for (i = 0; i < mp_irq_entries; i++) + if (mp_irqs[i].mpc_dstapic == old_id) + mp_irqs[i].mpc_dstapic + = mp_ioapics[apic].mpc_apicid; + + /* + * Read the right value from the MPC table and + * write it into the ID register. + */ + printk(KERN_INFO "...changing IO-APIC physical APIC ID to %d ...", + mp_ioapics[apic].mpc_apicid); + + reg_00.ID = mp_ioapics[apic].mpc_apicid; + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(apic, 0, *(int *)®_00); + spin_unlock_irqrestore(&ioapic_lock, flags); + + /* + * Sanity check + */ + spin_lock_irqsave(&ioapic_lock, flags); + *(int *)®_00 = io_apic_read(apic, 0); + spin_unlock_irqrestore(&ioapic_lock, flags); + if (reg_00.ID != mp_ioapics[apic].mpc_apicid) + panic("could not set ID!\n"); + else + printk(" ok.\n"); + } +} + +/* + * There is a nasty bug in some older SMP boards, their mptable lies + * about the timer IRQ. We do the following to work around the situation: + * + * - timer IRQ defaults to IO-APIC IRQ + * - if this function detects that timer IRQs are defunct, then we fall + * back to ISA timer IRQs + */ +static int __init timer_irq_works(void) +{ + unsigned int t1 = jiffies; + + sti(); + /* Let ten ticks pass... */ + mdelay((10 * 1000) / HZ); + + /* + * Expect a few ticks at least, to be sure some possible + * glue logic does not lock up after one or two first + * ticks in a non-ExtINT mode. Also the local APIC + * might have cached one ExtINT interrupt. Finally, at + * least one tick may be lost due to delays. + */ + if (jiffies - t1 > 4) + return 1; + + return 0; +} + +/* + * In the SMP+IOAPIC case it might happen that there are an unspecified + * number of pending IRQ events unhandled. These cases are very rare, + * so we 'resend' these IRQs via IPIs, to the same CPU. It's much + * better to do it this way as thus we do not have to be aware of + * 'pending' interrupts in the IRQ path, except at this point. + */ +/* + * Edge triggered needs to resend any interrupt + * that was delayed but this is now handled in the device + * independent code. + */ +#define enable_edge_ioapic_irq unmask_IO_APIC_irq + +static void disable_edge_ioapic_irq (unsigned int irq) { /* nothing */ } + +/* + * Starting up a edge-triggered IO-APIC interrupt is + * nasty - we need to make sure that we get the edge. + * If it is already asserted for some reason, we need + * return 1 to indicate that is was pending. + * + * This is not complete - we should be able to fake + * an edge even if it isn't on the 8259A... + */ + +static unsigned int startup_edge_ioapic_irq(unsigned int irq) +{ + int was_pending = 0; + unsigned long flags; + + spin_lock_irqsave(&ioapic_lock, flags); + if (irq < 16) { + disable_8259A_irq(irq); + if (i8259A_irq_pending(irq)) + was_pending = 1; + } + __unmask_IO_APIC_irq(irq); + spin_unlock_irqrestore(&ioapic_lock, flags); + + return was_pending; +} + +#define shutdown_edge_ioapic_irq disable_edge_ioapic_irq + +/* + * Once we have recorded IRQ_PENDING already, we can mask the + * interrupt for real. This prevents IRQ storms from unhandled + * devices. + */ +static void ack_edge_ioapic_irq(unsigned int irq) +{ + if ((irq_desc[irq].status & (IRQ_PENDING | IRQ_DISABLED)) + == (IRQ_PENDING | IRQ_DISABLED)) + mask_IO_APIC_irq(irq); + ack_APIC_irq(); +} + +static void end_edge_ioapic_irq (unsigned int i) { /* nothing */ } + + +/* + * Level triggered interrupts can just be masked, + * and shutting down and starting up the interrupt + * is the same as enabling and disabling them -- except + * with a startup need to return a "was pending" value. + * + * Level triggered interrupts are special because we + * do not touch any IO-APIC register while handling + * them. We ack the APIC in the end-IRQ handler, not + * in the start-IRQ-handler. Protection against reentrance + * from the same interrupt is still provided, both by the + * generic IRQ layer and by the fact that an unacked local + * APIC does not accept IRQs. + */ +static unsigned int startup_level_ioapic_irq (unsigned int irq) +{ + unmask_IO_APIC_irq(irq); + + return 0; /* don't check for pending */ +} + +#define shutdown_level_ioapic_irq mask_IO_APIC_irq +#define enable_level_ioapic_irq unmask_IO_APIC_irq +#define disable_level_ioapic_irq mask_IO_APIC_irq + +static void end_level_ioapic_irq (unsigned int irq) +{ + unsigned long v; + int i; + +/* + * It appears there is an erratum which affects at least version 0x11 + * of I/O APIC (that's the 82093AA and cores integrated into various + * chipsets). Under certain conditions a level-triggered interrupt is + * erroneously delivered as edge-triggered one but the respective IRR + * bit gets set nevertheless. As a result the I/O unit expects an EOI + * message but it will never arrive and further interrupts are blocked + * from the source. The exact reason is so far unknown, but the + * phenomenon was observed when two consecutive interrupt requests + * from a given source get delivered to the same CPU and the source is + * temporarily disabled in between. + * + * A workaround is to simulate an EOI message manually. We achieve it + * by setting the trigger mode to edge and then to level when the edge + * trigger mode gets detected in the TMR of a local APIC for a + * level-triggered interrupt. We mask the source for the time of the + * operation to prevent an edge-triggered interrupt escaping meanwhile. + * The idea is from Manfred Spraul. --macro + */ + i = IO_APIC_VECTOR(irq); + v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1)); + + ack_APIC_irq(); + + if (!(v & (1 << (i & 0x1f)))) { +#ifdef APIC_MISMATCH_DEBUG + atomic_inc(&irq_mis_count); +#endif + spin_lock(&ioapic_lock); + __mask_and_edge_IO_APIC_irq(irq); +#ifdef APIC_LOCKUP_DEBUG + for (;;) { + struct irq_pin_list *entry = irq_2_pin + irq; + unsigned int reg; + + if (entry->pin == -1) + break; + reg = io_apic_read(entry->apic, 0x10 + entry->pin * 2); + if (reg & 0x00004000) + printk(KERN_CRIT "Aieee!!! Remote IRR" + " still set after unlock!\n"); + if (!entry->next) + break; + entry = irq_2_pin + entry->next; + } +#endif + __unmask_and_level_IO_APIC_irq(irq); + spin_unlock(&ioapic_lock); + } +} + +static void mask_and_ack_level_ioapic_irq (unsigned int irq) { /* nothing */ } + +static void set_ioapic_affinity (unsigned int irq, unsigned long mask) +{ + unsigned long flags; + /* + * Only the first 8 bits are valid. + */ + mask = mask << 24; + + spin_lock_irqsave(&ioapic_lock, flags); + __DO_ACTION(1, = mask, ) + spin_unlock_irqrestore(&ioapic_lock, flags); +} + +/* + * Level and edge triggered IO-APIC interrupts need different handling, + * so we use two separate IRQ descriptors. Edge triggered IRQs can be + * handled with the level-triggered descriptor, but that one has slightly + * more overhead. Level-triggered interrupts cannot be handled with the + * edge-triggered handler, without risking IRQ storms and other ugly + * races. + */ + +static struct hw_interrupt_type ioapic_edge_irq_type = { + "IO-APIC-edge", + startup_edge_ioapic_irq, + shutdown_edge_ioapic_irq, + enable_edge_ioapic_irq, + disable_edge_ioapic_irq, + ack_edge_ioapic_irq, + end_edge_ioapic_irq, + set_ioapic_affinity, +}; + +static struct hw_interrupt_type ioapic_level_irq_type = { + "IO-APIC-level", + startup_level_ioapic_irq, + shutdown_level_ioapic_irq, + enable_level_ioapic_irq, + disable_level_ioapic_irq, + mask_and_ack_level_ioapic_irq, + end_level_ioapic_irq, + set_ioapic_affinity, +}; + +static inline void init_IO_APIC_traps(void) +{ + int irq; + + /* + * NOTE! The local APIC isn't very good at handling + * multiple interrupts at the same interrupt level. + * As the interrupt level is determined by taking the + * vector number and shifting that right by 4, we + * want to spread these out a bit so that they don't + * all fall in the same interrupt level. + * + * Also, we've got to be careful not to trash gate + * 0x80, because int 0x80 is hm, kind of importantish. ;) + */ + for (irq = 0; irq < NR_IRQS ; irq++) { + if (IO_APIC_IRQ(irq) && !IO_APIC_VECTOR(irq)) { + /* + * Hmm.. We don't have an entry for this, + * so default to an old-fashioned 8259 + * interrupt if we can.. + */ + if (irq < 16) + make_8259A_irq(irq); + else + /* Strange. Oh, well.. */ + irq_desc[irq].handler = &no_irq_type; + } + } +} + +static void enable_lapic_irq (unsigned int irq) +{ + unsigned long v; + + v = apic_read(APIC_LVT0); + apic_write_around(APIC_LVT0, v & ~APIC_LVT_MASKED); +} + +static void disable_lapic_irq (unsigned int irq) +{ + unsigned long v; + + v = apic_read(APIC_LVT0); + apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED); +} + +static void ack_lapic_irq (unsigned int irq) +{ + ack_APIC_irq(); +} + +static void end_lapic_irq (unsigned int i) { /* nothing */ } + +static struct hw_interrupt_type lapic_irq_type = { + "local-APIC-edge", + NULL, /* startup_irq() not used for IRQ0 */ + NULL, /* shutdown_irq() not used for IRQ0 */ + enable_lapic_irq, + disable_lapic_irq, + ack_lapic_irq, + end_lapic_irq +}; + +static void enable_NMI_through_LVT0 (void * dummy) +{ + unsigned int v, ver; + + ver = apic_read(APIC_LVR); + ver = GET_APIC_VERSION(ver); + v = APIC_DM_NMI; /* unmask and set to NMI */ + if (!APIC_INTEGRATED(ver)) /* 82489DX */ + v |= APIC_LVT_LEVEL_TRIGGER; + apic_write_around(APIC_LVT0, v); +} + +static void setup_nmi (void) +{ + /* + * Dirty trick to enable the NMI watchdog ... + * We put the 8259A master into AEOI mode and + * unmask on all local APICs LVT0 as NMI. + * + * The idea to use the 8259A in AEOI mode ('8259A Virtual Wire') + * is from Maciej W. Rozycki - so we do not have to EOI from + * the NMI handler or the timer interrupt. + */ + printk(KERN_INFO "activating NMI Watchdog ..."); + + smp_call_function(enable_NMI_through_LVT0, NULL, 1, 1); + enable_NMI_through_LVT0(NULL); + + printk(" done.\n"); +} + +/* + * This looks a bit hackish but it's about the only one way of sending + * a few INTA cycles to 8259As and any associated glue logic. ICR does + * not support the ExtINT mode, unfortunately. We need to send these + * cycles as some i82489DX-based boards have glue logic that keeps the + * 8259A interrupt line asserted until INTA. --macro + */ +static inline void unlock_ExtINT_logic(void) +{ + int pin, i; + struct IO_APIC_route_entry entry0, entry1; + unsigned char save_control, save_freq_select; + unsigned long flags; + + pin = find_isa_irq_pin(8, mp_INT); + if (pin == -1) + return; + + spin_lock_irqsave(&ioapic_lock, flags); + *(((int *)&entry0) + 1) = io_apic_read(0, 0x11 + 2 * pin); + *(((int *)&entry0) + 0) = io_apic_read(0, 0x10 + 2 * pin); + spin_unlock_irqrestore(&ioapic_lock, flags); + clear_IO_APIC_pin(0, pin); + + memset(&entry1, 0, sizeof(entry1)); + + entry1.dest_mode = 0; /* physical delivery */ + entry1.mask = 0; /* unmask IRQ now */ + entry1.dest.physical.physical_dest = hard_smp_processor_id(); + entry1.delivery_mode = dest_ExtINT; + entry1.polarity = entry0.polarity; + entry1.trigger = 0; + entry1.vector = 0; + + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry1) + 1)); + io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry1) + 0)); + spin_unlock_irqrestore(&ioapic_lock, flags); + + save_control = CMOS_READ(RTC_CONTROL); + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); + CMOS_WRITE((save_freq_select & ~RTC_RATE_SELECT) | 0x6, + RTC_FREQ_SELECT); + CMOS_WRITE(save_control | RTC_PIE, RTC_CONTROL); + + i = 100; + while (i-- > 0) { + mdelay(10); + if ((CMOS_READ(RTC_INTR_FLAGS) & RTC_PF) == RTC_PF) + i -= 10; + } + + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + clear_IO_APIC_pin(0, pin); + + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry0) + 1)); + io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry0) + 0)); + spin_unlock_irqrestore(&ioapic_lock, flags); +} + +/* + * This code may look a bit paranoid, but it's supposed to cooperate with + * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ + * is so screwy. Thanks to Brian Perkins for testing/hacking this beast + * fanatically on his truly buggy board. + */ +static inline void check_timer(void) +{ + extern int timer_ack; + int pin1, pin2; + int vector; + + /* + * get/set the timer IRQ vector: + */ + disable_8259A_irq(0); + vector = assign_irq_vector(0); + set_intr_gate(vector, interrupt[0]); + + /* + * Subtle, code in do_timer_interrupt() expects an AEOI + * mode for the 8259A whenever interrupts are routed + * through I/O APICs. Also IRQ0 has to be enabled in + * the 8259A which implies the virtual wire has to be + * disabled in the local APIC. + */ + apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); + init_8259A(1); + timer_ack = 1; + enable_8259A_irq(0); + + pin1 = find_isa_irq_pin(0, mp_INT); + pin2 = find_isa_irq_pin(0, mp_ExtINT); + + printk(KERN_INFO "..TIMER: vector=0x%02X pin1=%d pin2=%d\n", vector, pin1, pin2); + + if (pin1 != -1) { + /* + * Ok, does IRQ0 through the IOAPIC work? + */ + unmask_IO_APIC_irq(0); + if (timer_irq_works()) { + if (nmi_watchdog == NMI_IO_APIC) { + disable_8259A_irq(0); + setup_nmi(); + enable_8259A_irq(0); + check_nmi_watchdog(); + } + return; + } + clear_IO_APIC_pin(0, pin1); + printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n"); + } + + printk(KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... "); + if (pin2 != -1) { + printk("\n..... (found pin %d) ...", pin2); + /* + * legacy devices should be connected to IO APIC #0 + */ + setup_ExtINT_IRQ0_pin(pin2, vector); + if (timer_irq_works()) { + printk("works.\n"); + if (nmi_watchdog == NMI_IO_APIC) { + setup_nmi(); + check_nmi_watchdog(); + } + return; + } + /* + * Cleanup, just in case ... + */ + clear_IO_APIC_pin(0, pin2); + } + printk(" failed.\n"); + + if (nmi_watchdog) { + printk(KERN_WARNING "timer doesnt work through the IO-APIC - disabling NMI Watchdog!\n"); + nmi_watchdog = 0; + } + + printk(KERN_INFO "...trying to set up timer as Virtual Wire IRQ..."); + + disable_8259A_irq(0); + irq_desc[0].handler = &lapic_irq_type; + apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector); /* Fixed mode */ + enable_8259A_irq(0); + + if (timer_irq_works()) { + printk(" works.\n"); + return; + } + apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | vector); + printk(" failed.\n"); + + printk(KERN_INFO "...trying to set up timer as ExtINT IRQ..."); + + init_8259A(0); + make_8259A_irq(0); + apic_write_around(APIC_LVT0, APIC_DM_EXTINT); + + unlock_ExtINT_logic(); + + if (timer_irq_works()) { + printk(" works.\n"); + return; + } + printk(" failed :(.\n"); + panic("IO-APIC + timer doesn't work! pester mingo@redhat.com"); +} + +/* + * + * IRQ's that are handled by the old PIC in all cases: + * - IRQ2 is the cascade IRQ, and cannot be a io-apic IRQ. + * Linux doesn't really care, as it's not actually used + * for any interrupt handling anyway. + * - There used to be IRQ13 here as well, but all + * MPS-compliant must not use it for FPU coupling and we + * want to use exception 16 anyway. And there are + * systems who connect it to an I/O APIC for other uses. + * Thus we don't mark it special any longer. + * + * Additionally, something is definitely wrong with irq9 + * on PIIX4 boards. + */ +#define PIC_IRQS (1<<2) + +void __init setup_IO_APIC(void) +{ + enable_IO_APIC(); + + io_apic_irqs = ~PIC_IRQS; + printk("ENABLING IO-APIC IRQs\n"); + + /* + * Set up the IO-APIC IRQ routing table by parsing the MP-BIOS + * mptable: + */ + setup_ioapic_ids_from_mpc(); + sync_Arb_IDs(); + setup_IO_APIC_irqs(); + init_IO_APIC_traps(); + check_timer(); + print_IO_APIC(); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/ioport.c linux.ac/arch/x86_64/kernel/ioport.c --- linux.vanilla/arch/x86_64/kernel/ioport.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/ioport.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,114 @@ +/* + * linux/arch/i386/kernel/ioport.c + * + * This contains the io-permission bitmap code - written by obz, with changes + * by Linus. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */ +static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value) +{ + int mask; + unsigned long *bitmap_base = bitmap + (base >> 6); + unsigned short low_index = base & 0x3f; + int length = low_index + extent; + + if (low_index != 0) { + mask = (~0 << low_index); + if (length < 64) + mask &= ~(~0 << length); + if (new_value) + *bitmap_base++ |= mask; + else + *bitmap_base++ &= ~mask; + length -= 32; + } + + mask = (new_value ? ~0 : 0); + while (length >= 64) { + *bitmap_base++ = mask; + length -= 64; + } + + if (length > 0) { + mask = ~(~0 << length); + if (new_value) + *bitmap_base++ |= mask; + else + *bitmap_base++ &= ~mask; + } +} + +/* + * this changes the io permissions bitmap in the current task. + */ +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) +{ + struct thread_struct * t = ¤t->thread; + struct tss_struct * tss = init_tss + smp_processor_id(); + + if ((from + num <= from) || (from + num > IO_BITMAP_SIZE*32)) + return -EINVAL; + if (turn_on && !capable(CAP_SYS_RAWIO)) + return -EPERM; + /* + * If it's the first ioperm() call in this thread's lifetime, set the + * IO bitmap up. ioperm() is much less timing critical than clone(), + * this is why we delay this operation until now: + */ + if (!t->ioperm) { + /* + * just in case ... + */ + memset(t->io_bitmap,0xff,(IO_BITMAP_SIZE+1)*4); + t->ioperm = 1; + /* + * this activates it in the TSS + */ + tss->io_map_base = IO_BITMAP_OFFSET; + } + + /* + * do it in the per-thread copy and in the TSS ... + */ + set_bitmap(t->io_bitmap, from, num, !turn_on); + set_bitmap(tss->io_bitmap, from, num, !turn_on); + + return 0; +} + +/* + * sys_iopl has to be used when you want to access the IO ports + * beyond the 0x3ff range: to get the full 65536 ports bitmapped + * you'd need 8kB of bitmaps/process, which is a bit excessive. + * + * Here we just change the eflags value on the stack: we allow + * only the super-user to do it. This depends on the stack-layout + * on system-call entry - see also fork() and the signal handling + * code. + */ + +asmlinkage long sys_iopl(unsigned int level, struct pt_regs regs) +{ + unsigned int old = (regs.eflags >> 12) & 3; + + if (level > 3) + return -EINVAL; + /* Trying to gain more privileges? */ + if (level > old) { + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + } + regs.eflags = (regs.eflags & 0xffffffffffffcfff) | (level << 12); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/irq.c linux.ac/arch/x86_64/kernel/irq.c --- linux.vanilla/arch/x86_64/kernel/irq.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/irq.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,1199 @@ +/* + * linux/arch/i386/kernel/irq.c + * + * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + */ + +/* + * (mostly architecture independent, will move to kernel/irq.c in 2.5.) + * + * IRQs are in fact implemented a bit like signal handlers for the kernel. + * Naturally it's not a 1:1 relation, but there are similarities. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* + * Linux has a controller-independent x86 interrupt architecture. + * every controller has a 'controller-template', that is used + * by the main code to do the right thing. Each driver-visible + * interrupt source is transparently wired to the apropriate + * controller. Thus drivers need not be aware of the + * interrupt-controller. + * + * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC, + * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC. + * (IO-APICs assumed to be messaging to Pentium local-APICs) + * + * the code is designed to be easily extended with new/different + * interrupt controllers, without having to do assembly magic. + */ + +/* + * Controller mappings for all interrupt sources: + */ +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = + { [0 ... NR_IRQS-1] = { 0, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED}}; + +static void register_irq_proc (unsigned int irq); + +/* + * Special irq handlers. + */ + +void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } + +/* + * Generic no controller code + */ + +static void enable_none(unsigned int irq) { } +static unsigned int startup_none(unsigned int irq) { return 0; } +static void disable_none(unsigned int irq) { } +static void ack_none(unsigned int irq) +{ +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves, it doesnt deserve + * a generic callback i think. + */ +#if CONFIG_X86 + printk("unexpected IRQ trap at vector %02x\n", irq); +#ifdef CONFIG_X86_LOCAL_APIC + /* + * Currently unexpected vectors happen only on SMP and APIC. + * We _must_ ack these because every local APIC has only N + * irq slots per priority level, and a 'hanging, unacked' IRQ + * holds up an irq slot - in excessive cases (when multiple + * unexpected vectors occur) that might lock up the APIC + * completely. + */ + ack_APIC_irq(); +#endif +#endif +} + +/* startup is the same as "enable", shutdown is same as "disable" */ +#define shutdown_none disable_none +#define end_none enable_none + +struct hw_interrupt_type no_irq_type = { + "none", + startup_none, + shutdown_none, + enable_none, + disable_none, + ack_none, + end_none +}; + +atomic_t irq_err_count; +#ifdef CONFIG_X86_IO_APIC +#ifdef APIC_MISMATCH_DEBUG +atomic_t irq_mis_count; +#endif +#endif + +/* + * Generic, controller-independent functions: + */ + +int get_irq_list(char *buf) +{ + int i, j; + struct irqaction * action; + char *p = buf; + + p += sprintf(p, " "); + for (j=0; jtypename); + p += sprintf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + p += sprintf(p, ", %s", action->name); + *p++ = '\n'; + } + p += sprintf(p, "NMI: "); + for (j = 0; j < smp_num_cpus; j++) + p += sprintf(p, "%10u ", + nmi_count(cpu_logical_map(j))); + p += sprintf(p, "\n"); +#if CONFIG_X86_LOCAL_APIC + p += sprintf(p, "LOC: "); + for (j = 0; j < smp_num_cpus; j++) + p += sprintf(p, "%10u ", + apic_timer_irqs[cpu_logical_map(j)]); + p += sprintf(p, "\n"); +#endif + p += sprintf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); +#ifdef CONFIG_X86_IO_APIC +#ifdef APIC_MISMATCH_DEBUG + p += sprintf(p, "MIS: %10u\n", atomic_read(&irq_mis_count)); +#endif +#endif + return p - buf; +} + + +/* + * Global interrupt locks for SMP. Allow interrupts to come in on any + * CPU, yet make cli/sti act globally to protect critical regions.. + */ + +#ifdef CONFIG_SMP +unsigned char global_irq_holder = NO_PROC_ID; +unsigned volatile long global_irq_lock; /* pendantic: long for set_bit --RR */ + +extern void show_stack(unsigned long* esp); + + +/* XXX: this unfortunately doesn't support irqstacks currently, should check the other PDAs */ +static void show(char * str) +{ + int i; + int cpu = smp_processor_id(); + + printk("\n%s, CPU %d:\n", str, cpu); + printk("irq: %d [",irqs_running()); + for(i=0;i < smp_num_cpus;i++) + printk(" %d",local_irq_count(i)); + printk(" ]\nbh: %d [",spin_is_locked(&global_bh_lock) ? 1 : 0); + for(i=0;i < smp_num_cpus;i++) + printk(" %d",local_bh_count(i)); + + printk(" ]\nStack dumps:"); + for(i = 0; i < smp_num_cpus; i++) { + unsigned long esp; + if (i == cpu) + continue; + printk("\nCPU %d:",i); + esp = init_tss[i].rsp0; + if (!esp) { + /* tss->esp0 is set to NULL in cpu_init(), + * it's initialized when the cpu returns to user + * space. -- manfreds + */ + printk(" "); + continue; + } + esp &= ~(THREAD_SIZE-1); + esp += sizeof(struct task_struct); + show_stack((void*)esp); + } + printk("\nCPU %d:",cpu); + show_stack(NULL); + printk("\n"); +} + +#define MAXCOUNT 100000000 + +/* + * I had a lockup scenario where a tight loop doing + * spin_unlock()/spin_lock() on CPU#1 was racing with + * spin_lock() on CPU#0. CPU#0 should have noticed spin_unlock(), but + * apparently the spin_unlock() information did not make it + * through to CPU#0 ... nasty, is this by design, do we have to limit + * 'memory update oscillation frequency' artificially like here? + * + * Such 'high frequency update' races can be avoided by careful design, but + * some of our major constructs like spinlocks use similar techniques, + * it would be nice to clarify this issue. Set this define to 0 if you + * want to check whether your system freezes. I suspect the delay done + * by SYNC_OTHER_CORES() is in correlation with 'snooping latency', but + * i thought that such things are guaranteed by design, since we use + * the 'LOCK' prefix. + */ +#define SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND 0 + +#if SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND +# define SYNC_OTHER_CORES(x) udelay(x+1) +#else +/* + * We have to allow irqs to arrive between __sti and __cli + */ +# define SYNC_OTHER_CORES(x) __asm__ __volatile__ ("nop") +#endif + +static inline void wait_on_irq(int cpu) +{ + int count = MAXCOUNT; + + for (;;) { + + /* + * Wait until all interrupts are gone. Wait + * for bottom half handlers unless we're + * already executing in one.. + */ + if (!irqs_running()) + if (local_bh_count(cpu) || !spin_is_locked(&global_bh_lock)) + break; + + /* Duh, we have to loop. Release the lock to avoid deadlocks */ + clear_bit(0,&global_irq_lock); + + for (;;) { + if (!--count) { + show("wait_on_irq"); + count = ~0; + } + __sti(); + SYNC_OTHER_CORES(cpu); + __cli(); + if (irqs_running()) + continue; + if (global_irq_lock) + continue; + if (!local_bh_count(cpu) && spin_is_locked(&global_bh_lock)) + continue; + if (!test_and_set_bit(0,&global_irq_lock)) + break; + } + } +} + +/* + * This is called when we want to synchronize with + * interrupts. We may for example tell a device to + * stop sending interrupts: but to make sure there + * are no interrupts that are executing on another + * CPU we need to call this function. + */ +void synchronize_irq(void) +{ + if (irqs_running()) { + /* Stupid approach */ + cli(); + sti(); + } +} + +static inline void get_irqlock(int cpu) +{ + if (test_and_set_bit(0,&global_irq_lock)) { + /* do we already hold the lock? */ + if ((unsigned char) cpu == global_irq_holder) + return; + /* Uhhuh.. Somebody else got it. Wait.. */ + do { + do { + rep_nop(); + } while (test_bit(0,&global_irq_lock)); + } while (test_and_set_bit(0,&global_irq_lock)); + } + /* + * We also to make sure that nobody else is running + * in an interrupt context. + */ + wait_on_irq(cpu); + + /* + * Ok, finally.. + */ + global_irq_holder = cpu; +} + +#define EFLAGS_IF_SHIFT 9 + +/* + * A global "cli()" while in an interrupt context + * turns into just a local cli(). Interrupts + * should use spinlocks for the (very unlikely) + * case that they ever want to protect against + * each other. + * + * If we already have local interrupts disabled, + * this will not turn a local disable into a + * global one (problems with spinlocks: this makes + * save_flags+cli+sti usable inside a spinlock). + */ +void __global_cli(void) +{ + unsigned int flags; + + __save_flags(flags); + if (flags & (1 << EFLAGS_IF_SHIFT)) { + int cpu = smp_processor_id(); + __cli(); + if (!local_irq_count(cpu)) + get_irqlock(cpu); + } +} + +void __global_sti(void) +{ + int cpu = smp_processor_id(); + + if (!local_irq_count(cpu)) + release_irqlock(cpu); + __sti(); +} + +/* + * SMP flags value to restore to: + * 0 - global cli + * 1 - global sti + * 2 - local cli + * 3 - local sti + */ +unsigned long __global_save_flags(void) +{ + int retval; + int local_enabled; + unsigned long flags; + int cpu = smp_processor_id(); + + __save_flags(flags); + local_enabled = (flags >> EFLAGS_IF_SHIFT) & 1; + /* default to local */ + retval = 2 + local_enabled; + + /* check for global flags if we're not in an interrupt */ + if (!local_irq_count(cpu)) { + if (local_enabled) + retval = 1; + if (global_irq_holder == cpu) + retval = 0; + } + return retval; +} + +void __global_restore_flags(unsigned long flags) +{ + switch (flags) { + case 0: + __global_cli(); + break; + case 1: + __global_sti(); + break; + case 2: + __cli(); + break; + case 3: + __sti(); + break; + default: + printk("global_restore_flags: %08lx (%08lx)\n", + flags, (&flags)[-1]); + } +} + +#endif + +/* + * This should really return information about whether + * we should do bottom half handling etc. Right now we + * end up _always_ checking the bottom half, which is a + * waste of time and is not what some drivers would + * prefer. + */ +int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action) +{ + int status; + int cpu = smp_processor_id(); + + irq_enter(cpu, irq); + + status = 1; /* Force the "do bottom halves" bit */ + + if (!(action->flags & SA_INTERRUPT)) + __sti(); + + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); + + irq_exit(cpu, irq); + + return status; +} + +/* + * Generic enable/disable code: this just calls + * down into the PIC-specific version for the actual + * hardware disable after having gotten the irq + * controller lock. + */ + +/** + * disable_irq_nosync - disable an irq without waiting + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables and Enables are + * nested. + * Unlike disable_irq(), this function does not ensure existing + * instances of the IRQ handler have completed before returning. + * + * This function may be called from IRQ context. + */ + +inline void disable_irq_nosync(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->depth++) { + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/** + * disable_irq - disable an irq and wait for completion + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Enables and Disables are + * nested. + * This function waits for any pending IRQ handlers for this interrupt + * to complete before returning. If you use this function while + * holding a resource the IRQ handler may need you will deadlock. + * + * This function may be called - with care - from IRQ context. + */ + +void disable_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + + if (!local_irq_count(smp_processor_id())) { + do { + barrier(); + } while (irq_desc[irq].status & IRQ_INPROGRESS); + } +} + +/** + * enable_irq - enable handling of an irq + * @irq: Interrupt to enable + * + * Undoes the effect of one call to disable_irq(). If this + * matches the last disable, processing of interrupts on this + * IRQ line is re-enabled. + * + * This function may be called from IRQ context. + */ + +void enable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + switch (desc->depth) { + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + desc->status = status; + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status = status | IRQ_REPLAY; + hw_resend_irq(desc->handler,irq); + } + desc->handler->enable(irq); + /* fall-through */ + } + default: + desc->depth--; + break; + case 0: + printk("enable_irq(%u) unbalanced from %p\n", irq, + __builtin_return_address(0)); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/* + * do_IRQ handles all normal device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + */ +asmlinkage unsigned int do_IRQ(struct pt_regs *regs) +{ + /* + * We ack quickly, we don't want the irq controller + * thinking we're snobs just because some other CPU has + * disabled global interrupts (we have already done the + * INT_ACK cycles, it's too late to try to pretend to the + * controller that we aren't taking the interrupt). + * + * 0 return value means that this irq is already being + * handled by some other CPU. (or is disabled) + */ + int irq = regs->orig_rax & 0xff; /* high bits used in ret_from_ code */ + int cpu = smp_processor_id(); + irq_desc_t *desc = irq_desc + irq; + struct irqaction * action; + unsigned int status; + + kstat.irqs[cpu][irq]++; + spin_lock(&desc->lock); + desc->handler->ack(irq); + /* + REPLAY is when Linux resends an IRQ that was dropped earlier + WAITING is used by probe to mark irqs that are being tested + */ + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + status |= IRQ_PENDING; /* we _want_ to handle it */ + + /* + * If the IRQ is disabled for whatever reason, we cannot + * use the action we have. + */ + action = NULL; + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + action = desc->action; + status &= ~IRQ_PENDING; /* we commit to handling */ + status |= IRQ_INPROGRESS; /* we are handling it */ + } + desc->status = status; + + /* + * If there is no IRQ handler or it was disabled, exit early. + Since we set PENDING, if another processor is handling + a different instance of this same irq, the other processor + will take care of it. + */ + if (!action) + goto out; + + /* + * Edge triggered interrupts need to remember + * pending events. + * This applies to any hw interrupts that allow a second + * instance of the same irq to arrive while we are in do_IRQ + * or in the handler. But the code here only handles the _second_ + * instance of the irq, not the third or fourth. So it is mostly + * useful for irq hardware that does not mask cleanly in an + * SMP environment. + */ + for (;;) { + spin_unlock(&desc->lock); + handle_IRQ_event(irq, regs, action); + spin_lock(&desc->lock); + + if (!(desc->status & IRQ_PENDING)) + break; + desc->status &= ~IRQ_PENDING; + } + desc->status &= ~IRQ_INPROGRESS; +out: + /* + * The ->end() handler has to deal with interrupts which got + * disabled while the handler was running. + */ + desc->handler->end(irq); + spin_unlock(&desc->lock); + + if (softirq_pending(cpu)) + do_softirq(); + return 1; +} + +/** + * request_irq - allocate an interrupt line + * @irq: Interrupt line to allocate + * @handler: Function to be called when the IRQ occurs + * @irqflags: Interrupt type flags + * @devname: An ascii name for the claiming device + * @dev_id: A cookie passed back to the handler function + * + * This call allocates interrupt resources and enables the + * interrupt line and IRQ handling. From the point this + * call is made your handler function may be invoked. Since + * your handler function must clear any interrupt the board + * raises, you must take care both to initialise your hardware + * and to set up the interrupt handler in the right order. + * + * Dev_id must be globally unique. Normally the address of the + * device data structure is used as the cookie. Since the handler + * receives this value it makes sense to use it. + * + * If your interrupt is shared you must pass a non NULL dev_id + * as this is required when freeing the interrupt. + * + * Flags: + * + * SA_SHIRQ Interrupt is shared + * + * SA_INTERRUPT Disable local interrupts while processing + * + * SA_SAMPLE_RANDOM The interrupt can be used for entropy + * + */ + +int request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char * devname, + void *dev_id) +{ + int retval; + struct irqaction * action; + +#if 1 + /* + * Sanity-check: shared interrupts should REALLY pass in + * a real dev-ID, otherwise we'll have trouble later trying + * to figure out which interrupt is which (messes up the + * interrupt freeing logic etc). + */ + if (irqflags & SA_SHIRQ) { + if (!dev_id) + printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]); + } +#endif + + if (irq >= NR_IRQS) + return -EINVAL; + if (!handler) + return -EINVAL; + + action = (struct irqaction *) + kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + retval = setup_irq(irq, action); + if (retval) + kfree(action); + return retval; +} + +/** + * free_irq - free an interrupt + * @irq: Interrupt line to free + * @dev_id: Device identity to free + * + * Remove an interrupt handler. The handler is removed and if the + * interrupt line is no longer in use by any driver it is disabled. + * On a shared IRQ the caller must ensure the interrupt is disabled + * on the card it drives before calling this function. The function + * does not return until any executing interrupts for this IRQ + * have completed. + * + * This function may be called from interrupt context. + * + * Bugs: Attempting to free an irq in a handler for the same irq hangs + * the machine. + */ + +void free_irq(unsigned int irq, void *dev_id) +{ + irq_desc_t *desc; + struct irqaction **p; + unsigned long flags; + + if (irq >= NR_IRQS) + return; + + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + for (;;) { + struct irqaction * action = *p; + if (action) { + struct irqaction **pp = p; + p = &action->next; + if (action->dev_id != dev_id) + continue; + + /* Found it - now remove it from the list of entries */ + *pp = action->next; + if (!desc->action) { + desc->status |= IRQ_DISABLED; + desc->handler->shutdown(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + +#ifdef CONFIG_SMP + /* Wait to make sure it's not being used on another CPU */ + while (desc->status & IRQ_INPROGRESS) + barrier(); +#endif + kfree(action); + return; + } + printk("Trying to free free IRQ%d\n",irq); + spin_unlock_irqrestore(&desc->lock,flags); + return; + } +} + +/* + * IRQ autodetection code.. + * + * This depends on the fact that any interrupt that + * comes in on to an unassigned handler will get stuck + * with "IRQ_WAITING" cleared and the interrupt + * disabled. + */ + +static DECLARE_MUTEX(probe_sem); + +/** + * probe_irq_on - begin an interrupt autodetect + * + * Commence probing for an interrupt. The interrupts are scanned + * and a mask of potential interrupt lines is returned. + * + */ + +unsigned long probe_irq_on(void) +{ + unsigned int i; + irq_desc_t *desc; + unsigned long val; + unsigned long delay; + + down(&probe_sem); + /* + * something may have generated an irq long ago and we want to + * flush such a longstanding irq before considering it as spurious. + */ + for (i = NR_IRQS-1; i > 0; i--) { + desc = irq_desc + i; + + spin_lock_irq(&desc->lock); + if (!irq_desc[i].action) + irq_desc[i].handler->startup(i); + spin_unlock_irq(&desc->lock); + } + + /* Wait for longstanding interrupts to trigger. */ + for (delay = jiffies + HZ/50; time_after(delay, jiffies); ) + /* about 20ms delay */ synchronize_irq(); + + /* + * enable any unassigned irqs + * (we must startup again here because if a longstanding irq + * happened in the previous stage, it may have masked itself) + */ + for (i = NR_IRQS-1; i > 0; i--) { + desc = irq_desc + i; + + spin_lock_irq(&desc->lock); + if (!desc->action) { + desc->status |= IRQ_AUTODETECT | IRQ_WAITING; + if (desc->handler->startup(i)) + desc->status |= IRQ_PENDING; + } + spin_unlock_irq(&desc->lock); + } + + /* + * Wait for spurious interrupts to trigger + */ + for (delay = jiffies + HZ/10; time_after(delay, jiffies); ) + /* about 100ms delay */ synchronize_irq(); + + /* + * Now filter out any obviously spurious interrupts + */ + val = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + /* It triggered already - consider it spurious. */ + if (!(status & IRQ_WAITING)) { + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } else + if (i < 32) + val |= 1 << i; + } + spin_unlock_irq(&desc->lock); + } + + return val; +} + +/* + * Return a mask of triggered interrupts (this + * can handle only legacy ISA interrupts). + */ + +/** + * probe_irq_mask - scan a bitmap of interrupt lines + * @val: mask of interrupts to consider + * + * Scan the ISA bus interrupt lines and return a bitmap of + * active interrupts. The interrupt probe logic state is then + * returned to its previous value. + * + * Note: we need to scan all the irq's even though we will + * only return ISA irq numbers - just so that we reset them + * all to a known state. + */ +unsigned int probe_irq_mask(unsigned long val) +{ + int i; + unsigned int mask; + + mask = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + if (i < 16 && !(status & IRQ_WAITING)) + mask |= 1 << i; + + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } + spin_unlock_irq(&desc->lock); + } + up(&probe_sem); + + return mask & val; +} + +/* + * Return the one interrupt that triggered (this can + * handle any interrupt source). + */ + +/** + * probe_irq_off - end an interrupt autodetect + * @val: mask of potential interrupts (unused) + * + * Scans the unused interrupt lines and returns the line which + * appears to have triggered the interrupt. If no interrupt was + * found then zero is returned. If more than one interrupt is + * found then minus the first candidate is returned to indicate + * their is doubt. + * + * The interrupt probe logic state is returned to its previous + * value. + * + * BUGS: When used in a module (which arguably shouldnt happen) + * nothing prevents two IRQ probe callers from overlapping. The + * results of this are non-optimal. + */ + +int probe_irq_off(unsigned long val) +{ + int i, irq_found, nr_irqs; + + nr_irqs = 0; + irq_found = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + if (!(status & IRQ_WAITING)) { + if (!nr_irqs) + irq_found = i; + nr_irqs++; + } + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } + spin_unlock_irq(&desc->lock); + } + up(&probe_sem); + + if (nr_irqs > 1) + irq_found = -irq_found; + return irq_found; +} + +/* this was setup_x86_irq but it seems pretty generic */ +int setup_irq(unsigned int irq, struct irqaction * new) +{ + int shared = 0; + unsigned long flags; + struct irqaction *old, **p; + irq_desc_t *desc = irq_desc + irq; + + /* + * Some drivers like serial.c use request_irq() heavily, + * so we have to be careful not to interfere with a + * running system. + */ + if (new->flags & SA_SAMPLE_RANDOM) { + /* + * This function might sleep, we want to call it first, + * outside of the atomic block. + * Yes, this might clear the entropy pool if the wrong + * driver is attempted to be loaded, without actually + * installing a new handler, but is this really a problem, + * only the sysadmin is able to do this. + */ + rand_initialize_irq(irq); + } + + /* + * The following block of code has to be executed atomically + */ + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) { + spin_unlock_irqrestore(&desc->lock,flags); + return -EBUSY; + } + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + *p = new; + + if (!shared) { + desc->depth = 0; + desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING); + desc->handler->startup(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + + register_irq_proc(irq); + return 0; +} + +static struct proc_dir_entry * root_irq_dir; +static struct proc_dir_entry * irq_dir [NR_IRQS]; + +#define HEX_DIGITS 8 + +static unsigned int parse_hex_value (const char *buffer, + unsigned long count, unsigned long *ret) +{ + unsigned char hexnum [HEX_DIGITS]; + unsigned long value; + int i; + + if (!count) + return -EINVAL; + if (count > HEX_DIGITS) + count = HEX_DIGITS; + if (copy_from_user(hexnum, buffer, count)) + return -EFAULT; + + /* + * Parse the first 8 characters as a hex string, any non-hex char + * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same. + */ + value = 0; + + for (i = 0; i < count; i++) { + unsigned int c = hexnum[i]; + + switch (c) { + case '0' ... '9': c -= '0'; break; + case 'a' ... 'f': c -= 'a'-10; break; + case 'A' ... 'F': c -= 'A'-10; break; + default: + goto out; + } + value = (value << 4) | c; + } +out: + *ret = value; + return 0; +} + +#if CONFIG_SMP + +static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; + +static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; +static int irq_affinity_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", irq_affinity[(long)data]); +} + +static int irq_affinity_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int irq = (long) data, full_count = count, err; + unsigned long new_value; + + if (!irq_desc[irq].handler->set_affinity) + return -EIO; + + err = parse_hex_value(buffer, count, &new_value); + + /* + * Do not allow disabling IRQs completely - it's a too easy + * way to make the system unusable accidentally :-) At least + * one online CPU still has to be targeted. + */ + if (!(new_value & cpu_online_map)) + return -EINVAL; + + irq_affinity[irq] = new_value; + irq_desc[irq].handler->set_affinity(irq, new_value); + + return full_count; +} + +#endif + +static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long *mask = (unsigned long *) data; + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", *mask); +} + +static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned long *mask = (unsigned long *) data, full_count = count, err; + unsigned long new_value; + + err = parse_hex_value(buffer, count, &new_value); + if (err) + return err; + + *mask = new_value; + return full_count; +} + +#define MAX_NAMELEN 10 + +static void register_irq_proc (unsigned int irq) +{ + char name [MAX_NAMELEN]; + + if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) || + irq_dir[irq]) + return; + + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + irq_dir[irq] = proc_mkdir(name, root_irq_dir); + +#if CONFIG_SMP + { + struct proc_dir_entry *entry; + + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + + if (entry) { + entry->nlink = 1; + entry->data = (void *)(long)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + } + + smp_affinity_entry[irq] = entry; + } +#endif +} + +unsigned long prof_cpu_mask = -1; + +void init_irq_proc (void) +{ + struct proc_dir_entry *entry; + int i; + + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", 0); + + /* create /proc/irq/prof_cpu_mask */ + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); + + if (!entry) + return; + + entry->nlink = 1; + entry->data = (void *)&prof_cpu_mask; + entry->read_proc = prof_cpu_mask_read_proc; + entry->write_proc = prof_cpu_mask_write_proc; + + /* + * Create entries for all existing IRQs. + */ + for (i = 0; i < NR_IRQS; i++) + register_irq_proc(i); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/ldt.c linux.ac/arch/x86_64/kernel/ldt.c --- linux.vanilla/arch/x86_64/kernel/ldt.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/ldt.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,155 @@ +/* + * linux/kernel/ldt.c + * + * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds + * Copyright (C) 1999 Ingo Molnar + */ + +/* + * FIXME: forbid code segment setting for 64bit mode. doesn't work with SYSCALL + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * read_ldt() is not really atomic - this is not a problem since + * synchronization of reads and writes done to the LDT has to be + * assured by user-space anyway. Writes are atomic, to protect + * the security checks done on new descriptors. + */ +static int read_ldt(void * ptr, unsigned long bytecount) +{ + int err; + unsigned long size; + struct mm_struct * mm = current->mm; + + err = 0; + if (!mm->context.segments) + goto out; + + size = LDT_ENTRIES*LDT_ENTRY_SIZE; + if (size > bytecount) + size = bytecount; + + err = size; + if (copy_to_user(ptr, mm->context.segments, size)) + err = -EFAULT; +out: + return err; +} + +static int write_ldt(void * ptr, unsigned long bytecount, int oldmode) +{ + struct mm_struct * mm = current->mm; + __u32 entry_1, entry_2, *lp; + int error; + struct modify_ldt_ldt_s ldt_info; + + error = -EINVAL; + if (bytecount != sizeof(ldt_info)) + goto out; + error = -EFAULT; + if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info))) + goto out; + + error = -EINVAL; + if (ldt_info.entry_number >= LDT_ENTRIES) + goto out; + if (ldt_info.contents == 3) { + if (oldmode) + goto out; + if (ldt_info.seg_not_present == 0) + goto out; + } + + current->thread.fsindex = 0; + current->thread.gsindex = 0; + + /* + * the GDT index of the LDT is allocated dynamically, and is + * limited by MAX_LDT_DESCRIPTORS. + */ + down_write(&mm->mmap_sem); + if (!mm->context.segments) { + void * segments = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE); + error = -ENOMEM; + if (!segments) + goto out_unlock; + memset(segments, 0, LDT_ENTRIES*LDT_ENTRY_SIZE); + wmb(); + mm->context.segments = segments; + mm->context.cpuvalid = 1UL << smp_processor_id(); + load_LDT(mm); + } + + lp = (__u32 *) ((ldt_info.entry_number << 3) + (char *) mm->context.segments); + + /* Allow LDTs to be cleared by the user. */ + if (ldt_info.base_addr == 0 && ldt_info.limit == 0) { + if (oldmode || + (ldt_info.contents == 0 && + ldt_info.read_exec_only == 1 && + ldt_info.seg_32bit == 0 && + ldt_info.limit_in_pages == 0 && + ldt_info.seg_not_present == 1 && + ldt_info.useable == 0 )) { + entry_1 = 0; + entry_2 = 0; + goto install; + } + } + + entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) | + (ldt_info.limit & 0x0ffff); + entry_2 = (ldt_info.base_addr & 0xff000000) | + ((ldt_info.base_addr & 0x00ff0000) >> 16) | + (ldt_info.limit & 0xf0000) | + ((ldt_info.read_exec_only ^ 1) << 9) | + (ldt_info.contents << 10) | + ((ldt_info.seg_not_present ^ 1) << 15) | + (ldt_info.seg_32bit << 22) | + (ldt_info.limit_in_pages << 23) | + 0x7000; + if (!oldmode) + entry_2 |= (ldt_info.useable << 20); + + /* Install the new entry ... */ +install: + *lp = entry_1; + *(lp+1) = entry_2; + error = 0; + +out_unlock: + up_write(&mm->mmap_sem); +out: + return error; +} + +asmlinkage int sys_modify_ldt(int func, void *ptr, unsigned long bytecount) +{ + int ret = -ENOSYS; + + switch (func) { + case 0: + ret = read_ldt(ptr, bytecount); + break; + case 1: + ret = write_ldt(ptr, bytecount, 1); + break; + case 0x11: + ret = write_ldt(ptr, bytecount, 0); + break; + } + return ret; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/mpparse.c linux.ac/arch/x86_64/kernel/mpparse.c --- linux.vanilla/arch/x86_64/kernel/mpparse.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/mpparse.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,671 @@ +/* + * Intel Multiprocessor Specificiation 1.1 and 1.4 + * compliant MP-table parsing routines. + * + * (c) 1995 Alan Cox, Building #3 + * (c) 1998, 1999, 2000 Ingo Molnar + * + * Fixes + * Erich Boleyn : MP v1.4 and additional changes. + * Alan Cox : Added EBDA scanning + * Ingo Molnar : various cleanups and rewrites + * Maciej W. Rozycki : Bits for default MP configurations + */ + +/* This file will likely go away later and be replaced with an ACPI parser. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Have we found an MP table */ +int smp_found_config = 0; + +/* + * Various Linux-internal data structures created from the + * MP-table. + */ +int apic_version [MAX_APICS]; +int mp_bus_id_to_type [MAX_MP_BUSSES] = { [0 ... MAX_MP_BUSSES-1] = -1 }; +int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { [0 ... MAX_MP_BUSSES-1] = -1 }; +int mp_current_pci_id = 0; + +/* I/O APIC entries */ +struct mpc_config_ioapic mp_ioapics[MAX_IO_APICS]; + +/* # of MP IRQ source entries */ +struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES]; + +/* MP IRQ source entries */ +int mp_irq_entries; + +int nr_ioapics; +int pic_mode; +unsigned long mp_lapic_addr = 0; + +/* Processor that is doing the boot up */ +unsigned int boot_cpu_id = -1U; +/* Internal processor count */ +static unsigned int num_processors = 0; + +/* Bitmask of physically existing CPUs */ +unsigned long phys_cpu_present_map = 0; + +/* + * Intel MP BIOS table parsing routines: + */ + +#ifndef CONFIG_X86_VISWS_APIC +/* + * Checksum an MP configuration block. + */ + +static int __init mpf_checksum(unsigned char *mp, int len) +{ + int sum = 0; + + while (len--) + sum += *mp++; + + return sum & 0xFF; +} + +/* + * Processor encoding in an MP configuration block + */ + +static char __init *mpc_family(int family,int model) +{ + static char n[32]; + static char *model_defs[]= + { + "80486DX","80486DX", + "80486SX","80486DX/2 or 80487", + "80486SL","80486SX/2", + "Unknown","80486DX/2-WB", + "80486DX/4","80486DX/4-WB" + }; + + switch (family) { + case 0x04: + if (model < 10) + return model_defs[model]; + break; + + case 0x05: + return("Pentium(tm)"); + + case 0x06: + return("Pentium(tm) Pro"); + + case 0x0F: + if (model == 0x0F) + return("Special controller"); + } + sprintf(n,"Unknown CPU [%d:%d]",family, model); + return n; +} + +static void __init MP_processor_info (struct mpc_config_processor *m) +{ + int ver; + + if (!(m->mpc_cpuflag & CPU_ENABLED)) + return; + + printk("Processor #%d %s APIC version %d\n", + m->mpc_apicid, + mpc_family( (m->mpc_cpufeature & CPU_FAMILY_MASK)>>8 , + (m->mpc_cpufeature & CPU_MODEL_MASK)>>4), + m->mpc_apicver); + + if (m->mpc_featureflag&(1<<0)) + Dprintk(" Floating point unit present.\n"); + if (m->mpc_featureflag&(1<<7)) + Dprintk(" Machine Exception supported.\n"); + if (m->mpc_featureflag&(1<<8)) + Dprintk(" 64 bit compare & exchange supported.\n"); + if (m->mpc_featureflag&(1<<9)) + Dprintk(" Internal APIC present.\n"); + + if (m->mpc_cpuflag & CPU_BOOTPROCESSOR) { + Dprintk(" Bootup CPU\n"); + boot_cpu_id = m->mpc_apicid; + } + num_processors++; + + if (m->mpc_apicid > MAX_APICS) { + printk("Processor #%d INVALID. (Max ID: %d).\n", + m->mpc_apicid, MAX_APICS); + return; + } + ver = m->mpc_apicver; + + phys_cpu_present_map |= 1 << m->mpc_apicid; + /* + * Validate version + */ + if (ver == 0x0) { + printk("BIOS bug, APIC version is 0 for CPU#%d! fixing up to 0x10. (tell your hw vendor)\n", m->mpc_apicid); + ver = 0x10; + } + apic_version[m->mpc_apicid] = ver; +} + +static void __init MP_bus_info (struct mpc_config_bus *m) +{ + char str[7]; + + memcpy(str, m->mpc_bustype, 6); + str[6] = 0; + Dprintk("Bus #%d is %s\n", m->mpc_busid, str); + + if (strncmp(str, "ISA", 3) == 0) { + mp_bus_id_to_type[m->mpc_busid] = MP_BUS_ISA; + } else if (strncmp(str, "EISA", 4) == 0) { + mp_bus_id_to_type[m->mpc_busid] = MP_BUS_EISA; + } else if (strncmp(str, "PCI", 3) == 0) { + mp_bus_id_to_type[m->mpc_busid] = MP_BUS_PCI; + mp_bus_id_to_pci_bus[m->mpc_busid] = mp_current_pci_id; + mp_current_pci_id++; + } else if (strncmp(str, "MCA", 3) == 0) { + mp_bus_id_to_type[m->mpc_busid] = MP_BUS_MCA; + } else { + printk("Unknown bustype %s\n", str); + panic("cannot handle bus - mail to linux-smp@vger.kernel.org"); + } +} + +static void __init MP_ioapic_info (struct mpc_config_ioapic *m) +{ + if (!(m->mpc_flags & MPC_APIC_USABLE)) + return; + + printk("I/O APIC #%d Version %d at 0x%lX.\n", + m->mpc_apicid, m->mpc_apicver, (unsigned long)m->mpc_apicaddr); + if (nr_ioapics >= MAX_IO_APICS) { + printk("Max # of I/O APICs (%d) exceeded (found %d).\n", + MAX_IO_APICS, nr_ioapics); + panic("Recompile kernel with bigger MAX_IO_APICS!.\n"); + } + if (!m->mpc_apicaddr) { + printk(KERN_ERR "WARNING: bogus zero I/O APIC address" + " found in MP table, skipping!\n"); + return; + } + mp_ioapics[nr_ioapics] = *m; + nr_ioapics++; +} + +static void __init MP_intsrc_info (struct mpc_config_intsrc *m) +{ + mp_irqs [mp_irq_entries] = *m; + Dprintk("Int: type %d, pol %d, trig %d, bus %d," + " IRQ %02x, APIC ID %x, APIC INT %02x\n", + m->mpc_irqtype, m->mpc_irqflag & 3, + (m->mpc_irqflag >> 2) & 3, m->mpc_srcbus, + m->mpc_srcbusirq, m->mpc_dstapic, m->mpc_dstirq); + if (++mp_irq_entries == MAX_IRQ_SOURCES) + panic("Max # of irq sources exceeded!!\n"); +} + +static void __init MP_lintsrc_info (struct mpc_config_lintsrc *m) +{ + Dprintk("Lint: type %d, pol %d, trig %d, bus %d," + " IRQ %02x, APIC ID %x, APIC LINT %02x\n", + m->mpc_irqtype, m->mpc_irqflag & 3, + (m->mpc_irqflag >> 2) &3, m->mpc_srcbusid, + m->mpc_srcbusirq, m->mpc_destapic, m->mpc_destapiclint); + /* + * Well it seems all SMP boards in existence + * use ExtINT/LVT1 == LINT0 and + * NMI/LVT2 == LINT1 - the following check + * will show us if this assumptions is false. + * Until then we do not have to add baggage. + */ + if ((m->mpc_irqtype == mp_ExtINT) && + (m->mpc_destapiclint != 0)) + BUG(); + if ((m->mpc_irqtype == mp_NMI) && + (m->mpc_destapiclint != 1)) + BUG(); +} + +/* + * Read/parse the MPC + */ + +static int __init smp_read_mpc(struct mp_config_table *mpc) +{ + char str[16]; + int count=sizeof(*mpc); + unsigned char *mpt=((unsigned char *)mpc)+count; + + if (memcmp(mpc->mpc_signature,MPC_SIGNATURE,4)) { + panic("SMP mptable: bad signature [%c%c%c%c]!\n", + mpc->mpc_signature[0], + mpc->mpc_signature[1], + mpc->mpc_signature[2], + mpc->mpc_signature[3]); + return 0; + } + if (mpf_checksum((unsigned char *)mpc,mpc->mpc_length)) { + panic("SMP mptable: checksum error!\n"); + return 0; + } + if (mpc->mpc_spec!=0x01 && mpc->mpc_spec!=0x04) { + printk(KERN_ERR "SMP mptable: bad table version (%d)!!\n", + mpc->mpc_spec); + return 0; + } + if (!mpc->mpc_lapic) { + printk(KERN_ERR "SMP mptable: null local APIC address!\n"); + return 0; + } + memcpy(str,mpc->mpc_oem,8); + str[8]=0; + printk("OEM ID: %s ",str); + + memcpy(str,mpc->mpc_productid,12); + str[12]=0; + printk("Product ID: %s ",str); + + printk("APIC at: 0x%lX\n",mpc->mpc_lapic); + + /* save the local APIC address, it might be non-default */ + mp_lapic_addr = mpc->mpc_lapic; + + /* + * Now process the configuration blocks. + */ + while (count < mpc->mpc_length) { + switch(*mpt) { + case MP_PROCESSOR: + { + struct mpc_config_processor *m= + (struct mpc_config_processor *)mpt; + MP_processor_info(m); + mpt += sizeof(*m); + count += sizeof(*m); + break; + } + case MP_BUS: + { + struct mpc_config_bus *m= + (struct mpc_config_bus *)mpt; + MP_bus_info(m); + mpt += sizeof(*m); + count += sizeof(*m); + break; + } + case MP_IOAPIC: + { + struct mpc_config_ioapic *m= + (struct mpc_config_ioapic *)mpt; + MP_ioapic_info(m); + mpt+=sizeof(*m); + count+=sizeof(*m); + break; + } + case MP_INTSRC: + { + struct mpc_config_intsrc *m= + (struct mpc_config_intsrc *)mpt; + + MP_intsrc_info(m); + mpt+=sizeof(*m); + count+=sizeof(*m); + break; + } + case MP_LINTSRC: + { + struct mpc_config_lintsrc *m= + (struct mpc_config_lintsrc *)mpt; + MP_lintsrc_info(m); + mpt+=sizeof(*m); + count+=sizeof(*m); + break; + } + } + } + if (!num_processors) + printk(KERN_ERR "SMP mptable: no processors registered!\n"); + return num_processors; +} + +static int __init ELCR_trigger(unsigned int irq) +{ + unsigned int port; + + port = 0x4d0 + (irq >> 3); + return (inb(port) >> (irq & 7)) & 1; +} + +static void __init construct_default_ioirq_mptable(int mpc_default_type) +{ + struct mpc_config_intsrc intsrc; + int i; + int ELCR_fallback = 0; + + intsrc.mpc_type = MP_INTSRC; + intsrc.mpc_irqflag = 0; /* conforming */ + intsrc.mpc_srcbus = 0; + intsrc.mpc_dstapic = mp_ioapics[0].mpc_apicid; + + intsrc.mpc_irqtype = mp_INT; + + /* + * If true, we have an ISA/PCI system with no IRQ entries + * in the MP table. To prevent the PCI interrupts from being set up + * incorrectly, we try to use the ELCR. The sanity check to see if + * there is good ELCR data is very simple - IRQ0, 1, 2 and 13 can + * never be level sensitive, so we simply see if the ELCR agrees. + * If it does, we assume it's valid. + */ + if (mpc_default_type == 5) { + printk("ISA/PCI bus type with no IRQ information... falling back to ELCR\n"); + + if (ELCR_trigger(0) || ELCR_trigger(1) || ELCR_trigger(2) || ELCR_trigger(13)) + printk("ELCR contains invalid data... not using ELCR\n"); + else { + printk("Using ELCR to identify PCI interrupts\n"); + ELCR_fallback = 1; + } + } + + for (i = 0; i < 16; i++) { + switch (mpc_default_type) { + case 2: + if (i == 0 || i == 13) + continue; /* IRQ0 & IRQ13 not connected */ + /* fall through */ + default: + if (i == 2) + continue; /* IRQ2 is never connected */ + } + + if (ELCR_fallback) { + /* + * If the ELCR indicates a level-sensitive interrupt, we + * copy that information over to the MP table in the + * irqflag field (level sensitive, active high polarity). + */ + if (ELCR_trigger(i)) + intsrc.mpc_irqflag = 13; + else + intsrc.mpc_irqflag = 0; + } + + intsrc.mpc_srcbusirq = i; + intsrc.mpc_dstirq = i ? i : 2; /* IRQ0 to INTIN2 */ + MP_intsrc_info(&intsrc); + } + + intsrc.mpc_irqtype = mp_ExtINT; + intsrc.mpc_srcbusirq = 0; + intsrc.mpc_dstirq = 0; /* 8259A to INTIN0 */ + MP_intsrc_info(&intsrc); +} + +static inline void __init construct_default_ISA_mptable(int mpc_default_type) +{ + struct mpc_config_processor processor; + struct mpc_config_bus bus; + struct mpc_config_ioapic ioapic; + struct mpc_config_lintsrc lintsrc; + int linttypes[2] = { mp_ExtINT, mp_NMI }; + int i; + + /* + * local APIC has default address + */ + mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; + + /* + * 2 CPUs, numbered 0 & 1. + */ + processor.mpc_type = MP_PROCESSOR; + /* Either an integrated APIC or a discrete 82489DX. */ + processor.mpc_apicver = mpc_default_type > 4 ? 0x10 : 0x01; + processor.mpc_cpuflag = CPU_ENABLED; + processor.mpc_cpufeature = (boot_cpu_data.x86 << 8) | + (boot_cpu_data.x86_model << 4) | + boot_cpu_data.x86_mask; + processor.mpc_featureflag = boot_cpu_data.x86_capability; + processor.mpc_reserved[0] = 0; + processor.mpc_reserved[1] = 0; + for (i = 0; i < 2; i++) { + processor.mpc_apicid = i; + MP_processor_info(&processor); + } + + bus.mpc_type = MP_BUS; + bus.mpc_busid = 0; + switch (mpc_default_type) { + default: + printk("???\nUnknown standard configuration %d\n", + mpc_default_type); + /* fall through */ + case 1: + case 5: + memcpy(bus.mpc_bustype, "ISA ", 6); + break; + case 2: + case 6: + case 3: + memcpy(bus.mpc_bustype, "EISA ", 6); + break; + case 4: + case 7: + memcpy(bus.mpc_bustype, "MCA ", 6); + } + MP_bus_info(&bus); + if (mpc_default_type > 4) { + bus.mpc_busid = 1; + memcpy(bus.mpc_bustype, "PCI ", 6); + MP_bus_info(&bus); + } + + ioapic.mpc_type = MP_IOAPIC; + ioapic.mpc_apicid = 2; + ioapic.mpc_apicver = mpc_default_type > 4 ? 0x10 : 0x01; + ioapic.mpc_flags = MPC_APIC_USABLE; + ioapic.mpc_apicaddr = 0xFEC00000; + MP_ioapic_info(&ioapic); + + /* + * We set up most of the low 16 IO-APIC pins according to MPS rules. + */ + construct_default_ioirq_mptable(mpc_default_type); + + lintsrc.mpc_type = MP_LINTSRC; + lintsrc.mpc_irqflag = 0; /* conforming */ + lintsrc.mpc_srcbusid = 0; + lintsrc.mpc_srcbusirq = 0; + lintsrc.mpc_destapic = MP_APIC_ALL; + for (i = 0; i < 2; i++) { + lintsrc.mpc_irqtype = linttypes[i]; + lintsrc.mpc_destapiclint = i; + MP_lintsrc_info(&lintsrc); + } +} + +static struct intel_mp_floating *mpf_found; + +/* + * Scan the memory blocks for an SMP configuration block. + */ +void __init get_smp_config (void) +{ + struct intel_mp_floating *mpf = mpf_found; + printk("Intel MultiProcessor Specification v1.%d\n", mpf->mpf_specification); + if (mpf->mpf_feature2 & (1<<7)) { + printk(" IMCR and PIC compatibility mode.\n"); + pic_mode = 1; + } else { + printk(" Virtual Wire compatibility mode.\n"); + pic_mode = 0; + } + + /* + * Now see if we need to read further. + */ + if (mpf->mpf_feature1 != 0) { + + printk("Default MP configuration #%d\n", mpf->mpf_feature1); + construct_default_ISA_mptable(mpf->mpf_feature1); + + } else if (mpf->mpf_physptr) { + + /* + * Read the physical hardware table. Anything here will + * override the defaults. + */ + if (!smp_read_mpc((void *)mpf->mpf_physptr)) { + smp_found_config = 0; + printk(KERN_ERR "BIOS bug, MP table errors detected!...\n"); + printk(KERN_ERR "... disabling SMP support. (tell your hw vendor)\n"); + return; + } + /* + * If there are no explicit MP IRQ entries, then we are + * broken. We set up most of the low 16 IO-APIC pins to + * ISA defaults and hope it will work. + */ + if (!mp_irq_entries) { + struct mpc_config_bus bus; + + printk("BIOS bug, no explicit IRQ entries, using default mptable. (tell your hw vendor)\n"); + + bus.mpc_type = MP_BUS; + bus.mpc_busid = 0; + memcpy(bus.mpc_bustype, "ISA ", 6); + MP_bus_info(&bus); + + construct_default_ioirq_mptable(0); + } + + } else + BUG(); + + printk("Processors: %d\n", num_processors); + /* + * Only use the first configuration found. + */ +} + +static int __init smp_scan_config (unsigned long base, unsigned long length) +{ + unsigned long *bp = phys_to_virt(base); + struct intel_mp_floating *mpf; + + Dprintk("Scan SMP from %p for %ld bytes.\n", bp,length); + if (sizeof(*mpf) != 16) + printk("Error: MPF size\n"); + + while (length > 0) { + mpf = (struct intel_mp_floating *)bp; + if ((*bp == SMP_MAGIC_IDENT) && + (mpf->mpf_length == 1) && + !mpf_checksum((unsigned char *)bp, 16) && + ((mpf->mpf_specification == 1) + || (mpf->mpf_specification == 4)) ) { + + smp_found_config = 1; + printk("found SMP MP-table at %08lx\n", + virt_to_phys(mpf)); + reserve_bootmem(virt_to_phys(mpf), PAGE_SIZE); + if (mpf->mpf_physptr) + reserve_bootmem(mpf->mpf_physptr, PAGE_SIZE); + mpf_found = mpf; + return 1; + } + bp += 4; + length -= 16; + } + return 0; +} + +void __init find_intel_smp (void) +{ + unsigned int address; + + /* + * FIXME: Linux assumes you have 640K of base ram.. + * this continues the error... + * + * 1) Scan the bottom 1K for a signature + * 2) Scan the top 1K of base RAM + * 3) Scan the 64K of bios + */ + if (smp_scan_config(0x0,0x400) || + smp_scan_config(639*0x400,0x400) || + smp_scan_config(0xF0000,0x10000)) + return; + /* + * If it is an SMP machine we should know now, unless the + * configuration is in an EISA/MCA bus machine with an + * extended bios data area. + * + * there is a real-mode segmented pointer pointing to the + * 4K EBDA area at 0x40E, calculate and scan it here. + * + * NOTE! There are Linux loaders that will corrupt the EBDA + * area, and as such this kind of SMP config may be less + * trustworthy, simply because the SMP table may have been + * stomped on during early boot. These loaders are buggy and + * should be fixed. + */ + + address = *(unsigned short *)phys_to_virt(0x40E); + address <<= 4; + smp_scan_config(address, 0x1000); + if (smp_found_config) + printk(KERN_WARNING "WARNING: MP table in the EBDA can be UNSAFE, contact linux-smp@vger.kernel.org if you experience SMP problems!\n"); +} + +#else + +/* + * The Visual Workstation is Intel MP compliant in the hardware + * sense, but it doesnt have a BIOS(-configuration table). + * No problem for Linux. + */ +void __init find_visws_smp(void) +{ + smp_found_config = 1; + + phys_cpu_present_map |= 2; /* or in id 1 */ + apic_version[1] |= 0x10; /* integrated APIC */ + apic_version[0] |= 0x10; + + mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; +} + +#endif + +/* + * - Intel MP Configuration Table + * - or SGI Visual Workstation configuration + */ +void __init find_smp_config (void) +{ +#ifdef CONFIG_X86_LOCAL_APIC + find_intel_smp(); +#endif +#ifdef CONFIG_VISWS + find_visws_smp(); +#endif +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/msr.c linux.ac/arch/x86_64/kernel/msr.c --- linux.vanilla/arch/x86_64/kernel/msr.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/msr.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,278 @@ +#ident "$Id: msr.c,v 1.5 2001/08/15 07:52:55 ak Exp $" +/* ----------------------------------------------------------------------- * + * + * Copyright 2000 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * msr.c + * + * x86 MSR access device + * + * This device is accessed by lseek() to the appropriate register number + * and then read/write in chunks of 8 bytes. A larger size means multiple + * reads or writes of the same register. + * + * This driver uses /dev/cpu/%d/msr where %d is the minor number, and on + * an SMP box will direct the access to CPU %d. + */ + +/* It seems sledgehammer transfers 64bits for wrmsr as + edx:eax. Ouch. That means, however, that this driver can be used as + is. --pavel +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Note: "err" is handled in a funny way below. Otherwise one version + of gcc or another breaks. */ + +static inline int wrmsr_eio(u32 reg, u32 eax, u32 edx) +{ + int err; + + asm volatile( + "1: wrmsr\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3: movl %4,%0\n" + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .quad 1b,3b\n" + ".previous" + : "=&bDS" (err) + : "a" (eax), "d" (edx), "c" (reg), "i" (-EIO), "0" (0)); + + return err; +} + +static inline int rdmsr_eio(u32 reg, u32 *eax, u32 *edx) +{ + int err; + + asm volatile( + "1: rdmsr\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3: movl %4,%0\n" + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .quad 1b,3b\n" + ".previous" + : "=&bDS" (err), "=a" (*eax), "=d" (*edx) + : "c" (reg), "i" (-EIO), "0" (0)); + + return err; +} + +#ifdef CONFIG_SMP + +struct msr_command { + int cpu; + int err; + u32 reg; + u32 data[2]; +}; + +static void msr_smp_wrmsr(void *cmd_block) +{ + struct msr_command *cmd = (struct msr_command *) cmd_block; + + if ( cmd->cpu == smp_processor_id() ) + cmd->err = wrmsr_eio(cmd->reg, cmd->data[0], cmd->data[1]); +} + +static void msr_smp_rdmsr(void *cmd_block) +{ + struct msr_command *cmd = (struct msr_command *) cmd_block; + + if ( cmd->cpu == smp_processor_id() ) + cmd->err = rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]); +} + +static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx) +{ + struct msr_command cmd; + + if ( cpu == smp_processor_id() ) { + return wrmsr_eio(reg, eax, edx); + } else { + cmd.cpu = cpu; + cmd.reg = reg; + cmd.data[0] = eax; + cmd.data[1] = edx; + + smp_call_function(msr_smp_wrmsr, &cmd, 1, 1); + return cmd.err; + } +} + +static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx) +{ + struct msr_command cmd; + + if ( cpu == smp_processor_id() ) { + return rdmsr_eio(reg, eax, edx); + } else { + cmd.cpu = cpu; + cmd.reg = reg; + + smp_call_function(msr_smp_rdmsr, &cmd, 1, 1); + + *eax = cmd.data[0]; + *edx = cmd.data[1]; + + return cmd.err; + } +} + +#else /* ! CONFIG_SMP */ + +static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx) +{ + return wrmsr_eio(reg, eax, edx); +} + +static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx) +{ + return rdmsr_eio(reg, eax, edx); +} + +#endif /* ! CONFIG_SMP */ + +static loff_t msr_seek(struct file *file, loff_t offset, int orig) +{ + switch (orig) { + case 0: + file->f_pos = offset; + return file->f_pos; + case 1: + file->f_pos += offset; + return file->f_pos; + default: + return -EINVAL; /* SEEK_END not supported */ + } +} + +static ssize_t msr_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + u32 *tmp = (u32 *)buf; + u32 data[2]; + size_t rv; + u32 reg = *ppos; + int cpu = MINOR(file->f_dentry->d_inode->i_rdev); + int err; + + if ( count % 8 ) + return -EINVAL; /* Invalid chunk size */ + + for ( rv = 0 ; count ; count -= 8 ) { + err = do_rdmsr(cpu, reg, &data[0], &data[1]); + if ( err ) + return err; + if ( copy_to_user(tmp,&data,8) ) + return -EFAULT; + tmp += 2; + } + + return ((char *)tmp) - buf; +} + +static ssize_t msr_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + const u32 *tmp = (const u32 *)buf; + u32 data[2]; + size_t rv; + u32 reg = *ppos; + int cpu = MINOR(file->f_dentry->d_inode->i_rdev); + int err; + + if ( count % 8 ) + return -EINVAL; /* Invalid chunk size */ + + for ( rv = 0 ; count ; count -= 8 ) { + if ( copy_from_user(&data,tmp,8) ) + return -EFAULT; + err = do_wrmsr(cpu, reg, data[0], data[1]); + if ( err ) + return err; + tmp += 2; + } + + return ((char *)tmp) - buf; +} + +static int msr_open(struct inode *inode, struct file *file) +{ + int cpu = MINOR(file->f_dentry->d_inode->i_rdev); + struct cpuinfo_x86 *c = &(cpu_data)[cpu]; + + if ( !(cpu_online_map & (1UL << cpu)) ) + return -ENXIO; /* No such CPU */ + if ( !test_bit(X86_FEATURE_MSR, &c->x86_capability) ) + return -EIO; /* MSR not supported */ + + return 0; +} + +/* + * File operations we support + */ +static struct file_operations msr_fops = { + owner: THIS_MODULE, + llseek: msr_seek, + read: msr_read, + write: msr_write, + open: msr_open, +}; + +int __init msr_init(void) +{ + if (register_chrdev(MSR_MAJOR, "cpu/msr", &msr_fops)) { + printk(KERN_ERR "msr: unable to get major %d for msr\n", + MSR_MAJOR); + return -EBUSY; + } + + return 0; +} + +void __exit msr_exit(void) +{ + unregister_chrdev(MSR_MAJOR, "cpu/msr"); +} + +module_init(msr_init); +module_exit(msr_exit) + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("H. Peter Anvin "); +MODULE_DESCRIPTION("x86 generic MSR driver"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/mtrr.c linux.ac/arch/x86_64/kernel/mtrr.c --- linux.vanilla/arch/x86_64/kernel/mtrr.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/mtrr.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,2270 @@ +/* Generic MTRR (Memory Type Range Register) driver. + + Copyright (C) 1997-2000 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. + + Source: "Pentium Pro Family Developer's Manual, Volume 3: + Operating System Writer's Guide" (Intel document number 242692), + section 11.11.7 + + ChangeLog + + Prehistory Martin Tischhäuser + Initial register-setting code (from proform-1.0). + 19971216 Richard Gooch + Original version for /proc/mtrr interface, SMP-safe. + v1.0 + 19971217 Richard Gooch + Bug fix for ioctls()'s. + Added sample code in Documentation/mtrr.txt + v1.1 + 19971218 Richard Gooch + Disallow overlapping regions. + 19971219 Jens Maurer + Register-setting fixups. + v1.2 + 19971222 Richard Gooch + Fixups for kernel 2.1.75. + v1.3 + 19971229 David Wragg + Register-setting fixups and conformity with Intel conventions. + 19971229 Richard Gooch + Cosmetic changes and wrote this ChangeLog ;-) + 19980106 Richard Gooch + Fixups for kernel 2.1.78. + v1.4 + 19980119 David Wragg + Included passive-release enable code (elsewhere in PCI setup). + v1.5 + 19980131 Richard Gooch + Replaced global kernel lock with private spinlock. + v1.6 + 19980201 Richard Gooch + Added wait for other CPUs to complete changes. + v1.7 + 19980202 Richard Gooch + Bug fix in definition of for UP. + v1.8 + 19980319 Richard Gooch + Fixups for kernel 2.1.90. + 19980323 Richard Gooch + Move SMP BIOS fixup before secondary CPUs call + v1.9 + 19980325 Richard Gooch + Fixed test for overlapping regions: confused by adjacent regions + 19980326 Richard Gooch + Added wbinvd in . + 19980401 Richard Gooch + Bug fix for non-SMP compilation. + 19980418 David Wragg + Fixed-MTRR synchronisation for SMP and use atomic operations + instead of spinlocks. + 19980418 Richard Gooch + Differentiate different MTRR register classes for BIOS fixup. + v1.10 + 19980419 David Wragg + Bug fix in variable MTRR synchronisation. + v1.11 + 19980419 Richard Gooch + Fixups for kernel 2.1.97. + v1.12 + 19980421 Richard Gooch + Safer synchronisation across CPUs when changing MTRRs. + v1.13 + 19980423 Richard Gooch + Bugfix for SMP systems without MTRR support. + v1.14 + 19980427 Richard Gooch + Trap calls to and on non-MTRR machines. + v1.15 + 19980427 Richard Gooch + Use atomic bitops for setting SMP change mask. + v1.16 + 19980428 Richard Gooch + Removed spurious diagnostic message. + v1.17 + 19980429 Richard Gooch + Moved register-setting macros into this file. + Moved setup code from init/main.c to i386-specific areas. + v1.18 + 19980502 Richard Gooch + Moved MTRR detection outside conditionals in . + v1.19 + 19980502 Richard Gooch + Documentation improvement: mention Pentium II and AGP. + v1.20 + 19980521 Richard Gooch + Only manipulate interrupt enable flag on local CPU. + Allow enclosed uncachable regions. + v1.21 + 19980611 Richard Gooch + Always define . + v1.22 + 19980901 Richard Gooch + Removed module support in order to tidy up code. + Added sanity check for / before . + Created addition queue for prior to SMP commence. + v1.23 + 19980902 Richard Gooch + Ported patch to kernel 2.1.120-pre3. + v1.24 + 19980910 Richard Gooch + Removed sanity checks and addition queue: Linus prefers an OOPS. + v1.25 + 19981001 Richard Gooch + Fixed harmless compiler warning in include/asm-i386/mtrr.h + Fixed version numbering and history for v1.23 -> v1.24. + v1.26 + 19990118 Richard Gooch + Added devfs support. + v1.27 + 19990123 Richard Gooch + Changed locking to spin with reschedule. + Made use of new . + v1.28 + 19990201 Zoltán Böszörményi + Extended the driver to be able to use Cyrix style ARRs. + 19990204 Richard Gooch + Restructured Cyrix support. + v1.29 + 19990204 Zoltán Böszörményi + Refined ARR support: enable MAPEN in set_mtrr_prepare() + and disable MAPEN in set_mtrr_done(). + 19990205 Richard Gooch + Minor cleanups. + v1.30 + 19990208 Zoltán Böszörményi + Protect plain 6x86s (and other processors without the + Page Global Enable feature) against accessing CR4 in + set_mtrr_prepare() and set_mtrr_done(). + 19990210 Richard Gooch + Turned and into function pointers. + v1.31 + 19990212 Zoltán Böszörményi + Major rewrite of cyrix_arr_init(): do not touch ARRs, + leave them as the BIOS have set them up. + Enable usage of all 8 ARRs. + Avoid multiplications by 3 everywhere and other + code clean ups/speed ups. + 19990213 Zoltán Böszörményi + Set up other Cyrix processors identical to the boot cpu. + Since Cyrix don't support Intel APIC, this is l'art pour l'art. + Weigh ARRs by size: + If size <= 32M is given, set up ARR# we were given. + If size > 32M is given, set up ARR7 only if it is free, + fail otherwise. + 19990214 Zoltán Böszörményi + Also check for size >= 256K if we are to set up ARR7, + mtrr_add() returns the value it gets from set_mtrr() + 19990218 Zoltán Böszörményi + Remove Cyrix "coma bug" workaround from here. + Moved to linux/arch/i386/kernel/setup.c and + linux/include/asm-i386/bugs.h + 19990228 Richard Gooch + Added MTRRIOC_KILL_ENTRY ioctl(2) + Trap for counter underflow in . + Trap for 4 MiB aligned regions for PPro, stepping <= 7. + 19990301 Richard Gooch + Created hook. + 19990305 Richard Gooch + Temporarily disable AMD support now MTRR capability flag is set. + v1.32 + 19990308 Zoltán Böszörményi + Adjust my changes (19990212-19990218) to Richard Gooch's + latest changes. (19990228-19990305) + v1.33 + 19990309 Richard Gooch + Fixed typo in message. + 19990310 Richard Gooch + Support K6-II/III based on Alan Cox's patches. + v1.34 + 19990511 Bart Hartgers + Support Centaur C6 MCR's. + 19990512 Richard Gooch + Minor cleanups. + v1.35 + 19990707 Zoltán Böszörményi + Check whether ARR3 is protected in cyrix_get_free_region() + and mtrr_del(). The code won't attempt to delete or change it + from now on if the BIOS protected ARR3. It silently skips ARR3 + in cyrix_get_free_region() or returns with an error code from + mtrr_del(). + 19990711 Zoltán Böszörményi + Reset some bits in the CCRs in cyrix_arr_init() to disable SMM + if ARR3 isn't protected. This is needed because if SMM is active + and ARR3 isn't protected then deleting and setting ARR3 again + may lock up the processor. With SMM entirely disabled, it does + not happen. + 19990812 Zoltán Böszörményi + Rearrange switch() statements so the driver accomodates to + the fact that the AMD Athlon handles its MTRRs the same way + as Intel does. + 19990814 Zoltán Böszörményi + Double check for Intel in mtrr_add()'s big switch() because + that revision check is only valid for Intel CPUs. + 19990819 Alan Cox + Tested Zoltan's changes on a pre production Athlon - 100% + success. + 19991008 Manfred Spraul + replaced spin_lock_reschedule() with a normal semaphore. + v1.36 + 20000221 Richard Gooch + Compile fix if procfs and devfs not enabled. + Formatting changes. + v1.37 + 20001109 H. Peter Anvin + Use the new centralized CPU feature detects. + + v1.38 + 20010309 Dave Jones + Add support for Cyrix III. + + v1.39 + 20010312 Dave Jones + Ugh, I broke AMD support. + Reworked fix by Troels Walsted Hansen + + v1.40 + 20010327 Dave Jones + Adapted Cyrix III support to include VIA C3. + +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define MTRR_NEED_STRINGS +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MTRR_VERSION "1.40 (20010327)" + +#define TRUE 1 +#define FALSE 0 + +/* + * The code assumes all processors support the same MTRR + * interface. This is generally a good assumption, but could + * potentially be a problem. + */ +enum mtrr_if_type { + MTRR_IF_NONE, /* No MTRRs supported */ + MTRR_IF_INTEL, /* Intel (P6) standard MTRRs */ + MTRR_IF_AMD_K6, /* AMD pre-Athlon MTRRs */ + MTRR_IF_CYRIX_ARR, /* Cyrix ARRs */ + MTRR_IF_CENTAUR_MCR, /* Centaur MCRs */ +} mtrr_if = MTRR_IF_NONE; + +static __initdata char *mtrr_if_name[] = { + "none", "Intel", "AMD K6", "Cyrix ARR", "Centaur MCR" +}; + +#define MTRRcap_MSR 0x0fe +#define MTRRdefType_MSR 0x2ff + +#define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg)) +#define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1) + +#define NUM_FIXED_RANGES 88 +#define MTRRfix64K_00000_MSR 0x250 +#define MTRRfix16K_80000_MSR 0x258 +#define MTRRfix16K_A0000_MSR 0x259 +#define MTRRfix4K_C0000_MSR 0x268 +#define MTRRfix4K_C8000_MSR 0x269 +#define MTRRfix4K_D0000_MSR 0x26a +#define MTRRfix4K_D8000_MSR 0x26b +#define MTRRfix4K_E0000_MSR 0x26c +#define MTRRfix4K_E8000_MSR 0x26d +#define MTRRfix4K_F0000_MSR 0x26e +#define MTRRfix4K_F8000_MSR 0x26f + +#ifdef CONFIG_SMP +# define MTRR_CHANGE_MASK_FIXED 0x01 +# define MTRR_CHANGE_MASK_VARIABLE 0x02 +# define MTRR_CHANGE_MASK_DEFTYPE 0x04 +#endif + +/* In the Intel processor's MTRR interface, the MTRR type is always held in + an 8 bit field: */ +typedef u8 mtrr_type; + +#define LINE_SIZE 80 +#define JIFFIE_TIMEOUT 100 + +#ifdef CONFIG_SMP +# define set_mtrr(reg,base,size,type) set_mtrr_smp (reg, base, size, type) +#else +# define set_mtrr(reg,base,size,type) (*set_mtrr_up) (reg, base, size, type, \ + TRUE) +#endif + +#if defined(CONFIG_PROC_FS) || defined(CONFIG_DEVFS_FS) +# define USERSPACE_INTERFACE +#endif + +#ifndef USERSPACE_INTERFACE +# define compute_ascii() while (0) +#endif + +#ifdef USERSPACE_INTERFACE +static char *ascii_buffer; +static unsigned int ascii_buf_bytes; +#endif +static unsigned int *usage_table; +static DECLARE_MUTEX(main_lock); + +/* Private functions */ +#ifdef USERSPACE_INTERFACE +static void compute_ascii (void); +#endif + + +struct set_mtrr_context +{ + unsigned long flags; + unsigned long deftype_lo; + unsigned long deftype_hi; + unsigned long cr4val; + unsigned long ccr3; +}; + +static int arr3_protected; + +/* Put the processor into a state where MTRRs can be safely set */ +static void set_mtrr_prepare (struct set_mtrr_context *ctxt) +{ + unsigned long tmp; + + /* Disable interrupts locally */ + __save_flags (ctxt->flags); __cli (); + + if ( mtrr_if != MTRR_IF_INTEL && mtrr_if != MTRR_IF_CYRIX_ARR ) + return; + + /* Save value of CR4 and clear Page Global Enable (bit 7) */ + if ( test_bit(X86_FEATURE_PGE, &boot_cpu_data.x86_capability) ) { + ctxt->cr4val = read_cr4(); + write_cr4(ctxt->cr4val & (unsigned char) ~(1<<7)); + } + + /* Disable and flush caches. Note that wbinvd flushes the TLBs as + a side-effect */ + + { + long cr0 = read_cr0() | 0x40000000; + wbinvd(); + write_cr0( cr0 ); + wbinvd(); + } + + if ( mtrr_if == MTRR_IF_INTEL ) { + /* Disable MTRRs, and set the default type to uncached */ + rdmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + wrmsr (MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi); + } else { + /* Cyrix ARRs - everything else were excluded at the top */ + tmp = getCx86 (CX86_CCR3); + setCx86 (CX86_CCR3, (tmp & 0x0f) | 0x10); + ctxt->ccr3 = tmp; + } +} /* End Function set_mtrr_prepare */ + +/* Restore the processor after a set_mtrr_prepare */ +static void set_mtrr_done (struct set_mtrr_context *ctxt) +{ + if ( mtrr_if != MTRR_IF_INTEL && mtrr_if != MTRR_IF_CYRIX_ARR ) { + __restore_flags (ctxt->flags); + return; + } + + /* Flush caches and TLBs */ + wbinvd(); + + /* Restore MTRRdefType */ + if ( mtrr_if == MTRR_IF_INTEL ) { + /* Intel (P6) standard MTRRs */ + wrmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + } else { + /* Cyrix ARRs - everything else was excluded at the top */ + setCx86 (CX86_CCR3, ctxt->ccr3); + } + + /* Enable caches */ + write_cr0( read_cr0() & 0xbfffffff ); + + /* Restore value of CR4 */ + if ( test_bit(X86_FEATURE_PGE, &boot_cpu_data.x86_capability) ) + write_cr4(ctxt->cr4val); + + /* Re-enable interrupts locally (if enabled previously) */ + __restore_flags (ctxt->flags); +} /* End Function set_mtrr_done */ + +/* This function returns the number of variable MTRRs */ +static unsigned int get_num_var_ranges (void) +{ + unsigned long config, dummy; + + switch ( mtrr_if ) + { + case MTRR_IF_INTEL: + rdmsr (MTRRcap_MSR, config, dummy); + return (config & 0xff); + case MTRR_IF_AMD_K6: + return 2; + case MTRR_IF_CYRIX_ARR: + return 8; + case MTRR_IF_CENTAUR_MCR: + return 8; + default: + return 0; + } +} /* End Function get_num_var_ranges */ + +/* Returns non-zero if we have the write-combining memory type */ +static int have_wrcomb (void) +{ + unsigned long config, dummy; + struct pci_dev *dev = NULL; + + /* ServerWorks LE chipsets have problems with write-combining + Don't allow it and leave room for other chipsets to be tagged */ + + if ((dev = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8, NULL)) != NULL) { + switch(dev->vendor) { + case PCI_VENDOR_ID_SERVERWORKS: + switch (dev->device) { + case PCI_DEVICE_ID_SERVERWORKS_LE: + return 0; + break; + default: + break; + } + break; + default: + break; + } + } + + + switch ( mtrr_if ) + { + case MTRR_IF_INTEL: + rdmsr (MTRRcap_MSR, config, dummy); + return (config & (1<<10)); + return 1; + case MTRR_IF_AMD_K6: + case MTRR_IF_CENTAUR_MCR: + case MTRR_IF_CYRIX_ARR: + return 1; + default: + return 0; + } +} /* End Function have_wrcomb */ + +static u32 size_or_mask, size_and_mask; + +static void intel_get_mtrr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) +{ + unsigned long mask_lo, mask_hi, base_lo, base_hi; + + rdmsr (MTRRphysMask_MSR(reg), mask_lo, mask_hi); + if ( (mask_lo & 0x800) == 0 ) + { + /* Invalid (i.e. free) range */ + *base = 0; + *size = 0; + *type = 0; + return; + } + + rdmsr(MTRRphysBase_MSR(reg), base_lo, base_hi); + + /* Work out the shifted address mask. */ + mask_lo = size_or_mask | mask_hi << (32 - PAGE_SHIFT) + | mask_lo >> PAGE_SHIFT; + + /* This works correctly if size is a power of two, i.e. a + contiguous range. */ + *size = -mask_lo; + *base = base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT; + *type = base_lo & 0xff; +} /* End Function intel_get_mtrr */ + +static void cyrix_get_arr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) +{ + unsigned long flags; + unsigned char arr, ccr3, rcr, shift; + + arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ + + /* Save flags and disable interrupts */ + __save_flags (flags); __cli (); + + ccr3 = getCx86 (CX86_CCR3); + setCx86 (CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ + ((unsigned char *) base)[3] = getCx86 (arr); + ((unsigned char *) base)[2] = getCx86 (arr+1); + ((unsigned char *) base)[1] = getCx86 (arr+2); + rcr = getCx86(CX86_RCR_BASE + reg); + setCx86 (CX86_CCR3, ccr3); /* disable MAPEN */ + + /* Enable interrupts if it was enabled previously */ + __restore_flags (flags); + shift = ((unsigned char *) base)[1] & 0x0f; + *base >>= PAGE_SHIFT; + + /* Power of two, at least 4K on ARR0-ARR6, 256K on ARR7 + * Note: shift==0xf means 4G, this is unsupported. + */ + if (shift) + *size = (reg < 7 ? 0x1UL : 0x40UL) << (shift - 1); + else + *size = 0; + + /* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */ + if (reg < 7) + { + switch (rcr) + { + case 1: *type = MTRR_TYPE_UNCACHABLE; break; + case 8: *type = MTRR_TYPE_WRBACK; break; + case 9: *type = MTRR_TYPE_WRCOMB; break; + case 24: + default: *type = MTRR_TYPE_WRTHROUGH; break; + } + } else + { + switch (rcr) + { + case 0: *type = MTRR_TYPE_UNCACHABLE; break; + case 8: *type = MTRR_TYPE_WRCOMB; break; + case 9: *type = MTRR_TYPE_WRBACK; break; + case 25: + default: *type = MTRR_TYPE_WRTHROUGH; break; + } + } +} /* End Function cyrix_get_arr */ + +static void amd_get_mtrr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) +{ + unsigned long low, high; + + rdmsr (0xC0000085, low, high); + /* Upper dword is region 1, lower is region 0 */ + if (reg == 1) low = high; + /* The base masks off on the right alignment */ + *base = (low & 0xFFFE0000) >> PAGE_SHIFT; + *type = 0; + if (low & 1) *type = MTRR_TYPE_UNCACHABLE; + if (low & 2) *type = MTRR_TYPE_WRCOMB; + if ( !(low & 3) ) + { + *size = 0; + return; + } + /* + * This needs a little explaining. The size is stored as an + * inverted mask of bits of 128K granularity 15 bits long offset + * 2 bits + * + * So to get a size we do invert the mask and add 1 to the lowest + * mask bit (4 as its 2 bits in). This gives us a size we then shift + * to turn into 128K blocks + * + * eg 111 1111 1111 1100 is 512K + * + * invert 000 0000 0000 0011 + * +1 000 0000 0000 0100 + * *128K ... + */ + low = (~low) & 0x1FFFC; + *size = (low + 4) << (15 - PAGE_SHIFT); + return; +} /* End Function amd_get_mtrr */ + +static struct +{ + unsigned long high; + unsigned long low; +} centaur_mcr[8]; + +static u8 centaur_mcr_reserved; +static u8 centaur_mcr_type; /* 0 for winchip, 1 for winchip2 */ + +/* + * Report boot time MCR setups + */ + +void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi) +{ + centaur_mcr[mcr].low = lo; + centaur_mcr[mcr].high = hi; +} + +static void centaur_get_mcr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) +{ + *base = centaur_mcr[reg].high >> PAGE_SHIFT; + *size = -(centaur_mcr[reg].low & 0xfffff000) >> PAGE_SHIFT; + *type = MTRR_TYPE_WRCOMB; /* If it is there, it is write-combining */ + if(centaur_mcr_type==1 && ((centaur_mcr[reg].low&31)&2)) + *type = MTRR_TYPE_UNCACHABLE; + if(centaur_mcr_type==1 && (centaur_mcr[reg].low&31)==25) + *type = MTRR_TYPE_WRBACK; + if(centaur_mcr_type==0 && (centaur_mcr[reg].low&31)==31) + *type = MTRR_TYPE_WRBACK; + +} /* End Function centaur_get_mcr */ + +static void (*get_mtrr) (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type); + +static void intel_set_mtrr_up (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, int do_safe) +/* [SUMMARY] Set variable MTRR register on the local CPU. + The register to set. + The base address of the region. + The size of the region. If this is 0 the region is disabled. + The type of the region. + If TRUE, do the change safely. If FALSE, safety measures should + be done externally. + [RETURNS] Nothing. +*/ +{ + struct set_mtrr_context ctxt; + + if (do_safe) set_mtrr_prepare (&ctxt); + if (size == 0) + { + /* The invalid bit is kept in the mask, so we simply clear the + relevant mask register to disable a range. */ + wrmsr (MTRRphysMask_MSR (reg), 0, 0); + } + else + { + wrmsr (MTRRphysBase_MSR (reg), base << PAGE_SHIFT | type, + (base & size_and_mask) >> (32 - PAGE_SHIFT)); + wrmsr (MTRRphysMask_MSR (reg), -size << PAGE_SHIFT | 0x800, + (-size & size_and_mask) >> (32 - PAGE_SHIFT)); + } + if (do_safe) set_mtrr_done (&ctxt); +} /* End Function intel_set_mtrr_up */ + +static void cyrix_set_arr_up (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, int do_safe) +{ + struct set_mtrr_context ctxt; + unsigned char arr, arr_type, arr_size; + + arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ + + /* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */ + if (reg >= 7) + size >>= 6; + + size &= 0x7fff; /* make sure arr_size <= 14 */ + for(arr_size = 0; size; arr_size++, size >>= 1); + + if (reg<7) + { + switch (type) { + case MTRR_TYPE_UNCACHABLE: arr_type = 1; break; + case MTRR_TYPE_WRCOMB: arr_type = 9; break; + case MTRR_TYPE_WRTHROUGH: arr_type = 24; break; + default: arr_type = 8; break; + } + } + else + { + switch (type) + { + case MTRR_TYPE_UNCACHABLE: arr_type = 0; break; + case MTRR_TYPE_WRCOMB: arr_type = 8; break; + case MTRR_TYPE_WRTHROUGH: arr_type = 25; break; + default: arr_type = 9; break; + } + } + + if (do_safe) set_mtrr_prepare (&ctxt); + base <<= PAGE_SHIFT; + setCx86(arr, ((unsigned char *) &base)[3]); + setCx86(arr+1, ((unsigned char *) &base)[2]); + setCx86(arr+2, (((unsigned char *) &base)[1]) | arr_size); + setCx86(CX86_RCR_BASE + reg, arr_type); + if (do_safe) set_mtrr_done (&ctxt); +} /* End Function cyrix_set_arr_up */ + +static void amd_set_mtrr_up (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, int do_safe) +/* [SUMMARY] Set variable MTRR register on the local CPU. + The register to set. + The base address of the region. + The size of the region. If this is 0 the region is disabled. + The type of the region. + If TRUE, do the change safely. If FALSE, safety measures should + be done externally. + [RETURNS] Nothing. +*/ +{ + u32 regs[2]; + struct set_mtrr_context ctxt; + + if (do_safe) set_mtrr_prepare (&ctxt); + /* + * Low is MTRR0 , High MTRR 1 + */ + rdmsr (0xC0000085, regs[0], regs[1]); + /* + * Blank to disable + */ + if (size == 0) + regs[reg] = 0; + else + /* Set the register to the base, the type (off by one) and an + inverted bitmask of the size The size is the only odd + bit. We are fed say 512K We invert this and we get 111 1111 + 1111 1011 but if you subtract one and invert you get the + desired 111 1111 1111 1100 mask + + But ~(x - 1) == ~x + 1 == -x. Two's complement rocks! */ + regs[reg] = (-size>>(15-PAGE_SHIFT) & 0x0001FFFC) + | (base<base_lo, vr->base_hi); + rdmsr (MTRRphysMask_MSR (index), vr->mask_lo, vr->mask_hi); +} /* End Function get_mtrr_var_range */ + + +/* Set the MSR pair relating to a var range. Returns TRUE if + changes are made */ +static int __init set_mtrr_var_range_testing (unsigned int index, + struct mtrr_var_range *vr) +{ + unsigned int lo, hi; + int changed = FALSE; + + rdmsr(MTRRphysBase_MSR(index), lo, hi); + if ( (vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL) + || (vr->base_hi & 0xfUL) != (hi & 0xfUL) ) + { + wrmsr (MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); + changed = TRUE; + } + + rdmsr (MTRRphysMask_MSR(index), lo, hi); + + if ( (vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL) + || (vr->mask_hi & 0xfUL) != (hi & 0xfUL) ) + { + wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); + changed = TRUE; + } + return changed; +} /* End Function set_mtrr_var_range_testing */ + +static void __init get_fixed_ranges(mtrr_type *frs) +{ + unsigned long *p = (unsigned long *)frs; + int i; + + rdmsr(MTRRfix64K_00000_MSR, p[0], p[1]); + + for (i = 0; i < 2; i++) + rdmsr(MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]); + for (i = 0; i < 8; i++) + rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]); +} /* End Function get_fixed_ranges */ + +static int __init set_fixed_ranges_testing(mtrr_type *frs) +{ + unsigned long *p = (unsigned long *)frs; + int changed = FALSE; + int i; + unsigned long lo, hi; + + rdmsr(MTRRfix64K_00000_MSR, lo, hi); + if (p[0] != lo || p[1] != hi) + { + wrmsr (MTRRfix64K_00000_MSR, p[0], p[1]); + changed = TRUE; + } + + for (i = 0; i < 2; i++) + { + rdmsr (MTRRfix16K_80000_MSR + i, lo, hi); + if (p[2 + i*2] != lo || p[3 + i*2] != hi) + { + wrmsr (MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]); + changed = TRUE; + } + } + + for (i = 0; i < 8; i++) + { + rdmsr (MTRRfix4K_C0000_MSR + i, lo, hi); + if (p[6 + i*2] != lo || p[7 + i*2] != hi) + { + wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]); + changed = TRUE; + } + } + return changed; +} /* End Function set_fixed_ranges_testing */ + +struct mtrr_state +{ + unsigned int num_var_ranges; + struct mtrr_var_range *var_ranges; + mtrr_type fixed_ranges[NUM_FIXED_RANGES]; + unsigned char enabled; + mtrr_type def_type; +}; + + +/* Grab all of the MTRR state for this CPU into *state */ +static void __init get_mtrr_state(struct mtrr_state *state) +{ + unsigned int nvrs, i; + struct mtrr_var_range *vrs; + unsigned long lo, dummy; + + nvrs = state->num_var_ranges = get_num_var_ranges(); + vrs = state->var_ranges + = kmalloc (nvrs * sizeof (struct mtrr_var_range), GFP_KERNEL); + if (vrs == NULL) + nvrs = state->num_var_ranges = 0; + + for (i = 0; i < nvrs; i++) + get_mtrr_var_range (i, &vrs[i]); + get_fixed_ranges (state->fixed_ranges); + + rdmsr (MTRRdefType_MSR, lo, dummy); + state->def_type = (lo & 0xff); + state->enabled = (lo & 0xc00) >> 10; +} /* End Function get_mtrr_state */ + + +/* Free resources associated with a struct mtrr_state */ +static void __init finalize_mtrr_state(struct mtrr_state *state) +{ + if (state->var_ranges) kfree (state->var_ranges); +} /* End Function finalize_mtrr_state */ + + +static unsigned long __init set_mtrr_state (struct mtrr_state *state, + struct set_mtrr_context *ctxt) +/* [SUMMARY] Set the MTRR state for this CPU. + The MTRR state information to read. + Some relevant CPU context. + [NOTE] The CPU must already be in a safe state for MTRR changes. + [RETURNS] 0 if no changes made, else a mask indication what was changed. +*/ +{ + unsigned int i; + unsigned long change_mask = 0; + + for (i = 0; i < state->num_var_ranges; i++) + if ( set_mtrr_var_range_testing (i, &state->var_ranges[i]) ) + change_mask |= MTRR_CHANGE_MASK_VARIABLE; + + if ( set_fixed_ranges_testing(state->fixed_ranges) ) + change_mask |= MTRR_CHANGE_MASK_FIXED; + /* Set_mtrr_restore restores the old value of MTRRdefType, + so to set it we fiddle with the saved value */ + if ( (ctxt->deftype_lo & 0xff) != state->def_type + || ( (ctxt->deftype_lo & 0xc00) >> 10 ) != state->enabled) + { + ctxt->deftype_lo |= (state->def_type | state->enabled << 10); + change_mask |= MTRR_CHANGE_MASK_DEFTYPE; + } + + return change_mask; +} /* End Function set_mtrr_state */ + + +static atomic_t undone_count; +static volatile int wait_barrier_execute = FALSE; +static volatile int wait_barrier_cache_enable = FALSE; + +struct set_mtrr_data +{ + unsigned long smp_base; + unsigned long smp_size; + unsigned int smp_reg; + mtrr_type smp_type; +}; + +static void ipi_handler (void *info) +/* [SUMMARY] Synchronisation handler. Executed by "other" CPUs. + [RETURNS] Nothing. +*/ +{ + struct set_mtrr_data *data = info; + struct set_mtrr_context ctxt; + + set_mtrr_prepare (&ctxt); + /* Notify master that I've flushed and disabled my cache */ + atomic_dec (&undone_count); + while (wait_barrier_execute) barrier (); + /* The master has cleared me to execute */ + (*set_mtrr_up) (data->smp_reg, data->smp_base, data->smp_size, + data->smp_type, FALSE); + /* Notify master CPU that I've executed the function */ + atomic_dec (&undone_count); + /* Wait for master to clear me to enable cache and return */ + while (wait_barrier_cache_enable) barrier (); + set_mtrr_done (&ctxt); +} /* End Function ipi_handler */ + +static void set_mtrr_smp (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type) +{ + struct set_mtrr_data data; + struct set_mtrr_context ctxt; + + data.smp_reg = reg; + data.smp_base = base; + data.smp_size = size; + data.smp_type = type; + wait_barrier_execute = TRUE; + wait_barrier_cache_enable = TRUE; + atomic_set (&undone_count, smp_num_cpus - 1); + /* Start the ball rolling on other CPUs */ + if (smp_call_function (ipi_handler, &data, 1, 0) != 0) + panic ("mtrr: timed out waiting for other CPUs\n"); + /* Flush and disable the local CPU's cache */ + set_mtrr_prepare (&ctxt); + /* Wait for all other CPUs to flush and disable their caches */ + while (atomic_read (&undone_count) > 0) barrier (); + /* Set up for completion wait and then release other CPUs to change MTRRs*/ + atomic_set (&undone_count, smp_num_cpus - 1); + wait_barrier_execute = FALSE; + (*set_mtrr_up) (reg, base, size, type, FALSE); + /* Now wait for other CPUs to complete the function */ + while (atomic_read (&undone_count) > 0) barrier (); + /* Now all CPUs should have finished the function. Release the barrier to + allow them to re-enable their caches and return from their interrupt, + then enable the local cache and return */ + wait_barrier_cache_enable = FALSE; + set_mtrr_done (&ctxt); +} /* End Function set_mtrr_smp */ + + +/* Some BIOS's are fucked and don't set all MTRRs the same! */ +static void __init mtrr_state_warn(unsigned long mask) +{ + if (!mask) return; + if (mask & MTRR_CHANGE_MASK_FIXED) + printk ("mtrr: your CPUs had inconsistent fixed MTRR settings\n"); + if (mask & MTRR_CHANGE_MASK_VARIABLE) + printk ("mtrr: your CPUs had inconsistent variable MTRR settings\n"); + if (mask & MTRR_CHANGE_MASK_DEFTYPE) + printk ("mtrr: your CPUs had inconsistent MTRRdefType settings\n"); + printk ("mtrr: probably your BIOS does not setup all CPUs\n"); +} /* End Function mtrr_state_warn */ + +#endif /* CONFIG_SMP */ + +static char *attrib_to_str (int x) +{ + return (x <= 6) ? mtrr_strings[x] : "?"; +} /* End Function attrib_to_str */ + +static void init_table (void) +{ + int i, max; + + max = get_num_var_ranges (); + if ( ( usage_table = kmalloc (max * sizeof *usage_table, GFP_KERNEL) ) + == NULL ) + { + printk ("mtrr: could not allocate\n"); + return; + } + for (i = 0; i < max; i++) usage_table[i] = 1; +#ifdef USERSPACE_INTERFACE + if ( ( ascii_buffer = kmalloc (max * LINE_SIZE, GFP_KERNEL) ) == NULL ) + { + printk ("mtrr: could not allocate\n"); + return; + } + ascii_buf_bytes = 0; + compute_ascii (); +#endif +} /* End Function init_table */ + +static int generic_get_free_region (unsigned long base, unsigned long size) +/* [SUMMARY] Get a free MTRR. + The starting (base) address of the region. + The size (in bytes) of the region. + [RETURNS] The index of the region on success, else -1 on error. +*/ +{ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize; + + max = get_num_var_ranges (); + for (i = 0; i < max; ++i) + { + (*get_mtrr) (i, &lbase, &lsize, <ype); + if (lsize == 0) return i; + } + return -ENOSPC; +} /* End Function generic_get_free_region */ + +static int centaur_get_free_region (unsigned long base, unsigned long size) +/* [SUMMARY] Get a free MTRR. + The starting (base) address of the region. + The size (in bytes) of the region. + [RETURNS] The index of the region on success, else -1 on error. +*/ +{ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize; + + max = get_num_var_ranges (); + for (i = 0; i < max; ++i) + { + if(centaur_mcr_reserved & (1< The starting (base) address of the region. + The size (in bytes) of the region. + [RETURNS] The index of the region on success, else -1 on error. +*/ +{ + int i; + mtrr_type ltype; + unsigned long lbase, lsize; + + /* If we are to set up a region >32M then look at ARR7 immediately */ + if (size > 0x2000) + { + cyrix_get_arr (7, &lbase, &lsize, <ype); + if (lsize == 0) return 7; + /* Else try ARR0-ARR6 first */ + } + else + { + for (i = 0; i < 7; i++) + { + cyrix_get_arr (i, &lbase, &lsize, <ype); + if ((i == 3) && arr3_protected) continue; + if (lsize == 0) return i; + } + /* ARR0-ARR6 isn't free, try ARR7 but its size must be at least 256K */ + cyrix_get_arr (i, &lbase, &lsize, <ype); + if ((lsize == 0) && (size >= 0x40)) return i; + } + return -ENOSPC; +} /* End Function cyrix_get_free_region */ + +static int (*get_free_region) (unsigned long base, + unsigned long size) = generic_get_free_region; + +/** + * mtrr_add_page - Add a memory type region + * @base: Physical base address of region in pages (4 KB) + * @size: Physical size of region in pages (4 KB) + * @type: Type of MTRR desired + * @increment: If this is true do usage counting on the region + * + * Memory type region registers control the caching on newer Intel and + * non Intel processors. This function allows drivers to request an + * MTRR is added. The details and hardware specifics of each processor's + * implementation are hidden from the caller, but nevertheless the + * caller should expect to need to provide a power of two size on an + * equivalent power of two boundary. + * + * If the region cannot be added either because all regions are in use + * or the CPU cannot support it a negative value is returned. On success + * the register number for this entry is returned, but should be treated + * as a cookie only. + * + * On a multiprocessor machine the changes are made to all processors. + * This is required on x86 by the Intel processors. + * + * The available types are + * + * %MTRR_TYPE_UNCACHABLE - No caching + * + * %MTRR_TYPE_WRBACK - Write data back in bursts whenever + * + * %MTRR_TYPE_WRCOMB - Write data back soon but allow bursts + * + * %MTRR_TYPE_WRTHROUGH - Cache reads but not writes + * + * BUGS: Needs a quiet flag for the cases where drivers do not mind + * failures and do not wish system log messages to be sent. + */ + +int mtrr_add_page(unsigned long base, unsigned long size, unsigned int type, char increment) +{ +/* [SUMMARY] Add an MTRR entry. + The starting (base, in pages) address of the region. + The size of the region. (in pages) + The type of the new region. + If true and the region already exists, the usage count will be + incremented. + [RETURNS] The MTRR register on success, else a negative number indicating + the error code. + [NOTE] This routine uses a spinlock. +*/ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize, last; + + switch ( mtrr_if ) + { + case MTRR_IF_NONE: + return -ENXIO; /* No MTRRs whatsoever */ + + case MTRR_IF_AMD_K6: + /* Apply the K6 block alignment and size rules + In order + o Uncached or gathering only + o 128K or bigger block + o Power of 2 block + o base suitably aligned to the power + */ + if ( type > MTRR_TYPE_WRCOMB || size < (1 << (17-PAGE_SHIFT)) || + (size & ~(size-1))-size || ( base & (size-1) ) ) + return -EINVAL; + break; + + case MTRR_IF_INTEL: + /* For Intel PPro stepping <= 7, must be 4 MiB aligned */ + if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && + boot_cpu_data.x86 == 6 && + boot_cpu_data.x86_model == 1 && + boot_cpu_data.x86_mask <= 7 ) + { + if ( base & ((1 << (22-PAGE_SHIFT))-1) ) + { + printk (KERN_WARNING "mtrr: base(0x%lx000) is not 4 MiB aligned\n", base); + return -EINVAL; + } + } + /* Fall through */ + + case MTRR_IF_CYRIX_ARR: + case MTRR_IF_CENTAUR_MCR: + if ( mtrr_if == MTRR_IF_CENTAUR_MCR ) + { + /* + * FIXME: Winchip2 supports uncached + */ + if (type != MTRR_TYPE_WRCOMB && (centaur_mcr_type == 0 || type != MTRR_TYPE_UNCACHABLE)) + { + printk (KERN_WARNING "mtrr: only write-combining%s supported\n", + centaur_mcr_type?" and uncacheable are":" is"); + return -EINVAL; + } + } + else if (base + size < 0x100) + { + printk (KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%lx000,0x%lx000)\n", + base, size); + return -EINVAL; + } + /* Check upper bits of base and last are equal and lower bits are 0 + for base and 1 for last */ + last = base + size - 1; + for (lbase = base; !(lbase & 1) && (last & 1); + lbase = lbase >> 1, last = last >> 1); + if (lbase != last) + { + printk (KERN_WARNING "mtrr: base(0x%lx000) is not aligned on a size(0x%lx000) boundary\n", + base, size); + return -EINVAL; + } + break; + + default: + return -EINVAL; + } + + if (type >= MTRR_NUM_TYPES) + { + printk ("mtrr: type: %u illegal\n", type); + return -EINVAL; + } + + /* If the type is WC, check that this processor supports it */ + if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () ) + { + printk (KERN_WARNING "mtrr: your processor doesn't support write-combining\n"); + return -ENOSYS; + } + + if ( base & size_or_mask || size & size_or_mask ) + { + printk ("mtrr: base or size exceeds the MTRR width\n"); + return -EINVAL; + } + + increment = increment ? 1 : 0; + max = get_num_var_ranges (); + /* Search for existing MTRR */ + down(&main_lock); + for (i = 0; i < max; ++i) + { + (*get_mtrr) (i, &lbase, &lsize, <ype); + if (base >= lbase + lsize) continue; + if ( (base < lbase) && (base + size <= lbase) ) continue; + /* At this point we know there is some kind of overlap/enclosure */ + if ( (base < lbase) || (base + size > lbase + lsize) ) + { + up(&main_lock); + printk (KERN_WARNING "mtrr: 0x%lx000,0x%lx000 overlaps existing" + " 0x%lx000,0x%lx000\n", + base, size, lbase, lsize); + return -EINVAL; + } + /* New region is enclosed by an existing region */ + if (ltype != type) + { + if (type == MTRR_TYPE_UNCACHABLE) continue; + up(&main_lock); + printk ( "mtrr: type mismatch for %lx000,%lx000 old: %s new: %s\n", + base, size, attrib_to_str (ltype), attrib_to_str (type) ); + return -EINVAL; + } + if (increment) ++usage_table[i]; + compute_ascii (); + up(&main_lock); + return i; + } + /* Search for an empty MTRR */ + i = (*get_free_region) (base, size); + if (i < 0) + { + up(&main_lock); + printk ("mtrr: no more MTRRs available\n"); + return i; + } + set_mtrr (i, base, size, type); + usage_table[i] = 1; + compute_ascii (); + up(&main_lock); + return i; +} /* End Function mtrr_add_page */ + +/** + * mtrr_add - Add a memory type region + * @base: Physical base address of region + * @size: Physical size of region + * @type: Type of MTRR desired + * @increment: If this is true do usage counting on the region + * + * Memory type region registers control the caching on newer Intel and + * non Intel processors. This function allows drivers to request an + * MTRR is added. The details and hardware specifics of each processor's + * implementation are hidden from the caller, but nevertheless the + * caller should expect to need to provide a power of two size on an + * equivalent power of two boundary. + * + * If the region cannot be added either because all regions are in use + * or the CPU cannot support it a negative value is returned. On success + * the register number for this entry is returned, but should be treated + * as a cookie only. + * + * On a multiprocessor machine the changes are made to all processors. + * This is required on x86 by the Intel processors. + * + * The available types are + * + * %MTRR_TYPE_UNCACHABLE - No caching + * + * %MTRR_TYPE_WRBACK - Write data back in bursts whenever + * + * %MTRR_TYPE_WRCOMB - Write data back soon but allow bursts + * + * %MTRR_TYPE_WRTHROUGH - Cache reads but not writes + * + * BUGS: Needs a quiet flag for the cases where drivers do not mind + * failures and do not wish system log messages to be sent. + */ + +int mtrr_add(unsigned long base, unsigned long size, unsigned int type, char increment) +{ +/* [SUMMARY] Add an MTRR entry. + The starting (base) address of the region. + The size (in bytes) of the region. + The type of the new region. + If true and the region already exists, the usage count will be + incremented. + [RETURNS] The MTRR register on success, else a negative number indicating + the error code. +*/ + + if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) + { + printk ("mtrr: size and base must be multiples of 4 kiB\n"); + printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); + return -EINVAL; + } + return mtrr_add_page(base >> PAGE_SHIFT, size >> PAGE_SHIFT, type, increment); +} /* End Function mtrr_add */ + +/** + * mtrr_del_page - delete a memory type region + * @reg: Register returned by mtrr_add + * @base: Physical base address + * @size: Size of region + * + * If register is supplied then base and size are ignored. This is + * how drivers should call it. + * + * Releases an MTRR region. If the usage count drops to zero the + * register is freed and the region returns to default state. + * On success the register is returned, on failure a negative error + * code. + */ + +int mtrr_del_page (int reg, unsigned long base, unsigned long size) +/* [SUMMARY] Delete MTRR/decrement usage count. + The register. If this is less than 0 then <> and <> must + be supplied. + The base address of the region. This is ignored if <> is >= 0. + The size of the region. This is ignored if <> is >= 0. + [RETURNS] The register on success, else a negative number indicating + the error code. + [NOTE] This routine uses a spinlock. +*/ +{ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize; + + if ( mtrr_if == MTRR_IF_NONE ) return -ENXIO; + + max = get_num_var_ranges (); + down (&main_lock); + if (reg < 0) + { + /* Search for existing MTRR */ + for (i = 0; i < max; ++i) + { + (*get_mtrr) (i, &lbase, &lsize, <ype); + if (lbase == base && lsize == size) + { + reg = i; + break; + } + } + if (reg < 0) + { + up(&main_lock); + printk ("mtrr: no MTRR for %lx000,%lx000 found\n", base, size); + return -EINVAL; + } + } + if (reg >= max) + { + up (&main_lock); + printk ("mtrr: register: %d too big\n", reg); + return -EINVAL; + } + if ( mtrr_if == MTRR_IF_CYRIX_ARR ) + { + if ( (reg == 3) && arr3_protected ) + { + up (&main_lock); + printk ("mtrr: ARR3 cannot be changed\n"); + return -EINVAL; + } + } + (*get_mtrr) (reg, &lbase, &lsize, <ype); + if (lsize < 1) + { + up (&main_lock); + printk ("mtrr: MTRR %d not used\n", reg); + return -EINVAL; + } + if (usage_table[reg] < 1) + { + up (&main_lock); + printk ("mtrr: reg: %d has count=0\n", reg); + return -EINVAL; + } + if (--usage_table[reg] < 1) set_mtrr (reg, 0, 0, 0); + compute_ascii (); + up (&main_lock); + return reg; +} /* End Function mtrr_del_page */ + +/** + * mtrr_del - delete a memory type region + * @reg: Register returned by mtrr_add + * @base: Physical base address + * @size: Size of region + * + * If register is supplied then base and size are ignored. This is + * how drivers should call it. + * + * Releases an MTRR region. If the usage count drops to zero the + * register is freed and the region returns to default state. + * On success the register is returned, on failure a negative error + * code. + */ + +int mtrr_del (int reg, unsigned long base, unsigned long size) +/* [SUMMARY] Delete MTRR/decrement usage count. + The register. If this is less than 0 then <> and <> must + be supplied. + The base address of the region. This is ignored if <> is >= 0. + The size of the region. This is ignored if <> is >= 0. + [RETURNS] The register on success, else a negative number indicating + the error code. +*/ +{ + if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) + { + printk ("mtrr: size and base must be multiples of 4 kiB\n"); + printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); + return -EINVAL; + } + return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT); +} + +#ifdef USERSPACE_INTERFACE + +static int mtrr_file_add (unsigned long base, unsigned long size, + unsigned int type, char increment, struct file *file, int page) +{ + int reg, max; + unsigned int *fcount = file->private_data; + + max = get_num_var_ranges (); + if (fcount == NULL) + { + if ( ( fcount = kmalloc (max * sizeof *fcount, GFP_KERNEL) ) == NULL ) + { + printk ("mtrr: could not allocate\n"); + return -ENOMEM; + } + memset (fcount, 0, max * sizeof *fcount); + file->private_data = fcount; + } + if (!page) { + if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) + { + printk ("mtrr: size and base must be multiples of 4 kiB\n"); + printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); + return -EINVAL; + } + base >>= PAGE_SHIFT; + size >>= PAGE_SHIFT; + } + reg = mtrr_add_page (base, size, type, 1); + if (reg >= 0) ++fcount[reg]; + return reg; +} /* End Function mtrr_file_add */ + +static int mtrr_file_del (unsigned long base, unsigned long size, + struct file *file, int page) +{ + int reg; + unsigned int *fcount = file->private_data; + + if (!page) { + if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) + { + printk ("mtrr: size and base must be multiples of 4 kiB\n"); + printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); + return -EINVAL; + } + base >>= PAGE_SHIFT; + size >>= PAGE_SHIFT; + } + reg = mtrr_del_page (-1, base, size); + if (reg < 0) return reg; + if (fcount == NULL) return reg; + if (fcount[reg] < 1) return -EINVAL; + --fcount[reg]; + return reg; +} /* End Function mtrr_file_del */ + +static ssize_t mtrr_read (struct file *file, char *buf, size_t len, + loff_t *ppos) +{ + if (*ppos >= ascii_buf_bytes) return 0; + if (*ppos + len > ascii_buf_bytes) len = ascii_buf_bytes - *ppos; + if ( copy_to_user (buf, ascii_buffer + *ppos, len) ) return -EFAULT; + *ppos += len; + return len; +} /* End Function mtrr_read */ + +static ssize_t mtrr_write (struct file *file, const char *buf, size_t len, + loff_t *ppos) +/* Format of control line: + "base=%Lx size=%Lx type=%s" OR: + "disable=%d" +*/ +{ + int i, err; + unsigned long reg; + unsigned long long base, size; + char *ptr; + char line[LINE_SIZE]; + + if ( !suser () ) return -EPERM; + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) return -ESPIPE; + memset (line, 0, LINE_SIZE); + if (len > LINE_SIZE) len = LINE_SIZE; + if ( copy_from_user (line, buf, len - 1) ) return -EFAULT; + ptr = line + strlen (line) - 1; + if (*ptr == '\n') *ptr = '\0'; + if ( !strncmp (line, "disable=", 8) ) + { + reg = simple_strtoul (line + 8, &ptr, 0); + err = mtrr_del_page (reg, 0, 0); + if (err < 0) return err; + return len; + } + if ( strncmp (line, "base=", 5) ) + { + printk ("mtrr: no \"base=\" in line: \"%s\"\n", line); + return -EINVAL; + } + base = simple_strtoull (line + 5, &ptr, 0); + for (; isspace (*ptr); ++ptr); + if ( strncmp (ptr, "size=", 5) ) + { + printk ("mtrr: no \"size=\" in line: \"%s\"\n", line); + return -EINVAL; + } + size = simple_strtoull (ptr + 5, &ptr, 0); + if ( (base & 0xfff) || (size & 0xfff) ) + { + printk ("mtrr: size and base must be multiples of 4 kiB\n"); + printk ("mtrr: size: 0x%Lx base: 0x%Lx\n", size, base); + return -EINVAL; + } + for (; isspace (*ptr); ++ptr); + if ( strncmp (ptr, "type=", 5) ) + { + printk ("mtrr: no \"type=\" in line: \"%s\"\n", line); + return -EINVAL; + } + ptr += 5; + for (; isspace (*ptr); ++ptr); + for (i = 0; i < MTRR_NUM_TYPES; ++i) + { + if ( strcmp (ptr, mtrr_strings[i]) ) continue; + base >>= PAGE_SHIFT; + size >>= PAGE_SHIFT; + err = mtrr_add_page ((unsigned long)base, (unsigned long)size, i, 1); + if (err < 0) return err; + return len; + } + printk ("mtrr: illegal type: \"%s\"\n", ptr); + return -EINVAL; +} /* End Function mtrr_write */ + +static int mtrr_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err; + mtrr_type type; + struct mtrr_sentry sentry; + struct mtrr_gentry gentry; + + switch (cmd) + { + default: + return -ENOIOCTLCMD; + case MTRRIOC_ADD_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_file_add (sentry.base, sentry.size, sentry.type, 1, file, 0); + if (err < 0) return err; + break; + case MTRRIOC_SET_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_add (sentry.base, sentry.size, sentry.type, 0); + if (err < 0) return err; + break; + case MTRRIOC_DEL_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_file_del (sentry.base, sentry.size, file, 0); + if (err < 0) return err; + break; + case MTRRIOC_KILL_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_del (-1, sentry.base, sentry.size); + if (err < 0) return err; + break; + case MTRRIOC_GET_ENTRY: + if ( copy_from_user (&gentry, (void *) arg, sizeof gentry) ) + return -EFAULT; + if ( gentry.regnum >= get_num_var_ranges () ) return -EINVAL; + (*get_mtrr) (gentry.regnum, &gentry.base, &gentry.size, &type); + + /* Hide entries that go above 4GB */ + if (gentry.base + gentry.size > 0x100000 || gentry.size == 0x100000) + gentry.base = gentry.size = gentry.type = 0; + else { + gentry.base <<= PAGE_SHIFT; + gentry.size <<= PAGE_SHIFT; + gentry.type = type; + } + + if ( copy_to_user ( (void *) arg, &gentry, sizeof gentry) ) + return -EFAULT; + break; + case MTRRIOC_ADD_PAGE_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_file_add (sentry.base, sentry.size, sentry.type, 1, file, 1); + if (err < 0) return err; + break; + case MTRRIOC_SET_PAGE_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_add_page (sentry.base, sentry.size, sentry.type, 0); + if (err < 0) return err; + break; + case MTRRIOC_DEL_PAGE_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_file_del (sentry.base, sentry.size, file, 1); + if (err < 0) return err; + break; + case MTRRIOC_KILL_PAGE_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_del_page (-1, sentry.base, sentry.size); + if (err < 0) return err; + break; + case MTRRIOC_GET_PAGE_ENTRY: + if ( copy_from_user (&gentry, (void *) arg, sizeof gentry) ) + return -EFAULT; + if ( gentry.regnum >= get_num_var_ranges () ) return -EINVAL; + (*get_mtrr) (gentry.regnum, &gentry.base, &gentry.size, &type); + gentry.type = type; + + if ( copy_to_user ( (void *) arg, &gentry, sizeof gentry) ) + return -EFAULT; + break; + } + return 0; +} /* End Function mtrr_ioctl */ + +static int mtrr_close (struct inode *ino, struct file *file) +{ + int i, max; + unsigned int *fcount = file->private_data; + + if (fcount == NULL) return 0; + lock_kernel(); + max = get_num_var_ranges (); + for (i = 0; i < max; ++i) + { + while (fcount[i] > 0) + { + if (mtrr_del (i, 0, 0) < 0) printk ("mtrr: reg %d not used\n", i); + --fcount[i]; + } + } + unlock_kernel(); + kfree (fcount); + file->private_data = NULL; + return 0; +} /* End Function mtrr_close */ + +static struct file_operations mtrr_fops = +{ + owner: THIS_MODULE, + read: mtrr_read, + write: mtrr_write, + ioctl: mtrr_ioctl, + release: mtrr_close, +}; + +# ifdef CONFIG_PROC_FS + +static struct proc_dir_entry *proc_root_mtrr; + +# endif /* CONFIG_PROC_FS */ + +static devfs_handle_t devfs_handle; + +static void compute_ascii (void) +{ + char factor; + int i, max; + mtrr_type type; + unsigned long base, size; + + ascii_buf_bytes = 0; + max = get_num_var_ranges (); + for (i = 0; i < max; i++) + { + (*get_mtrr) (i, &base, &size, &type); + if (size == 0) usage_table[i] = 0; + else + { + if (size < (0x100000 >> PAGE_SHIFT)) + { + /* less than 1MB */ + factor = 'K'; + size <<= PAGE_SHIFT - 10; + } + else + { + factor = 'M'; + size >>= 20 - PAGE_SHIFT; + } + sprintf + (ascii_buffer + ascii_buf_bytes, + "reg%02i: base=0x%05lx000 (%4liMB), size=%4li%cB: %s, count=%d\n", + i, base, base >> (20 - PAGE_SHIFT), size, factor, + attrib_to_str (type), usage_table[i]); + ascii_buf_bytes += strlen (ascii_buffer + ascii_buf_bytes); + } + } + devfs_set_file_size (devfs_handle, ascii_buf_bytes); +# ifdef CONFIG_PROC_FS + if (proc_root_mtrr) + proc_root_mtrr->size = ascii_buf_bytes; +# endif /* CONFIG_PROC_FS */ +} /* End Function compute_ascii */ + +#endif /* USERSPACE_INTERFACE */ + +EXPORT_SYMBOL(mtrr_add); +EXPORT_SYMBOL(mtrr_del); + +#ifdef CONFIG_SMP + +typedef struct +{ + unsigned long base; + unsigned long size; + mtrr_type type; +} arr_state_t; + +arr_state_t arr_state[8] __initdata = +{ + {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, + {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL} +}; + +unsigned char ccr_state[7] __initdata = { 0, 0, 0, 0, 0, 0, 0 }; + +static void __init cyrix_arr_init_secondary(void) +{ + struct set_mtrr_context ctxt; + int i; + + set_mtrr_prepare (&ctxt); /* flush cache and enable MAPEN */ + + /* the CCRs are not contiguous */ + for(i=0; i<4; i++) setCx86(CX86_CCR0 + i, ccr_state[i]); + for( ; i<7; i++) setCx86(CX86_CCR4 + i, ccr_state[i]); + for(i=0; i<8; i++) + cyrix_set_arr_up(i, + arr_state[i].base, arr_state[i].size, arr_state[i].type, FALSE); + + set_mtrr_done (&ctxt); /* flush cache and disable MAPEN */ +} /* End Function cyrix_arr_init_secondary */ + +#endif + +/* + * On Cyrix 6x86(MX) and M II the ARR3 is special: it has connection + * with the SMM (System Management Mode) mode. So we need the following: + * Check whether SMI_LOCK (CCR3 bit 0) is set + * if it is set, write a warning message: ARR3 cannot be changed! + * (it cannot be changed until the next processor reset) + * if it is reset, then we can change it, set all the needed bits: + * - disable access to SMM memory through ARR3 range (CCR1 bit 7 reset) + * - disable access to SMM memory (CCR1 bit 2 reset) + * - disable SMM mode (CCR1 bit 1 reset) + * - disable write protection of ARR3 (CCR6 bit 1 reset) + * - (maybe) disable ARR3 + * Just to be sure, we enable ARR usage by the processor (CCR5 bit 5 set) + */ +static void __init cyrix_arr_init(void) +{ + struct set_mtrr_context ctxt; + unsigned char ccr[7]; + int ccrc[7] = { 0, 0, 0, 0, 0, 0, 0 }; +#ifdef CONFIG_SMP + int i; +#endif + + set_mtrr_prepare (&ctxt); /* flush cache and enable MAPEN */ + + /* Save all CCRs locally */ + ccr[0] = getCx86 (CX86_CCR0); + ccr[1] = getCx86 (CX86_CCR1); + ccr[2] = getCx86 (CX86_CCR2); + ccr[3] = ctxt.ccr3; + ccr[4] = getCx86 (CX86_CCR4); + ccr[5] = getCx86 (CX86_CCR5); + ccr[6] = getCx86 (CX86_CCR6); + + if (ccr[3] & 1) + { + ccrc[3] = 1; + arr3_protected = 1; + } + else + { + /* Disable SMM mode (bit 1), access to SMM memory (bit 2) and + * access to SMM memory through ARR3 (bit 7). + */ + if (ccr[1] & 0x80) { ccr[1] &= 0x7f; ccrc[1] |= 0x80; } + if (ccr[1] & 0x04) { ccr[1] &= 0xfb; ccrc[1] |= 0x04; } + if (ccr[1] & 0x02) { ccr[1] &= 0xfd; ccrc[1] |= 0x02; } + arr3_protected = 0; + if (ccr[6] & 0x02) { + ccr[6] &= 0xfd; ccrc[6] = 1; /* Disable write protection of ARR3 */ + setCx86 (CX86_CCR6, ccr[6]); + } + /* Disable ARR3. This is safe now that we disabled SMM. */ + /* cyrix_set_arr_up (3, 0, 0, 0, FALSE); */ + } + /* If we changed CCR1 in memory, change it in the processor, too. */ + if (ccrc[1]) setCx86 (CX86_CCR1, ccr[1]); + + /* Enable ARR usage by the processor */ + if (!(ccr[5] & 0x20)) + { + ccr[5] |= 0x20; ccrc[5] = 1; + setCx86 (CX86_CCR5, ccr[5]); + } + +#ifdef CONFIG_SMP + for(i=0; i<7; i++) ccr_state[i] = ccr[i]; + for(i=0; i<8; i++) + cyrix_get_arr(i, + &arr_state[i].base, &arr_state[i].size, &arr_state[i].type); +#endif + + set_mtrr_done (&ctxt); /* flush cache and disable MAPEN */ + + if ( ccrc[5] ) printk ("mtrr: ARR usage was not enabled, enabled manually\n"); + if ( ccrc[3] ) printk ("mtrr: ARR3 cannot be changed\n"); +/* + if ( ccrc[1] & 0x80) printk ("mtrr: SMM memory access through ARR3 disabled\n"); + if ( ccrc[1] & 0x04) printk ("mtrr: SMM memory access disabled\n"); + if ( ccrc[1] & 0x02) printk ("mtrr: SMM mode disabled\n"); +*/ + if ( ccrc[6] ) printk ("mtrr: ARR3 was write protected, unprotected\n"); +} /* End Function cyrix_arr_init */ + +/* + * Initialise the later (saner) Winchip MCR variant. In this version + * the BIOS can pass us the registers it has used (but not their values) + * and the control register is read/write + */ + +static void __init centaur_mcr1_init(void) +{ + unsigned i; + u32 lo, hi; + + /* Unfortunately, MCR's are read-only, so there is no way to + * find out what the bios might have done. + */ + + rdmsr(0x120, lo, hi); + if(((lo>>17)&7)==1) /* Type 1 Winchip2 MCR */ + { + lo&= ~0x1C0; /* clear key */ + lo|= 0x040; /* set key to 1 */ + wrmsr(0x120, lo, hi); /* unlock MCR */ + } + + centaur_mcr_type = 1; + + /* + * Clear any unconfigured MCR's. + */ + + for (i = 0; i < 8; ++i) + { + if(centaur_mcr[i]. high == 0 && centaur_mcr[i].low == 0) + { + if(!(lo & (1<<(9+i)))) + wrmsr (0x110 + i , 0, 0); + else + /* + * If the BIOS set up an MCR we cannot see it + * but we don't wish to obliterate it + */ + centaur_mcr_reserved |= (1<= 0x80000008)) { + u32 phys_addr; + phys_addr = cpuid_eax(0x80000008) & 0xff ; + size_or_mask = ~((1 << (phys_addr - PAGE_SHIFT)) - 1); + size_and_mask = ~size_or_mask & 0xfff00000; + break; + } + size_or_mask = 0xff000000; /* 36 bits */ + size_and_mask = 0x00f00000; + break; + + case X86_VENDOR_CENTAUR: + /* Cyrix III has Intel style MTRRs, but doesn't support PAE */ + if (boot_cpu_data.x86 == 6 && + (boot_cpu_data.x86_model == 6 || + boot_cpu_data.x86_model == 7)) { + size_or_mask = 0xfff00000; /* 32 bits */ + size_and_mask = 0; + } + break; + + default: + /* Intel, etc. */ + size_or_mask = 0xff000000; /* 36 bits */ + size_and_mask = 0x00f00000; + break; + } + + } else if ( test_bit(X86_FEATURE_K6_MTRR, &boot_cpu_data.x86_capability) ) { + /* Pre-Athlon (K6) AMD CPU MTRRs */ + mtrr_if = MTRR_IF_AMD_K6; + get_mtrr = amd_get_mtrr; + set_mtrr_up = amd_set_mtrr_up; + size_or_mask = 0xfff00000; /* 32 bits */ + size_and_mask = 0; + } else if ( test_bit(X86_FEATURE_CYRIX_ARR, &boot_cpu_data.x86_capability) ) { + /* Cyrix ARRs */ + mtrr_if = MTRR_IF_CYRIX_ARR; + get_mtrr = cyrix_get_arr; + set_mtrr_up = cyrix_set_arr_up; + get_free_region = cyrix_get_free_region; + cyrix_arr_init(); + size_or_mask = 0xfff00000; /* 32 bits */ + size_and_mask = 0; + } else if ( test_bit(X86_FEATURE_CENTAUR_MCR, &boot_cpu_data.x86_capability) ) { + /* Centaur MCRs */ + mtrr_if = MTRR_IF_CENTAUR_MCR; + get_mtrr = centaur_get_mcr; + set_mtrr_up = centaur_set_mcr_up; + get_free_region = centaur_get_free_region; + centaur_mcr_init(); + size_or_mask = 0xfff00000; /* 32 bits */ + size_and_mask = 0; + } else { + /* No supported MTRR interface */ + mtrr_if = MTRR_IF_NONE; + } + + printk ("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n" + "mtrr: detected mtrr type: %s\n", + MTRR_VERSION, mtrr_if_name[mtrr_if]); + + return (mtrr_if != MTRR_IF_NONE); +} /* End Function mtrr_setup */ + +#ifdef CONFIG_SMP + +static volatile unsigned long smp_changes_mask __initdata = 0; +static struct mtrr_state smp_mtrr_state __initdata = {0, 0}; + +void __init mtrr_init_boot_cpu(void) +{ + if ( !mtrr_setup () ) + return; + + if ( mtrr_if == MTRR_IF_INTEL ) { + /* Only for Intel MTRRs */ + get_mtrr_state (&smp_mtrr_state); + } +} /* End Function mtrr_init_boot_cpu */ + +static void __init intel_mtrr_init_secondary_cpu(void) +{ + unsigned long mask, count; + struct set_mtrr_context ctxt; + + /* Note that this is not ideal, since the cache is only flushed/disabled + for this CPU while the MTRRs are changed, but changing this requires + more invasive changes to the way the kernel boots */ + set_mtrr_prepare (&ctxt); + mask = set_mtrr_state (&smp_mtrr_state, &ctxt); + set_mtrr_done (&ctxt); + /* Use the atomic bitops to update the global mask */ + for (count = 0; count < sizeof mask * 8; ++count) + { + if (mask & 0x01) set_bit (count, &smp_changes_mask); + mask >>= 1; + } +} /* End Function intel_mtrr_init_secondary_cpu */ + +void __init mtrr_init_secondary_cpu(void) +{ + switch ( mtrr_if ) { + case MTRR_IF_INTEL: + /* Intel (P6) standard MTRRs */ + intel_mtrr_init_secondary_cpu(); + break; + case MTRR_IF_CYRIX_ARR: + /* This is _completely theoretical_! + * I assume here that one day Cyrix will support Intel APIC. + * In reality on non-Intel CPUs we won't even get to this routine. + * Hopefully no one will plug two Cyrix processors in a dual P5 board. + * :-) + */ + cyrix_arr_init_secondary (); + break; + default: + /* I see no MTRRs I can support in SMP mode... */ + printk ("mtrr: SMP support incomplete for this vendor\n"); + } +} /* End Function mtrr_init_secondary_cpu */ +#endif /* CONFIG_SMP */ + +int __init mtrr_init(void) +{ +#ifdef CONFIG_SMP + /* mtrr_setup() should already have been called from mtrr_init_boot_cpu() */ + + if ( mtrr_if == MTRR_IF_INTEL ) { + finalize_mtrr_state (&smp_mtrr_state); + mtrr_state_warn (smp_changes_mask); + } +#else + if ( !mtrr_setup() ) + return 0; /* MTRRs not supported? */ +#endif + +#ifdef CONFIG_PROC_FS + proc_root_mtrr = create_proc_entry ("mtrr", S_IWUSR | S_IRUGO, &proc_root); + if (proc_root_mtrr) { + proc_root_mtrr->owner = THIS_MODULE; + proc_root_mtrr->proc_fops = &mtrr_fops; + } +#endif +#ifdef CONFIG_DEVFS_FS + devfs_handle = devfs_register (NULL, "cpu/mtrr", DEVFS_FL_DEFAULT, 0, 0, + S_IFREG | S_IRUGO | S_IWUSR, + &mtrr_fops, NULL); +#endif + init_table (); + return 0; +} /* End Function mtrr_init */ + +/* + * Local Variables: + * mode:c + * c-file-style:"k&r" + * c-basic-offset:4 + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/nmi.c linux.ac/arch/x86_64/kernel/nmi.c --- linux.vanilla/arch/x86_64/kernel/nmi.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/nmi.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,299 @@ +/* + * linux/arch/x86_64/nmi.c + * + * NMI watchdog support on APIC systems + * + * Started by Ingo Molnar + * + * Fixes: + * Mikael Pettersson : AMD K7 support for local APIC NMI watchdog. + * Mikael Pettersson : Power Management for local APIC NMI watchdog. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +unsigned int nmi_watchdog = NMI_NONE; +static unsigned int nmi_hz = HZ; +unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */ + +#define K7_EVNTSEL_ENABLE (1 << 22) +#define K7_EVNTSEL_INT (1 << 20) +#define K7_EVNTSEL_OS (1 << 17) +#define K7_EVNTSEL_USR (1 << 16) +#define K7_EVENT_CYCLES_PROCESSOR_IS_RUNNING 0x76 +#define K7_NMI_EVENT K7_EVENT_CYCLES_PROCESSOR_IS_RUNNING + +#define P6_EVNTSEL0_ENABLE (1 << 22) +#define P6_EVNTSEL_INT (1 << 20) +#define P6_EVNTSEL_OS (1 << 17) +#define P6_EVNTSEL_USR (1 << 16) +#define P6_EVENT_CPU_CLOCKS_NOT_HALTED 0x79 +#define P6_NMI_EVENT P6_EVENT_CPU_CLOCKS_NOT_HALTED + +int __init check_nmi_watchdog (void) +{ + irq_cpustat_t tmp[NR_CPUS]; + int j, cpu; + + printk(KERN_INFO "testing NMI watchdog ... "); + + memcpy(tmp, irq_stat, sizeof(tmp)); + sti(); + mdelay((10*1000)/nmi_hz); // wait 10 ticks + + for (j = 0; j < smp_num_cpus; j++) { + cpu = cpu_logical_map(j); + if (nmi_count(cpu) - tmp[cpu].__nmi_count <= 5) { + printk("CPU#%d: NMI appears to be stuck!\n", cpu); + return -1; + } + } + printk("OK.\n"); + + /* now that we know it works we can reduce NMI frequency to + something more reasonable; makes a difference in some configs */ + if (nmi_watchdog == NMI_LOCAL_APIC) + nmi_hz = 1; + + return 0; +} + +static int __init setup_nmi_watchdog(char *str) +{ + int nmi; + + get_option(&str, &nmi); + + if (nmi >= NMI_INVALID) + return 0; + if (nmi == NMI_NONE) + nmi_watchdog = nmi; + /* + * If any other x86 CPU has a local APIC, then + * please test the NMI stuff there and send me the + * missing bits. Right now Intel P6 and AMD K7 only. + */ + if ((nmi == NMI_LOCAL_APIC) && + (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) && + (boot_cpu_data.x86 == 6)) + nmi_watchdog = nmi; + if ((nmi == NMI_LOCAL_APIC) && + (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && + (boot_cpu_data.x86 == 6)) + nmi_watchdog = nmi; + /* + * We can enable the IO-APIC watchdog + * unconditionally. + */ + if (nmi == NMI_IO_APIC) + nmi_watchdog = nmi; + return 1; +} + +__setup("nmi_watchdog=", setup_nmi_watchdog); + +#ifdef CONFIG_PM + +#include + +struct pm_dev *nmi_pmdev; + +static void disable_apic_nmi_watchdog(void) +{ + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + wrmsr(MSR_K7_EVNTSEL0, 0, 0); + break; + case X86_VENDOR_INTEL: + wrmsr(MSR_IA32_EVNTSEL0, 0, 0); + break; + } +} + +static int nmi_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + switch (rqst) { + case PM_SUSPEND: + disable_apic_nmi_watchdog(); + break; + case PM_RESUME: + setup_apic_nmi_watchdog(); + break; + } + return 0; +} + +static void nmi_pm_init(void) +{ + if (!nmi_pmdev) + nmi_pmdev = apic_pm_register(PM_SYS_DEV, 0, nmi_pm_callback); +} + +#define __pminit /*empty*/ + +#else /* CONFIG_PM */ + +static inline void nmi_pm_init(void) { } + +#define __pminit __init + +#endif /* CONFIG_PM */ + +/* + * Activate the NMI watchdog via the local APIC. + * Original code written by Keith Owens. + */ + +static void __pminit setup_k7_watchdog(void) +{ + int i; + unsigned int evntsel; + + nmi_perfctr_msr = MSR_K7_PERFCTR0; + + for(i = 0; i < 4; ++i) { + wrmsr(MSR_K7_EVNTSEL0+i, 0, 0); + wrmsr(MSR_K7_PERFCTR0+i, 0, 0); + } + + evntsel = K7_EVNTSEL_INT + | K7_EVNTSEL_OS + | K7_EVNTSEL_USR + | K7_NMI_EVENT; + + wrmsr(MSR_K7_EVNTSEL0, evntsel, 0); + Dprintk("setting K7_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000)); + wrmsr(MSR_K7_PERFCTR0, -(cpu_khz/nmi_hz*1000), -1); + apic_write(APIC_LVTPC, APIC_DM_NMI); + evntsel |= K7_EVNTSEL_ENABLE; + wrmsr(MSR_K7_EVNTSEL0, evntsel, 0); +} + +static void __pminit setup_p6_watchdog(void) +{ + int i; + unsigned int evntsel; + + nmi_perfctr_msr = MSR_IA32_PERFCTR0; + + for(i = 0; i < 2; ++i) { + wrmsr(MSR_IA32_EVNTSEL0+i, 0, 0); + wrmsr(MSR_IA32_PERFCTR0+i, 0, 0); + } + + evntsel = P6_EVNTSEL_INT + | P6_EVNTSEL_OS + | P6_EVNTSEL_USR + | P6_NMI_EVENT; + + wrmsr(MSR_IA32_EVNTSEL0, evntsel, 0); + Dprintk("setting IA32_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000)); + wrmsr(MSR_IA32_PERFCTR0, -(cpu_khz/nmi_hz*1000), 0); + apic_write(APIC_LVTPC, APIC_DM_NMI); + evntsel |= P6_EVNTSEL0_ENABLE; + wrmsr(MSR_IA32_EVNTSEL0, evntsel, 0); +} + +void __pminit setup_apic_nmi_watchdog (void) +{ + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if (boot_cpu_data.x86 != 6) + return; + setup_k7_watchdog(); + break; + case X86_VENDOR_INTEL: + if (boot_cpu_data.x86 != 6) + return; + setup_p6_watchdog(); + break; + default: + return; + } + nmi_pm_init(); +} + +static spinlock_t nmi_print_lock = SPIN_LOCK_UNLOCKED; + +/* + * the best way to detect whether a CPU has a 'hard lockup' problem + * is to check it's local APIC timer IRQ counts. If they are not + * changing then that CPU has some problem. + * + * as these watchdog NMI IRQs are generated on every CPU, we only + * have to check the current processor. + * + * since NMIs dont listen to _any_ locks, we have to be extremely + * careful not to rely on unsafe variables. The printk might lock + * up though, so we have to break up any console locks first ... + * [when there will be more tty-related locks, break them up + * here too!] + */ + +static unsigned int + last_irq_sums [NR_CPUS], + alert_counter [NR_CPUS]; + +void touch_nmi_watchdog (void) +{ + int i; + + /* + * Just reset the alert counters, (other CPUs might be + * spinning on locks we hold): + */ + for (i = 0; i < smp_num_cpus; i++) + alert_counter[i] = 0; +} + +void nmi_watchdog_tick (struct pt_regs * regs) +{ + + /* + * Since current-> is always on the stack, and we always switch + * the stack NMI-atomically, it's safe to use smp_processor_id(). + */ + int sum, cpu = smp_processor_id(); + + sum = apic_timer_irqs[cpu]; + + if (last_irq_sums[cpu] == sum) { + /* + * Ayiee, looks like this CPU is stuck ... + * wait a few IRQs (5 seconds) before doing the oops ... + */ + alert_counter[cpu]++; + if (alert_counter[cpu] == 5*nmi_hz) { + spin_lock(&nmi_print_lock); + /* + * We are in trouble anyway, lets at least try + * to get a message out. + */ + bust_spinlocks(1); + printk("NMI Watchdog detected LOCKUP on CPU%d, registers:\n", cpu); + show_registers(regs); + printk("console shuts up ...\n"); + console_silent(); + spin_unlock(&nmi_print_lock); + bust_spinlocks(0); + do_exit(SIGSEGV); + } + } else { + last_irq_sums[cpu] = sum; + alert_counter[cpu] = 0; + } + if (nmi_perfctr_msr) + wrmsr(nmi_perfctr_msr, -(cpu_khz/nmi_hz*1000), -1); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/pci-dma.c linux.ac/arch/x86_64/kernel/pci-dma.c --- linux.vanilla/arch/x86_64/kernel/pci-dma.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/pci-dma.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,38 @@ +/* + * Dynamic DMA mapping support. + * + * On i386 there is no hardware dynamic DMA address translation, + * so consistent alloc/free are merely page allocation/freeing. + * The rest of the dynamic DMA mapping interface is implemented + * in asm/pci.h. + */ + +#include +#include +#include +#include +#include + +void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + void *ret; + int gfp = GFP_ATOMIC; + + /* We need to always allocate below 4Gig. We probably need new + GPF mask to say that */ + gfp |= GFP_DMA; + ret = (void *)__get_free_pages(gfp, get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + } + return ret; +} + +void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long)vaddr, get_order(size)); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/pci-irq.c linux.ac/arch/x86_64/kernel/pci-irq.c --- linux.vanilla/arch/x86_64/kernel/pci-irq.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/pci-irq.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,757 @@ +/* + * Low-Level PCI Support for PC -- Routing of Interrupts + * + * (c) 1999--2000 Martin Mares + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "pci-x86_64.h" + +#define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24)) +#define PIRQ_VERSION 0x0100 + +static struct irq_routing_table *pirq_table; + +/* + * Never use: 0, 1, 2 (timer, keyboard, and cascade) + * Avoid using: 13, 14 and 15 (FP error and IDE). + * Penalize: 3, 4, 6, 7, 12 (known ISA uses: serial, floppy, parallel and mouse) + */ +unsigned int pcibios_irq_mask = 0xfff8; + +static int pirq_penalty[16] = { + 1000000, 1000000, 1000000, 1000, 1000, 0, 1000, 1000, + 0, 0, 0, 0, 1000, 100000, 100000, 100000 +}; + +struct irq_router { + char *name; + u16 vendor, device; + int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq); + int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, int new); +}; + +/* + * Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table. + */ + +static struct irq_routing_table * __init pirq_find_routing_table(void) +{ + u8 *addr; + struct irq_routing_table *rt; + int i; + u8 sum; + + for(addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) { + rt = (struct irq_routing_table *) addr; + if (rt->signature != PIRQ_SIGNATURE || + rt->version != PIRQ_VERSION || + rt->size % 16 || + rt->size < sizeof(struct irq_routing_table)) + continue; + sum = 0; + for(i=0; isize; i++) + sum += addr[i]; + if (!sum) { + DBG("PCI: Interrupt Routing Table found at 0x%p\n", rt); + return rt; + } + } + return NULL; +} + +/* + * If we have a IRQ routing table, use it to search for peer host + * bridges. It's a gross hack, but since there are no other known + * ways how to get a list of buses, we have to go this way. + * + * [maybe x86-64 architecture should define way to query this info in + more reasonable way?] + */ + +static void __init pirq_peer_trick(void) +{ + struct irq_routing_table *rt = pirq_table; + u8 busmap[256]; + int i; + struct irq_info *e; + + memset(busmap, 0, sizeof(busmap)); + for(i=0; i < (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); i++) { + e = &rt->slots[i]; +#ifdef DEBUG + { + int j; + DBG("%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot); + for(j=0; j<4; j++) + DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap); + DBG("\n"); + } +#endif + busmap[e->bus] = 1; + } + for(i=1; i<256; i++) + /* + * It might be a secondary bus, but in this case its parent is already + * known (ascending bus order) and therefore pci_scan_bus returns immediately. + */ + if (busmap[i] && pci_scan_bus(i, pci_root_bus->ops, NULL)) + printk(KERN_INFO "PCI: Discovered primary peer bus %02x [IRQ]\n", i); + pcibios_last_bus = -1; +} + +/* + * Code for querying and setting of IRQ routes on various interrupt routers. + */ + +static void eisa_set_level_irq(unsigned int irq) +{ + unsigned char mask = 1 << (irq & 7); + unsigned int port = 0x4d0 + (irq >> 3); + unsigned char val = inb(port); + + if (!(val & mask)) { + DBG(" -> edge"); + outb(val | mask, port); + } +} + +/* + * Common IRQ routing practice: nybbles in config space, + * offset by some magic constant. + */ +static unsigned int read_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr) +{ + u8 x; + unsigned reg = offset + (nr >> 1); + + pci_read_config_byte(router, reg, &x); + return (nr & 1) ? (x >> 4) : (x & 0xf); +} + +static void write_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr, unsigned int val) +{ + u8 x; + unsigned reg = offset + (nr >> 1); + + pci_read_config_byte(router, reg, &x); + x = (nr & 1) ? ((x & 0x0f) | (val << 4)) : ((x & 0xf0) | val); + pci_write_config_byte(router, reg, x); +} + +/* + * ALI pirq entries are damn ugly, and completely undocumented. + * This has been figured out from pirq tables, and it's not a pretty + * picture. + */ +static int pirq_ali_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + static unsigned char irqmap[16] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 }; + + return irqmap[read_config_nybble(router, 0x48, pirq-1)]; +} + +static int pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + static unsigned char irqmap[16] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 }; + unsigned int val = irqmap[irq]; + + if (val) { + write_config_nybble(router, 0x48, pirq-1, val); + return 1; + } + return 0; +} + +/* + * The Intel PIIX4 pirq rules are fairly simple: "pirq" is + * just a pointer to the config space. + */ +static int pirq_piix_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + u8 x; + + pci_read_config_byte(router, pirq, &x); + return (x < 16) ? x : 0; +} + +static int pirq_piix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + pci_write_config_byte(router, pirq, irq); + return 1; +} + +/* + * The VIA pirq rules are nibble-based, like ALI, + * but without the ugly irq number munging. + */ +static int pirq_via_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + return read_config_nybble(router, 0x55, pirq); +} + +static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + write_config_nybble(router, 0x55, pirq, irq); + return 1; +} + +/* + * OPTI: high four bits are nibble pointer.. + * I wonder what the low bits do? + */ +static int pirq_opti_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + return read_config_nybble(router, 0xb8, pirq >> 4); +} + +static int pirq_opti_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + write_config_nybble(router, 0xb8, pirq >> 4, irq); + return 1; +} + +/* + * Cyrix: nibble offset 0x5C + * 0x5C bits 7:4 is INTB bits 3:0 is INTA + * 0x5D bits 7:4 is INTD bits 3:0 is INTC + */ + +static int pirq_cyrix_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + return read_config_nybble(router, 0x5C, pirq^1); +} + +static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + write_config_nybble(router, 0x5C, pirq^1, irq); + return 1; +} + +/* + * PIRQ routing for SiS 85C503 router used in several SiS chipsets + * According to the SiS 5595 datasheet (preliminary V1.0, 12/24/1997) + * the related registers work as follows: + * + * general: one byte per re-routable IRQ, + * bit 7 IRQ mapping enabled (0) or disabled (1) + * bits [6:4] reserved + * bits [3:0] IRQ to map to + * allowed: 3-7, 9-12, 14-15 + * reserved: 0, 1, 2, 8, 13 + * + * individual registers in device config space: + * + * 0x41/0x42/0x43/0x44: PCI INT A/B/C/D - bits as in general case + * + * 0x61: IDEIRQ: bits as in general case - but: + * bits [6:5] must be written 01 + * bit 4 channel-select primary (0), secondary (1) + * + * 0x62: USBIRQ: bits as in general case - but: + * bit 4 OHCI function disabled (0), enabled (1) + * + * 0x6a: ACPI/SCI IRQ - bits as in general case + * + * 0x7e: Data Acq. Module IRQ - bits as in general case + * + * Apparently there are systems implementing PCI routing table using both + * link values 0x01-0x04 and 0x41-0x44 for PCI INTA..D, but register offsets + * like 0x62 as link values for USBIRQ e.g. So there is no simple + * "register = offset + pirq" relation. + * Currently we support PCI INTA..D and USBIRQ and try our best to handle + * both link mappings. + * IDE/ACPI/DAQ mapping is currently unsupported (left untouched as set by BIOS). + */ + +static int pirq_sis_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + u8 x; + int reg = pirq; + + switch(pirq) { + case 0x01: + case 0x02: + case 0x03: + case 0x04: + reg += 0x40; + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x62: + pci_read_config_byte(router, reg, &x); + if (reg != 0x62) + break; + if (!(x & 0x40)) + return 0; + break; + case 0x61: + case 0x6a: + case 0x7e: + printk(KERN_INFO "SiS pirq: advanced IDE/ACPI/DAQ mapping not yet implemented\n"); + return 0; + default: + printk(KERN_INFO "SiS router pirq escape (%d)\n", pirq); + return 0; + } + return (x & 0x80) ? 0 : (x & 0x0f); +} + +static int pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + u8 x; + int reg = pirq; + + switch(pirq) { + case 0x01: + case 0x02: + case 0x03: + case 0x04: + reg += 0x40; + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x62: + x = (irq&0x0f) ? (irq&0x0f) : 0x80; + if (reg != 0x62) + break; + /* always mark OHCI enabled, as nothing else knows about this */ + x |= 0x40; + break; + case 0x61: + case 0x6a: + case 0x7e: + printk(KERN_INFO "advanced SiS pirq mapping not yet implemented\n"); + return 0; + default: + printk(KERN_INFO "SiS router pirq escape (%d)\n", pirq); + return 0; + } + pci_write_config_byte(router, reg, x); + + return 1; +} + +/* + * VLSI: nibble offset 0x74 - educated guess due to routing table and + * config space of VLSI 82C534 PCI-bridge/router (1004:0102) + * Tested on HP OmniBook 800 covering PIRQ 1, 2, 4, 8 for onboard + * devices, PIRQ 3 for non-pci(!) soundchip and (untested) PIRQ 6 + * for the busbridge to the docking station. + */ + +static int pirq_vlsi_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + if (pirq > 8) { + printk(KERN_INFO "VLSI router pirq escape (%d)\n", pirq); + return 0; + } + return read_config_nybble(router, 0x74, pirq-1); +} + +static int pirq_vlsi_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + if (pirq > 8) { + printk(KERN_INFO "VLSI router pirq escape (%d)\n", pirq); + return 0; + } + write_config_nybble(router, 0x74, pirq-1, irq); + return 1; +} + +/* + * ServerWorks: PCI interrupts mapped to system IRQ lines through Index + * and Redirect I/O registers (0x0c00 and 0x0c01). The Index register + * format is (PCIIRQ## | 0x10), e.g.: PCIIRQ10=0x1a. The Redirect + * register is a straight binary coding of desired PIC IRQ (low nibble). + * + * The 'link' value in the PIRQ table is already in the correct format + * for the Index register. There are some special index values: + * 0x00 for ACPI (SCI), 0x01 for USB, 0x02 for IDE0, 0x04 for IDE1, + * and 0x03 for SMBus. + */ +static int pirq_serverworks_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + outb_p(pirq, 0xc00); + return inb(0xc01) & 0xf; +} + +static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + outb_p(pirq, 0xc00); + outb_p(irq, 0xc01); + return 1; +} + +/* Support for AMD756 PCI IRQ Routing + * Jhon H. Caicedo + * Jun/21/2001 0.2.0 Release, fixed to use "nybble" functions... (jhcaiced) + * Jun/19/2001 Alpha Release 0.1.0 (jhcaiced) + * The AMD756 pirq rules are nibble-based + * offset 0x56 0-3 PIRQA 4-7 PIRQB + * offset 0x57 0-3 PIRQC 4-7 PIRQD + */ +static int pirq_amd756_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + u8 irq; + irq = 0; + if (pirq <= 4) + { + irq = read_config_nybble(router, 0x56, pirq - 1); + } + printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d get irq : %2d\n", + dev->vendor, dev->device, pirq, irq); + return irq; +} + +static int pirq_amd756_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d SET irq : %2d\n", + dev->vendor, dev->device, pirq, irq); + if (pirq <= 4) + { + write_config_nybble(router, 0x56, pirq - 1, irq); + } + return 1; +} + +#ifdef CONFIG_PCI_BIOS + +static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + struct pci_dev *bridge; + int pin = pci_get_interrupt_pin(dev, &bridge); + return pcibios_set_irq_routing(bridge, pin, irq); +} + +static struct irq_router pirq_bios_router = + { "BIOS", 0, 0, NULL, pirq_bios_set }; + +#endif + +static struct irq_router pirq_routers[] = { + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, pirq_piix_get, pirq_piix_set }, + + { "ALI", PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pirq_ali_get, pirq_ali_set }, + + { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, pirq_via_get, pirq_via_set }, + { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596, pirq_via_get, pirq_via_set }, + { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, pirq_via_get, pirq_via_set }, + + { "OPTI", PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C700, pirq_opti_get, pirq_opti_set }, + + { "NatSemi", PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, pirq_cyrix_get, pirq_cyrix_set }, + { "SIS", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, pirq_sis_get, pirq_sis_set }, + { "VLSI 82C534", PCI_VENDOR_ID_VLSI, PCI_DEVICE_ID_VLSI_82C534, pirq_vlsi_get, pirq_vlsi_set }, + { "ServerWorks", PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4, + pirq_serverworks_get, pirq_serverworks_set }, + { "AMD756 VIPER", PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B, + pirq_amd756_get, pirq_amd756_set }, + + { "default", 0, 0, NULL, NULL } +}; + +static struct irq_router *pirq_router; +static struct pci_dev *pirq_router_dev; + +static void __init pirq_find_router(void) +{ + struct irq_routing_table *rt = pirq_table; + struct irq_router *r; + +#ifdef CONFIG_PCI_BIOS + if (!rt->signature) { + printk(KERN_INFO "PCI: Using BIOS for IRQ routing\n"); + pirq_router = &pirq_bios_router; + return; + } +#endif + + DBG("PCI: Attempting to find IRQ router for %04x:%04x\n", + rt->rtr_vendor, rt->rtr_device); + + /* fall back to default router if nothing else found */ + pirq_router = &pirq_routers[ARRAY_SIZE(pirq_routers) - 1]; + + pirq_router_dev = pci_find_slot(rt->rtr_bus, rt->rtr_devfn); + if (!pirq_router_dev) { + DBG("PCI: Interrupt router not found at %02x:%02x\n", rt->rtr_bus, rt->rtr_devfn); + return; + } + + for(r=pirq_routers; r->vendor; r++) { + /* Exact match against router table entry? Use it! */ + if (r->vendor == rt->rtr_vendor && r->device == rt->rtr_device) { + pirq_router = r; + break; + } + /* Match against router device entry? Use it as a fallback */ + if (r->vendor == pirq_router_dev->vendor && r->device == pirq_router_dev->device) { + pirq_router = r; + } + } + printk(KERN_INFO "PCI: Using IRQ router %s [%04x/%04x] at %s\n", + pirq_router->name, + pirq_router_dev->vendor, + pirq_router_dev->device, + pirq_router_dev->slot_name); +} + +static struct irq_info *pirq_get_info(struct pci_dev *dev) +{ + struct irq_routing_table *rt = pirq_table; + int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); + struct irq_info *info; + + for (info = rt->slots; entries--; info++) + if (info->bus == dev->bus->number && PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn)) + return info; + return NULL; +} + +static void pcibios_test_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +static int pcibios_lookup_irq(struct pci_dev *dev, int assign) +{ + u8 pin; + struct irq_info *info; + int i, pirq, newirq; + int irq = 0; + u32 mask; + struct irq_router *r = pirq_router; + struct pci_dev *dev2; + char *msg = NULL; + + if (!pirq_table) + return 0; + + /* Find IRQ routing entry */ + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (!pin) { + DBG(" -> no interrupt pin\n"); + return 0; + } + pin = pin - 1; + + DBG("IRQ for %s:%d", dev->slot_name, pin); + info = pirq_get_info(dev); + if (!info) { + DBG(" -> not found in routing table\n"); + return 0; + } + pirq = info->irq[pin].link; + mask = info->irq[pin].bitmap; + if (!pirq) { + DBG(" -> not routed\n"); + return 0; + } + DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs); + mask &= pcibios_irq_mask; + + /* + * Find the best IRQ to assign: use the one + * reported by the device if possible. + */ + newirq = dev->irq; + if (!newirq && assign) { + for (i = 0; i < 16; i++) { + if (!(mask & (1 << i))) + continue; + if (pirq_penalty[i] < pirq_penalty[newirq] && + !request_irq(i, pcibios_test_irq_handler, SA_SHIRQ, "pci-test", dev)) { + free_irq(i, dev); + newirq = i; + } + } + } + DBG(" -> newirq=%d", newirq); + + /* Check if it is hardcoded */ + if ((pirq & 0xf0) == 0xf0) { + irq = pirq & 0xf; + DBG(" -> hardcoded IRQ %d\n", irq); + msg = "Hardcoded"; + } else if (r->get && (irq = r->get(pirq_router_dev, dev, pirq))) { + DBG(" -> got IRQ %d\n", irq); + msg = "Found"; + } else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) { + DBG(" -> assigning IRQ %d", newirq); + if (r->set(pirq_router_dev, dev, pirq, newirq)) { + eisa_set_level_irq(newirq); + DBG(" ... OK\n"); + msg = "Assigned"; + irq = newirq; + } + } + + if (!irq) { + DBG(" ... failed\n"); + if (newirq && mask == (1 << newirq)) { + msg = "Guessed"; + irq = newirq; + } else + return 0; + } + printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq, dev->slot_name); + + /* Update IRQ for all devices with the same pirq value */ + pci_for_each_dev(dev2) { + pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin); + if (!pin) + continue; + pin--; + info = pirq_get_info(dev2); + if (!info) + continue; + if (info->irq[pin].link == pirq) { + /* We refuse to override the dev->irq information. Give a warning! */ + if (dev2->irq && dev2->irq != irq) { + printk(KERN_INFO "IRQ routing conflict for %s, have irq %d, want irq %d\n", + dev2->slot_name, dev2->irq, irq); + continue; + } + dev2->irq = irq; + pirq_penalty[irq]++; + if (dev != dev2) + printk(KERN_INFO "PCI: Sharing IRQ %d with %s\n", irq, dev2->slot_name); + } + } + return 1; +} + +void __init pcibios_irq_init(void) +{ + DBG("PCI: IRQ init\n"); + pirq_table = pirq_find_routing_table(); +#ifdef CONFIG_PCI_BIOS + if (!pirq_table && (pci_probe & PCI_BIOS_IRQ_SCAN)) + pirq_table = pcibios_get_irq_routing_table(); +#endif + if (pirq_table) { + pirq_peer_trick(); + pirq_find_router(); + if (pirq_table->exclusive_irqs) { + int i; + for (i=0; i<16; i++) + if (!(pirq_table->exclusive_irqs & (1 << i))) + pirq_penalty[i] += 100; + } + /* If we're using the I/O APIC, avoid using the PCI IRQ routing table */ + if (io_apic_assign_pci_irqs) + pirq_table = NULL; + } +} + +void __init pcibios_fixup_irqs(void) +{ + struct pci_dev *dev; + u8 pin; + + DBG("PCI: IRQ fixup\n"); + pci_for_each_dev(dev) { + /* + * If the BIOS has set an out of range IRQ number, just ignore it. + * Also keep track of which IRQ's are already in use. + */ + if (dev->irq >= 16) { + DBG("%s: ignoring bogus IRQ %d\n", dev->slot_name, dev->irq); + dev->irq = 0; + } + /* If the IRQ is already assigned to a PCI device, ignore its ISA use penalty */ + if (pirq_penalty[dev->irq] >= 100 && pirq_penalty[dev->irq] < 100000) + pirq_penalty[dev->irq] = 0; + pirq_penalty[dev->irq]++; + } + + pci_for_each_dev(dev) { + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); +#ifdef CONFIG_X86_IO_APIC + /* + * Recalculate IRQ numbers if we use the I/O APIC. + */ + if (io_apic_assign_pci_irqs) + { + int irq; + + if (pin) { + pin--; /* interrupt pins are numbered starting from 1 */ + irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin); + /* + * Busses behind bridges are typically not listed in the MP-table. + * In this case we have to look up the IRQ based on the parent bus, + * parent slot, and pin number. The SMP code detects such bridged + * busses itself so we should get into this branch reliably. + */ + if (irq < 0 && dev->bus->parent) { /* go back to the bridge */ + struct pci_dev * bridge = dev->bus->self; + + pin = (pin + PCI_SLOT(dev->devfn)) % 4; + irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, + PCI_SLOT(bridge->devfn), pin); + if (irq >= 0) + printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n", + bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq); + } + if (irq >= 0) { + printk(KERN_INFO "PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n", + dev->bus->number, PCI_SLOT(dev->devfn), pin, irq); + dev->irq = irq; + } + } + } +#endif + /* + * Still no IRQ? Try to lookup one... + */ + if (pin && !dev->irq) + pcibios_lookup_irq(dev, 0); + } +} + +void pcibios_penalize_isa_irq(int irq) +{ + /* + * If any ISAPnP device reports an IRQ in its list of possible + * IRQ's, we try to avoid assigning it to PCI devices. + */ + pirq_penalty[irq] += 100; +} + +void pcibios_enable_irq(struct pci_dev *dev) +{ + u8 pin; + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) { + char *msg; + if (io_apic_assign_pci_irqs) + msg = " Probably buggy MP table."; + else if (pci_probe & PCI_BIOS_IRQ_SCAN) + msg = ""; + else + msg = " Please try using pci=biosirq."; + printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n", + 'A' + pin - 1, dev->slot_name, msg); + } +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/pci-pc.c linux.ac/arch/x86_64/kernel/pci-pc.c --- linux.vanilla/arch/x86_64/kernel/pci-pc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/pci-pc.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,1119 @@ +/* + * Low-Level PCI Support for PC + * + * (c) 1999--2000 Martin Mares + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "pci-x86_64.h" + +unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2; + +int pcibios_last_bus = -1; +struct pci_bus *pci_root_bus; +struct pci_ops *pci_root_ops; + +/* + * Direct access to PCI hardware... + */ + +#ifdef CONFIG_PCI_DIRECT + +/* + * Functions for accessing PCI configuration space with type 1 accesses + */ + +#define CONFIG_CMD(dev, where) (0x80000000 | (dev->bus->number << 16) | (dev->devfn << 8) | (where & ~3)) + +static int pci_conf1_read_config_byte(struct pci_dev *dev, int where, u8 *value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + *value = inb(0xCFC + (where&3)); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_read_config_word(struct pci_dev *dev, int where, u16 *value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + *value = inw(0xCFC + (where&2)); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_read_config_dword(struct pci_dev *dev, int where, u32 *value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + *value = inl(0xCFC); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_write_config_byte(struct pci_dev *dev, int where, u8 value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + outb(value, 0xCFC + (where&3)); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_write_config_word(struct pci_dev *dev, int where, u16 value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + outw(value, 0xCFC + (where&2)); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_write_config_dword(struct pci_dev *dev, int where, u32 value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + outl(value, 0xCFC); + return PCIBIOS_SUCCESSFUL; +} + +#undef CONFIG_CMD + +static struct pci_ops pci_direct_conf1 = { + pci_conf1_read_config_byte, + pci_conf1_read_config_word, + pci_conf1_read_config_dword, + pci_conf1_write_config_byte, + pci_conf1_write_config_word, + pci_conf1_write_config_dword +}; + +/* + * Functions for accessing PCI configuration space with type 2 accesses + */ + +#define IOADDR(devfn, where) ((0xC000 | ((devfn & 0x78) << 5)) + where) +#define FUNC(devfn) (((devfn & 7) << 1) | 0xf0) +#define SET(dev) if (dev->devfn & 0x80) return PCIBIOS_DEVICE_NOT_FOUND; \ + outb(FUNC(dev->devfn), 0xCF8); \ + outb(dev->bus->number, 0xCFA); + +static int pci_conf2_read_config_byte(struct pci_dev *dev, int where, u8 *value) +{ + SET(dev); + *value = inb(IOADDR(dev->devfn,where)); + outb (0, 0xCF8); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_read_config_word(struct pci_dev *dev, int where, u16 *value) +{ + SET(dev); + *value = inw(IOADDR(dev->devfn,where)); + outb (0, 0xCF8); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_read_config_dword(struct pci_dev *dev, int where, u32 *value) +{ + SET(dev); + *value = inl (IOADDR(dev->devfn,where)); + outb (0, 0xCF8); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_write_config_byte(struct pci_dev *dev, int where, u8 value) +{ + SET(dev); + outb (value, IOADDR(dev->devfn,where)); + outb (0, 0xCF8); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_write_config_word(struct pci_dev *dev, int where, u16 value) +{ + SET(dev); + outw (value, IOADDR(dev->devfn,where)); + outb (0, 0xCF8); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_write_config_dword(struct pci_dev *dev, int where, u32 value) +{ + SET(dev); + outl (value, IOADDR(dev->devfn,where)); + outb (0, 0xCF8); + return PCIBIOS_SUCCESSFUL; +} + +#undef SET +#undef IOADDR +#undef FUNC + +static struct pci_ops pci_direct_conf2 = { + pci_conf2_read_config_byte, + pci_conf2_read_config_word, + pci_conf2_read_config_dword, + pci_conf2_write_config_byte, + pci_conf2_write_config_word, + pci_conf2_write_config_dword +}; + +/* + * Before we decide to use direct hardware access mechanisms, we try to do some + * trivial checks to ensure it at least _seems_ to be working -- we just test + * whether bus 00 contains a host bridge (this is similar to checking + * techniques used in XFree86, but ours should be more reliable since we + * attempt to make use of direct access hints provided by the PCI BIOS). + * + * This should be close to trivial, but it isn't, because there are buggy + * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID. + */ +static int __init pci_sanity_check(struct pci_ops *o) +{ + u16 x; + struct pci_bus bus; /* Fake bus and device */ + struct pci_dev dev; + + if (pci_probe & PCI_NO_CHECKS) + return 1; + bus.number = 0; + dev.bus = &bus; + for(dev.devfn=0; dev.devfn < 0x100; dev.devfn++) + if ((!o->read_word(&dev, PCI_CLASS_DEVICE, &x) && + (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)) || + (!o->read_word(&dev, PCI_VENDOR_ID, &x) && + (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ))) + return 1; + DBG("PCI: Sanity check failed\n"); + return 0; +} + +static struct pci_ops * __init pci_check_direct(void) +{ + unsigned int tmp; + unsigned long flags; + + __save_flags(flags); __cli(); + + /* + * Check if configuration type 1 works. + */ + if (pci_probe & PCI_PROBE_CONF1) { + outb (0x01, 0xCFB); + tmp = inl (0xCF8); + outl (0x80000000, 0xCF8); + if (inl (0xCF8) == 0x80000000 && + pci_sanity_check(&pci_direct_conf1)) { + outl (tmp, 0xCF8); + __restore_flags(flags); + printk("PCI: Using configuration type 1\n"); + request_region(0xCF8, 8, "PCI conf1"); + return &pci_direct_conf1; + } + outl (tmp, 0xCF8); + } + + /* + * Check if configuration type 2 works. + */ + if (pci_probe & PCI_PROBE_CONF2) { + outb (0x00, 0xCFB); + outb (0x00, 0xCF8); + outb (0x00, 0xCFA); + if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00 && + pci_sanity_check(&pci_direct_conf2)) { + __restore_flags(flags); + printk("PCI: Using configuration type 2\n"); + request_region(0xCF8, 4, "PCI conf2"); + return &pci_direct_conf2; + } + } + + __restore_flags(flags); + return NULL; +} + +#endif + +/* + * BIOS32 and PCI BIOS handling. + */ + +#ifdef CONFIG_PCI_BIOS + +#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX +#define PCIBIOS_PCI_BIOS_PRESENT 0xb101 +#define PCIBIOS_FIND_PCI_DEVICE 0xb102 +#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103 +#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106 +#define PCIBIOS_READ_CONFIG_BYTE 0xb108 +#define PCIBIOS_READ_CONFIG_WORD 0xb109 +#define PCIBIOS_READ_CONFIG_DWORD 0xb10a +#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b +#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c +#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d +#define PCIBIOS_GET_ROUTING_OPTIONS 0xb10e +#define PCIBIOS_SET_PCI_HW_INT 0xb10f + +/* BIOS32 signature: "_32_" */ +#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24)) + +/* PCI signature: "PCI " */ +#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24)) + +/* PCI service signature: "$PCI" */ +#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24)) + +/* PCI BIOS hardware mechanism flags */ +#define PCIBIOS_HW_TYPE1 0x01 +#define PCIBIOS_HW_TYPE2 0x02 +#define PCIBIOS_HW_TYPE1_SPEC 0x10 +#define PCIBIOS_HW_TYPE2_SPEC 0x20 + +/* + * This is the standard structure used to identify the entry point + * to the BIOS32 Service Directory, as documented in + * Standard BIOS 32-bit Service Directory Proposal + * Revision 0.4 May 24, 1993 + * Phoenix Technologies Ltd. + * Norwood, MA + * and the PCI BIOS specification. + */ + +union bios32 { + struct { + unsigned long signature; /* _32_ */ + unsigned long entry; /* 32 bit physical address */ + unsigned char revision; /* Revision level, 0 */ + unsigned char length; /* Length in paragraphs should be 01 */ + unsigned char checksum; /* All bytes must add up to zero */ + unsigned char reserved[5]; /* Must be zero */ + } fields; + char chars[16]; +}; + +/* + * Physical address of the service directory. I don't know if we're + * allowed to have more than one of these or not, so just in case + * we'll make pcibios_present() take a memory start parameter and store + * the array there. + */ + +static struct { + unsigned long address; + unsigned short segment; +} bios32_indirect = { 0, __KERNEL_CS }; + +/* + * Returns the entry point for the given service, NULL on error + */ + +/* bios32 services will need to be defined for long mode. --pavel */ +static unsigned long bios32_service(unsigned long service) +{ + unsigned char return_code; /* %al */ + unsigned long address; /* %ebx */ + unsigned long length; /* %ecx */ + unsigned long entry; /* %edx */ + unsigned long flags; + + __save_flags(flags); __cli(); + __asm__("lcall (%%edi); cld" + : "=a" (return_code), + "=b" (address), + "=c" (length), + "=d" (entry) + : "0" (service), + "1" (0), + "D" (&bios32_indirect)); + __restore_flags(flags); + + switch (return_code) { + case 0: + return address + entry; + case 0x80: /* Not present */ + printk("bios32_service(0x%lx): not present\n", service); + return 0; + default: /* Shouldn't happen */ + printk("bios32_service(0x%lx): returned 0x%x -- BIOS bug!\n", + service, return_code); + return 0; + } +} + +static struct { + unsigned long address; + unsigned short segment; +} pci_indirect = { 0, __KERNEL_CS }; + +static int pci_bios_present; + +static int __init check_pcibios(void) +{ + u32 signature, eax, ebx, ecx; + u8 status, major_ver, minor_ver, hw_mech; + unsigned long flags, pcibios_entry; + + if ((pcibios_entry = bios32_service(PCI_SERVICE))) { + pci_indirect.address = pcibios_entry + PAGE_OFFSET; + + __save_flags(flags); __cli(); + __asm__( + "lcall (%%edi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=d" (signature), + "=a" (eax), + "=b" (ebx), + "=c" (ecx) + : "1" (PCIBIOS_PCI_BIOS_PRESENT), + "D" (&pci_indirect) + : "memory"); + __restore_flags(flags); + + status = (eax >> 8) & 0xff; + hw_mech = eax & 0xff; + major_ver = (ebx >> 8) & 0xff; + minor_ver = ebx & 0xff; + if (pcibios_last_bus < 0) + pcibios_last_bus = ecx & 0xff; + DBG("PCI: BIOS probe returned s=%02x hw=%02x ver=%02x.%02x l=%02x\n", + status, hw_mech, major_ver, minor_ver, pcibios_last_bus); + if (status || signature != PCI_SIGNATURE) { + printk (KERN_ERR "PCI: BIOS BUG #%x[%08x] found, report to \n", + status, signature); + return 0; + } + printk("PCI: PCI BIOS revision %x.%02x entry at 0x%lx, last bus=%d\n", + major_ver, minor_ver, pcibios_entry, pcibios_last_bus); +#ifdef CONFIG_PCI_DIRECT + if (!(hw_mech & PCIBIOS_HW_TYPE1)) + pci_probe &= ~PCI_PROBE_CONF1; + if (!(hw_mech & PCIBIOS_HW_TYPE2)) + pci_probe &= ~PCI_PROBE_CONF2; +#endif + return 1; + } + return 0; +} + +static int __init pci_bios_find_device (unsigned short vendor, unsigned short device_id, + unsigned short index, unsigned char *bus, unsigned char *device_fn) +{ + unsigned short bx; + unsigned short ret; + + __asm__("lcall (%%edi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=b" (bx), + "=a" (ret) + : "1" (PCIBIOS_FIND_PCI_DEVICE), + "c" (device_id), + "d" (vendor), + "S" ((int) index), + "D" (&pci_indirect)); + *bus = (bx >> 8) & 0xff; + *device_fn = bx & 0xff; + return (int) (ret & 0xff00) >> 8; +} + +static int pci_bios_read_config_byte(struct pci_dev *dev, int where, u8 *value) +{ + unsigned long ret; + unsigned long bx = (dev->bus->number << 8) | dev->devfn; + + __asm__("lcall (%%esi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_BYTE), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + return (int) (ret & 0xff00) >> 8; +} + +static int pci_bios_read_config_word(struct pci_dev *dev, int where, u16 *value) +{ + unsigned long ret; + unsigned long bx = (dev->bus->number << 8) | dev->devfn; + + __asm__("lcall (%%esi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_WORD), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + return (int) (ret & 0xff00) >> 8; +} + +static int pci_bios_read_config_dword(struct pci_dev *dev, int where, u32 *value) +{ + unsigned long ret; + unsigned long bx = (dev->bus->number << 8) | dev->devfn; + + __asm__("lcall (%%esi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_DWORD), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + return (int) (ret & 0xff00) >> 8; +} + +static int pci_bios_write_config_byte(struct pci_dev *dev, int where, u8 value) +{ + unsigned long ret; + unsigned long bx = (dev->bus->number << 8) | dev->devfn; + + __asm__("lcall (%%esi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_BYTE), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + return (int) (ret & 0xff00) >> 8; +} + +static int pci_bios_write_config_word(struct pci_dev *dev, int where, u16 value) +{ + unsigned long ret; + unsigned long bx = (dev->bus->number << 8) | dev->devfn; + + __asm__("lcall (%%esi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_WORD), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + return (int) (ret & 0xff00) >> 8; +} + +static int pci_bios_write_config_dword(struct pci_dev *dev, int where, u32 value) +{ + unsigned long ret; + unsigned long bx = (dev->bus->number << 8) | dev->devfn; + + __asm__("lcall (%%esi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_DWORD), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + return (int) (ret & 0xff00) >> 8; +} + +/* + * Function table for BIOS32 access + */ + +static struct pci_ops pci_bios_access = { + pci_bios_read_config_byte, + pci_bios_read_config_word, + pci_bios_read_config_dword, + pci_bios_write_config_byte, + pci_bios_write_config_word, + pci_bios_write_config_dword +}; + +/* + * Try to find PCI BIOS. + */ + +static struct pci_ops * __init pci_find_bios(void) +{ + union bios32 *check; + unsigned char sum; + int i, length; + + /* + * Follow the standard procedure for locating the BIOS32 Service + * directory by scanning the permissible address range from + * 0xe0000 through 0xfffff for a valid BIOS32 structure. + */ + + for (check = (union bios32 *) __va(0xe0000); + check <= (union bios32 *) __va(0xffff0); + ++check) { + if (check->fields.signature != BIOS32_SIGNATURE) + continue; + length = check->fields.length * 16; + if (!length) + continue; + sum = 0; + for (i = 0; i < length ; ++i) + sum += check->chars[i]; + if (sum != 0) + continue; + if (check->fields.revision != 0) { + printk("PCI: unsupported BIOS32 revision %d at 0x%p, report to \n", + check->fields.revision, check); + continue; + } + DBG("PCI: BIOS32 Service Directory structure at 0x%p\n", check); + if (check->fields.entry >= 0x100000) { + printk("PCI: BIOS32 entry (0x%p) in high memory, cannot use.\n", check); + return NULL; + } else { + unsigned long bios32_entry = check->fields.entry; + DBG("PCI: BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); + bios32_indirect.address = bios32_entry + PAGE_OFFSET; + if (check_pcibios()) + return &pci_bios_access; + } + break; /* Hopefully more than one BIOS32 cannot happen... */ + } + + return NULL; +} + +/* + * Sort the device list according to PCI BIOS. Nasty hack, but since some + * fool forgot to define the `correct' device order in the PCI BIOS specs + * and we want to be (possibly bug-to-bug ;-]) compatible with older kernels + * which used BIOS ordering, we are bound to do this... + */ + +static void __init pcibios_sort(void) +{ + LIST_HEAD(sorted_devices); + struct list_head *ln; + struct pci_dev *dev, *d; + int idx, found; + unsigned char bus, devfn; + + DBG("PCI: Sorting device list...\n"); + while (!list_empty(&pci_devices)) { + ln = pci_devices.next; + dev = pci_dev_g(ln); + idx = found = 0; + while (pci_bios_find_device(dev->vendor, dev->device, idx, &bus, &devfn) == PCIBIOS_SUCCESSFUL) { + idx++; + for (ln=pci_devices.next; ln != &pci_devices; ln=ln->next) { + d = pci_dev_g(ln); + if (d->bus->number == bus && d->devfn == devfn) { + list_del(&d->global_list); + list_add_tail(&d->global_list, &sorted_devices); + if (d == dev) + found = 1; + break; + } + } + if (ln == &pci_devices) { + printk("PCI: BIOS reporting unknown device %02x:%02x\n", bus, devfn); + /* + * We must not continue scanning as several buggy BIOSes + * return garbage after the last device. Grr. + */ + break; + } + } + if (!found) { + printk("PCI: Device %02x:%02x not found by BIOS\n", + dev->bus->number, dev->devfn); + list_del(&dev->global_list); + list_add_tail(&dev->global_list, &sorted_devices); + } + } + list_splice(&sorted_devices, &pci_devices); +} + +/* + * BIOS Functions for IRQ Routing + */ + +struct irq_routing_options { + u16 size; + struct irq_info *table; + u16 segment; +} __attribute__((packed)); + +struct irq_routing_table * __init pcibios_get_irq_routing_table(void) +{ + struct irq_routing_options opt; + struct irq_routing_table *rt = NULL; + int ret, map; + unsigned long page; + + if (!pci_bios_present) + return NULL; + page = __get_free_page(GFP_KERNEL); + if (!page) + return NULL; + opt.table = (struct irq_info *) page; + opt.size = PAGE_SIZE; + opt.segment = __KERNEL_DS; + + DBG("PCI: Fetching IRQ routing table... "); + __asm__("push %%es\n\t" + "push %%ds\n\t" + "pop %%es\n\t" + "lcall (%%esi); cld\n\t" + "pop %%es\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret), + "=b" (map) + : "0" (PCIBIOS_GET_ROUTING_OPTIONS), + "1" (0), + "D" ((long) &opt), + "S" (&pci_indirect)); + DBG("OK ret=%d, size=%d, map=%x\n", ret, opt.size, map); + if (ret & 0xff00) + printk(KERN_ERR "PCI: Error %02x when fetching IRQ routing table.\n", (ret >> 8) & 0xff); + else if (opt.size) { + rt = kmalloc(sizeof(struct irq_routing_table) + opt.size, GFP_KERNEL); + if (rt) { + memset(rt, 0, sizeof(struct irq_routing_table)); + rt->size = opt.size + sizeof(struct irq_routing_table); + rt->exclusive_irqs = map; + memcpy(rt->slots, (void *) page, opt.size); + printk("PCI: Using BIOS Interrupt Routing Table\n"); + } + } + free_page(page); + return rt; +} + + +int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq) +{ + int ret; + + __asm__("lcall (%%esi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_SET_PCI_HW_INT), + "b" ((dev->bus->number << 8) | dev->devfn), + "c" ((irq << 8) | (pin + 10)), + "S" (&pci_indirect)); + return !(ret & 0xff00); +} + +#endif + +/* + * Several buggy motherboards address only 16 devices and mirror + * them to next 16 IDs. We try to detect this `feature' on all + * primary buses (those containing host bridges as they are + * expected to be unique) and remove the ghost devices. + */ + +static void __init pcibios_fixup_ghosts(struct pci_bus *b) +{ + struct list_head *ln, *mn; + struct pci_dev *d, *e; + int mirror = PCI_DEVFN(16,0); + int seen_host_bridge = 0; + int i; + + DBG("PCI: Scanning for ghost devices on bus %d\n", b->number); + for (ln=b->devices.next; ln != &b->devices; ln=ln->next) { + d = pci_dev_b(ln); + if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST) + seen_host_bridge++; + for (mn=ln->next; mn != &b->devices; mn=mn->next) { + e = pci_dev_b(mn); + if (e->devfn != d->devfn + mirror || + e->vendor != d->vendor || + e->device != d->device || + e->class != d->class) + continue; + for(i=0; iresource[i].start != d->resource[i].start || + e->resource[i].end != d->resource[i].end || + e->resource[i].flags != d->resource[i].flags) + continue; + break; + } + if (mn == &b->devices) + return; + } + if (!seen_host_bridge) + return; + printk("PCI: Ignoring ghost devices on bus %02x\n", b->number); + + ln = &b->devices; + while (ln->next != &b->devices) { + d = pci_dev_b(ln->next); + if (d->devfn >= mirror) { + list_del(&d->global_list); + list_del(&d->bus_list); + kfree(d); + } else + ln = ln->next; + } +} + +/* + * Discover remaining PCI buses in case there are peer host bridges. + * We use the number of last PCI bus provided by the PCI BIOS. + */ +static void __init pcibios_fixup_peer_bridges(void) +{ + int n; + struct pci_bus bus; + struct pci_dev dev; + u16 l; + + if (pcibios_last_bus <= 0 || pcibios_last_bus >= 0xff) + return; + DBG("PCI: Peer bridge fixup\n"); + for (n=0; n <= pcibios_last_bus; n++) { + if (pci_bus_exists(&pci_root_buses, n)) + continue; + bus.number = n; + bus.ops = pci_root_ops; + dev.bus = &bus; + for(dev.devfn=0; dev.devfn<256; dev.devfn += 8) + if (!pci_read_config_word(&dev, PCI_VENDOR_ID, &l) && + l != 0x0000 && l != 0xffff) { + DBG("Found device at %02x:%02x [%04x]\n", n, dev.devfn, l); + printk("PCI: Discovered peer bus %02x\n", n); + pci_scan_bus(n, pci_root_ops, NULL); + break; + } + } +} + +/* + * Exceptions for specific devices. Usually work-arounds for fatal design flaws. + */ + +static void __init pci_fixup_i450nx(struct pci_dev *d) +{ + /* + * i450NX -- Find and scan all secondary buses on all PXB's. + */ + int pxb, reg; + u8 busno, suba, subb; + printk("PCI: Searching for i450NX host bridges on %s\n", d->slot_name); + reg = 0xd0; + for(pxb=0; pxb<2; pxb++) { + pci_read_config_byte(d, reg++, &busno); + pci_read_config_byte(d, reg++, &suba); + pci_read_config_byte(d, reg++, &subb); + DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb); + if (busno) + pci_scan_bus(busno, pci_root_ops, NULL); /* Bus A */ + if (suba < subb) + pci_scan_bus(suba+1, pci_root_ops, NULL); /* Bus B */ + } + pcibios_last_bus = -1; +} + +static void __init pci_fixup_i450gx(struct pci_dev *d) +{ + /* + * i450GX and i450KX -- Find and scan all secondary buses. + * (called separately for each PCI bridge found) + */ + u8 busno; + pci_read_config_byte(d, 0x4a, &busno); + printk("PCI: i440KX/GX host bridge %s: secondary bus %02x\n", d->slot_name, busno); + pci_scan_bus(busno, pci_root_ops, NULL); + pcibios_last_bus = -1; +} + +#if 0 +/* Until we get proper handling pray the BIOS gets it right */ +/* + * ServerWorks host bridges -- Find and scan all secondary buses. + * Register 0x44 contains first, 0x45 last bus number routed there. + */ +static void __init pci_fixup_serverworks(struct pci_dev *d) +{ + u8 busno1, busno2; + + pci_read_config_byte(d, 0x44, &busno1); + pci_read_config_byte(d, 0x45, &busno2); + if (busno2 < busno1) + busno2 = busno1; + if (busno2 > pcibios_last_bus) { + pcibios_last_bus = busno2; + printk("PCI: ServerWorks host bridge: last bus %02x\n", pcibios_last_bus); + } +} +#endif + +#if 0 +/* Our bus code shouldnt need this fixup any more. Delete once verified */ +/* + * Compaq host bridges -- Find and scan all secondary buses. + * This time registers 0xc8 and 0xc9. + */ +static void __init pci_fixup_compaq(struct pci_dev *d) +{ + u8 busno1, busno2; + + pci_read_config_byte(d, 0xc8, &busno1); + pci_read_config_byte(d, 0xc9, &busno2); + if (busno2 < busno1) + busno2 = busno1; + if (busno2 > pcibios_last_bus) { + pcibios_last_bus = busno2; + printk("PCI: Compaq host bridge: last bus %02x\n", busno2); + } +} +#endif + +static void __init pci_fixup_umc_ide(struct pci_dev *d) +{ + /* + * UM8886BF IDE controller sets region type bits incorrectly, + * therefore they look like memory despite of them being I/O. + */ + int i; + + printk("PCI: Fixing base address flags for device %s\n", d->slot_name); + for(i=0; i<4; i++) + d->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO; +} + +static void __init pci_fixup_ide_bases(struct pci_dev *d) +{ + int i; + + /* + * PCI IDE controllers use non-standard I/O port decoding, respect it. + */ + if ((d->class >> 8) != PCI_CLASS_STORAGE_IDE) + return; + DBG("PCI: IDE base address fixup for %s\n", d->slot_name); + for(i=0; i<4; i++) { + struct resource *r = &d->resource[i]; + if ((r->start & ~0x80) == 0x374) { + r->start |= 2; + r->end = r->start; + } + } +} + +static void __init pci_fixup_ide_trash(struct pci_dev *d) +{ + int i; + + /* + * There exist PCI IDE controllers which have utter garbage + * in first four base registers. Ignore that. + */ + DBG("PCI: IDE base address trash cleared for %s\n", d->slot_name); + for(i=0; i<4; i++) + d->resource[i].start = d->resource[i].end = d->resource[i].flags = 0; +} + +static void __init pci_fixup_latency(struct pci_dev *d) +{ + /* + * SiS 5597 and 5598 chipsets require latency timer set to + * at most 32 to avoid lockups. + */ + DBG("PCI: Setting max latency to 32\n"); + pcibios_max_latency = 32; +} + +static void __init pci_fixup_piix4_acpi(struct pci_dev *d) +{ + /* + * PIIX4 ACPI device: hardwired IRQ9 + */ + d->irq = 9; +} + +static void __init pci_fixup_via691(struct pci_dev *d) +{ + /* + * The VIA bridge corrupts with Posting enabled + */ + u8 tmp; + + pci_read_config_byte(d, 0x70, &tmp); + if(tmp & (1<<7)) { + printk("PCI: Disabled enhanced CPU to PCI posting\n"); + pci_write_config_byte(d, 0x70, tmp & ~(1<<7)); + } +} +static void __init pci_fixup_via691_2(struct pci_dev *d) +{ + /* + * The VIA bridge corrupts with Posting enabled + */ + u8 tmp; + + pci_read_config_byte(d, 0x40, &tmp); + if(tmp & (1<<7)) { + printk("PCI: Disabled enhanced CPU to PCI posting #2\n"); + pci_write_config_byte(d, 0x40, tmp & ~(1<<7)); + } +} + + +struct pci_fixup pcibios_fixups[] = { + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454GX, pci_fixup_i450gx }, +#if 0 +/* Until we get proper handling pray the BIOS gets it right */ + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HE, pci_fixup_serverworks }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_LE, pci_fixup_serverworks }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CMIC_HE, pci_fixup_serverworks }, +#endif + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, pci_fixup_umc_ide }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, pci_fixup_ide_trash }, + { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, pci_fixup_latency }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5598, pci_fixup_latency }, +#if 0 +/* This seems completely bogus so I am removing it -- Alan */ + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C691, pci_fixup_via691 }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C598_1, pci_fixup_via691_2 }, +#endif + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, pci_fixup_piix4_acpi }, + { 0 } +}; + +/* + * Called after each bus is probed, but before its children + * are examined. + */ + +void __init pcibios_fixup_bus(struct pci_bus *b) +{ + pcibios_fixup_ghosts(b); + pci_read_bridge_bases(b); +} + +/* + * Initialization. Try all known PCI access methods. Note that we support + * using both PCI BIOS and direct access: in such cases, we use I/O ports + * to access config space, but we still keep BIOS order of cards to be + * compatible with 2.0.X. This should go away some day. + */ + +void __init pcibios_init(void) +{ + struct pci_ops *bios = NULL; + struct pci_ops *dir = NULL; + +#ifdef CONFIG_PCI_BIOS + if ((pci_probe & PCI_PROBE_BIOS) && ((bios = pci_find_bios()))) { + pci_probe |= PCI_BIOS_SORT; + pci_bios_present = 1; + } +#endif +#ifdef CONFIG_PCI_DIRECT + if (pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2)) + dir = pci_check_direct(); +#endif + if (dir) + pci_root_ops = dir; + else if (bios) + pci_root_ops = bios; + else { + printk("PCI: No PCI bus detected\n"); + return; + } + + printk("PCI: Probing PCI hardware\n"); + pci_root_bus = pci_scan_bus(0, pci_root_ops, NULL); + + pcibios_irq_init(); + pcibios_fixup_peer_bridges(); + pcibios_fixup_irqs(); + pcibios_resource_survey(); + +#ifdef CONFIG_PCI_BIOS + if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT)) + pcibios_sort(); +#endif +} + +char * __init pcibios_setup(char *str) +{ + if (!strcmp(str, "off")) { + pci_probe = 0; + return NULL; + } +#ifdef CONFIG_PCI_BIOS + else if (!strcmp(str, "bios")) { + pci_probe = PCI_PROBE_BIOS; + return NULL; + } else if (!strcmp(str, "nobios")) { + pci_probe &= ~PCI_PROBE_BIOS; + return NULL; + } else if (!strcmp(str, "nosort")) { + pci_probe |= PCI_NO_SORT; + return NULL; + } else if (!strcmp(str, "biosirq")) { + pci_probe |= PCI_BIOS_IRQ_SCAN; + return NULL; + } +#endif +#ifdef CONFIG_PCI_DIRECT + else if (!strcmp(str, "conf1")) { + pci_probe = PCI_PROBE_CONF1 | PCI_NO_CHECKS; + return NULL; + } + else if (!strcmp(str, "conf2")) { + pci_probe = PCI_PROBE_CONF2 | PCI_NO_CHECKS; + return NULL; + } +#endif + else if (!strcmp(str, "rom")) { + pci_probe |= PCI_ASSIGN_ROMS; + return NULL; + } else if (!strcmp(str, "assign-busses")) { + pci_probe |= PCI_ASSIGN_ALL_BUSSES; + return NULL; + } else if (!strncmp(str, "irqmask=", 8)) { + pcibios_irq_mask = simple_strtol(str+8, NULL, 0); + return NULL; + } else if (!strncmp(str, "lastbus=", 8)) { + pcibios_last_bus = simple_strtol(str+8, NULL, 0); + return NULL; + } + return str; +} + +unsigned int pcibios_assign_all_busses(void) +{ + return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0; +} + +int pcibios_enable_device(struct pci_dev *dev) +{ + int err; + + if ((err = pcibios_enable_resources(dev)) < 0) + return err; + pcibios_enable_irq(dev); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/pci-x86_64.c linux.ac/arch/x86_64/kernel/pci-x86_64.c --- linux.vanilla/arch/x86_64/kernel/pci-x86_64.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/pci-x86_64.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,384 @@ +/* + * Low-Level PCI Access for i386 machines + * + * Copyright 1993, 1994 Drew Eckhardt + * Visionary Computing + * (Unix and Linux consulting and custom programming) + * Drew@Colorado.EDU + * +1 (303) 786-7975 + * + * Drew's work was sponsored by: + * iX Multiuser Multitasking Magazine + * Hannover, Germany + * hm@ix.de + * + * Copyright 1997--2000 Martin Mares + * + * For more information, please consult the following manuals (look at + * http://www.pcisig.com/ for how to get them): + * + * PCI BIOS Specification + * PCI Local Bus Specification + * PCI to PCI Bridge Specification + * PCI System Design Guide + * + * + * CHANGELOG : + * Jun 17, 1994 : Modified to accommodate the broken pre-PCI BIOS SPECIFICATION + * Revision 2.0 present on 's ASUS mainboard. + * + * Jan 5, 1995 : Modified to probe PCI hardware at boot time by Frederic + * Potter, potter@cao-vlsi.ibp.fr + * + * Jan 10, 1995 : Modified to store the information about configured pci + * devices into a list, which can be accessed via /proc/pci by + * Curtis Varner, cvarner@cs.ucr.edu + * + * Jan 12, 1995 : CPU-PCI bridge optimization support by Frederic Potter. + * Alpha version. Intel & UMC chipset support only. + * + * Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the code + * moved to drivers/pci/pci.c. + * + * Dec 7, 1996 : Added support for direct configuration access of boards + * with Intel compatible access schemes (tsbogend@alpha.franken.de) + * + * Feb 3, 1997 : Set internal functions to static, save/restore flags + * avoid dead locks reading broken PCI BIOS, werner@suse.de + * + * Apr 26, 1997 : Fixed case when there is BIOS32, but not PCI BIOS + * (mj@atrey.karlin.mff.cuni.cz) + * + * May 7, 1997 : Added some missing cli()'s. [mj] + * + * Jun 20, 1997 : Corrected problems in "conf1" type accesses. + * (paubert@iram.es) + * + * Aug 2, 1997 : Split to PCI BIOS handling and direct PCI access parts + * and cleaned it up... Martin Mares + * + * Feb 6, 1998 : No longer using BIOS to find devices and device classes. [mj] + * + * May 1, 1998 : Support for peer host bridges. [mj] + * + * Jun 19, 1998 : Changed to use spinlocks, so that PCI configuration space + * can be accessed from interrupts even on SMP systems. [mj] + * + * August 1998 : Better support for peer host bridges and more paranoid + * checks for direct hardware access. Ugh, this file starts to look as + * a large gallery of common hardware bug workarounds (watch the comments) + * -- the PCI specs themselves are sane, but most implementors should be + * hit hard with \hammer scaled \magstep5. [mj] + * + * Jan 23, 1999 : More improvements to peer host bridge logic. i450NX fixup. [mj] + * + * Feb 8, 1999 : Added UM8886BF I/O address fixup. [mj] + * + * August 1999 : New resource management and configuration access stuff. [mj] + * + * Sep 19, 1999 : Use PCI IRQ routing tables for detection of peer host bridges. + * Based on ideas by Chris Frantz and David Hinds. [mj] + * + * Sep 28, 1999 : Handle unreported/unassigned IRQs. Thanks to Shuu Yamaguchi + * for a lot of patience during testing. [mj] + * + * Oct 8, 1999 : Split to pci-i386.c, pci-pc.c and pci-visws.c. [mj] + */ + +#include +#include +#include +#include +#include +#include + +#include "pci-x86_64.h" + +void +pcibios_update_resource(struct pci_dev *dev, struct resource *root, + struct resource *res, int resource) +{ + u32 new, check; + int reg; + + new = res->start | (res->flags & PCI_REGION_FLAG_MASK); + if (resource < 6) { + reg = PCI_BASE_ADDRESS_0 + 4*resource; + } else if (resource == PCI_ROM_RESOURCE) { + res->flags |= PCI_ROM_ADDRESS_ENABLE; + new |= PCI_ROM_ADDRESS_ENABLE; + reg = dev->rom_base_reg; + } else { + /* Somebody might have asked allocation of a non-standard resource */ + return; + } + + pci_write_config_dword(dev, reg, new); + pci_read_config_dword(dev, reg, &check); + if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { + printk(KERN_ERR "PCI: Error while updating region " + "%s/%d (%08x != %08x)\n", dev->slot_name, resource, + new, check); + } +} + +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might have be mirrored at 0x0100-0x03ff.. + */ +void +pcibios_align_resource(void *data, struct resource *res, unsigned long size) +{ + if (res->flags & IORESOURCE_IO) { + unsigned long start = res->start; + + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + res->start = start; + } + } +} + + +/* + * Handle resources of PCI devices. If the world were perfect, we could + * just allocate all the resource regions and do nothing more. It isn't. + * On the other hand, we cannot just re-allocate all devices, as it would + * require us to know lots of host bridge internals. So we attempt to + * keep as much of the original configuration as possible, but tweak it + * when it's found to be wrong. + * + * Known BIOS problems we have to work around: + * - I/O or memory regions not configured + * - regions configured, but not enabled in the command register + * - bogus I/O addresses above 64K used + * - expansion ROMs left enabled (this may sound harmless, but given + * the fact the PCI specs explicitly allow address decoders to be + * shared between expansion ROMs and other resource regions, it's + * at least dangerous) + * + * Our solution: + * (1) Allocate resources for all buses behind PCI-to-PCI bridges. + * This gives us fixed barriers on where we can allocate. + * (2) Allocate resources for all enabled devices. If there is + * a collision, just mark the resource as unallocated. Also + * disable expansion ROMs during this step. + * (3) Try to allocate resources for disabled devices. If the + * resources were assigned correctly, everything goes well, + * if they weren't, they won't disturb allocation of other + * resources. + * (4) Assign new addresses to resources which were either + * not configured at all or misconfigured. If explicitly + * requested by the user, configure expansion ROM address + * as well. + */ + +static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) +{ + struct list_head *ln; + struct pci_bus *bus; + struct pci_dev *dev; + int idx; + struct resource *r, *pr; + + /* Depth-First Search on bus tree */ + for (ln=bus_list->next; ln != bus_list; ln=ln->next) { + bus = pci_bus_b(ln); + if ((dev = bus->self)) { + for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { + r = &dev->resource[idx]; + if (!r->start) + continue; + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) + printk(KERN_ERR "PCI: Cannot allocate resource region %d of bridge %s\n", idx, dev->slot_name); + } + } + pcibios_allocate_bus_resources(&bus->children); + } +} + +static void __init pcibios_allocate_resources(int pass) +{ + struct pci_dev *dev; + int idx, disabled; + u16 command; + struct resource *r, *pr; + + pci_for_each_dev(dev) { + pci_read_config_word(dev, PCI_COMMAND, &command); + for(idx = 0; idx < 6; idx++) { + r = &dev->resource[idx]; + if (r->parent) /* Already allocated */ + continue; + if (!r->start) /* Address not assigned at all */ + continue; + if (r->flags & IORESOURCE_IO) + disabled = !(command & PCI_COMMAND_IO); + else + disabled = !(command & PCI_COMMAND_MEMORY); + if (pass == disabled) { + DBG("PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%d)\n", + r->start, r->end, r->flags, disabled, pass); + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) { + printk(KERN_ERR "PCI: Cannot allocate resource region %d of device %s\n", idx, dev->slot_name); + /* We'll assign a new address later */ + r->end -= r->start; + r->start = 0; + } + } + } + if (!pass) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (r->flags & PCI_ROM_ADDRESS_ENABLE) { + /* Turn the ROM off, leave the resource region, but keep it unregistered. */ + u32 reg; + DBG("PCI: Switching off ROM of %s\n", dev->slot_name); + r->flags &= ~PCI_ROM_ADDRESS_ENABLE; + pci_read_config_dword(dev, dev->rom_base_reg, ®); + pci_write_config_dword(dev, dev->rom_base_reg, reg & ~PCI_ROM_ADDRESS_ENABLE); + } + } + } +} + +static void __init pcibios_assign_resources(void) +{ + struct pci_dev *dev; + int idx; + struct resource *r; + + pci_for_each_dev(dev) { + int class = dev->class >> 8; + + /* Don't touch classless devices and host bridges */ + if (!class || class == PCI_CLASS_BRIDGE_HOST) + continue; + + for(idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + + /* + * Don't touch IDE controllers and I/O ports of video cards! + */ + if ((class == PCI_CLASS_STORAGE_IDE && idx < 4) || + (class == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO))) + continue; + + /* + * We shall assign a new address to this resource, either because + * the BIOS forgot to do so or because we have decided the old + * address was unusable for some reason. + */ + if (!r->start && r->end) + pci_assign_resource(dev, idx); + } + + if (pci_probe & PCI_ASSIGN_ROMS) { + r = &dev->resource[PCI_ROM_RESOURCE]; + r->end -= r->start; + r->start = 0; + if (r->end) + pci_assign_resource(dev, PCI_ROM_RESOURCE); + } + } +} + +void __init pcibios_resource_survey(void) +{ + DBG("PCI: Allocating resources\n"); + pcibios_allocate_bus_resources(&pci_root_buses); + pcibios_allocate_resources(0); + pcibios_allocate_resources(1); + pcibios_assign_resources(); +} + +int pcibios_enable_resources(struct pci_dev *dev) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for(idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + if (!r->start && r->end) { + printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (dev->resource[PCI_ROM_RESOURCE].start) + cmd |= PCI_COMMAND_MEMORY; + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + +/* + * If we set up a device for bus mastering, we need to check the latency + * timer as certain crappy BIOSes forget to set it properly. + */ +unsigned int pcibios_max_latency = 255; + +void pcibios_set_master(struct pci_dev *dev) +{ + u8 lat; + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + if (lat < 16) + lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency; + else if (lat > pcibios_max_latency) + lat = pcibios_max_latency; + else + return; + printk("PCI: Setting latency timer of device %s to %d\n", dev->slot_name, lat); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); +} + +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + unsigned long prot; + + /* I/O space cannot be accessed via normal processor loads and + * stores on this platform. + */ + if (mmap_state == pci_mmap_io) + return -EINVAL; + + /* Leave vm_pgoff as-is, the PCI space address is the physical + * address on this platform. + */ + vma->vm_flags |= (VM_SHM | VM_LOCKED | VM_IO); + + prot = pgprot_val(vma->vm_page_prot); + if (boot_cpu_data.x86 > 3) + prot |= _PAGE_PCD | _PAGE_PWT; + vma->vm_page_prot = __pgprot(prot); + + /* Write-combine setting is ignored, it is changed via the mtrr + * interfaces on this platform. + */ + if (remap_page_range(vma->vm_start, vma->vm_pgoff << PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/pci-x86_64.h linux.ac/arch/x86_64/kernel/pci-x86_64.h --- linux.vanilla/arch/x86_64/kernel/pci-x86_64.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/pci-x86_64.h Wed Oct 10 01:47:38 2001 @@ -0,0 +1,72 @@ +/* + * Low-Level PCI Access for i386 machines. + * + * (c) 1999 Martin Mares + */ + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define PCI_PROBE_BIOS 0x0001 +#define PCI_PROBE_CONF1 0x0002 +#define PCI_PROBE_CONF2 0x0004 +#define PCI_NO_SORT 0x0100 +#define PCI_BIOS_SORT 0x0200 +#define PCI_NO_CHECKS 0x0400 +#define PCI_ASSIGN_ROMS 0x1000 +#define PCI_BIOS_IRQ_SCAN 0x2000 +#define PCI_ASSIGN_ALL_BUSSES 0x4000 + +extern unsigned int pci_probe; + +/* pci-i386.c */ + +extern unsigned int pcibios_max_latency; + +void pcibios_resource_survey(void); +int pcibios_enable_resources(struct pci_dev *); + +/* pci-pc.c */ + +extern int pcibios_last_bus; +extern struct pci_bus *pci_root_bus; +extern struct pci_ops *pci_root_ops; + +struct irq_routing_table *pcibios_get_irq_routing_table(void); +int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq); + +/* pci-irq.c */ + +struct irq_info { + u8 bus, devfn; /* Bus, device and function */ + struct { + u8 link; /* IRQ line ID, chipset dependent, 0=not routed */ + u16 bitmap; /* Available IRQs */ + } __attribute__((packed)) irq[4]; + u8 slot; /* Slot number, 0=onboard */ + u8 rfu; +} __attribute__((packed)); + +struct irq_routing_table { + u32 signature; /* PIRQ_SIGNATURE should be here */ + u16 version; /* PIRQ_VERSION */ + u16 size; /* Table size in bytes */ + u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ + u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ + u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ + u32 miniport_data; /* Crap */ + u8 rfu[11]; + u8 checksum; /* Modulo 256 checksum must give zero */ + struct irq_info slots[0]; +} __attribute__((packed)); + +extern unsigned int pcibios_irq_mask; + +void pcibios_irq_init(void); +void pcibios_fixup_irqs(void); +void pcibios_enable_irq(struct pci_dev *dev); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/process.c linux.ac/arch/x86_64/kernel/process.c --- linux.vanilla/arch/x86_64/kernel/process.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/process.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,629 @@ +/* + * linux/arch/x86-64/kernel/process.c + * + * Copyright (C) 1995 Linus Torvalds + * + * Pentium III FXSR, SSE support + * Gareth Hughes , May 2000 + * + * X86-64 port + * Andi Kleen + * + * $Id: process.c,v 1.34 2001/08/15 07:52:55 ak Exp $ + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#define __KERNEL_SYSCALLS__ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +asmlinkage extern void ret_from_fork(void); + +int hlt_counter; + +/* + * Powermanagement idle function, if any.. + */ +void (*pm_idle)(void); + +/* + * Power off function, if any + */ +void (*pm_power_off)(void); + +void disable_hlt(void) +{ + hlt_counter++; +} + +void enable_hlt(void) +{ + hlt_counter--; +} + +/* + * We use this if we don't have any better + * idle routine.. + */ +static void default_idle(void) +{ + if (!hlt_counter) { + __cli(); + if (!current->need_resched) + safe_halt(); + else + __sti(); + } +} + +/* + * On SMP it's slightly faster (but much more power-consuming!) + * to poll the ->need_resched flag instead of waiting for the + * cross-CPU IPI to arrive. Use this option with caution. + */ +static void poll_idle (void) +{ + int oldval; + + __sti(); + + /* + * Deal with another CPU just having chosen a thread to + * run here: + */ + oldval = xchg(¤t->need_resched, -1); + + if (!oldval) + asm volatile( + "2:" + "cmpl $-1, %0;" + "rep; nop;" + "je 2b;" + : :"m" (current->need_resched)); +} + +/* + * The idle thread. There's no useful work to be + * done, so just try to conserve power and have a + * low exit latency (ie sit in a loop waiting for + * somebody to say that they'd like to reschedule) + */ +void cpu_idle (void) +{ + /* endless idle loop with no priority at all */ + init_idle(); + current->nice = 20; + current->counter = -100; + + while (1) { + void (*idle)(void) = pm_idle; + if (!idle) + idle = default_idle; + while (!current->need_resched) + idle(); + schedule(); + check_pgt_cache(); + } +} + +static int __init idle_setup (char *str) +{ + if (!strncmp(str, "poll", 4)) { + printk("using polling idle threads.\n"); + pm_idle = poll_idle; + } + + return 1; +} + +__setup("idle=", idle_setup); + +void machine_real_restart(unsigned char *code, int length) +{ + cli(); + panic( "restart is currently not implemented.\n" ); + while(1); +} + +/* Dummies currently */ +int reboot_smp; +int reboot_thru_bios; + +void machine_restart(char * __unused) +{ +#if CONFIG_SMP + /* + * Stop all CPUs and turn off local APICs and the IO-APIC, so + * other OSs see a clean IRQ state. + */ + smp_send_stop(); + disable_IO_APIC(); +#endif + + machine_real_restart(0,0); +} + +void machine_halt(void) +{ +} + +void machine_power_off(void) +{ + if (pm_power_off) + pm_power_off(); +} + +/* Prints also some state that isn't saved in the pt_regs */ +void show_regs(struct pt_regs * regs) +{ + unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L, fs, gs; + unsigned int fsindex,gsindex; + unsigned int ds,cs; + + printk("\n"); + printk("RIP: %04x:[<%016lx>]", (unsigned)regs->cs & 0xffff, regs->rip); + printk(" RSP: %016lx", regs->rsp); + printk(" EFLAGS: %08lx %s\n",regs->eflags, print_tainted()); + printk("RAX: %016lx RBX: %016lx RCX: %016lx RDX: %016lx\n", + regs->rax,regs->rbx,regs->rcx,regs->rdx); + printk("RSI: %016lx RDI: %016lx RBP: %016lx\n", + regs->rsi, regs->rdi, regs->rbp); + printk("R08: %016lx R09: %016lx R10: %016lx R11: %016lx\n", + regs->r8, regs->r9, regs->r10, regs->r11); + printk("R12: %016lx R13: %016lx R14: %016lx R15: %016lx\n", + regs->r12, regs->r13, regs->r14, regs->r15); + + asm("movl %%ds,%0" : "=r" (ds)); + asm("movl %%cs,%0" : "=r" (cs)); + asm("movl %%fs,%0" : "=r" (fsindex)); + asm("movl %%gs,%0" : "=r" (gsindex)); + + rdmsrl(0xc0000100, fs); + rdmsrl(0xc0000101, gs); + printk("FS: %016lx(%04x) GS: %016lx(%04x) DS: %04x CS: %04x\n", + fs,fsindex,gs,gsindex,ds,cs); + + asm("movq %%cr0, %0": "=r" (cr0)); + asm("movq %%cr2, %0": "=r" (cr2)); + asm("movq %%cr3, %0": "=r" (cr3)); + asm("movq %%cr4, %0": "=r" (cr4)); + + printk("CR0: %016lx CR2: %016lx CR3: %016lx CR4: %016lx\n", + cr0, cr2, cr3, cr4); +} + +/* + * No need to lock the MM as we are the last user + */ +void release_segments(struct mm_struct *mm) +{ + void * ldt = mm->context.segments; + + /* + * free the LDT + */ + if (ldt) { + mm->context.segments = NULL; + clear_LDT(); + vfree(ldt); + } +} + +#define __STR(x) #x +#define __STR2(x) __STR(x) + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ + /* nothing to do ... */ +} + +void flush_thread(void) +{ + struct task_struct *tsk = current; + + memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8); + /* + * Forget coprocessor state.. + */ + clear_fpu(tsk); + tsk->used_math = 0; +} + +void release_thread(struct task_struct *dead_task) +{ + if (dead_task->mm) { + void * ldt = dead_task->mm->context.segments; + + // temporary debugging check + if (ldt) { + printk("WARNING: dead process %8s still has LDT? <%p>\n", + dead_task->comm, ldt); + BUG(); + } + } +} + +/* + * we do not have to muck with descriptors here, that is + * done in switch_mm() as needed. + */ +void copy_segments(struct task_struct *p, struct mm_struct *new_mm) +{ + struct mm_struct * old_mm; + void *old_ldt, *ldt; + + ldt = NULL; + old_mm = current->mm; + if (old_mm && (old_ldt = old_mm->context.segments) != NULL) { + /* + * Completely new LDT, we initialize it from the parent: + */ + ldt = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE); + if (!ldt) + printk(KERN_WARNING "ldt allocation failed\n"); + else + memcpy(ldt, old_ldt, LDT_ENTRIES*LDT_ENTRY_SIZE); + } + new_mm->context.segments = ldt; + new_mm->context.cpuvalid = 0UL; + return; +} + +int copy_thread(int nr, unsigned long clone_flags, unsigned long rsp, + unsigned long unused, + struct task_struct * p, struct pt_regs * regs) +{ + struct pt_regs * childregs; + struct task_struct *me = current; + + childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p)) - 1; + + *childregs = *regs; + + childregs->rax = 0; + childregs->rsp = rsp; + if (rsp == ~0) { + childregs->rsp = (__u64)childregs; + } + + p->thread.rsp = (unsigned long) childregs; + p->thread.rsp0 = (unsigned long) (childregs+1); + p->thread.userrsp = current->thread.userrsp; + + p->thread.rip = (unsigned long) ret_from_fork; + + p->thread.fs = me->thread.fs; + p->thread.gs = me->thread.gs; + + asm("movl %%gs,%0" : "=m" (p->thread.gsindex)); + asm("movl %%fs,%0" : "=m" (p->thread.fsindex)); + asm("movl %%es,%0" : "=m" (p->thread.es)); + asm("movl %%ds,%0" : "=m" (p->thread.ds)); + + unlazy_fpu(current); + p->thread.i387 = current->thread.i387; + + return 0; +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + int i; + +/* changed the size calculations - should hopefully work better. lbt */ + dump->magic = CMAGIC; + dump->start_code = 0; + dump->start_stack = regs->rsp & ~(PAGE_SIZE - 1); + dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; + dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT; + dump->u_dsize -= dump->u_tsize; + dump->u_ssize = 0; + for (i = 0; i < 8; i++) + dump->u_debugreg[i] = current->thread.debugreg[i]; + + if (dump->start_stack < TASK_SIZE) + dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; + +#define SAVE(reg) dump->regs.reg = regs->reg + SAVE(rax); + SAVE(rbx); + SAVE(rcx); + SAVE(rdx); + SAVE(rsi); + SAVE(rdi); + SAVE(rbp); + SAVE(r8); + SAVE(r9); + SAVE(r10); + SAVE(r11); + SAVE(r12); + SAVE(r13); + SAVE(r14); + SAVE(r15); + SAVE(orig_rax); + SAVE(rip); +#undef SAVE + + /* FIXME: Should use symbolic names for msr-s! */ + rdmsrl(0xc0000100, dump->regs.fs_base); + rdmsrl(0xc0000101, dump->regs.kernel_gs_base); + + dump->u_fpvalid = dump_fpu (regs, &dump->i387); +} + +/* + * This special macro can be used to load a debugging register + */ +#define loaddebug(thread,register) \ + set_debug(thread->debugreg[register], register) + +/* + * switch_to(x,y) should switch tasks from x to y. + * + * We fsave/fwait so that an exception goes off at the right time + * (as a call from the fsave or fwait in effect) rather than to + * the wrong process. + * + * This could still be optimized: + * - fold all the options into a flag word and test it with a single test. + * - could test fs/gs bitsliced + */ +void __switch_to(struct task_struct *prev_p, struct task_struct *next_p) +{ + struct thread_struct *prev = &prev_p->thread, + *next = &next_p->thread; + struct tss_struct *tss = init_tss + smp_processor_id(); + + + unlazy_fpu(prev_p); + + /* + * Reload esp0, LDT and the page table pointer: + */ + tss->rsp0 = next->rsp0; + + /* + * Switch DS and ES. + */ + asm volatile("movl %%es,%0" : "=m" (prev->es)); + if (unlikely(next->es != prev->es)) + loadsegment(es, next->es); + + asm volatile ("movl %%ds,%0" : "=m" (prev->ds)); + if (unlikely(next->ds != prev->ds)) + loadsegment(ds, next->ds); + + /* + * Switch FS and GS. + */ + { + unsigned int fsindex; + + asm volatile("movl %%fs,%0" : "=g" (fsindex)); + if (unlikely(fsindex != next->fsindex)) /* or likely? */ + loadsegment(fs, next->fsindex); + if (unlikely(fsindex != prev->fsindex)) + prev->fs = 0; + if (fsindex != prev->fsindex || prev->fs != next->fs) + wrmsrl(MSR_FS_BASE, next->fs); + prev->fsindex = fsindex; + } + + { + unsigned int gsindex; + + asm volatile("movl %%gs,%0" : "=g" (gsindex)); + if (unlikely(gsindex != next->gsindex)) { + /* should load gs in syscall exit after swapgs instead */ + int nr = smp_processor_id(); + __cli(); + loadsegment(gs, next->gsindex); + wrmsrl(MSR_GS_BASE, cpu_pda+nr); + __sti(); + } + if (unlikely(gsindex != prev->gsindex)) + prev->gs = 0; + if (gsindex != prev->gsindex || prev->gs != next->gs) + wrmsrl(MSR_KERNEL_GS_BASE, next->gs); + prev->gsindex = gsindex; + } + + /* + * Switch the PDA context. + */ + prev->userrsp = read_pda(oldrsp); + write_pda(oldrsp, next->userrsp); + write_pda(pcurrent, next_p); + write_pda(kernelstack, (unsigned long)next_p + THREAD_SIZE - PDA_STACKOFFSET); + + /* + * Now maybe reload the debug registers + */ + if (unlikely(next->debugreg[7])) { + loaddebug(next, 0); + loaddebug(next, 1); + loaddebug(next, 2); + loaddebug(next, 3); + /* no 4 and 5 */ + loaddebug(next, 6); + loaddebug(next, 7); + } + + + /* + * Handle the IO bitmap + */ + if (unlikely(prev->ioperm || next->ioperm)) { + if (next->ioperm) { + /* + * 4 cachelines copy ... not good, but not that + * bad either. Anyone got something better? + * This only affects processes which use ioperm(). + * [Putting the TSSs into 4k-tlb mapped regions + * and playing VM tricks to switch the IO bitmap + * is not really acceptable.] + * On x86-64 we could put multiple bitmaps into + * the GDT and just switch offsets + * This would require ugly special cases on overflow + * though -AK + */ + memcpy(tss->io_bitmap, next->io_bitmap, + IO_BITMAP_SIZE*sizeof(u32)); + tss->io_map_base = IO_BITMAP_OFFSET; + } else { + /* + * a bitmap offset pointing outside of the TSS limit + * causes a nicely controllable SIGSEGV if a process + * tries to use a port IO instruction. The first + * sys_ioperm() call sets up the bitmap properly. + */ + tss->io_map_base = INVALID_IO_BITMAP_OFFSET; + } + } +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage +long sys_execve(char *name, char **argv,char **envp, struct pt_regs regs) +{ + long error; + char * filename; + + filename = getname(name); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + return error; + error = do_execve(filename, argv, envp, ®s); + if (error == 0) + current->ptrace &= ~PT_DTRACE; + putname(filename); + return error; +} + +void set_personality_64bit(void) +{ + /* inherit personality from parent */ + + /* Make sure to be in 64bit mode */ + current->thread.flags = 0; +} + +asmlinkage long sys_fork(struct pt_regs regs) +{ + return do_fork(SIGCHLD, regs.rsp, ®s, 0); +} + +asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp, struct pt_regs regs) +{ + if (!newsp) + newsp = regs.rsp; + return do_fork(clone_flags, newsp, ®s, 0); +} + +/* + * This is trivial, and on the face of it looks like it + * could equally well be done in user mode. + * + * Not so, for quite unobvious reasons - register pressure. + * In user mode vfork() cannot have a stack frame, and if + * done by calling the "clone()" system call directly, you + * do not have enough call-clobbered registers to hold all + * the information you need. + */ +asmlinkage long sys_vfork(struct pt_regs regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.rsp, ®s, 0); +} + +/* + * These bracket the sleeping functions.. + */ +extern void scheduling_functions_start_here(void); +extern void scheduling_functions_end_here(void); +#define first_sched ((unsigned long) scheduling_functions_start_here) +#define last_sched ((unsigned long) scheduling_functions_end_here) + +unsigned long get_wchan(struct task_struct *p) +{ + return -1; /* FIXME */ +} +#undef last_sched +#undef first_sched + +asmlinkage int sys_arch_prctl(int code, unsigned long addr) +{ + int ret = 0; + unsigned long tmp; + switch (code) { + case ARCH_SET_GS: + asm volatile("movw %%gs,%0" : "=g" (current->thread.gsindex)); + current->thread.gs = addr; + ret = checking_wrmsrl(MSR_KERNEL_GS_BASE, addr); + break; + case ARCH_SET_FS: + asm volatile("movw %%fs,%0" : "=g" (current->thread.fsindex)); + current->thread.fs = addr; + ret = checking_wrmsrl(MSR_FS_BASE, addr); + break; + + /* Returned value may not be correct when the user changed fs/gs */ + case ARCH_GET_FS: + rdmsrl(MSR_FS_BASE, tmp); + ret = put_user(tmp, (unsigned long *)addr); + break; + + case ARCH_GET_GS: + rdmsrl(MSR_KERNEL_GS_BASE, tmp); + ret = put_user(tmp, (unsigned long *)addr); + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/ptrace.c linux.ac/arch/x86_64/kernel/ptrace.c --- linux.vanilla/arch/x86_64/kernel/ptrace.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/ptrace.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,426 @@ +/* ptrace.c */ +/* By Ross Biro 1/23/92 */ +/* + * Pentium III FXSR, SSE support + * Gareth Hughes , May 2000 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* determines which flags the user has access to. */ +/* 1 = access 0 = no access */ +#define FLAG_MASK 0x44dd5UL + +/* set's the trap flag. */ +#define TRAP_FLAG 0x100UL + +/* + * Offset of eflags on child stack.. + */ +#define EFL_OFFSET (-24) /* (EFLAGS-sizeof(struct pt_regs)) */ + +/* + * this routine will get a word off of the processes privileged stack. + * the offset is how far from the base addr as stored in the TSS. + * this routine assumes that all the privileged stacks are in our + * data space. + */ +static inline unsigned long get_stack_long(struct task_struct *task, int offset) +{ + unsigned char *stack; + + stack = (unsigned char *)task->thread.rsp0; + stack += offset; + return (*((unsigned long *)stack)); +} + +/* + * this routine will put a word on the processes privileged stack. + * the offset is how far from the base addr as stored in the TSS. + * this routine assumes that all the privileged stacks are in our + * data space. + */ +static inline long put_stack_long(struct task_struct *task, int offset, + unsigned long data) +{ + unsigned char * stack; + + stack = (unsigned char *) task->thread.rsp0; + stack += offset; + *(unsigned long *) stack = data; + return 0; +} + +static int putreg(struct task_struct *child, + unsigned long regno, unsigned long value) +{ + unsigned long tmp; + switch (regno >> 2) { + // XXX: add 64bit setting. + case FS: + if (value && (value & 3) != 3) + return -EIO; + child->thread.fs = value; + return 0; + case GS: + if (value && (value & 3) != 3) + return -EIO; + child->thread.gs = value; + return 0; + case EFLAGS: + value &= FLAG_MASK; + tmp = get_stack_long(child, EFL_OFFSET); + tmp &= ~FLAG_MASK; + value |= tmp; + break; + } + /* assumption about sizes... */ + if (regno > GS*4) + regno -= 2*4; + /* This has to be changes to put_stack_64() */ + /* Hmm, with 32 bit applications being around... this will be + rather funny */ + put_stack_long(child, regno - sizeof(struct pt_regs), value); + return 0; +} + +static unsigned long getreg(struct task_struct *child, + unsigned long regno) +{ + switch (regno >> 3) { + case FS: + return child->thread.fs; + case GS: + return child->thread.gs; + default: + regno = regno - sizeof(struct pt_regs); + return get_stack_long(child, regno); + } + +} + +asmlinkage long sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + struct user * dummy = NULL; + long i, ret; + + /* This lock_kernel fixes a subtle race with suid exec */ + lock_kernel(); + ret = -EPERM; + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + ret = -ESRCH; + if (!(child->ptrace & PT_PTRACED)) + goto out_tsk; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out_tsk; + } + if (child->p_pptr != current) + goto out_tsk; + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + + ret = -EIO; + if ((addr & 3) || addr < 0 || + addr > sizeof(struct user) - 3) + break; + + tmp = 0; /* Default return condition */ + if(addr < 20*sizeof(long)) + tmp = getreg(child, addr); + if(addr >= (long) &dummy->u_debugreg[0] && + addr <= (long) &dummy->u_debugreg[7]){ + addr -= (long) &dummy->u_debugreg[0]; + addr = addr >> 2; + tmp = child->thread.debugreg[addr]; + } + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* when I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) + break; + ret = -EIO; + break; + + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ + ret = -EIO; + if ((addr & 3) || addr < 0 || + addr > sizeof(struct user) - 3) + break; + + if (addr < 20*sizeof(long)) { + ret = putreg(child, addr, data); + break; + } + /* We need to be very careful here. We implicitly + want to modify a portion of the task_struct, and we + have to be selective about what portions we allow someone + to modify. */ + + ret = -EIO; + if(addr >= (long) &dummy->u_debugreg[0] && + addr <= (long) &dummy->u_debugreg[7]){ + + if(addr == (long) &dummy->u_debugreg[4]) break; + if(addr == (long) &dummy->u_debugreg[5]) break; + if(addr < (long) &dummy->u_debugreg[4] && + ((unsigned long) data) >= TASK_SIZE-3) break; + + if(addr == (long) &dummy->u_debugreg[7]) { + data &= ~DR_CONTROL_RESERVED; + for(i=0; i<4; i++) + if ((0x5454 >> ((data >> (16 + 4*i)) & 0xf)) & 1) + goto out_tsk; + } + + addr -= (long) &dummy->u_debugreg; + addr = addr >> 2; + child->thread.debugreg[addr] = data; + ret = 0; + } + break; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + long tmp; + + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + child->ptrace |= PT_TRACESYS; + else + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + /* make sure the single step bit is not set. */ + tmp = get_stack_long(child, EFL_OFFSET); + tmp &= ~TRAP_FLAG; + put_stack_long(child, EFL_OFFSET,tmp); + wake_up_process(child); + ret = 0; + break; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + long tmp; + + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ + tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; + put_stack_long(child, EFL_OFFSET, tmp); + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + long tmp; + + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~PT_TRACESYS; + if ((child->ptrace & PT_DTRACE) == 0) { + /* Spurious delayed TF traps may occur */ + child->ptrace |= PT_DTRACE; + } + tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG; + put_stack_long(child, EFL_OFFSET, tmp); + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: { /* detach a process that was attached. */ + long tmp; + + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~(PT_PTRACED|PT_TRACESYS); + child->exit_code = data; + write_lock_irq(&tasklist_lock); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irq(&tasklist_lock); + /* make sure the single step bit is not set. */ + tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; + put_stack_long(child, EFL_OFFSET, tmp); + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_GETREGS: { /* Get all gp regs from the child. */ + if (!access_ok(VERIFY_WRITE, (unsigned *)data, FRAME_SIZE)) { + ret = -EIO; + break; + } + for ( i = 0; i < FRAME_SIZE; i += sizeof(long) ) { + __put_user(getreg(child, i),(unsigned long *) data); + data += sizeof(long); + } + ret = 0; + break; + } + + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp; + if (!access_ok(VERIFY_READ, (unsigned *)data, FRAME_SIZE)) { + ret = -EIO; + break; + } + for ( i = 0; i < FRAME_SIZE; i += sizeof(long) ) { + __get_user(tmp, (unsigned long *) data); + putreg(child, i, tmp); + data += sizeof(long); + } + ret = 0; + break; + } + + case PTRACE_GETFPREGS: { /* Get the child extended FPU state. */ + if (!access_ok(VERIFY_WRITE, (unsigned *)data, + sizeof(struct user_i387_struct))) { + ret = -EIO; + break; + } + if ( !child->used_math ) { + /* Simulate an empty FPU. */ + set_fpu_cwd(child, 0x037f); + set_fpu_swd(child, 0x0000); + set_fpu_twd(child, 0xffff); + set_fpu_mxcsr(child, 0x1f80); + } + ret = get_fpregs((struct user_i387_struct *)data, child); + break; + } + + case PTRACE_SETFPREGS: { /* Set the child extended FPU state. */ + if (!access_ok(VERIFY_READ, (unsigned *)data, + sizeof(struct user_i387_struct))) { + ret = -EIO; + break; + } + child->used_math = 1; + ret = set_fpregs(child, (struct user_i387_struct *)data); + break; + } + + case PTRACE_SETOPTIONS: { + if (data & PTRACE_O_TRACESYSGOOD) + child->ptrace |= PT_TRACESYSGOOD; + else + child->ptrace &= ~PT_TRACESYSGOOD; + ret = 0; + break; + } + + default: + ret = -EIO; + break; + } +out_tsk: + free_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +asmlinkage void syscall_trace(struct pt_regs *regs) +{ + if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) != + (PT_PTRACED|PT_TRACESYS)) + return; + + current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0); + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/semaphore.c linux.ac/arch/x86_64/kernel/semaphore.c --- linux.vanilla/arch/x86_64/kernel/semaphore.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/semaphore.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,224 @@ +/* + * i386 semaphore implementation. + * + * (C) Copyright 1999 Linus Torvalds + * + * Portions Copyright 1999 Red Hat, Inc. + * + * 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. + * + * rw semaphores implemented November 1999 by Benjamin LaHaise + */ +#include +#include + +#include + +/* + * Semaphores are implemented using a two-way counter: + * The "count" variable is decremented for each process + * that tries to acquire the semaphore, while the "sleeping" + * variable is a count of such acquires. + * + * Notably, the inline "up()" and "down()" functions can + * efficiently test if they need to do any extra work (up + * needs to do something only if count was negative before + * the increment operation. + * + * "sleeping" and the contention routine ordering is + * protected by the semaphore spinlock. + * + * Note that these functions are only called when there is + * contention on the lock, and as such all this is the + * "non-critical" part of the whole semaphore business. The + * critical part is the inline stuff in + * where we want to avoid any extra jumps and calls. + */ + +/* + * Logic: + * - only on a boundary condition do we need to care. When we go + * from a negative count to a non-negative, we wake people up. + * - when we go from a non-negative count to a negative do we + * (a) synchronize with the "sleeper" count and (b) make sure + * that we're on the wakeup list before we synchronize so that + * we cannot lose wakeup events. + */ + +void __up(struct semaphore *sem) +{ + wake_up(&sem->wait); +} + +static spinlock_t semaphore_lock = SPIN_LOCK_UNLOCKED; + +void __down(struct semaphore * sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + tsk->state = TASK_UNINTERRUPTIBLE; + add_wait_queue_exclusive(&sem->wait, &wait); + + spin_lock_irq(&semaphore_lock); + sem->sleepers++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irq(&semaphore_lock); + + schedule(); + tsk->state = TASK_UNINTERRUPTIBLE; + spin_lock_irq(&semaphore_lock); + } + spin_unlock_irq(&semaphore_lock); + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + wake_up(&sem->wait); +} + +int __down_interruptible(struct semaphore * sem) +{ + int retval = 0; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + tsk->state = TASK_INTERRUPTIBLE; + add_wait_queue_exclusive(&sem->wait, &wait); + + spin_lock_irq(&semaphore_lock); + sem->sleepers ++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * With signals pending, this turns into + * the trylock failure case - we won't be + * sleeping, and we* can't get the lock as + * it has contention. Just correct the count + * and exit. + */ + if (signal_pending(current)) { + retval = -EINTR; + sem->sleepers = 0; + atomic_add(sleepers, &sem->count); + break; + } + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock. The + * "-1" is because we're still hoping to get + * the lock. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irq(&semaphore_lock); + + schedule(); + tsk->state = TASK_INTERRUPTIBLE; + spin_lock_irq(&semaphore_lock); + } + spin_unlock_irq(&semaphore_lock); + tsk->state = TASK_RUNNING; + remove_wait_queue(&sem->wait, &wait); + wake_up(&sem->wait); + return retval; +} + +/* + * Trylock failed - make sure we correct for + * having decremented the count. + * + * We could have done the trylock with a + * single "cmpxchg" without failure cases, + * but then it wouldn't work on a 386. + */ +int __down_trylock(struct semaphore * sem) +{ + int sleepers; + unsigned long flags; + + spin_lock_irqsave(&semaphore_lock, flags); + sleepers = sem->sleepers + 1; + sem->sleepers = 0; + + /* + * Add "everybody else" and us into it. They aren't + * playing, because we own the spinlock. + */ + if (!atomic_add_negative(sleepers, &sem->count)) + wake_up(&sem->wait); + + spin_unlock_irqrestore(&semaphore_lock, flags); + return 1; +} + + +/* + * The semaphore operations have a special calling sequence that + * allow us to do a simpler in-line version of them. These routines + * need to convert that sequence back into the C sequence when + * there is contention on the semaphore. + * + * %rcx contains the semaphore pointer on entry. Save all the callee + * clobbered registers. It would be better if the compiler had a way + * to specify that for the callee. + */ + + +#define PUSH_CLOBBER "pushq %rdi ; pushq %rsi ; pushq %rdx ; pushq %rcx ;" \ + "pushq %rbx ; pushq %r8 ; push %r9\n\t" +#define POP_CLOBBER "popq %r9 ; popq %r8 ; popq %rbx ; popq %rcx ; " \ + "popq %rdx ; popq %rsi ; popq %rdi\n\t" + +#define SEM_ENTRY(label, name) asm( \ + ".p2align\n\t.globl " #label "\n\t" \ + #label ":\n\t" PUSH_CLOBBER "call " #name "\n\t" POP_CLOBBER "ret" ) + +SEM_ENTRY(__down_failed, __down); +SEM_ENTRY(__down_failed_interruptible, __down_interruptible); +SEM_ENTRY(__down_failed_trylock, __down_trylock); +SEM_ENTRY(__up_wakeup, __up); + + +#if defined(CONFIG_SMP) +asm( +".p2align" +"\n.globl __write_lock_failed" +"\n__write_lock_failed:" +"\n " LOCK "addl $" RW_LOCK_BIAS_STR ",(%rax)" +"\n1: cmpl $" RW_LOCK_BIAS_STR ",(%rax)" +"\n jne 1b" + +"\n " LOCK "subl $" RW_LOCK_BIAS_STR ",(%rax)" +"\n jnz __write_lock_failed" +"\n ret" + + +"\n.p2align" +"\n.globl __read_lock_failed" +"\n__read_lock_failed:" +"\n lock ; incl (%rax)" +"\n1: cmpl $1,(%rax)" +"\n js 1b" + +"\n lock ; decl (%rax)" +"\n js __read_lock_failed" +"\n ret" +); +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/setup.c linux.ac/arch/x86_64/kernel/setup.c --- linux.vanilla/arch/x86_64/kernel/setup.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/setup.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,2789 @@ +/* + * linux/arch/i386/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * + * Enhanced CPU type detection by Mike Jagdis, Patrick St. Jean + * and Martin Mares, November 1997. + * + * Force Cyrix 6x86(MX) and M II processors to report MTRR capability + * and Cyrix "coma bug" recognition by + * Zoltán Böszörményi February 1999. + * + * Force Centaur C6 processors to report MTRR capability. + * Bart Hartgers , May 1999. + * + * Intel Mobile Pentium II detection fix. Sean Gilley, June 1999. + * + * IDT Winchip tweaks, misc clean ups. + * Dave Jones , August 1999 + * + * Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999 + * + * Better detection of Centaur/IDT WinChip models. + * Bart Hartgers , August 1999. + * + * Memory region support + * David Parsons , July-August 1999 + * + * Cleaned up cache-detection code + * Dave Jones , October 1999 + * + * Added proper L2 cache detection for Coppermine + * Dragan Stancevic , October 1999 + * + * Added the original array for capability flags but forgot to credit + * myself :) (~1998) Fixed/cleaned up some cpu_model_info and other stuff + * Jauder Ho , January 2000 + * + * Detection for Celeron coppermine, identify_cpu() overhauled, + * and a few other clean ups. + * Dave Jones , April 2000 + * + * Pentium III FXSR, SSE support + * General FPU state handling cleanups + * Gareth Hughes , May 2000 + * + * Added proper Cascades CPU and L2 cache detection for Cascades + * and 8-way type cache happy bunch from Intel:^) + * Dragan Stancevic , May 2000 + * + * Forward port AMD Duron errata T13 from 2.2.17pre + * Dave Jones , August 2000 + * + * Forward port lots of fixes/improvements from 2.2.18pre + * Cyrix III, Pentium IV support. + * Dave Jones , October 2000 + * + * Massive cleanup of CPU detection and bug handling; + * Transmeta CPU detection, + * H. Peter Anvin , November 2000 + * + * Added E820 sanitization routine (removes overlapping memory regions); + * Brian Moyle , February 2001 + * + * VIA C3 Support. + * Dave Jones , March 2001 + * + * AMD Athlon/Duron/Thunderbird bluesmoke support. + * Dave Jones , April 2001. + */ + +/* + * This file handles the architecture-dependent parts of initialization + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_BLK_DEV_RAM +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * Machine setup.. + */ + +char ignore_irq13; /* set if exception 16 works */ +struct cpuinfo_x86 boot_cpu_data = { 0, 0, 0, 0, -1, 1, 0, 0, -1 }; + +unsigned long mmu_cr4_features; + +/* + * Bus types .. + */ +#ifdef CONFIG_EISA +int EISA_bus; +#endif +int MCA_bus; + +/* for MCA, but anyone else can use it if they want */ +unsigned int machine_id; +unsigned int machine_submodel_id; +unsigned int BIOS_revision; +unsigned int mca_pentium_flag; + +/* For PCI or other memory-mapped resources */ +unsigned long pci_mem_start = 0x10000000; + +/* + * Setup options + */ +struct drive_info_struct { char dummy[32]; } drive_info; +struct screen_info screen_info; +struct apm_info apm_info; +struct sys_desc_table_struct { + unsigned short length; + unsigned char table[0]; +}; + +struct e820map e820; + +unsigned char aux_device_present; + +extern int root_mountflags; +extern char _text, _etext, _edata, _end; + +static int disable_x86_serial_nr __initdata = 1; +static int disable_x86_fxsr __initdata = 0; + +#include + +#ifdef CONFIG_VISWS +char visws_board_type = -1; +char visws_board_rev = -1; + +#define PIIX_PM_START 0x0F80 + +#define SIO_GPIO_START 0x0FC0 + +#define SIO_PM_START 0x0FC8 + +#define PMBASE PIIX_PM_START +#define GPIREG0 (PMBASE+0x30) +#define GPIREG(x) (GPIREG0+((x)/8)) +#define PIIX_GPI_BD_ID1 18 +#define PIIX_GPI_BD_REG GPIREG(PIIX_GPI_BD_ID1) + +#define PIIX_GPI_BD_SHIFT (PIIX_GPI_BD_ID1 % 8) + +#define SIO_INDEX 0x2e +#define SIO_DATA 0x2f + +#define SIO_DEV_SEL 0x7 +#define SIO_DEV_ENB 0x30 +#define SIO_DEV_MSB 0x60 +#define SIO_DEV_LSB 0x61 + +#define SIO_GP_DEV 0x7 + +#define SIO_GP_BASE SIO_GPIO_START +#define SIO_GP_MSB (SIO_GP_BASE>>8) +#define SIO_GP_LSB (SIO_GP_BASE&0xff) + +#define SIO_GP_DATA1 (SIO_GP_BASE+0) + +#define SIO_PM_DEV 0x8 + +#define SIO_PM_BASE SIO_PM_START +#define SIO_PM_MSB (SIO_PM_BASE>>8) +#define SIO_PM_LSB (SIO_PM_BASE&0xff) +#define SIO_PM_INDEX (SIO_PM_BASE+0) +#define SIO_PM_DATA (SIO_PM_BASE+1) + +#define SIO_PM_FER2 0x1 + +#define SIO_PM_GP_EN 0x80 + +static void +visws_get_board_type_and_rev(void) +{ + int raw; + + visws_board_type = (char)(inb_p(PIIX_GPI_BD_REG) & PIIX_GPI_BD_REG) + >> PIIX_GPI_BD_SHIFT; +/* + * Get Board rev. + * First, we have to initialize the 307 part to allow us access + * to the GPIO registers. Let's map them at 0x0fc0 which is right + * after the PIIX4 PM section. + */ + outb_p(SIO_DEV_SEL, SIO_INDEX); + outb_p(SIO_GP_DEV, SIO_DATA); /* Talk to GPIO regs. */ + + outb_p(SIO_DEV_MSB, SIO_INDEX); + outb_p(SIO_GP_MSB, SIO_DATA); /* MSB of GPIO base address */ + + outb_p(SIO_DEV_LSB, SIO_INDEX); + outb_p(SIO_GP_LSB, SIO_DATA); /* LSB of GPIO base address */ + + outb_p(SIO_DEV_ENB, SIO_INDEX); + outb_p(1, SIO_DATA); /* Enable GPIO registers. */ + +/* + * Now, we have to map the power management section to write + * a bit which enables access to the GPIO registers. + * What lunatic came up with this shit? + */ + outb_p(SIO_DEV_SEL, SIO_INDEX); + outb_p(SIO_PM_DEV, SIO_DATA); /* Talk to GPIO regs. */ + + outb_p(SIO_DEV_MSB, SIO_INDEX); + outb_p(SIO_PM_MSB, SIO_DATA); /* MSB of PM base address */ + + outb_p(SIO_DEV_LSB, SIO_INDEX); + outb_p(SIO_PM_LSB, SIO_DATA); /* LSB of PM base address */ + + outb_p(SIO_DEV_ENB, SIO_INDEX); + outb_p(1, SIO_DATA); /* Enable PM registers. */ + +/* + * Now, write the PM register which enables the GPIO registers. + */ + outb_p(SIO_PM_FER2, SIO_PM_INDEX); + outb_p(SIO_PM_GP_EN, SIO_PM_DATA); + +/* + * Now, initialize the GPIO registers. + * We want them all to be inputs which is the + * power on default, so let's leave them alone. + * So, let's just read the board rev! + */ + raw = inb_p(SIO_GP_DATA1); + raw &= 0x7f; /* 7 bits of valid board revision ID. */ + + if (visws_board_type == VISWS_320) { + if (raw < 0x6) { + visws_board_rev = 4; + } else if (raw < 0xc) { + visws_board_rev = 5; + } else { + visws_board_rev = 6; + + } + } else if (visws_board_type == VISWS_540) { + visws_board_rev = 2; + } else { + visws_board_rev = raw; + } + + printk(KERN_INFO "Silicon Graphics %s (rev %d)\n", + visws_board_type == VISWS_320 ? "320" : + (visws_board_type == VISWS_540 ? "540" : + "unknown"), + visws_board_rev); + } +#endif + + +static char command_line[COMMAND_LINE_SIZE]; + char saved_command_line[COMMAND_LINE_SIZE]; + +struct resource standard_io_resources[] = { + { "dma1", 0x00, 0x1f, IORESOURCE_BUSY }, + { "pic1", 0x20, 0x3f, IORESOURCE_BUSY }, + { "timer", 0x40, 0x5f, IORESOURCE_BUSY }, + { "keyboard", 0x60, 0x6f, IORESOURCE_BUSY }, + { "dma page reg", 0x80, 0x8f, IORESOURCE_BUSY }, + { "pic2", 0xa0, 0xbf, IORESOURCE_BUSY }, + { "dma2", 0xc0, 0xdf, IORESOURCE_BUSY }, + { "fpu", 0xf0, 0xff, IORESOURCE_BUSY } +}; + +#define STANDARD_IO_RESOURCES (sizeof(standard_io_resources)/sizeof(struct resource)) + +static struct resource code_resource = { "Kernel code", 0x100000, 0 }; +static struct resource data_resource = { "Kernel data", 0, 0 }; +static struct resource vram_resource = { "Video RAM area", 0xa0000, 0xbffff, IORESOURCE_BUSY }; + +/* System ROM resources */ +#define MAXROMS 6 +static struct resource rom_resources[MAXROMS] = { + { "System ROM", 0xF0000, 0xFFFFF, IORESOURCE_BUSY }, + { "Video ROM", 0xc0000, 0xc7fff, IORESOURCE_BUSY } +}; + +#define romsignature(x) (*(unsigned short *)(x) == 0xaa55) + +static void __init probe_roms(void) +{ + int roms = 1; + unsigned long base; + unsigned char *romstart; + + request_resource(&iomem_resource, rom_resources+0); + + /* Video ROM is standard at C000:0000 - C7FF:0000, check signature */ + for (base = 0xC0000; base < 0xE0000; base += 2048) { + romstart = bus_to_virt(base); + if (!romsignature(romstart)) + continue; + request_resource(&iomem_resource, rom_resources + roms); + roms++; + break; + } + + /* Extension roms at C800:0000 - DFFF:0000 */ + for (base = 0xC8000; base < 0xE0000; base += 2048) { + unsigned long length; + + romstart = bus_to_virt(base); + if (!romsignature(romstart)) + continue; + length = romstart[2] * 512; + if (length) { + unsigned int i; + unsigned char chksum; + + chksum = 0; + for (i = 0; i < length; i++) + chksum += romstart[i]; + + /* Good checksum? */ + if (!chksum) { + rom_resources[roms].start = base; + rom_resources[roms].end = base + length - 1; + rom_resources[roms].name = "Extension ROM"; + rom_resources[roms].flags = IORESOURCE_BUSY; + + request_resource(&iomem_resource, rom_resources + roms); + roms++; + if (roms >= MAXROMS) + return; + } + } + } + + /* Final check for motherboard extension rom at E000:0000 */ + base = 0xE0000; + romstart = bus_to_virt(base); + + if (romsignature(romstart)) { + rom_resources[roms].start = base; + rom_resources[roms].end = base + 65535; + rom_resources[roms].name = "Extension ROM"; + rom_resources[roms].flags = IORESOURCE_BUSY; + + request_resource(&iomem_resource, rom_resources + roms); + } +} + +void __init add_memory_region(unsigned long long start, + unsigned long long size, int type) +{ + int x = e820.nr_map; + + if (x == E820MAX) { + printk(KERN_ERR "Ooops! Too many entries in the memory map!\n"); + return; + } + + e820.map[x].addr = start; + e820.map[x].size = size; + e820.map[x].type = type; + e820.nr_map++; +} /* add_memory_region */ + +#define E820_DEBUG 1 + +static void __init print_memory_map(char *who) +{ + int i; + + for (i = 0; i < e820.nr_map; i++) { + printk(" %s: %016Lx - %016Lx ", who, + e820.map[i].addr, + e820.map[i].addr + e820.map[i].size); + switch (e820.map[i].type) { + case E820_RAM: printk("(usable)\n"); + break; + case E820_RESERVED: + printk("(reserved)\n"); + break; + case E820_ACPI: + printk("(ACPI data)\n"); + break; + case E820_NVS: + printk("(ACPI NVS)\n"); + break; + default: printk("type %lu\n", e820.map[i].type); + break; + } + } +} + +/* + * Sanitize the BIOS e820 map. + * + * Some e820 responses include overlapping entries. The following + * replaces the original e820 map with a new one, removing overlaps. + * + */ +static int __init sanitize_e820_map(struct e820entry * biosmap, char * pnr_map) +{ + struct change_member { + struct e820entry *pbios; /* pointer to original bios entry */ + unsigned long long addr; /* address for this change point */ + }; + struct change_member change_point_list[2*E820MAX]; + struct change_member *change_point[2*E820MAX]; + struct e820entry *overlap_list[E820MAX]; + struct e820entry new_bios[E820MAX]; + struct change_member *change_tmp; + unsigned long current_type, last_type; + unsigned long long last_addr; + int chgidx, still_changing; + int overlap_entries; + int new_bios_entry; + int old_nr, new_nr; + int i; + + /* + Visually we're performing the following (1,2,3,4 = memory types)... + + Sample memory map (w/overlaps): + ____22__________________ + ______________________4_ + ____1111________________ + _44_____________________ + 11111111________________ + ____________________33__ + ___________44___________ + __________33333_________ + ______________22________ + ___________________2222_ + _________111111111______ + _____________________11_ + _________________4______ + + Sanitized equivalent (no overlap): + 1_______________________ + _44_____________________ + ___1____________________ + ____22__________________ + ______11________________ + _________1______________ + __________3_____________ + ___________44___________ + _____________33_________ + _______________2________ + ________________1_______ + _________________4______ + ___________________2____ + ____________________33__ + ______________________4_ + */ + + /* if there's only one memory region, don't bother */ + if (*pnr_map < 2) + return -1; + + old_nr = *pnr_map; + + /* bail out if we find any unreasonable addresses in bios map */ + for (i=0; iaddr = biosmap[i].addr; + change_point[chgidx++]->pbios = &biosmap[i]; + change_point[chgidx]->addr = biosmap[i].addr + biosmap[i].size; + change_point[chgidx++]->pbios = &biosmap[i]; + } + + /* sort change-point list by memory addresses (low -> high) */ + still_changing = 1; + while (still_changing) { + still_changing = 0; + for (i=1; i < 2*old_nr; i++) { + /* if > , swap */ + /* or, if current= & last=, swap */ + if ((change_point[i]->addr < change_point[i-1]->addr) || + ((change_point[i]->addr == change_point[i-1]->addr) && + (change_point[i]->addr == change_point[i]->pbios->addr) && + (change_point[i-1]->addr != change_point[i-1]->pbios->addr)) + ) + { + change_tmp = change_point[i]; + change_point[i] = change_point[i-1]; + change_point[i-1] = change_tmp; + still_changing=1; + } + } + } + + /* create a new bios memory map, removing overlaps */ + overlap_entries=0; /* number of entries in the overlap table */ + new_bios_entry=0; /* index for creating new bios map entries */ + last_type = 0; /* start with undefined memory type */ + last_addr = 0; /* start with 0 as last starting address */ + /* loop through change-points, determining affect on the new bios map */ + for (chgidx=0; chgidx < 2*old_nr; chgidx++) + { + /* keep track of all overlapping bios entries */ + if (change_point[chgidx]->addr == change_point[chgidx]->pbios->addr) + { + /* add map entry to overlap list (> 1 entry implies an overlap) */ + overlap_list[overlap_entries++]=change_point[chgidx]->pbios; + } + else + { + /* remove entry from list (order independent, so swap with last) */ + for (i=0; ipbios) + overlap_list[i] = overlap_list[overlap_entries-1]; + } + overlap_entries--; + } + /* if there are overlapping entries, decide which "type" to use */ + /* (larger value takes precedence -- 1=usable, 2,3,4,4+=unusable) */ + current_type = 0; + for (i=0; itype > current_type) + current_type = overlap_list[i]->type; + /* continue building up new bios map based on this information */ + if (current_type != last_type) { + if (last_type != 0) { + new_bios[new_bios_entry].size = + change_point[chgidx]->addr - last_addr; + /* move forward only if the new size was non-zero */ + if (new_bios[new_bios_entry].size != 0) + if (++new_bios_entry >= E820MAX) + break; /* no more space left for new bios entries */ + } + if (current_type != 0) { + new_bios[new_bios_entry].addr = change_point[chgidx]->addr; + new_bios[new_bios_entry].type = current_type; + last_addr=change_point[chgidx]->addr; + } + last_type = current_type; + } + } + new_nr = new_bios_entry; /* retain count for new bios entries */ + + /* copy new bios mapping into original location */ + memcpy(biosmap, new_bios, new_nr*sizeof(struct e820entry)); + *pnr_map = new_nr; + + return 0; +} + +/* + * Copy the BIOS e820 map into a safe place. + * + * Sanity-check it while we're at it.. + * + * If we're lucky and live on a modern system, the setup code + * will have given us a memory map that we can use to properly + * set up memory. If we aren't, we'll fake a memory map. + * + * We check to see that the memory map contains at least 2 elements + * before we'll use it, because the detection code in setup.S may + * not be perfect and most every PC known to man has two memory + * regions: one from 0 to 640k, and one from 1mb up. (The IBM + * thinkpad 560x, for example, does not cooperate with the memory + * detection code.) + */ +static int __init copy_e820_map(struct e820entry * biosmap, int nr_map) +{ + /* Only one memory region (or negative)? Ignore it */ + if (nr_map < 2) + return -1; + + do { + unsigned long long start = biosmap->addr; + unsigned long long size = biosmap->size; + unsigned long long end = start + size; + unsigned long type = biosmap->type; + + /* Overflow in 64 bits? Ignore the memory map. */ + if (start > end) + return -1; + + /* + * Some BIOSes claim RAM in the 640k - 1M region. + * Not right. Fix it up. + */ + if (type == E820_RAM) { + if (start < 0x100000ULL && end > 0xA0000ULL) { + if (start < 0xA0000ULL) + add_memory_region(start, 0xA0000ULL-start, type); + if (end <= 0x100000ULL) + continue; + start = 0x100000ULL; + size = end - start; + } + } + add_memory_region(start, size, type); + } while (biosmap++,--nr_map); + return 0; +} + +/* + * Do NOT EVER look at the BIOS memory size location. + * It does not work on many machines. + */ +#define LOWMEMSIZE() (0x9f000) + +void __init setup_memory_region(void) +{ + char *who = "BIOS-e820"; + + /* + * Try to copy the BIOS-supplied E820-map. + * + * Otherwise fake a memory map; one section from 0k->640k, + * the next section from 1mb->appropriate_mem_k + */ + sanitize_e820_map(E820_MAP, &E820_MAP_NR); + if (copy_e820_map(E820_MAP, E820_MAP_NR) < 0) { + unsigned long mem_size; + + /* compare results from other methods and take the greater */ + if (ALT_MEM_K < EXT_MEM_K) { + mem_size = EXT_MEM_K; + who = "BIOS-88"; + } else { + mem_size = ALT_MEM_K; + who = "BIOS-e801"; + } + + e820.nr_map = 0; + add_memory_region(0, LOWMEMSIZE(), E820_RAM); + add_memory_region(HIGH_MEMORY, mem_size << 10, E820_RAM); + } + printk(KERN_INFO "BIOS-provided physical RAM map:\n"); + print_memory_map(who); +} /* setup_memory_region */ + + +static inline void parse_mem_cmdline (char ** cmdline_p) +{ + char c = ' ', *to = command_line, *from = COMMAND_LINE; + int len = 0; + int usermem = 0; + + /* Save unparsed command line copy for /proc/cmdline */ + memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); + saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; + + for (;;) { + /* + * "mem=nopentium" disables the 4MB page tables. + * "mem=XXX[kKmM]" defines a memory region from HIGH_MEM + * to , overriding the bios size. + * "mem=XXX[KkmM]@XXX[KkmM]" defines a memory region from + * to +, overriding the bios size. + */ + if (c == ' ' && !memcmp(from, "mem=", 4)) { + if (to != command_line) + to--; + if (!memcmp(from+4, "nopentium", 9)) { + from += 9+4; + clear_bit(X86_FEATURE_PSE, &boot_cpu_data.x86_capability); + } else if (!memcmp(from+4, "exactmap", 8)) { + from += 8+4; + e820.nr_map = 0; + usermem = 1; + } else { + /* If the user specifies memory size, we + * blow away any automatically generated + * size + */ + unsigned long long start_at, mem_size; + + if (usermem == 0) { + /* first time in: zap the whitelist + * and reinitialize it with the + * standard low-memory region. + */ + e820.nr_map = 0; + usermem = 1; + add_memory_region(0, LOWMEMSIZE(), E820_RAM); + } + mem_size = memparse(from+4, &from); + if (*from == '@') + start_at = memparse(from+1, &from); + else { + start_at = HIGH_MEMORY; + mem_size -= HIGH_MEMORY; + usermem=0; + } + add_memory_region(start_at, mem_size, E820_RAM); + } + } + c = *(from++); + if (!c) + break; + if (COMMAND_LINE_SIZE <= ++len) + break; + *(to++) = c; + } + *to = '\0'; + *cmdline_p = command_line; + if (usermem) { + printk(KERN_INFO "user-defined physical RAM map:\n"); + print_memory_map("user"); + } +} + +void __init setup_arch(char **cmdline_p) +{ + unsigned long bootmap_size, low_mem_size; + unsigned long start_pfn, max_pfn, max_low_pfn; + int i; + +#ifdef CONFIG_VISWS + visws_get_board_type_and_rev(); +#endif + + ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); + drive_info = DRIVE_INFO; + screen_info = SCREEN_INFO; + apm_info.bios = APM_BIOS_INFO; + if( SYS_DESC_TABLE.length != 0 ) { + MCA_bus = SYS_DESC_TABLE.table[3] &0x2; + machine_id = SYS_DESC_TABLE.table[0]; + machine_submodel_id = SYS_DESC_TABLE.table[1]; + BIOS_revision = SYS_DESC_TABLE.table[2]; + } + aux_device_present = AUX_DEVICE_INFO; + +#ifdef CONFIG_BLK_DEV_RAM + rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; + rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); + rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); +#endif + setup_memory_region(); + + if (!MOUNT_ROOT_RDONLY) + root_mountflags &= ~MS_RDONLY; + init_mm.start_code = (unsigned long) &_text; + init_mm.end_code = (unsigned long) &_etext; + init_mm.end_data = (unsigned long) &_edata; + init_mm.brk = (unsigned long) &_end; + + code_resource.start = virt_to_bus(&_text); + code_resource.end = virt_to_bus(&_etext)-1; + data_resource.start = virt_to_bus(&_etext); + data_resource.end = virt_to_bus(&_edata)-1; + + parse_mem_cmdline(cmdline_p); + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) + +/* + * 128MB for vmalloc and initrd + */ +#define VMALLOC_RESERVE (unsigned long)(128 << 20) +#define MAXMEM (unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE) +#define MAXMEM_PFN PFN_DOWN(MAXMEM) +#define MAX_NONPAE_PFN (1 << 20) + + /* + * partially used pages are not usable - thus + * we are rounding upwards: + */ + start_pfn = PFN_UP(__pa(&_end)); + + /* + * Find the highest page frame number we have available + */ + max_pfn = 0; + for (i = 0; i < e820.nr_map; i++) { + unsigned long start, end; + /* RAM? */ + if (e820.map[i].type != E820_RAM) + continue; + start = PFN_UP(e820.map[i].addr); + end = PFN_DOWN(e820.map[i].addr + e820.map[i].size); + if (start >= end) + continue; + if (end > max_pfn) + max_pfn = end; + } + + /* + * Determine low and high memory ranges: + */ + max_low_pfn = max_pfn; + if (max_low_pfn > MAXMEM_PFN) { + max_low_pfn = MAXMEM_PFN; +#ifndef CONFIG_HIGHMEM + /* Maximum memory usable is what is directly addressable */ + printk(KERN_WARNING "Warning only %ldMB will be used.\n", + MAXMEM>>20); + if (max_pfn > MAX_NONPAE_PFN) + printk(KERN_WARNING "Use a PAE enabled kernel.\n"); + else + printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n"); +#else /* !CONFIG_HIGHMEM */ +#ifndef CONFIG_X86_PAE + if (max_pfn > MAX_NONPAE_PFN) { + max_pfn = MAX_NONPAE_PFN; + printk(KERN_WARNING "Warning only 4GB will be used.\n"); + printk(KERN_WARNING "Use a PAE enabled kernel.\n"); + } +#endif /* !CONFIG_X86_PAE */ +#endif /* !CONFIG_HIGHMEM */ + } + +#ifdef CONFIG_HIGHMEM + highstart_pfn = highend_pfn = max_pfn; + if (max_pfn > MAXMEM_PFN) { + highstart_pfn = MAXMEM_PFN; + printk(KERN_NOTICE "%ldMB HIGHMEM available.\n", + pages_to_mb(highend_pfn - highstart_pfn)); + } +#endif + /* + * Initialize the boot-time allocator (with low memory only): + */ + bootmap_size = init_bootmem(start_pfn, max_low_pfn); + + /* + * Register fully available low RAM pages with the bootmem allocator. + */ + for (i = 0; i < e820.nr_map; i++) { + unsigned long curr_pfn, last_pfn, size; + /* + * Reserve usable low memory + */ + if (e820.map[i].type != E820_RAM) + continue; + /* + * We are rounding up the start address of usable memory: + */ + curr_pfn = PFN_UP(e820.map[i].addr); + if (curr_pfn >= max_low_pfn) + continue; + /* + * ... and at the end of the usable range downwards: + */ + last_pfn = PFN_DOWN(e820.map[i].addr + e820.map[i].size); + + if (last_pfn > max_low_pfn) + last_pfn = max_low_pfn; + + /* + * .. finally, did all the rounding and playing + * around just make the area go away? + */ + if (last_pfn <= curr_pfn) + continue; + + size = last_pfn - curr_pfn; + free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size)); + } + /* + * Reserve the bootmem bitmap itself as well. We do this in two + * steps (first step was init_bootmem()) because this catches + * the (very unlikely) case of us accidentally initializing the + * bootmem allocator with an invalid RAM area. + */ + reserve_bootmem(HIGH_MEMORY, (PFN_PHYS(start_pfn) + + bootmap_size + PAGE_SIZE-1) - (HIGH_MEMORY)); + + /* + * reserve physical page 0 - it's a special BIOS page on many boxes, + * enabling clean reboots, SMP operation, laptop functions. + */ + reserve_bootmem(0, PAGE_SIZE); + +#ifdef CONFIG_SMP + /* + * But first pinch a few for the stack/trampoline stuff + * FIXME: Don't need the extra page at 4K, but need to fix + * trampoline before removing it. (see the GDT stuff) + */ + reserve_bootmem(PAGE_SIZE, PAGE_SIZE); +#endif + +#ifdef CONFIG_X86_LOCAL_APIC + /* + * Find and reserve possible boot-time SMP configuration: + */ + find_smp_config(); +#endif +#ifdef CONFIG_BLK_DEV_INITRD + if (LOADER_TYPE && INITRD_START) { + if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) { + reserve_bootmem(INITRD_START, INITRD_SIZE); + initrd_start = + INITRD_START ? INITRD_START + PAGE_OFFSET : 0; + initrd_end = initrd_start+INITRD_SIZE; + } + else { + printk(KERN_ERR "initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + INITRD_START + INITRD_SIZE, + max_low_pfn << PAGE_SHIFT); + initrd_start = 0; + } + } +#endif + + /* + * NOTE: before this point _nobody_ is allowed to allocate + * any memory using the bootmem allocator. + */ + +#ifdef CONFIG_SMP + smp_alloc_memory(); /* AP processor realmode stacks in low memory*/ +#endif + paging_init(); +#ifdef CONFIG_X86_LOCAL_APIC + /* + * get boot-time SMP configuration: + */ + if (smp_found_config) + get_smp_config(); + init_apic_mappings(); +#endif + + + /* + * Request address space for all standard RAM and ROM resources + * and also for regions reported as reserved by the e820. + */ + probe_roms(); + for (i = 0; i < e820.nr_map; i++) { + struct resource *res; + if (e820.map[i].addr + e820.map[i].size > 0x100000000ULL) + continue; + res = alloc_bootmem_low(sizeof(struct resource)); + switch (e820.map[i].type) { + case E820_RAM: res->name = "System RAM"; break; + case E820_ACPI: res->name = "ACPI Tables"; break; + case E820_NVS: res->name = "ACPI Non-volatile Storage"; break; + default: res->name = "reserved"; + } + res->start = e820.map[i].addr; + res->end = res->start + e820.map[i].size - 1; + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + request_resource(&iomem_resource, res); + if (e820.map[i].type == E820_RAM) { + /* + * We dont't know which RAM region contains kernel data, + * so we try it repeatedly and let the resource manager + * test it. + */ + request_resource(res, &code_resource); + request_resource(res, &data_resource); + } + } + request_resource(&iomem_resource, &vram_resource); + + /* request I/O space for devices used on all i[345]86 PCs */ + for (i = 0; i < STANDARD_IO_RESOURCES; i++) + request_resource(&ioport_resource, standard_io_resources+i); + + /* Tell the PCI layer not to allocate too close to the RAM area.. */ + low_mem_size = ((max_low_pfn << PAGE_SHIFT) + 0xfffff) & ~0xfffff; + if (low_mem_size > pci_mem_start) + pci_mem_start = low_mem_size; + +#ifdef CONFIG_VT +#if defined(CONFIG_VGA_CONSOLE) + conswitchp = &vga_con; +#elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif +#endif +} + +#ifndef CONFIG_X86_TSC +static int tsc_disable __initdata = 0; + +static int __init tsc_setup(char *str) +{ + tsc_disable = 1; + return 1; +} + +__setup("notsc", tsc_setup); +#endif + + +static void __init interpret_eblcr(struct cpuinfo_x86 *c, u32 lo, int coding) +{ + static unsigned int __initdata mult[32] = { 10, 6, 8, 0, 11, 7, 9, 0, + 10, 14, 16, 12, 0, 15, 0, 13 , + 0, 0, 0, 0, 0, 0, 17, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; + int mul = (lo>>22)&15; + + /* The mobile pIII added bit 27. This is zero on other intel and + on the cyrix III */ + + if(lo&(1>>27)) + mul+=16; + + c->clockmul = mult[mul]; + + if (mult[mul]==0) + { + printk(KERN_INFO "Unknown CPU/BUS multiplier (%d, %x).\n", + mul, lo); + return; + } +} + + +static int __init get_model_name(struct cpuinfo_x86 *c) +{ + unsigned int *v; + char *p, *q; + + if (cpuid_eax(0x80000000) < 0x80000004) + return 0; + + v = (unsigned int *) c->x86_model_id; + cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]); + cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]); + cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]); + c->x86_model_id[48] = 0; + + /* Intel chips right-justify this string for some dumb reason; + undo that brain damage */ + p = q = &c->x86_model_id[0]; + while ( *p == ' ' ) + p++; + if ( p != q ) { + while ( *p ) + *q++ = *p++; + while ( q <= &c->x86_model_id[48] ) + *q++ = '\0'; /* Zero-pad the rest */ + } + + return 1; +} + + +static void __init display_cacheinfo(struct cpuinfo_x86 *c) +{ + unsigned int n, dummy, ecx, edx, l2size; + + n = cpuid_eax(0x80000000); + + if (n >= 0x80000005) { + cpuid(0x80000005, &dummy, &dummy, &ecx, &edx); + printk(KERN_INFO "CPU: L1 I Cache: %dK (%d bytes/line), D cache %dK (%d bytes/line)\n", + edx>>24, edx&0xFF, ecx>>24, ecx&0xFF); + c->x86_cache_size=(ecx>>24)+(edx>>24); + } + + if (n < 0x80000006) /* Some chips just has a large L1. */ + return; + + ecx = cpuid_ecx(0x80000006); + l2size = ecx >> 16; + + /* AMD errata T13 (order #21922) */ + if (c->x86_vendor == X86_VENDOR_AMD && + c->x86 == 6 && + c->x86_model == 3 && + c->x86_mask == 0) { + l2size = 64; + } + + if ( l2size == 0 ) + return; /* Again, no L2 cache is possible */ + + c->x86_cache_size = l2size; + + printk(KERN_INFO "CPU: L2 Cache: %dK (%d bytes/line)\n", + l2size, ecx & 0xFF); +} + +/* + * B step AMD K6 before B 9730xxxx have hardware bugs that can cause + * misexecution of code under Linux. Owners of such processors should + * contact AMD for precise details and a CPU swap. + * + * See http://www.mygale.com/~poulot/k6bug.html + * http://www.amd.com/K6/k6docs/revgd.html + * + * The following test is erm.. interesting. AMD neglected to up + * the chip setting when fixing the bug but they also tweaked some + * performance at the same time.. + */ + +extern void vide(void); +__asm__(".align 4\nvide: ret"); + +static int __init init_amd(struct cpuinfo_x86 *c) +{ + u32 l, h; + int mbytes = max_mapnr >> (20-PAGE_SHIFT); + int r; + + /* + * FIXME: We should handle the K5 here. Set up the write + * range and also turn on MSR 83 bits 4 and 31 (wite alloc, + * no bus pipeline) + */ + + /* Bit 31 in normal CPUID used for nonstandard 3DNow ID; + 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway */ + clear_bit(0*32+31, &c->x86_capability); + + r = get_model_name(c); + + switch(c->x86) + { + case 5: + if( c->x86_model < 6 ) + { + /* Based on AMD doc 20734R - June 2000 */ + if ( c->x86_model == 0 ) { + clear_bit(X86_FEATURE_APIC, &c->x86_capability); + set_bit(X86_FEATURE_PGE, &c->x86_capability); + } + break; + } + + if ( c->x86_model == 6 && c->x86_mask == 1 ) { + const int K6_BUG_LOOP = 1000000; + int n; + void (*f_vide)(void); + unsigned long d, d2; + + printk(KERN_INFO "AMD K6 stepping B detected - "); + + /* + * It looks like AMD fixed the 2.6.2 bug and improved indirect + * calls at the same time. + */ + + n = K6_BUG_LOOP; + f_vide = vide; + rdtscl(d); + while (n--) + f_vide(); + rdtscl(d2); + d = d2-d; + + /* Knock these two lines out if it debugs out ok */ + printk(KERN_INFO "K6 BUG %ld %d (Report these if test report is incorrect)\n", d, 20*K6_BUG_LOOP); + printk(KERN_INFO "AMD K6 stepping B detected - "); + /* -- cut here -- */ + if (d > 20*K6_BUG_LOOP) + printk("system stability may be impaired when more than 32 MB are used.\n"); + else + printk("probably OK (after B9730xxxx).\n"); + printk(KERN_INFO "Please see http://www.mygale.com/~poulot/k6bug.html\n"); + } + + /* K6 with old style WHCR */ + if( c->x86_model < 8 || + (c->x86_model== 8 && c->x86_mask < 8)) + { + /* We can only write allocate on the low 508Mb */ + if(mbytes>508) + mbytes=508; + + rdmsr(0xC0000082, l, h); + if ((l&0x0000FFFF)==0) { + unsigned long flags; + l=(1<<0)|((mbytes/4)<<1); + local_irq_save(flags); + __asm__ __volatile__ ("wbinvd": : :"memory"); + wrmsr(0xC0000082, l, h); + local_irq_restore(flags); + printk(KERN_INFO "Enabling old style K6 write allocation for %d Mb\n", + mbytes); + + } + break; + } + if (c->x86_model == 8 || c->x86_model == 9 || c->x86_model == 13) + { + /* The more serious chips .. */ + + if(mbytes>4092) + mbytes=4092; + + rdmsr(0xC0000082, l, h); + if ((l&0xFFFF0000)==0) { + unsigned long flags; + l=((mbytes>>2)<<22)|(1<<16); + local_irq_save(flags); + __asm__ __volatile__ ("wbinvd": : :"memory"); + wrmsr(0xC0000082, l, h); + local_irq_restore(flags); + printk(KERN_INFO "Enabling new style K6 write allocation for %d Mb\n", + mbytes); + } + + /* Set MTRR capability flag if appropriate */ + if ( (c->x86_model == 13) || + (c->x86_model == 9) || + ((c->x86_model == 8) && + (c->x86_mask >= 8)) ) + set_bit(X86_FEATURE_K6_MTRR, &c->x86_capability); + break; + } + break; + + case 6: /* An Athlon/Duron. We can trust the BIOS probably */ + //mcheck_init(c); + break; + } + + display_cacheinfo(c); + return r; +} + +/* + * Read Cyrix DEVID registers (DIR) to get more detailed info. about the CPU + */ +static void do_cyrix_devid(unsigned char *dir0, unsigned char *dir1) +{ + unsigned char ccr2, ccr3; + unsigned long flags; + + /* we test for DEVID by checking whether CCR3 is writable */ + local_irq_save(flags); + ccr3 = getCx86(CX86_CCR3); + setCx86(CX86_CCR3, ccr3 ^ 0x80); + getCx86(0xc0); /* dummy to change bus */ + + if (getCx86(CX86_CCR3) == ccr3) { /* no DEVID regs. */ + ccr2 = getCx86(CX86_CCR2); + setCx86(CX86_CCR2, ccr2 ^ 0x04); + getCx86(0xc0); /* dummy */ + + if (getCx86(CX86_CCR2) == ccr2) /* old Cx486SLC/DLC */ + *dir0 = 0xfd; + else { /* Cx486S A step */ + setCx86(CX86_CCR2, ccr2); + *dir0 = 0xfe; + } + } + else { + setCx86(CX86_CCR3, ccr3); /* restore CCR3 */ + + /* read DIR0 and DIR1 CPU registers */ + *dir0 = getCx86(CX86_DIR0); + *dir1 = getCx86(CX86_DIR1); + } + local_irq_restore(flags); +} + +/* + * Cx86_dir0_msb is a HACK needed by check_cx686_cpuid/slop in bugs.h in + * order to identify the Cyrix CPU model after we're out of setup.c + * + * Actually since bugs.h doesnt even reference this perhaps someone should + * fix the documentation ??? + */ +unsigned char Cx86_dir0_msb __initdata = 0; + +static char Cx86_model[][9] __initdata = { + "Cx486", "Cx486", "5x86 ", "6x86", "MediaGX ", "6x86MX ", + "M II ", "Unknown" +}; +static char Cx486_name[][5] __initdata = { + "SLC", "DLC", "SLC2", "DLC2", "SRx", "DRx", + "SRx2", "DRx2" +}; +static char Cx486S_name[][4] __initdata = { + "S", "S2", "Se", "S2e" +}; +static char Cx486D_name[][4] __initdata = { + "DX", "DX2", "?", "?", "?", "DX4" +}; +static char Cx86_cb[] __initdata = "?.5x Core/Bus Clock"; +static char cyrix_model_mult1[] __initdata = "12??43"; +static char cyrix_model_mult2[] __initdata = "12233445"; + +/* + * Reset the slow-loop (SLOP) bit on the 686(L) which is set by some old + * BIOSes for compatability with DOS games. This makes the udelay loop + * work correctly, and improves performance. + * + * FIXME: our newer udelay uses the tsc. We dont need to frob with SLOP + */ + +extern void calibrate_delay(void) __init; + +static void __init check_cx686_slop(struct cpuinfo_x86 *c) +{ + unsigned long flags; + + if (Cx86_dir0_msb == 3) { + unsigned char ccr3, ccr5; + + local_irq_save(flags); + ccr3 = getCx86(CX86_CCR3); + setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ + ccr5 = getCx86(CX86_CCR5); + if (ccr5 & 2) + setCx86(CX86_CCR5, ccr5 & 0xfd); /* reset SLOP */ + setCx86(CX86_CCR3, ccr3); /* disable MAPEN */ + local_irq_restore(flags); + + if (ccr5 & 2) { /* possible wrong calibration done */ + printk(KERN_INFO "Recalibrating delay loop with SLOP bit reset\n"); + calibrate_delay(); + c->loops_per_jiffy = loops_per_jiffy; + } + } +} + +static void __init init_cyrix(struct cpuinfo_x86 *c) +{ + unsigned char dir0, dir0_msn, dir0_lsn, dir1 = 0; + char *buf = c->x86_model_id; + const char *p = NULL; + + /* Bit 31 in normal CPUID used for nonstandard 3DNow ID; + 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway */ + clear_bit(0*32+31, &c->x86_capability); + + /* Cyrix used bit 24 in extended (AMD) CPUID for Cyrix MMX extensions */ + if ( test_bit(1*32+24, &c->x86_capability) ) { + clear_bit(1*32+24, &c->x86_capability); + set_bit(X86_FEATURE_CXMMX, &c->x86_capability); + } + + do_cyrix_devid(&dir0, &dir1); + + check_cx686_slop(c); + + Cx86_dir0_msb = dir0_msn = dir0 >> 4; /* identifies CPU "family" */ + dir0_lsn = dir0 & 0xf; /* model or clock multiplier */ + + /* common case step number/rev -- exceptions handled below */ + c->x86_model = (dir1 >> 4) + 1; + c->x86_mask = dir1 & 0xf; + + /* Now cook; the original recipe is by Channing Corn, from Cyrix. + * We do the same thing for each generation: we work out + * the model, multiplier and stepping. Black magic included, + * to make the silicon step/rev numbers match the printed ones. + */ + + switch (dir0_msn) { + unsigned char tmp; + + case 0: /* Cx486SLC/DLC/SRx/DRx */ + p = Cx486_name[dir0_lsn & 7]; + break; + + case 1: /* Cx486S/DX/DX2/DX4 */ + p = (dir0_lsn & 8) ? Cx486D_name[dir0_lsn & 5] + : Cx486S_name[dir0_lsn & 3]; + break; + + case 2: /* 5x86 */ + Cx86_cb[2] = cyrix_model_mult1[dir0_lsn & 5]; + p = Cx86_cb+2; + break; + + case 3: /* 6x86/6x86L */ + Cx86_cb[1] = ' '; + Cx86_cb[2] = cyrix_model_mult1[dir0_lsn & 5]; + if (dir1 > 0x21) { /* 686L */ + Cx86_cb[0] = 'L'; + p = Cx86_cb; + (c->x86_model)++; + } else /* 686 */ + p = Cx86_cb+1; + /* Emulate MTRRs using Cyrix's ARRs. */ + set_bit(X86_FEATURE_CYRIX_ARR, &c->x86_capability); + /* 6x86's contain this bug */ + c->coma_bug = 1; + break; + + case 4: /* MediaGX/GXm */ + /* + * Life sometimes gets weiiiiiiiird if we use this + * on the MediaGX. So we turn it off for now. + */ + +#ifdef CONFIG_PCI + /* It isnt really a PCI quirk directly, but the cure is the + same. The MediaGX has deep magic SMM stuff that handles the + SB emulation. It thows away the fifo on disable_dma() which + is wrong and ruins the audio. + + Bug2: VSA1 has a wrap bug so that using maximum sized DMA + causes bad things. According to NatSemi VSA2 has another + bug to do with 'hlt'. I've not seen any boards using VSA2 + and X doesn't seem to support it either so who cares 8). + VSA1 we work around however. + */ + + printk(KERN_INFO "Working around Cyrix MediaGX virtual DMA bugs.\n"); + isa_dma_bridge_buggy = 2; +#endif + c->x86_cache_size=16; /* Yep 16K integrated cache thats it */ + + /* GXm supports extended cpuid levels 'ala' AMD */ + if (c->cpuid_level == 2) { + get_model_name(c); /* get CPU marketing name */ + clear_bit(X86_FEATURE_TSC, c->x86_capability); + return; + } + else { /* MediaGX */ + Cx86_cb[2] = (dir0_lsn & 1) ? '3' : '4'; + p = Cx86_cb+2; + c->x86_model = (dir1 & 0x20) ? 1 : 2; + clear_bit(X86_FEATURE_TSC, &c->x86_capability); + } + break; + + case 5: /* 6x86MX/M II */ + if (dir1 > 7) + { + dir0_msn++; /* M II */ + /* Enable MMX extensions (App note 108) */ + setCx86(CX86_CCR7, getCx86(CX86_CCR7)|1); + } + else + { + c->coma_bug = 1; /* 6x86MX, it has the bug. */ + } + tmp = (!(dir0_lsn & 7) || dir0_lsn & 1) ? 2 : 0; + Cx86_cb[tmp] = cyrix_model_mult2[dir0_lsn & 7]; + p = Cx86_cb+tmp; + if (((dir1 & 0x0f) > 4) || ((dir1 & 0xf0) == 0x20)) + (c->x86_model)++; + /* Emulate MTRRs using Cyrix's ARRs. */ + set_bit(X86_FEATURE_CYRIX_ARR, &c->x86_capability); + break; + + case 0xf: /* Cyrix 486 without DEVID registers */ + switch (dir0_lsn) { + case 0xd: /* either a 486SLC or DLC w/o DEVID */ + dir0_msn = 0; + p = Cx486_name[(c->hard_math) ? 1 : 0]; + break; + + case 0xe: /* a 486S A step */ + dir0_msn = 0; + p = Cx486S_name[0]; + break; + } + break; + + default: /* unknown (shouldn't happen, we know everyone ;-) */ + dir0_msn = 7; + break; + } + strcpy(buf, Cx86_model[dir0_msn & 7]); + if (p) strcat(buf, p); + return; +} + +#ifdef CONFIG_X86_OOSTORE + +static u32 __init power2(u32 x) +{ + u32 s=1; + while(s<=x) + s<<=1; + return s>>=1; +} + +/* + * Set up an actual MCR + */ + +static void __init winchip_mcr_insert(int reg, u32 base, u32 size, int key) +{ + u32 lo, hi; + + hi = base & ~0xFFF; + lo = ~(size-1); /* Size is a power of 2 so this makes a mask */ + lo &= ~0xFFF; /* Remove the ctrl value bits */ + lo |= key; /* Attribute we wish to set */ + wrmsr(reg+0x110, lo, hi); + mtrr_centaur_report_mcr(reg, lo, hi); /* Tell the mtrr driver */ +} + +/* + * Figure what we can cover with MCR's + * + * Shortcut: We know you can't put 4Gig of RAM on a winchip + */ + +static u32 __init ramtop(void) /* 16388 */ +{ + int i; + u32 top = 0; + u32 clip = 0xFFFFFFFFUL; + + for (i = 0; i < e820.nr_map; i++) { + unsigned long start, end; + + if (e820.map[i].addr > 0xFFFFFFFFUL) + continue; + /* + * Don't MCR over reserved space. Ignore the ISA hole + * we frob around that catastrophy already + */ + + if (e820.map[i].type == E820_RESERVED) + { + if(e820.map[i].addr >= 0x100000UL && e820.map[i].addr < clip) + clip = e820.map[i].addr; + continue; + } + start = e820.map[i].addr; + end = e820.map[i].addr + e820.map[i].size; + if (start >= end) + continue; + if (end > top) + top = end; + } + /* Everything below 'top' should be RAM except for the ISA hole. + Because of the limited MCR's we want to map NV/ACPI into our + MCR range for gunk in RAM + + Clip might cause us to MCR insufficient RAM but that is an + acceptable failure mode and should only bite obscure boxes with + a VESA hole at 15Mb + + The second case Clip sometimes kicks in is when the EBDA is marked + as reserved. Again we fail safe with reasonable results + */ + + if(top>clip) + top=clip; + + return top; +} + +/* + * Compute a set of MCR's to give maximum coverage + */ + +static int __init winchip_mcr_compute(int nr, int key) +{ + u32 mem = ramtop(); + u32 root = power2(mem); + u32 base = root; + u32 top = root; + u32 floor = 0; + int ct = 0; + + while(ct high && fspace > low) + { + winchip_mcr_insert(ct, floor, fspace, key); + floor += fspace; + } + else if(high > low) + { + winchip_mcr_insert(ct, top, high, key); + top += high; + } + else if(low > 0) + { + base -= low; + winchip_mcr_insert(ct, base, low, key); + } + else break; + ct++; + } + /* + * We loaded ct values. We now need to set the mask. The caller + * must do this bit. + */ + + return ct; +} + +static void __init winchip_create_optimal_mcr(void) +{ + int i; + /* + * Allocate up to 6 mcrs to mark as much of ram as possible + * as write combining and weak write ordered. + * + * To experiment with: Linux never uses stack operations for + * mmio spaces so we could globally enable stack operation wc + * + * Load the registers with type 31 - full write combining, all + * writes weakly ordered. + */ + int used = winchip_mcr_compute(6, 31); + + /* + * Wipe unused MCRs + */ + + for(i=used;i<8;i++) + wrmsr(0x110+i, 0, 0); +} + +static void __init winchip2_create_optimal_mcr(void) +{ + u32 lo, hi; + int i; + + /* + * Allocate up to 6 mcrs to mark as much of ram as possible + * as write combining, weak store ordered. + * + * Load the registers with type 25 + * 8 - weak write ordering + * 16 - weak read ordering + * 1 - write combining + */ + + int used = winchip_mcr_compute(6, 25); + + /* + * Mark the registers we are using. + */ + + rdmsr(0x120, lo, hi); + for(i=0;i>17) & 7; + lo |= key<<6; /* replace with unlock key */ + wrmsr(0x120, lo, hi); +} + +static void __init winchip2_protect_mcr(void) +{ + u32 lo, hi; + + rdmsr(0x120, lo, hi); + lo&=~0x1C0; /* blank bits 8-6 */ + wrmsr(0x120, lo, hi); +} + +#endif + +static void __init init_centaur(struct cpuinfo_x86 *c) +{ + enum { + ECX8=1<<1, + EIERRINT=1<<2, + DPM=1<<3, + DMCE=1<<4, + DSTPCLK=1<<5, + ELINEAR=1<<6, + DSMC=1<<7, + DTLOCK=1<<8, + EDCTLB=1<<8, + EMMX=1<<9, + DPDC=1<<11, + EBRPRED=1<<12, + DIC=1<<13, + DDC=1<<14, + DNA=1<<15, + ERETSTK=1<<16, + E2MMX=1<<19, + EAMD3D=1<<20, + }; + + char *name; + u32 fcr_set=0; + u32 fcr_clr=0; + u32 lo,hi,newlo; + u32 aa,bb,cc,dd; + + /* Bit 31 in normal CPUID used for nonstandard 3DNow ID; + 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway */ + clear_bit(0*32+31, &c->x86_capability); + + switch (c->x86) { + + case 5: + switch(c->x86_model) { + case 4: + name="C6"; + fcr_set=ECX8|DSMC|EDCTLB|EMMX|ERETSTK; + fcr_clr=DPDC; + printk(KERN_NOTICE "Disabling bugged TSC.\n"); + clear_bit(X86_FEATURE_TSC, &c->x86_capability); +#ifdef CONFIG_X86_OOSTORE + winchip_create_optimal_mcr(); + /* Enable + write combining on non-stack, non-string + write combining on string, all types + weak write ordering + + The C6 original lacks weak read order + + Note 0x120 is write only on Winchip 1 */ + + wrmsr(0x120, 0x01F0001F, 0); +#endif + break; + case 8: + switch(c->x86_mask) { + default: + name="2"; + break; + case 7 ... 9: + name="2A"; + break; + case 10 ... 15: + name="2B"; + break; + } + fcr_set=ECX8|DSMC|DTLOCK|EMMX|EBRPRED|ERETSTK|E2MMX|EAMD3D; + fcr_clr=DPDC; +#ifdef CONFIG_X86_OOSTORE + winchip2_unprotect_mcr(); + winchip2_create_optimal_mcr(); + rdmsr(0x120, lo, hi); + /* Enable + write combining on non-stack, non-string + write combining on string, all types + weak write ordering + */ + lo|=31; + wrmsr(0x120, lo, hi); + winchip2_protect_mcr(); +#endif + break; + case 9: + name="3"; + fcr_set=ECX8|DSMC|DTLOCK|EMMX|EBRPRED|ERETSTK|E2MMX|EAMD3D; + fcr_clr=DPDC; +#ifdef CONFIG_X86_OOSTORE + winchip2_unprotect_mcr(); + winchip2_create_optimal_mcr(); + rdmsr(0x120, lo, hi); + /* Enable + write combining on non-stack, non-string + write combining on string, all types + weak write ordering + */ + lo|=31; + wrmsr(0x120, lo, hi); + winchip2_protect_mcr(); +#endif + break; + case 10: + name="4"; + /* no info on the WC4 yet */ + break; + default: + name="??"; + } + + /* get FCR */ + rdmsr(0x107, lo, hi); + + newlo=(lo|fcr_set) & (~fcr_clr); + + if (newlo!=lo) { + printk(KERN_INFO "Centaur FCR was 0x%X now 0x%X\n", lo, newlo ); + wrmsr(0x107, newlo, hi ); + } else { + printk(KERN_INFO "Centaur FCR is 0x%X\n",lo); + } + /* Emulate MTRRs using Centaur's MCR. */ + set_bit(X86_FEATURE_CENTAUR_MCR, &c->x86_capability); + /* Report CX8 */ + set_bit(X86_FEATURE_CX8, &c->x86_capability); + /* Set 3DNow! on Winchip 2 and above. */ + if (c->x86_model >=8) + set_bit(X86_FEATURE_3DNOW, &c->x86_capability); + /* See if we can find out some more. */ + if ( cpuid_eax(0x80000000) >= 0x80000005 ) { + /* Yes, we can. */ + cpuid(0x80000005,&aa,&bb,&cc,&dd); + /* Add L1 data and code cache sizes. */ + c->x86_cache_size = (cc>>24)+(dd>>24); + } + sprintf( c->x86_model_id, "WinChip %s", name ); + //mcheck_init(c); + break; + + case 6: + switch (c->x86_model) { + case 6 ... 7: /* Cyrix III or C3 */ + rdmsr (0x1107, lo, hi); + lo |= (1<<1 | 1<<7); /* Report CX8 & enable PGE */ + wrmsr (0x1107, lo, hi); + + set_bit(X86_FEATURE_CX8, &c->x86_capability); + set_bit(X86_FEATURE_3DNOW, &c->x86_capability); + + get_model_name(c); + display_cacheinfo(c); + + rdmsr(0x2A, lo, hi); + interpret_eblcr(c, lo, 0); + break; + } + break; + } +} + + +static void __init init_transmeta(struct cpuinfo_x86 *c) +{ + unsigned int cap_mask, uk, max, dummy; + unsigned int cms_rev1, cms_rev2; + unsigned int cpu_rev, cpu_freq, cpu_flags; + char cpu_info[65]; + + get_model_name(c); /* Same as AMD/Cyrix */ + display_cacheinfo(c); + + /* Print CMS and CPU revision */ + max = cpuid_eax(0x80860000); + if ( max >= 0x80860001 ) { + cpuid(0x80860001, &dummy, &cpu_rev, &cpu_freq, &cpu_flags); + printk(KERN_INFO "CPU: Processor revision %u.%u.%u.%u, %u MHz\n", + (cpu_rev >> 24) & 0xff, + (cpu_rev >> 16) & 0xff, + (cpu_rev >> 8) & 0xff, + cpu_rev & 0xff, + cpu_freq); + } + if ( max >= 0x80860002 ) { + cpuid(0x80860002, &dummy, &cms_rev1, &cms_rev2, &dummy); + printk(KERN_INFO "CPU: Code Morphing Software revision %u.%u.%u-%u-%u\n", + (cms_rev1 >> 24) & 0xff, + (cms_rev1 >> 16) & 0xff, + (cms_rev1 >> 8) & 0xff, + cms_rev1 & 0xff, + cms_rev2); + } + if ( max >= 0x80860006 ) { + cpuid(0x80860003, + (void *)&cpu_info[0], + (void *)&cpu_info[4], + (void *)&cpu_info[8], + (void *)&cpu_info[12]); + cpuid(0x80860004, + (void *)&cpu_info[16], + (void *)&cpu_info[20], + (void *)&cpu_info[24], + (void *)&cpu_info[28]); + cpuid(0x80860005, + (void *)&cpu_info[32], + (void *)&cpu_info[36], + (void *)&cpu_info[40], + (void *)&cpu_info[44]); + cpuid(0x80860006, + (void *)&cpu_info[48], + (void *)&cpu_info[52], + (void *)&cpu_info[56], + (void *)&cpu_info[60]); + cpu_info[64] = '\0'; + printk(KERN_INFO "CPU: %s\n", cpu_info); + } + + /* Unhide possibly hidden capability flags */ + rdmsr(0x80860004, cap_mask, uk); + wrmsr(0x80860004, ~0, uk); + c->x86_capability[0] = cpuid_edx(0x00000001); + wrmsr(0x80860004, cap_mask, uk); +} + + +static void __init init_rise(struct cpuinfo_x86 *c) +{ + printk("CPU: Rise iDragon"); + if (c->x86_model > 2) + printk(" II"); + printk("\n"); + + /* Unhide possibly hidden capability flags + The mp6 iDragon family don't have MSRs. + We switch on extra features with this cpuid weirdness: */ + __asm__ ( + "movl $0x6363452a, %%eax\n\t" + "movl $0x3231206c, %%ecx\n\t" + "movl $0x2a32313a, %%edx\n\t" + "cpuid\n\t" + "movl $0x63634523, %%eax\n\t" + "movl $0x32315f6c, %%ecx\n\t" + "movl $0x2333313a, %%edx\n\t" + "cpuid\n\t" : : : "eax", "ebx", "ecx", "edx" + ); + set_bit(X86_FEATURE_CX8, &c->x86_capability); +} + + +extern void trap_init_f00f_bug(void); + +static void __init init_intel(struct cpuinfo_x86 *c) +{ +#if !defined(CONFIG_M686) && !defined(CONFIG_MK8) + static int f00f_workaround_enabled = 0; +#endif +#ifdef CONFIG_MCA + extern void mcheck_init(struct cpuinfo_x86 *c); +#endif + char *p = NULL; + unsigned int l1i = 0, l1d = 0, l2 = 0, l3 = 0; /* Cache sizes */ + +#if !defined(CONFIG_M686) && !defined(CONFIG_MK8) + /* + * All current models of Pentium and Pentium with MMX technology CPUs + * have the F0 0F bug, which lets nonpriviledged users lock up the system. + * Note that the workaround only should be initialized once... + */ + c->f00f_bug = 0; + if ( c->x86 == 5 ) { + c->f00f_bug = 1; + if ( !f00f_workaround_enabled ) { + trap_init_f00f_bug(); + printk(KERN_NOTICE "Intel Pentium with F0 0F bug - workaround enabled.\n"); + f00f_workaround_enabled = 1; + } + } +#endif + + + if (c->cpuid_level > 1) { + /* supports eax=2 call */ + int i, j, n; + int regs[4]; + unsigned char *dp = (unsigned char *)regs; + + /* Number of times to iterate */ + n = cpuid_eax(2) & 0xFF; + + for ( i = 0 ; i < n ; i++ ) { + cpuid(2, ®s[0], ®s[1], ®s[2], ®s[3]); + + /* If bit 31 is set, this is an unknown format */ + for ( j = 0 ; j < 3 ; j++ ) { + if ( regs[j] < 0 ) regs[j] = 0; + } + + /* Byte 0 is level count, not a descriptor */ + for ( j = 1 ; j < 16 ; j++ ) { + unsigned char des = dp[j]; + unsigned char dl, dh; + unsigned int cs; + + dh = des >> 4; + dl = des & 0x0F; + + /* Black magic... */ + + switch ( dh ) + { + case 0: + switch ( dl ) { + case 6: + /* L1 I cache */ + l1i += 8; + break; + case 8: + /* L1 I cache */ + l1i += 16; + break; + case 10: + /* L1 D cache */ + l1d += 8; + break; + case 12: + /* L1 D cache */ + l1d += 16; + break; + default:; + /* TLB, or unknown */ + } + break; + case 2: + if ( dl ) { + /* L3 cache */ + cs = (dl-1) << 9; + l3 += cs; + } + break; + case 4: + if ( c->x86 > 6 && dl ) { + /* P4 family */ + /* L3 cache */ + cs = 128 << (dl-1); + l3 += cs; + break; + } + /* else same as 8 - fall through */ + case 8: + if ( dl ) { + /* L2 cache */ + cs = 128 << (dl-1); + l2 += cs; + } + break; + case 6: + if (dl > 5) { + /* L1 D cache */ + cs = 8<<(dl-6); + l1d += cs; + } + break; + case 7: + if ( dl >= 8 ) + { + /* L2 cache */ + cs = 64<<(dl-8); + l2 += cs; + } else { + /* L0 I cache, count as L1 */ + cs = dl ? (16 << (dl-1)) : 12; + l1i += cs; + } + break; + default: + /* TLB, or something else we don't know about */ + break; + } + } + } + if ( l1i || l1d ) + printk(KERN_INFO "CPU: L1 I cache: %dK, L1 D cache: %dK\n", + l1i, l1d); + if ( l2 ) + printk(KERN_INFO "CPU: L2 cache: %dK\n", l2); + if ( l3 ) + printk(KERN_INFO "CPU: L3 cache: %dK\n", l3); + + /* + * This assumes the L3 cache is shared; it typically lives in + * the northbridge. The L1 caches are included by the L2 + * cache, and so should not be included for the purpose of + * SMP switching weights. + */ + c->x86_cache_size = l2 ? l2 : (l1i+l1d); + } + + /* SEP CPUID bug: Pentium Pro reports SEP but doesn't have it */ + if ( c->x86 == 6 && c->x86_model < 3 && c->x86_mask < 3 ) + clear_bit(X86_FEATURE_SEP, &c->x86_capability); + + if ( c->x86 == 6 && c->x86_model >= 3) + { + u32 lo, hi; + rdmsr(0x2A, lo, hi); + interpret_eblcr(c, lo, (c->x86_model >= 7) ? 1: 0); + } + + /* Names for the Pentium II/Celeron processors + detectable only by also checking the cache size. + Dixon is NOT a Celeron. */ + if (c->x86 == 6) { + switch (c->x86_model) { + case 5: + if (l2 == 0) + p = "Celeron (Covington)"; + if (l2 == 256) + p = "Mobile Pentium II (Dixon)"; + break; + + case 6: + if (l2 == 128) + p = "Celeron (Mendocino)"; + break; + + case 8: + if (l2 == 128) + p = "Celeron (Coppermine)"; + break; + } + } + + if ( p ) + strcpy(c->x86_model_id, p); + +#ifdef CONFIG_MCA + /* Enable MCA if available */ + mcheck_init(c); +#endif +} + +void __init get_cpu_vendor(struct cpuinfo_x86 *c) +{ + char *v = c->x86_vendor_id; + + if (!strcmp(v, "GenuineIntel")) + c->x86_vendor = X86_VENDOR_INTEL; + else if (!strcmp(v, "AuthenticAMD")) + c->x86_vendor = X86_VENDOR_AMD; + else if (!strcmp(v, "CyrixInstead")) + c->x86_vendor = X86_VENDOR_CYRIX; + else if (!strcmp(v, "UMC UMC UMC ")) + c->x86_vendor = X86_VENDOR_UMC; + else if (!strcmp(v, "CentaurHauls")) + c->x86_vendor = X86_VENDOR_CENTAUR; + else if (!strcmp(v, "NexGenDriven")) + c->x86_vendor = X86_VENDOR_NEXGEN; + else if (!strcmp(v, "RiseRiseRise")) + c->x86_vendor = X86_VENDOR_RISE; + else if (!strcmp(v, "GenuineTMx86") || + !strcmp(v, "TransmetaCPU")) + c->x86_vendor = X86_VENDOR_TRANSMETA; + else + c->x86_vendor = X86_VENDOR_UNKNOWN; +} + +struct cpu_model_info { + int vendor; + int family; + char *model_names[16]; +}; + +/* Naming convention should be: [()] */ +/* This table only is used unless init_() below doesn't set it; */ +/* in particular, if CPUID levels 0x80000002..4 are supported, this isn't used */ +static struct cpu_model_info cpu_models[] __initdata = { + { X86_VENDOR_INTEL, 4, + { "486 DX-25/33", "486 DX-50", "486 SX", "486 DX/2", "486 SL", + "486 SX/2", NULL, "486 DX/2-WB", "486 DX/4", "486 DX/4-WB", NULL, + NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_INTEL, 5, + { "Pentium 60/66 A-step", "Pentium 60/66", "Pentium 75 - 200", + "OverDrive PODP5V83", "Pentium MMX", NULL, NULL, + "Mobile Pentium 75 - 200", "Mobile Pentium MMX", NULL, NULL, NULL, + NULL, NULL, NULL, NULL }}, + { X86_VENDOR_INTEL, 6, + { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)", + NULL, "Pentium II (Deschutes)", "Mobile Pentium II", + "Pentium III (Katmai)", "Pentium III (Coppermine)", NULL, + "Pentium III (Cascades)", NULL, NULL, NULL, NULL }}, + { X86_VENDOR_AMD, 4, + { NULL, NULL, NULL, "486 DX/2", NULL, NULL, NULL, "486 DX/2-WB", + "486 DX/4", "486 DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", + "Am5x86-WB" }}, + { X86_VENDOR_AMD, 5, /* Is this this really necessary?? */ + { "K5/SSA5", "K5", + "K5", "K5", NULL, NULL, + "K6", "K6", "K6-2", + "K6-3", NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_AMD, 6, /* Is this this really necessary?? */ + { "Athlon", "Athlon", + "Athlon", NULL, "Athlon", NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_UMC, 4, + { NULL, "U5D", "U5S", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_NEXGEN, 5, + { "Nx586", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_RISE, 5, + { "iDragon", NULL, "iDragon", NULL, NULL, NULL, NULL, + NULL, "iDragon II", "iDragon II", NULL, NULL, NULL, NULL, NULL, NULL }}, +}; + +/* Look up CPU names by table lookup. */ +static char __init *table_lookup_model(struct cpuinfo_x86 *c) +{ + struct cpu_model_info *info = cpu_models; + int i; + + if ( c->x86_model >= 16 ) + return NULL; /* Range check */ + + for ( i = 0 ; i < sizeof(cpu_models)/sizeof(struct cpu_model_info) ; i++ ) { + if ( info->vendor == c->x86_vendor && + info->family == c->x86 ) { + return info->model_names[c->x86_model]; + } + info++; + } + return NULL; /* Not found */ +} + +/* + * Detect a NexGen CPU running without BIOS hypercode new enough + * to have CPUID. (Thanks to Herbert Oppmann) + */ + +static int __init deep_magic_nexgen_probe(void) +{ + int ret; + + __asm__ __volatile__ ( + " movw $0x5555, %%ax\n" + " xorw %%dx,%%dx\n" + " movw $2, %%cx\n" + " divw %%cx\n" + " movl $0, %%eax\n" + " jnz 1f\n" + " movl $1, %%eax\n" + "1:\n" + : "=a" (ret) : : "cx", "dx" ); + return ret; +} + +static void __init squash_the_stupid_serial_number(struct cpuinfo_x86 *c) +{ + if( test_bit(X86_FEATURE_PN, &c->x86_capability) && + disable_x86_serial_nr ) { + /* Disable processor serial number */ + unsigned long lo,hi; + rdmsr(0x119,lo,hi); + lo |= 0x200000; + wrmsr(0x119,lo,hi); + printk(KERN_NOTICE "CPU serial number disabled.\n"); + clear_bit(X86_FEATURE_PN, &c->x86_capability); + + /* Disabling the serial number may affect the cpuid level */ + c->cpuid_level = cpuid_eax(0); + } +} + + +int __init x86_serial_nr_setup(char *s) +{ + disable_x86_serial_nr = 0; + return 1; +} +__setup("serialnumber", x86_serial_nr_setup); + +int __init x86_fxsr_setup(char * s) +{ + disable_x86_fxsr = 1; + return 1; +} +__setup("nofxsr", x86_fxsr_setup); + + +/* Standard macro to see if a specific flag is changeable */ +static inline int flag_is_changeable_p(unsigned long flag) +{ + unsigned long f1, f2; + + asm("pushf\n\t" + "pushf\n\t" + "pop %0\n\t" + "mov %0,%1\n\t" + "xor %2,%0\n\t" + "push %0\n\t" + "popf\n\t" + "pushf\n\t" + "pop %0\n\t" + "popf\n\t" + : "=&r" (f1), "=&r" (f2) + : "ir" (flag)); + + return ((f1^f2) & flag) != 0; +} + + +/* Probe for the CPUID instruction */ +static int __init have_cpuid_p(void) +{ + return flag_is_changeable_p(X86_EFLAGS_ID); +} + +/* + * Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected + * by the fact that they preserve the flags across the division of 5/2. + * PII and PPro exhibit this behavior too, but they have cpuid available. + */ + +/* + * Perform the Cyrix 5/2 test. A Cyrix won't change + * the flags, while other 486 chips will. + */ +static inline int test_cyrix_52div(void) +{ + unsigned long test; + + __asm__ __volatile__( + "push %1\n\t" + "popf\n\t" + "div %b2\n\t" /* divide 5 by 2 */ + "pushf\n\t" + "pop %0" + : "=a" (test) + : "0" (5UL), "q" (2) + : "cc"); + + /* AH is 0x02 on Cyrix after the divide.. */ + return (unsigned char) (test >> 8) == 0x02; +} + +/* Try to detect a CPU with disabled CPUID, and if so, enable. This routine + may also be used to detect non-CPUID processors and fill in some of + the information manually. */ +static int __init id_and_try_enable_cpuid(struct cpuinfo_x86 *c) +{ + /* First of all, decide if this is a 486 or higher */ + /* It's a 486 if we can modify the AC flag */ + if ( flag_is_changeable_p(X86_EFLAGS_AC) ) + c->x86 = 4; + else + c->x86 = 3; + + /* Detect Cyrix with disabled CPUID */ + if ( c->x86 == 4 && test_cyrix_52div() ) { + unsigned char dir0, dir1; + + strcpy(c->x86_vendor_id, "CyrixInstead"); + c->x86_vendor = X86_VENDOR_CYRIX; + + /* Actually enable cpuid on the older cyrix */ + + /* Retrieve CPU revisions */ + + do_cyrix_devid(&dir0, &dir1); + + dir0>>=4; + + /* Check it is an affected model */ + + if (dir0 == 5 || dir0 == 3) + { + unsigned char ccr3, ccr4; + unsigned long flags; + printk(KERN_INFO "Enabling CPUID on Cyrix processor.\n"); + local_irq_save(flags); + ccr3 = getCx86(CX86_CCR3); + setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ + ccr4 = getCx86(CX86_CCR4); + setCx86(CX86_CCR4, ccr4 | 0x80); /* enable cpuid */ + setCx86(CX86_CCR3, ccr3); /* disable MAPEN */ + local_irq_restore(flags); + } + } else + + /* Detect NexGen with old hypercode */ + if ( deep_magic_nexgen_probe() ) { + strcpy(c->x86_vendor_id, "NexGenDriven"); + } + + return have_cpuid_p(); /* Check to see if CPUID now enabled? */ +} + +/* + * This does the hard work of actually picking apart the CPU stuff... + */ +void __init identify_cpu(struct cpuinfo_x86 *c) +{ + int junk, i; + u32 xlvl, tfms; + + c->loops_per_jiffy = loops_per_jiffy; + c->x86_cache_size = -1; + c->x86_vendor = X86_VENDOR_UNKNOWN; + c->cpuid_level = -1; /* CPUID not detected */ + c->x86_model = c->x86_mask = 0; /* So far unknown... */ + c->x86_vendor_id[0] = '\0'; /* Unset */ + c->x86_model_id[0] = '\0'; /* Unset */ + memset(&c->x86_capability, 0, sizeof c->x86_capability); + + if ( !have_cpuid_p() && !id_and_try_enable_cpuid(c) ) { + /* CPU doesn't have CPUID */ + + /* If there are any capabilities, they're vendor-specific */ + /* enable_cpuid() would have set c->x86 for us. */ + } else { + /* CPU does have CPUID */ + + /* Get vendor name */ + cpuid(0x00000000, &c->cpuid_level, + (int *)&c->x86_vendor_id[0], + (int *)&c->x86_vendor_id[8], + (int *)&c->x86_vendor_id[4]); + + get_cpu_vendor(c); + /* Initialize the standard set of capabilities */ + /* Note that the vendor-specific code below might override */ + + /* Intel-defined flags: level 0x00000001 */ + if ( c->cpuid_level >= 0x00000001 ) { + cpuid(0x00000001, &tfms, &junk, &junk, + &c->x86_capability[0]); + c->x86 = (tfms >> 8) & 15; + c->x86_model = (tfms >> 4) & 15; + c->x86_mask = tfms & 15; + } else { + /* Have CPUID level 0 only - unheard of */ + c->x86 = 4; + } + + /* AMD-defined flags: level 0x80000001 */ + xlvl = cpuid_eax(0x80000000); + if ( (xlvl & 0xffff0000) == 0x80000000 ) { + if ( xlvl >= 0x80000001 ) + c->x86_capability[1] = cpuid_edx(0x80000001); + if ( xlvl >= 0x80000004 ) + get_model_name(c); /* Default name */ + } + + /* Transmeta-defined flags: level 0x80860001 */ + xlvl = cpuid_eax(0x80860000); + if ( (xlvl & 0xffff0000) == 0x80860000 ) { + if ( xlvl >= 0x80860001 ) + c->x86_capability[2] = cpuid_edx(0x80860001); + } + } + + printk(KERN_DEBUG "CPU: Before vendor init, caps: %08x %08x %08x, vendor = %d\n", + c->x86_capability[0], + c->x86_capability[1], + c->x86_capability[2], + c->x86_vendor); + + /* + * Vendor-specific initialization. In this section we + * canonicalize the feature flags, meaning if there are + * features a certain CPU supports which CPUID doesn't + * tell us, CPUID claiming incorrect flags, or other bugs, + * we handle them here. + * + * At the end of this section, c->x86_capability better + * indicate the features this CPU genuinely supports! + */ + switch ( c->x86_vendor ) { + case X86_VENDOR_UNKNOWN: + default: + /* Not much we can do here... */ + /* Check if at least it has cpuid */ + if (c->cpuid_level == -1) + { + /* No cpuid. It must be an ancient CPU */ + if (c->x86 == 4) + strcpy(c->x86_model_id, "486"); + else if (c->x86 == 3) + strcpy(c->x86_model_id, "386"); + } + break; + + case X86_VENDOR_CYRIX: + init_cyrix(c); + break; + + case X86_VENDOR_AMD: + init_amd(c); + break; + + case X86_VENDOR_CENTAUR: + init_centaur(c); + break; + + case X86_VENDOR_INTEL: + init_intel(c); + break; + + case X86_VENDOR_NEXGEN: + c->x86_cache_size = 256; /* A few had 1 MB... */ + break; + + case X86_VENDOR_TRANSMETA: + init_transmeta(c); + break; + + case X86_VENDOR_RISE: + init_rise(c); + break; + } + + printk(KERN_DEBUG "CPU: After vendor init, caps: %08x %08x %08x %08x\n", + c->x86_capability[0], + c->x86_capability[1], + c->x86_capability[2], + c->x86_capability[3]); + + /* + * The vendor-specific functions might have changed features. Now + * we do "generic changes." + */ + + /* TSC disabled? */ +#ifndef CONFIG_X86_TSC + if ( tsc_disable ) + clear_bit(X86_FEATURE_TSC, &c->x86_capability); +#endif + + /* FXSR disabled? */ + if (disable_x86_fxsr) { + clear_bit(X86_FEATURE_FXSR, &c->x86_capability); + clear_bit(X86_FEATURE_XMM, &c->x86_capability); + } + + /* Disable the PN if appropriate */ + squash_the_stupid_serial_number(c); + + /* If the model name is still unset, do table lookup. */ + if ( !c->x86_model_id[0] ) { + char *p; + p = table_lookup_model(c); + if ( p ) + strcpy(c->x86_model_id, p); + else + /* Last resort... */ + sprintf(c->x86_model_id, "%02x/%02x", + c->x86_vendor, c->x86_model); + } + + /* Now the feature flags better reflect actual CPU features! */ + + printk(KERN_DEBUG "CPU: After generic, caps: %08x %08x %08x %08x\n", + c->x86_capability[0], + c->x86_capability[1], + c->x86_capability[2], + c->x86_capability[3]); + + /* + * On SMP, boot_cpu_data holds the common feature set between + * all CPUs; so make sure that we indicate which features are + * common between the CPUs. The first time this routine gets + * executed, c == &boot_cpu_data. + */ + if ( c != &boot_cpu_data ) { + /* AND the already accumulated flags with these */ + for ( i = 0 ; i < NCAPINTS ; i++ ) + boot_cpu_data.x86_capability[i] &= c->x86_capability[i]; + } + + printk(KERN_DEBUG "CPU: Common caps: %08x %08x %08x %08x\n", + boot_cpu_data.x86_capability[0], + boot_cpu_data.x86_capability[1], + boot_cpu_data.x86_capability[2], + boot_cpu_data.x86_capability[3]); +} +/* + * Perform early boot up checks for a valid TSC. See arch/i386/kernel/time.c + */ + +void __init dodgy_tsc(void) +{ + get_cpu_vendor(&boot_cpu_data); + + if ( boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX ) + init_cyrix(&boot_cpu_data); +} + + +/* These need to match */ +static char *cpu_vendor_names[] __initdata = { + "Intel", "Cyrix", "AMD", "UMC", "NexGen", "Centaur", "Rise", "Transmeta" }; + + +void __init print_cpu_info(struct cpuinfo_x86 *c) +{ + char *vendor = NULL; + + if (c->x86_vendor < sizeof(cpu_vendor_names)/sizeof(char *)) + vendor = cpu_vendor_names[c->x86_vendor]; + else if (c->cpuid_level >= 0) + vendor = c->x86_vendor_id; + + if (vendor && strncmp(c->x86_model_id, vendor, strlen(vendor))) + printk("%s ", vendor); + + if (!c->x86_model_id[0]) + printk("%d86", c->x86); + else + printk("%s", c->x86_model_id); + + if (c->x86_mask || c->cpuid_level >= 0) + printk(" stepping %02x\n", c->x86_mask); + else + printk("\n"); +} + +/* + * get_cpuinfo - Get information on one CPU for use by procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. + */ + +int get_cpuinfo(char *buffer, unsigned *cpu_np) +{ + /* + * These flag bits must match the definitions in . + * NULL means this bit is undefined or reserved; either way it doesn't + * have meaning as far as Linux is concerned. Note that it's important + * to realize there is a difference between this table and CPUID -- if + * applications want to get the raw CPUID data, they should access + * /dev/cpu//cpuid instead. + */ + static char *x86_cap_flags[] = { + /* Intel-defined */ + "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", + "cx8", "apic", NULL, "sep", "mtrr", "pge", "mca", "cmov", + "pat", "pse36", "pn", "clflush", NULL, "dts", "acpi", "mmx", + "fxsr", "sse", "sse2", "ss", NULL, "tm", "ia64", NULL, + + /* AMD-defined */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "syscall", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, "mmxext", NULL, + NULL, NULL, NULL, NULL, NULL, "lm", "3dnowext", "3dnow", + + /* Transmeta-defined */ + "recovery", "longrun", NULL, "lrti", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + /* Other (Linux-defined) */ + "cxmmx", "k6_mtrr", "cyrix_arr", "centaur_mcr", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + }; + char *p = buffer; + struct cpuinfo_x86 *c; + int fpu_exception; + int i; + unsigned n; + + n = *cpu_np; + while (n < NR_CPUS && (cpu_online_map & (1 << n)) == 0) { + ++n; + } + if (n >= NR_CPUS) { + *cpu_np = NR_CPUS; + return (0); + } + *cpu_np = n + 1; + c = cpu_data + n; + + p += sprintf(p,"processor\t: %u\n" + "vendor_id\t: %s\n" + "cpu family\t: %d\n" + "model\t\t: %d\n" + "model name\t: %s\n", + n, + c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown", + c->x86, + c->x86_model, + c->x86_model_id[0] ? c->x86_model_id : "unknown"); + + if (c->x86_mask || c->cpuid_level >= 0) + p += sprintf(p, "stepping\t: %d\n", c->x86_mask); + else + p += sprintf(p, "stepping\t: unknown\n"); + + if ( test_bit(X86_FEATURE_TSC, &c->x86_capability) ) { + p += sprintf(p, "cpu MHz\t\t: %lu.%03lu\n", + cpu_khz / 1000, (cpu_khz % 1000)); + } + + /* Cache size */ + if (c->x86_cache_size >= 0) + p += sprintf(p, "cache size\t: %d KB\n", c->x86_cache_size); + + /* We use exception 16 if we have hardware math and we've either seen it or the CPU claims it is internal */ + fpu_exception = c->hard_math && (ignore_irq13 || cpu_has_fpu); + p += sprintf(p, "fdiv_bug\t: %s\n" + "hlt_bug\t\t: %s\n" + "f00f_bug\t: %s\n" + "coma_bug\t: %s\n" + "fpu\t\t: %s\n" + "fpu_exception\t: %s\n" + "cpuid level\t: %d\n" + "wp\t\t: %s\n" + "flags\t\t:", + c->fdiv_bug ? "yes" : "no", + c->hlt_works_ok ? "no" : "yes", + c->f00f_bug ? "yes" : "no", + c->coma_bug ? "yes" : "no", + c->hard_math ? "yes" : "no", + fpu_exception ? "yes" : "no", + c->cpuid_level, + c->wp_works_ok ? "yes" : "no"); + + for ( i = 0 ; i < 32*NCAPINTS ; i++ ) + if ( test_bit(i, &c->x86_capability) && + x86_cap_flags[i] != NULL ) + p += sprintf(p, " %s", x86_cap_flags[i]); + + p += sprintf(p, "\nbogomips\t: %lu.%02lu\n\n", + c->loops_per_jiffy/(500000/HZ), + (c->loops_per_jiffy/(5000/HZ)) % 100); + + return p - buffer; +} + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/setup64.c linux.ac/arch/x86_64/kernel/setup64.c --- linux.vanilla/arch/x86_64/kernel/setup64.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/setup64.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,137 @@ +/* + * X86-64 specific setup part. + * Copyright (C) 1995 Linus Torvalds + * Copyright 2001 SuSE Labs / Andi Kleen. + * See setup.c for older changelog. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char x86_boot_params[2048] __initdata = {0,}; + +static unsigned long cpu_initialized __initdata = 0; + +struct x8664_pda cpu_pda[NR_CPUS] __cacheline_aligned; + +extern void system_call(void); +extern void ia32_cstar_target(void); + +struct desc_ptr gdt_descr = { 0 /* filled in */, (unsigned long) gdt_table }; +struct desc_ptr idt_descr = { 256 * 16, (unsigned long) idt_table }; + +void pda_init(int cpu) +{ + cpu_pda[cpu].cpunumber = cpu; + cpu_pda[cpu].irqcount = -1; + cpu_pda[cpu].irqstackptr = cpu_pda[cpu].irqstack + sizeof(cpu_pda[0].irqstack); + if (cpu == 0) + cpu_pda[cpu].pcurrent = init_tasks[cpu]; /* others are initialized in smpboot.c */ + + wrmsrl(MSR_GS_BASE, cpu_pda + cpu); +} + +/* + * cpu_init() initializes state that is per-CPU. Some data is already + * initialized (naturally) in the bootstrap process, such as the GDT + * and IDT. We reload them nevertheless, this function acts as a + * 'CPU state barrier', nothing should get across. + */ +void __init cpu_init (void) +{ +#ifdef CONFIG_SMP + int nr = stack_smp_processor_id(); +#else + int nr = smp_processor_id(); +#endif + struct tss_struct * t = &init_tss[nr]; + unsigned long v; + + /* CPU 0 is initialised in head64.c */ + if (nr != 0) + pda_init(nr); + + if (test_and_set_bit(nr, &cpu_initialized)) { + printk("CPU#%d already initialized!\n", nr); + for (;;) __sti(); + } + printk("Initializing CPU#%d\n", nr); + + if (cpu_has_vme || cpu_has_tsc || cpu_has_de) + clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE); + + gdt_descr.size = (__u8*) gdt_end - (__u8*)gdt_table; + + __asm__ __volatile__("lgdt %0": "=m" (gdt_descr)); + __asm__ __volatile__("lidt %0": "=m" (idt_descr)); + + /* + * Delete NT + */ + + __asm__ volatile("pushfq ; popq %%rax ; btr $14,%%rax ; pushq %%rax ; popfq" :: : "eax"); + + /* + * LSTAR and STAR live in a bit strange symbiosis. + * They both write to the same internal register. STAR allows to set CS/DS + * but only a 32bit target. LSTAR sets the 64bit rip. + */ + wrmsrl(MSR_STAR, ((u64)__USER32_CS)<<48 | ((u64)__KERNEL_CS)<<32); + wrmsrl(MSR_LSTAR, system_call); + +#ifdef CONFIG_IA32_EMULATION + wrmsrl(MSR_CSTAR, ia32_cstar_target); +#endif + + rdmsrl(MSR_EFER, v); + wrmsrl(MSR_EFER, v|1); + + /* Flags to clear on syscall */ + wrmsrl(MSR_SYSCALL_MASK, EF_TF|EF_DF|EF_IE); + + + wrmsrl(MSR_FS_BASE, 0); + wrmsrl(MSR_KERNEL_GS_BASE, 0); + barrier(); + + /* + * set up and load the per-CPU TSS and LDT + */ + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + if(current->mm) + BUG(); + enter_lazy_tlb(&init_mm, current, nr); + + set_tssldt_descriptor((__u8 *)tss_start + (nr*16), (unsigned long) t, + DESC_TSS, + offsetof(struct tss_struct, io_bitmap)); + load_TR(nr); + load_LDT(&init_mm); + + /* + * Clear all 6 debug registers: + */ + + set_debug(0UL, 0); + set_debug(0UL, 1); + set_debug(0UL, 2); + set_debug(0UL, 3); + set_debug(0UL, 6); + set_debug(0UL, 7); + + /* + * Force FPU initialization: + */ + current->flags &= ~PF_USEDFPU; + current->used_math = 0; + stts(); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/signal.c linux.ac/arch/x86_64/kernel/signal.c --- linux.vanilla/arch/x86_64/kernel/signal.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/signal.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,575 @@ +/* + * linux/arch/x86_64/kernel/signal.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 2000, 2001 SuSE Labs + * + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + * 2000-06-20 Pentium III FXSR, SSE support by Gareth Hughes + * 2000-2001 x86-64 support by Andi Kleen + * + * $Id: signal.c,v 1.16 2001/07/21 13:04:12 ak Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset); + +void ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs * regs); +void ia32_setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs); + +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + +asmlinkage long +sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, struct pt_regs regs) +{ + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); +#if DEBUG_SIG + printk("rt_sigsuspend savset(%lx) newset(%lx) regs(%p) rip(%lx)\n", + saveset, newset, ®s, regs.rip); +#endif + regs.rax = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(®s, &saveset)) + return -EINTR; + } +} + +asmlinkage long +sys_sigaltstack(const stack_t *uss, stack_t *uoss, struct pt_regs regs) +{ + return do_sigaltstack(uss, uoss, regs.rsp); +} + + +/* + * Do a signal return; undo the signal stack. + */ + +struct rt_sigframe +{ + char *pretcode; + struct ucontext uc; + struct siginfo info; + struct _fpstate fpstate; + char retcode[8]; +}; + +static int +restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc, unsigned long *prax) +{ + unsigned int err = 0; + +#define COPY(x) err |= __get_user(regs->x, &sc->x) + +#define COPY_SEG(seg) \ + { unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + regs->x##seg = tmp; } + +#define COPY_SEG_STRICT(seg) \ + { unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + regs->x##seg = tmp|3; } + +#define GET_SEG(seg) \ + { unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + loadsegment(seg,tmp); } + + /* XXX: rdmsr for 64bits */ + GET_SEG(gs); + GET_SEG(fs); + COPY(rdi); COPY(rsi); COPY(rbp); COPY(rsp); COPY(rbx); + COPY(rdx); COPY(rcx); COPY(rip); + COPY(r8); + COPY(r9); + COPY(r10); + COPY(r11); + COPY(r12); + COPY(r13); + COPY(r14); + COPY(r15); + + + { + unsigned int tmpflags; + err |= __get_user(tmpflags, &sc->eflags); + regs->eflags = (regs->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); + regs->orig_rax = -1; /* disable syscall checks */ + } + + { + struct _fpstate * buf; + err |= __get_user(buf, &sc->fpstate); + if (buf) { + if (verify_area(VERIFY_READ, buf, sizeof(*buf))) + goto badframe; + err |= restore_i387(buf); + } + } + + err |= __get_user(*prax, &sc->rax); + return err; + +badframe: + return 1; +} + +asmlinkage long sys_rt_sigreturn(struct pt_regs regs) +{ + struct rt_sigframe *frame = (struct rt_sigframe *)(regs.rsp - 8); + sigset_t set; + stack_t st; + long eax; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigcontext(®s, &frame->uc.uc_mcontext, &eax)) + goto badframe; + +#if DEBUG_SIG + printk("%d sigreturn rip:%lx rsp:%lx frame:%p rax:%lx\n",current->pid,regs.rip,regs.rsp,frame,eax); +#endif + + if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) + goto badframe; + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack(&st, NULL, regs.rsp); + + return eax; + +badframe: +#if DEBUG_SIG + printk("%d bad frame %p\n",current->pid,frame); +#endif + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Set up a signal frame. + */ + +static int +setup_sigcontext(struct sigcontext *sc, struct _fpstate *fpstate, + struct pt_regs *regs, unsigned long mask) +{ + int tmp, err = 0; + + tmp = 0; + __asm__("movl %%gs,%0" : "=r"(tmp): "0"(tmp)); + err |= __put_user(tmp, (unsigned int *)&sc->gs); + __asm__("movl %%fs,%0" : "=r"(tmp): "0"(tmp)); + err |= __put_user(tmp, (unsigned int *)&sc->fs); + + err |= __put_user(regs->rdi, &sc->rdi); + err |= __put_user(regs->rsi, &sc->rsi); + err |= __put_user(regs->rbp, &sc->rbp); + err |= __put_user(regs->rsp, &sc->rsp); + err |= __put_user(regs->rbx, &sc->rbx); + err |= __put_user(regs->rdx, &sc->rdx); + err |= __put_user(regs->rcx, &sc->rcx); + err |= __put_user(regs->rax, &sc->rax); + err |= __put_user(regs->r8, &sc->r8); + err |= __put_user(regs->r9, &sc->r9); + err |= __put_user(regs->r10, &sc->r10); + err |= __put_user(regs->r11, &sc->r11); + err |= __put_user(regs->r12, &sc->r12); + err |= __put_user(regs->r13, &sc->r13); + err |= __put_user(regs->r14, &sc->r14); + err |= __put_user(regs->r15, &sc->r15); + err |= __put_user(current->thread.trap_no, &sc->trapno); + err |= __put_user(current->thread.error_code, &sc->err); + err |= __put_user(regs->rip, &sc->rip); + err |= __put_user(regs->eflags, &sc->eflags); + err |= __put_user(regs->rsp, &sc->rsp_at_signal); + + tmp = save_i387(fpstate); + if (tmp < 0) + err = 1; + else + err |= __put_user(tmp ? fpstate : NULL, &sc->fpstate); + + /* non-iBCS2 extensions.. */ + err |= __put_user(mask, &sc->oldmask); + err |= __put_user(current->thread.cr2, &sc->cr2); + + return err; +} + +/* + * Determine which stack to use.. + */ +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) +{ + unsigned long rsp; + + /* Default to using normal stack - redzone*/ + rsp = regs->rsp - 128; + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (! sas_ss_flags(rsp) == 0) + rsp = current->sas_ss_sp + current->sas_ss_size; + } + + return (void *)((rsp - frame_size) & -16UL); +} + +static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs * regs) +{ + struct rt_sigframe *frame; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + if (ka->sa.sa_flags & SA_SIGINFO) { + err |= copy_siginfo_to_user(&frame->info, info); + if (err) + goto give_sigsegv; + } + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->rsp), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate, + regs, set->sig[0]); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + goto give_sigsegv; + + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + /* x86-64 should always use SA_RESTORER. */ + if (ka->sa.sa_flags & SA_RESTORER) { + err |= __put_user(ka->sa.sa_restorer, &frame->pretcode); + } else { + printk("%s forgot to set SA_RESTORER for signal %d.\n", current->comm, sig); + goto give_sigsegv; + } + + if (err) + goto give_sigsegv; + +#if DEBUG_SIG + printk("%d old rip %lx old rsp %lx old rax %lx\n", current->pid,regs->rip,regs->rsp,regs->rax); +#endif + + /* Set up registers for signal handler */ + regs->rdi = (current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? current->exec_domain->signal_invmap[sig] + : sig); + regs->rax = 0; /* In case the signal handler was declared without prototypes */ + + + /* This also works for non SA_SIGINFO handlers because they expect the + next argument after the signal number on the stack. */ + regs->rsi = &frame->info; + regs->rdx = &frame->uc; + regs->rsp = (unsigned long) frame; + regs->rip = (unsigned long) ka->sa.sa_handler; + + set_fs(USER_DS); + // XXX: cs + regs->eflags &= ~TF_MASK; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->rip, frame->pretcode); +#endif + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +/* + * OK, we're invoking a handler + */ + +static void +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) +{ +#if DEBUG_SIG + printk("handle_signal pid:%d sig:%lu rip:%lx rsp:%lx regs=%p\n", current->pid, sig, + regs->rip, regs->rsp, regs); +#endif + + /* Are we from a system call? */ + if (regs->orig_rax >= 0) { + /* If so, check system call restarting.. */ + switch (regs->rax) { + case -ERESTARTNOHAND: + regs->rax = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->rax = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + regs->rax = regs->orig_rax; + regs->rip -= 2; + } + } + +#ifdef CONFIG_IA32_EMULATION + if (current->thread.flags & THREAD_IA32) { + if (ka->sa.sa_flags & SA_SIGINFO) + ia32_setup_rt_frame(sig, ka, info, oldset, regs); + else + ia32_setup_frame(sig, ka, oldset, regs); + } else +#endif + setup_rt_frame(sig, ka, info, oldset, regs); + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +int do_signal(struct pt_regs *regs, sigset_t *oldset) +{ + siginfo_t info; + struct k_sigaction *ka; + + /* + * We want the common case to go fast, which + * is why we may in certain cases get here from + * kernel mode. Just return without doing anything + * if so. + */ + if ((regs->cs & 3) != 3) { + return 1; + } + + if (!oldset) + oldset = ¤t->blocked; + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) { + break; + } + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if (do_coredump(signr, regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + sigaddset(¤t->pending.signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + /* Reenable any watchpoints before delivering the + * signal to user space. The processor register will + * have been cleared if the watchpoint triggered + * inside the kernel. + */ + __asm__("movq %0,%%db7" : : "r" (current->thread.debugreg[7])); + + /* Whee! Actually deliver the signal. */ + handle_signal(signr, ka, &info, oldset, regs); + return 1; + } + + /* Did we come from a system call? */ + if (regs->orig_rax >= 0) { + /* Restart the system call - no handlers present */ + if (regs->rax == -ERESTARTNOHAND || + regs->rax == -ERESTARTSYS || + regs->rax == -ERESTARTNOINTR) { + regs->rax = regs->orig_rax; + regs->rcx -= 2; + } + } + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/smp.c linux.ac/arch/x86_64/kernel/smp.c --- linux.vanilla/arch/x86_64/kernel/smp.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/smp.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,558 @@ +/* + * Intel SMP support routines. + * + * (c) 1995 Alan Cox, Building #3 + * (c) 1998-99, 2000 Ingo Molnar + * + * This code is released under the GNU General Public License version 2 or + * later. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Some notes on x86 processor bugs affecting SMP operation: + * + * Pentium, Pentium Pro, II, III (and all CPUs) have bugs. + * The Linux implications for SMP are handled as follows: + * + * Pentium III / [Xeon] + * None of the E1AP-E3AP errata are visible to the user. + * + * E1AP. see PII A1AP + * E2AP. see PII A2AP + * E3AP. see PII A3AP + * + * Pentium II / [Xeon] + * None of the A1AP-A3AP errata are visible to the user. + * + * A1AP. see PPro 1AP + * A2AP. see PPro 2AP + * A3AP. see PPro 7AP + * + * Pentium Pro + * None of 1AP-9AP errata are visible to the normal user, + * except occasional delivery of 'spurious interrupt' as trap #15. + * This is very rare and a non-problem. + * + * 1AP. Linux maps APIC as non-cacheable + * 2AP. worked around in hardware + * 3AP. fixed in C0 and above steppings microcode update. + * Linux does not use excessive STARTUP_IPIs. + * 4AP. worked around in hardware + * 5AP. symmetric IO mode (normal Linux operation) not affected. + * 'noapic' mode has vector 0xf filled out properly. + * 6AP. 'noapic' mode might be affected - fixed in later steppings + * 7AP. We do not assume writes to the LVT deassering IRQs + * 8AP. We do not enable low power mode (deep sleep) during MP bootup + * 9AP. We do not use mixed mode + * + * Pentium + * There is a marginal case where REP MOVS on 100MHz SMP + * machines with B stepping processors can fail. XXX should provide + * an L1cache=Writethrough or L1cache=off option. + * + * B stepping CPUs may hang. There are hardware work arounds + * for this. We warn about it in case your board doesnt have the work + * arounds. Basically thats so I can tell anyone with a B stepping + * CPU and SMP problems "tough". + * + * Specific items [From Pentium Processor Specification Update] + * + * 1AP. Linux doesn't use remote read + * 2AP. Linux doesn't trust APIC errors + * 3AP. We work around this + * 4AP. Linux never generated 3 interrupts of the same priority + * to cause a lost local interrupt. + * 5AP. Remote read is never used + * 6AP. not affected - worked around in hardware + * 7AP. not affected - worked around in hardware + * 8AP. worked around in hardware - we get explicit CS errors if not + * 9AP. only 'noapic' mode affected. Might generate spurious + * interrupts, we log only the first one and count the + * rest silently. + * 10AP. not affected - worked around in hardware + * 11AP. Linux reads the APIC between writes to avoid this, as per + * the documentation. Make sure you preserve this as it affects + * the C stepping chips too. + * 12AP. not affected - worked around in hardware + * 13AP. not affected - worked around in hardware + * 14AP. we always deassert INIT during bootup + * 15AP. not affected - worked around in hardware + * 16AP. not affected - worked around in hardware + * 17AP. not affected - worked around in hardware + * 18AP. not affected - worked around in hardware + * 19AP. not affected - worked around in BIOS + * + * If this sounds worrying believe me these bugs are either ___RARE___, + * or are signal timing bugs worked around in hardware and there's + * about nothing of note with C stepping upwards. + */ + +/* The 'big kernel lock' */ +spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; + +struct tlb_state cpu_tlbstate[NR_CPUS] = {[0 ... NR_CPUS-1] = { &init_mm, 0 }}; + +/* + * the following functions deal with sending IPIs between CPUs. + * + * We use 'broadcast', CPU->CPU IPIs and self-IPIs too. + */ + +static inline int __prepare_ICR (unsigned int shortcut, int vector) +{ + return APIC_DM_FIXED | shortcut | vector | APIC_DEST_LOGICAL; +} + +static inline int __prepare_ICR2 (unsigned int mask) +{ + return SET_APIC_DEST_FIELD(mask); +} + +static inline void __send_IPI_shortcut(unsigned int shortcut, int vector) +{ + /* + * Subtle. In the case of the 'never do double writes' workaround + * we have to lock out interrupts to be safe. As we don't care + * of the value read we use an atomic rmw access to avoid costly + * cli/sti. Otherwise we use an even cheaper single atomic write + * to the APIC. + */ + unsigned int cfg; + + /* + * Wait for idle. + */ + apic_wait_icr_idle(); + + /* + * No need to touch the target chip field + */ + cfg = __prepare_ICR(shortcut, vector); + + /* + * Send the IPI. The write to APIC_ICR fires this off. + */ + apic_write_around(APIC_ICR, cfg); +} + +static inline void send_IPI_allbutself(int vector) +{ + /* + * if there are no other CPUs in the system then + * we get an APIC send error if we try to broadcast. + * thus we have to avoid sending IPIs in this case. + */ + if (smp_num_cpus > 1) + __send_IPI_shortcut(APIC_DEST_ALLBUT, vector); +} + +static inline void send_IPI_all(int vector) +{ + __send_IPI_shortcut(APIC_DEST_ALLINC, vector); +} + +void send_IPI_self(int vector) +{ + __send_IPI_shortcut(APIC_DEST_SELF, vector); +} + +static inline void send_IPI_mask(int mask, int vector) +{ + unsigned long cfg; + unsigned long flags; + + __save_flags(flags); + __cli(); + + /* + * Wait for idle. + */ + apic_wait_icr_idle(); + + /* + * prepare target chip field + */ + cfg = __prepare_ICR2(mask); + apic_write_around(APIC_ICR2, cfg); + + /* + * program the ICR + */ + cfg = __prepare_ICR(0, vector); + + /* + * Send the IPI. The write to APIC_ICR fires this off. + */ + apic_write_around(APIC_ICR, cfg); + __restore_flags(flags); +} + +/* + * Smarter SMP flushing macros. + * c/o Linus Torvalds. + * + * These mean you can really definitely utterly forget about + * writing to user space from interrupts. (Its not allowed anyway). + * + * Optimizations Manfred Spraul + */ + +static volatile unsigned long flush_cpumask; +static struct mm_struct * flush_mm; +static unsigned long flush_va; +static spinlock_t tlbstate_lock = SPIN_LOCK_UNLOCKED; +#define FLUSH_ALL 0xffffffff + +/* + * We cannot call mmdrop() because we are in interrupt context, + * instead update mm->cpu_vm_mask. + */ +static void inline leave_mm (unsigned long cpu) +{ + if (cpu_tlbstate[cpu].state == TLBSTATE_OK) + BUG(); + clear_bit(cpu, &cpu_tlbstate[cpu].active_mm->cpu_vm_mask); +} + +/* + * + * The flush IPI assumes that a thread switch happens in this order: + * [cpu0: the cpu that switches] + * 1) switch_mm() either 1a) or 1b) + * 1a) thread switch to a different mm + * 1a1) clear_bit(cpu, &old_mm->cpu_vm_mask); + * Stop ipi delivery for the old mm. This is not synchronized with + * the other cpus, but smp_invalidate_interrupt ignore flush ipis + * for the wrong mm, and in the worst case we perform a superflous + * tlb flush. + * 1a2) set cpu_tlbstate to TLBSTATE_OK + * Now the smp_invalidate_interrupt won't call leave_mm if cpu0 + * was in lazy tlb mode. + * 1a3) update cpu_tlbstate[].active_mm + * Now cpu0 accepts tlb flushes for the new mm. + * 1a4) set_bit(cpu, &new_mm->cpu_vm_mask); + * Now the other cpus will send tlb flush ipis. + * 1a4) change cr3. + * 1b) thread switch without mm change + * cpu_tlbstate[].active_mm is correct, cpu0 already handles + * flush ipis. + * 1b1) set cpu_tlbstate to TLBSTATE_OK + * 1b2) test_and_set the cpu bit in cpu_vm_mask. + * Atomically set the bit [other cpus will start sending flush ipis], + * and test the bit. + * 1b3) if the bit was 0: leave_mm was called, flush the tlb. + * 2) switch %%esp, ie current + * + * The interrupt must handle 2 special cases: + * - cr3 is changed before %%esp, ie. it cannot use current->{active_,}mm. + * - the cpu performs speculative tlb reads, i.e. even if the cpu only + * runs in kernel space, the cpu could load tlb entries for user space + * pages. + * + * The good news is that cpu_tlbstate is local to each cpu, no + * write/read ordering problems. + */ + +/* + * TLB flush IPI: + * + * 1) Flush the tlb entries if the cpu uses the mm that's being flushed. + * 2) Leave the mm if we are in the lazy tlb mode. + */ + +asmlinkage void smp_invalidate_interrupt (void) +{ + unsigned long cpu = smp_processor_id(); + + if (!test_bit(cpu, &flush_cpumask)) + return; + /* + * This was a BUG() but until someone can quote me the + * line from the intel manual that guarantees an IPI to + * multiple CPUs is retried _only_ on the erroring CPUs + * its staying as a return + * + * BUG(); + */ + + if (flush_mm == cpu_tlbstate[cpu].active_mm) { + if (cpu_tlbstate[cpu].state == TLBSTATE_OK) { + if (flush_va == FLUSH_ALL) + local_flush_tlb(); + else + __flush_tlb_one(flush_va); + } else + leave_mm(cpu); + } + ack_APIC_irq(); + clear_bit(cpu, &flush_cpumask); +} + +static void flush_tlb_others (unsigned long cpumask, struct mm_struct *mm, + unsigned long va) +{ + /* + * A couple of (to be removed) sanity checks: + * + * - we do not send IPIs to not-yet booted CPUs. + * - current CPU must not be in mask + * - mask must exist :) + */ + if (!cpumask) + BUG(); + if ((cpumask & cpu_online_map) != cpumask) + BUG(); + if (cpumask & (1 << smp_processor_id())) + BUG(); + if (!mm) + BUG(); + + /* + * i'm not happy about this global shared spinlock in the + * MM hot path, but we'll see how contended it is. + * Temporarily this turns IRQs off, so that lockups are + * detected by the NMI watchdog. + */ + spin_lock(&tlbstate_lock); + + flush_mm = mm; + flush_va = va; + atomic_set_mask(cpumask, &flush_cpumask); + /* + * We have to send the IPI only to + * CPUs affected. + */ + send_IPI_mask(cpumask, INVALIDATE_TLB_VECTOR); + + while (flush_cpumask) + /* nothing. lockup detection does not belong here */; + + flush_mm = NULL; + flush_va = 0; + spin_unlock(&tlbstate_lock); +} + +void flush_tlb_current_task(void) +{ + struct mm_struct *mm = current->mm; + unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id()); + + local_flush_tlb(); + if (cpu_mask) + flush_tlb_others(cpu_mask, mm, FLUSH_ALL); +} + +void flush_tlb_mm (struct mm_struct * mm) +{ + unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id()); + + if (current->active_mm == mm) { + if (current->mm) + local_flush_tlb(); + else + leave_mm(smp_processor_id()); + } + if (cpu_mask) + flush_tlb_others(cpu_mask, mm, FLUSH_ALL); +} + +void flush_tlb_page(struct vm_area_struct * vma, unsigned long va) +{ + struct mm_struct *mm = vma->vm_mm; + unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id()); + + if (current->active_mm == mm) { + if(current->mm) + __flush_tlb_one(va); + else + leave_mm(smp_processor_id()); + } + + if (cpu_mask) + flush_tlb_others(cpu_mask, mm, va); +} + +static inline void do_flush_tlb_all_local(void) +{ + unsigned long cpu = smp_processor_id(); + + __flush_tlb_all(); + if (cpu_tlbstate[cpu].state == TLBSTATE_LAZY) + leave_mm(cpu); +} + +static void flush_tlb_all_ipi(void* info) +{ + do_flush_tlb_all_local(); +} + +void flush_tlb_all(void) +{ + smp_call_function (flush_tlb_all_ipi,0,1,1); + + do_flush_tlb_all_local(); +} + +/* + * this function sends a 'reschedule' IPI to another CPU. + * it goes straight through and wastes no time serializing + * anything. Worst case is that we lose a reschedule ... + */ + +void smp_send_reschedule(int cpu) +{ + send_IPI_mask(1 << cpu, RESCHEDULE_VECTOR); +} + +/* + * Structure and data for smp_call_function(). This is designed to minimise + * static memory requirements. It also looks cleaner. + */ +static spinlock_t call_lock = SPIN_LOCK_UNLOCKED; + +struct call_data_struct { + void (*func) (void *info); + void *info; + atomic_t started; + atomic_t finished; + int wait; +} __attribute__ ((__aligned__(SMP_CACHE_BYTES))); + +static struct call_data_struct * call_data; +static struct call_data_struct call_data_array[NR_CPUS]; + +/* + * this function sends a 'generic call function' IPI to all other CPUs + * in the system. + */ + +int smp_call_function (void (*func) (void *info), void *info, int nonatomic, + int wait) +/* + * [SUMMARY] Run a function on all other CPUs. + * The function to run. This must be fast and non-blocking. + * An arbitrary pointer to pass to the function. + * currently unused. + * If true, wait (atomically) until function has completed on other CPUs. + * [RETURNS] 0 on success, else a negative status code. Does not return until + * remote CPUs are nearly ready to execute <> or are or have executed. + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler, you may call it from a bottom half handler. + */ +{ + struct call_data_struct *data; + int cpus = (cpu_online_map & ~(1 << smp_processor_id())); + + if (!cpus) + return 0; + + data = &call_data_array[smp_processor_id()]; + + data->func = func; + data->info = info; + data->wait = wait; + if (wait) + atomic_set(&data->finished, 0); + /* We have do to this one last to make sure that the IPI service + * code desn't get confused if it gets an unexpected repeat + * trigger of an old IPI while we're still setting up the new + * one. */ + atomic_set(&data->started, 0); + + local_bh_disable(); + spin_lock(&call_lock); + call_data = data; + /* Send a message to all other CPUs and wait for them to respond */ + send_IPI_allbutself(CALL_FUNCTION_VECTOR); + + /* Wait for response */ + while (atomic_read(&data->started) != cpus) + barrier(); + + /* It is now safe to reuse the "call_data" global, but we need + * to keep local bottom-halves disabled until after waiters have + * been acknowledged to prevent reuse of the per-cpu call data + * entry. */ + spin_unlock(&call_lock); + + if (wait) + while (atomic_read(&data->finished) != cpus) + barrier(); + local_bh_enable(); + + return 0; +} + +static void stop_this_cpu (void * dummy) +{ + /* + * Remove this CPU: + */ + clear_bit(smp_processor_id(), &cpu_online_map); + __cli(); + disable_local_APIC(); + if (cpu_data[smp_processor_id()].hlt_works_ok) + for(;;) __asm__("hlt"); + for (;;); +} + +/* + * this function calls the 'stop' function on all other CPUs in the system. + */ + +void smp_send_stop(void) +{ + smp_call_function(stop_this_cpu, NULL, 1, 0); + smp_num_cpus = 1; + + __cli(); + disable_local_APIC(); + __sti(); +} + +/* + * Reschedule call back. Nothing to do, + * all the work is done automatically when + * we return from the interrupt. + */ +asmlinkage void smp_reschedule_interrupt(void) +{ + ack_APIC_irq(); +} + +asmlinkage void smp_call_function_interrupt(void) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + ack_APIC_irq(); + /* + * Notify initiating CPU that I've grabbed the data and am about + * to execute the function (and avoid servicing any single IPI + * twice) + */ + if (test_and_set_bit(smp_processor_id(), &call_data->started)) + return; + /* + * At this point the info structure may be out of scope unless wait==1 + */ + (*func)(info); + if (wait) + set_bit(smp_processor_id(), &call_data->finished); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/smpboot.c linux.ac/arch/x86_64/kernel/smpboot.c --- linux.vanilla/arch/x86_64/kernel/smpboot.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/smpboot.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,1025 @@ +/* + * x86 SMP booting functions + * + * (c) 1995 Alan Cox, Building #3 + * (c) 1998, 1999, 2000 Ingo Molnar + * Copyright 2001 Andi Kleen, SuSE Labs. + * + * Much of the core SMP work is based on previous work by Thomas Radke, to + * whom a great many thanks are extended. + * + * Thanks to Intel for making available several different Pentium, + * Pentium Pro and Pentium-II/Xeon MP machines. + * Original development of Linux SMP code supported by Caldera. + * + * This code is released under the GNU General Public License version 2 or + * later. + * + * Fixes + * Felix Koop : NR_CPUS used properly + * Jose Renau : Handle single CPU case. + * Alan Cox : By repeated request 8) - Total BogoMIP report. + * Greg Wright : Fix for kernel stacks panic. + * Erich Boleyn : MP v1.4 and additional changes. + * Matthias Sattler : Changes for 2.1 kernel map. + * Michel Lespinasse : Changes for 2.1 kernel map. + * Michael Chastain : Change trampoline.S to gnu as. + * Alan Cox : Dumb bug: 'B' step PPro's are fine + * Ingo Molnar : Added APIC timers, based on code + * from Jose Renau + * Ingo Molnar : various cleanups and rewrites + * Tigran Aivazian : fixed "0.00 in /proc/uptime on SMP" bug. + * Maciej W. Rozycki : Bits for genuine 82489DX APICs + * Andi Kleen : Changed for SMP boot into long mode. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Set if we find a B stepping CPU */ +static int smp_b_stepping; + +/* Setup configured maximum number of CPUs to activate */ +static int max_cpus = -1; + +/* Total count of live CPUs */ +int smp_num_cpus = 1; + +/* Bitmask of currently online CPUs */ +unsigned long cpu_online_map; + +/* which CPU (physical APIC ID) maps to which logical CPU number */ +volatile int x86_apicid_to_cpu[NR_CPUS]; +/* which logical CPU number maps to which CPU (physical APIC ID) */ +volatile int x86_cpu_to_apicid[NR_CPUS]; + +static volatile unsigned long cpu_callin_map; +static volatile unsigned long cpu_callout_map; + +/* Per CPU bogomips and other parameters */ +struct cpuinfo_x86 cpu_data[NR_CPUS]; + +/* Set when the idlers are all forked */ +int smp_threads_ready; + +/* + * Setup routine for controlling SMP activation + * + * Command-line option of "nosmp" or "maxcpus=0" will disable SMP + * activation entirely (the MPS table probe still happens, though). + * + * Command-line option of "maxcpus=", where is an integer + * greater than 0, limits the maximum number of CPUs activated in + * SMP mode to . + */ + +static int __init nosmp(char *str) +{ + max_cpus = 0; + return 1; +} + +__setup("nosmp", nosmp); + +static int __init maxcpus(char *str) +{ + get_option(&str, &max_cpus); + return 1; +} + +__setup("maxcpus=", maxcpus); + +/* + * Trampoline 80x86 program as an array. + */ + +extern unsigned char trampoline_data []; +extern unsigned char trampoline_end []; +static unsigned char *trampoline_base; + +/* + * Currently trivial. Write the real->protected mode + * bootstrap into the page concerned. The caller + * has made sure it's suitably aligned. + */ + +static unsigned long __init setup_trampoline(void) +{ + extern __u32 tramp_gdt_ptr; + tramp_gdt_ptr = (__u32)virt_to_phys(&gdt_table); + memcpy(trampoline_base, trampoline_data, trampoline_end - trampoline_data); + return virt_to_phys(trampoline_base); +} + +/* + * We are called very early to get the low memory for the + * SMP bootup trampoline page. + */ +void __init smp_alloc_memory(void) +{ + trampoline_base = (void *) alloc_bootmem_low_pages(PAGE_SIZE); + /* + * Has to be in very low memory so we can execute + * real-mode AP code. + */ + if (__pa(trampoline_base) >= 0x9F000) + BUG(); +} + +/* + * The bootstrap kernel entry code has set these up. Save them for + * a given CPU + */ + +void __init smp_store_cpu_info(int id) +{ + struct cpuinfo_x86 *c = cpu_data + id; + + *c = boot_cpu_data; + identify_cpu(c); + /* + * Mask B, Pentium, but not Pentium MMX + */ + if (c->x86_vendor == X86_VENDOR_INTEL && + c->x86 == 5 && + c->x86_mask >= 1 && c->x86_mask <= 4 && + c->x86_model <= 3) + /* + * Remember we have B step Pentia with bugs + */ + smp_b_stepping = 1; +} + +/* + * Architecture specific routine called by the kernel just before init is + * fired off. This allows the BP to have everything in order [we hope]. + * At the end of this all the APs will hit the system scheduling and off + * we go. Each AP will load the system gdt's and jump through the kernel + * init into idle(). At this point the scheduler will one day take over + * and give them jobs to do. smp_callin is a standard routine + * we use to track CPUs as they power up. + */ + +static atomic_t smp_commenced = ATOMIC_INIT(0); + +void __init smp_commence(void) +{ + /* + * Lets the callins below out of their loop. + */ + Dprintk("Setting commenced=1, go go go\n"); + + wmb(); + atomic_set(&smp_commenced,1); +} + +/* + * TSC synchronization. + * + * We first check wether all CPUs have their TSC's synchronized, + * then we print a warning if not, and always resync. + */ + +static atomic_t tsc_start_flag = ATOMIC_INIT(0); +static atomic_t tsc_count_start = ATOMIC_INIT(0); +static atomic_t tsc_count_stop = ATOMIC_INIT(0); +static unsigned long long tsc_values[NR_CPUS]; + +#define NR_LOOPS 5 + +extern unsigned long fast_gettimeoffset_quotient; + +/* + * accurate 64-bit/32-bit division, expanded to 32-bit divisions and 64-bit + * multiplication. Not terribly optimized but we need it at boot time only + * anyway. + * + * result == a / b + * == (a1 + a2*(2^32)) / b + * == a1/b + a2*(2^32/b) + * == a1/b + a2*((2^32-1)/b) + a2/b + (a2*((2^32-1) % b))/b + * ^---- (this multiplication can overflow) + */ + +static unsigned long long div64 (unsigned long long a, unsigned long b0) +{ + unsigned int a1, a2; + unsigned long long res; + + a1 = ((unsigned int*)&a)[0]; + a2 = ((unsigned int*)&a)[1]; + + res = a1/b0 + + (unsigned long long)a2 * (unsigned long long)(0xffffffff/b0) + + a2 / b0 + + (a2 * (0xffffffff % b0)) / b0; + + return res; +} + +static void __init synchronize_tsc_bp (void) +{ + int i; + unsigned long long t0; + unsigned long long sum, avg; + long long delta; + unsigned long one_usec; + int buggy = 0; + + printk("checking TSC synchronization across CPUs: "); + + one_usec = ((1<<30)/fast_gettimeoffset_quotient)*(1<<2); + + atomic_set(&tsc_start_flag, 1); + wmb(); + + /* + * We loop a few times to get a primed instruction cache, + * then the last pass is more or less synchronized and + * the BP and APs set their cycle counters to zero all at + * once. This reduces the chance of having random offsets + * between the processors, and guarantees that the maximum + * delay between the cycle counters is never bigger than + * the latency of information-passing (cachelines) between + * two CPUs. + */ + for (i = 0; i < NR_LOOPS; i++) { + /* + * all APs synchronize but they loop on '== num_cpus' + */ + while (atomic_read(&tsc_count_start) != smp_num_cpus-1) mb(); + atomic_set(&tsc_count_stop, 0); + wmb(); + /* + * this lets the APs save their current TSC: + */ + atomic_inc(&tsc_count_start); + + rdtscll(tsc_values[smp_processor_id()]); + /* + * We clear the TSC in the last loop: + */ + if (i == NR_LOOPS-1) + write_tsc(0, 0); + + /* + * Wait for all APs to leave the synchronization point: + */ + while (atomic_read(&tsc_count_stop) != smp_num_cpus-1) mb(); + atomic_set(&tsc_count_start, 0); + wmb(); + atomic_inc(&tsc_count_stop); + } + + sum = 0; + for (i = 0; i < smp_num_cpus; i++) { + t0 = tsc_values[i]; + sum += t0; + } + avg = div64(sum, smp_num_cpus); + + sum = 0; + for (i = 0; i < smp_num_cpus; i++) { + delta = tsc_values[i] - avg; + if (delta < 0) + delta = -delta; + /* + * We report bigger than 2 microseconds clock differences. + */ + if (delta > 2*one_usec) { + long realdelta; + if (!buggy) { + buggy = 1; + printk("\n"); + } + realdelta = div64(delta, one_usec); + if (tsc_values[i] < avg) + realdelta = -realdelta; + + printk("BIOS BUG: CPU#%d improperly initialized, has %ld usecs TSC skew! FIXED.\n", + i, realdelta); + } + + sum += delta; + } + if (!buggy) + printk("passed.\n"); +} + +static void __init synchronize_tsc_ap (void) +{ + int i; + + /* + * smp_num_cpus is not necessarily known at the time + * this gets called, so we first wait for the BP to + * finish SMP initialization: + */ + while (!atomic_read(&tsc_start_flag)) mb(); + + for (i = 0; i < NR_LOOPS; i++) { + atomic_inc(&tsc_count_start); + while (atomic_read(&tsc_count_start) != smp_num_cpus) mb(); + + rdtscll(tsc_values[smp_processor_id()]); + if (i == NR_LOOPS-1) + write_tsc(0, 0); + + atomic_inc(&tsc_count_stop); + while (atomic_read(&tsc_count_stop) != smp_num_cpus) mb(); + } +} +#undef NR_LOOPS + +extern void calibrate_delay(void); + +static atomic_t init_deasserted; + +void __init smp_callin(void) +{ + int cpuid, phys_id; + unsigned long timeout; + + /* + * If waken up by an INIT in an 82489DX configuration + * we may get here before an INIT-deassert IPI reaches + * our local APIC. We have to wait for the IPI or we'll + * lock up on an APIC access. + */ + while (!atomic_read(&init_deasserted)); + + /* + * (This works even if the APIC is not enabled.) + */ + phys_id = GET_APIC_ID(apic_read(APIC_ID)); + cpuid = current->processor; + if (test_and_set_bit(cpuid, &cpu_online_map)) { + printk("huh, phys CPU#%d, CPU#%d already present??\n", + phys_id, cpuid); + BUG(); + } + Dprintk("CPU#%d (phys ID: %d) waiting for CALLOUT\n", cpuid, phys_id); + + /* + * STARTUP IPIs are fragile beasts as they might sometimes + * trigger some glue motherboard logic. Complete APIC bus + * silence for 1 second, this overestimates the time the + * boot CPU is spending to send the up to 2 STARTUP IPIs + * by a factor of two. This should be enough. + */ + + /* + * Waiting 2s total for startup (udelay is not yet working) + */ + timeout = jiffies + 2*HZ; + while (time_before(jiffies, timeout)) { + /* + * Has the boot CPU finished it's STARTUP sequence? + */ + if (test_bit(cpuid, &cpu_callout_map)) + break; + rep_nop(); + } + + if (!time_before(jiffies, timeout)) { + printk("BUG: CPU%d started up but did not get a callout!\n", + cpuid); + BUG(); + } + + /* + * the boot CPU has finished the init stage and is spinning + * on callin_map until we finish. We are free to set up this + * CPU, first the APIC. (this is probably redundant on most + * boards) + */ + + Dprintk("CALLIN, before setup_local_APIC().\n"); + setup_local_APIC(); + + sti(); + +#ifdef CONFIG_MTRR + /* + * Must be done before calibration delay is computed + */ + mtrr_init_secondary_cpu (); +#endif + /* + * Get our bogomips. + */ + calibrate_delay(); + Dprintk("Stack at about %p\n",&cpuid); + + /* + * Save our processor parameters + */ + smp_store_cpu_info(cpuid); + + /* + * Allow the master to continue. + */ + set_bit(cpuid, &cpu_callin_map); + + /* + * Synchronize the TSC with the BP + */ + if (cpu_has_tsc) + synchronize_tsc_ap(); +} + +int cpucount; + +extern int cpu_idle(void); + +/* + * Activate a secondary processor. + */ +int __init start_secondary(void *unused) +{ + /* + * Dont put anything before smp_callin(), SMP + * booting is too fragile that we want to limit the + * things done here to the most necessary things. + */ + cpu_init(); + smp_callin(); + while (!atomic_read(&smp_commenced)) + rep_nop(); + /* + * low-memory mappings have been cleared, flush them from + * the local TLBs too. + */ + local_flush_tlb(); + + return cpu_idle(); +} + +/* + * Everything has been set up for the secondary + * CPUs - they just need to reload everything + * from the task structure + * This function must not return. + */ +void __init initialize_secondary(void) +{ + /* + * We don't actually need to load the full TSS, + * basically just the stack pointer and the eip. + */ + + asm volatile( + "movq %0,%%rsp\n\t" + "jmp *%1" + : + :"r" (current->thread.rsp),"r" (current->thread.rip)); +} + +extern void *init_rsp; +extern void (*initial_code)(void); + +static int __init fork_by_hand(void) +{ + struct pt_regs regs; + /* + * don't care about the eip and regs settings since + * we'll never reschedule the forked task. + */ + return do_fork(CLONE_VM|CLONE_PID, 0, ®s, 0); +} + +#if APIC_DEBUG +static inline void inquire_remote_apic(int apicid) +{ + int i, regs[] = { APIC_ID >> 4, APIC_LVR >> 4, APIC_SPIV >> 4 }; + char *names[] = { "ID", "VERSION", "SPIV" }; + int timeout, status; + + printk("Inquiring remote APIC #%d...\n", apicid); + + for (i = 0; i < sizeof(regs) / sizeof(*regs); i++) { + printk("... APIC #%d %s: ", apicid, names[i]); + + /* + * Wait for idle. + */ + apic_wait_icr_idle(); + + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + apic_write_around(APIC_ICR, APIC_DM_REMRD | regs[i]); + + timeout = 0; + do { + udelay(100); + status = apic_read(APIC_ICR) & APIC_ICR_RR_MASK; + } while (status == APIC_ICR_RR_INPROG && timeout++ < 1000); + + switch (status) { + case APIC_ICR_RR_VALID: + status = apic_read(APIC_RRR); + printk("%08x\n", status); + break; + default: + printk("failed\n"); + } + } +} +#endif + +static void __init do_boot_cpu (int apicid) +{ + struct task_struct *idle; + unsigned long send_status, accept_status, boot_status, maxlvt; + int timeout, num_starts, j, cpu; + unsigned long start_eip; + + cpu = ++cpucount; + /* + * We can't use kernel_thread since we must avoid to + * reschedule the child. + */ + if (fork_by_hand() < 0) + panic("failed fork for CPU %d", cpu); + + /* + * We remove it from the pidhash and the runqueue + * once we got the process: + */ + idle = init_task.prev_task; + if (!idle) + panic("No idle process for CPU %d", cpu); + + idle->processor = cpu; + x86_cpu_to_apicid[cpu] = apicid; + x86_apicid_to_cpu[apicid] = cpu; + idle->has_cpu = 1; /* we schedule the first task manually */ + idle->thread.rip = (unsigned long) start_secondary; + + del_from_runqueue(idle); + unhash_process(idle); + cpu_pda[cpu].pcurrent = init_tasks[cpu] = idle; + + /* start_eip had better be page-aligned! */ + start_eip = setup_trampoline(); + + /* So we see what's up */ + printk("Booting processor %d/%d eip %lx\n", cpu, apicid, start_eip); + init_rsp = (void *) (1024 + PAGE_SIZE + (char *)idle); + initial_code = initialize_secondary; + + /* + * This grunge runs the startup process for + * the targeted processor. + */ + + atomic_set(&init_deasserted, 0); + + Dprintk("Setting warm reset code and vector.\n"); + + CMOS_WRITE(0xa, 0xf); + local_flush_tlb(); + Dprintk("1.\n"); + *((volatile unsigned short *) phys_to_virt(0x469)) = start_eip >> 4; + Dprintk("2.\n"); + *((volatile unsigned short *) phys_to_virt(0x467)) = start_eip & 0xf; + Dprintk("3.\n"); + + /* + * Be paranoid about clearing APIC errors. + */ + if (APIC_INTEGRATED(apic_version[apicid])) { + apic_read_around(APIC_SPIV); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + } + + /* + * Status is now clean + */ + send_status = 0; + accept_status = 0; + boot_status = 0; + + /* + * Starting actual IPI sequence... + */ + + Dprintk("Asserting INIT.\n"); + + /* + * Turn INIT on target chip + */ + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + + /* + * Send IPI + */ + apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_INT_ASSERT + | APIC_DM_INIT); + + Dprintk("Waiting for send to finish...\n"); + timeout = 0; + do { + Dprintk("+"); + udelay(100); + send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; + } while (send_status && (timeout++ < 1000)); + + mdelay(10); + + Dprintk("Deasserting INIT.\n"); + + /* Target chip */ + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + + /* Send IPI */ + apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT); + + Dprintk("Waiting for send to finish...\n"); + timeout = 0; + do { + Dprintk("+"); + udelay(100); + send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; + } while (send_status && (timeout++ < 1000)); + + atomic_set(&init_deasserted, 1); + + /* + * Should we send STARTUP IPIs ? + * + * Determine this based on the APIC version. + * If we don't have an integrated APIC, don't + * send the STARTUP IPIs. + */ + if (APIC_INTEGRATED(apic_version[apicid])) + num_starts = 2; + else + num_starts = 0; + + /* + * Run STARTUP IPI loop. + */ + Dprintk("#startup loops: %d.\n", num_starts); + + maxlvt = get_maxlvt(); + + for (j = 1; j <= num_starts; j++) { + Dprintk("Sending STARTUP #%d.\n",j); + apic_read_around(APIC_SPIV); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + Dprintk("After apic_write.\n"); + + /* + * STARTUP IPI + */ + + /* Target chip */ + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + + /* Boot on the stack */ + /* Kick the second */ + apic_write_around(APIC_ICR, APIC_DM_STARTUP + | (start_eip >> 12)); + + /* + * Give the other CPU some time to accept the IPI. + */ + udelay(300); + + Dprintk("Startup point 1.\n"); + + Dprintk("Waiting for send to finish...\n"); + timeout = 0; + do { + Dprintk("+"); + udelay(100); + send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; + } while (send_status && (timeout++ < 1000)); + + /* + * Give the other CPU some time to accept the IPI. + */ + udelay(200); + /* + * Due to the Pentium erratum 3AP. + */ + if (maxlvt > 3) { + apic_read_around(APIC_SPIV); + apic_write(APIC_ESR, 0); + } + accept_status = (apic_read(APIC_ESR) & 0xEF); + if (send_status || accept_status) + break; + } + Dprintk("After Startup.\n"); + + if (send_status) + printk("APIC never delivered???\n"); + if (accept_status) + printk("APIC delivery error (%lx).\n", accept_status); + + if (!send_status && !accept_status) { + /* + * allow APs to start initializing. + */ + Dprintk("Before Callout %d.\n", cpu); + set_bit(cpu, &cpu_callout_map); + Dprintk("After Callout %d.\n", cpu); + + /* + * Wait 5s total for a response + */ + for (timeout = 0; timeout < 50000; timeout++) { + if (test_bit(cpu, &cpu_callin_map)) + break; /* It has booted */ + udelay(100); + } + + if (test_bit(cpu, &cpu_callin_map)) { + /* number CPUs logically, starting from 1 (BSP is 0) */ + Dprintk("OK.\n"); + printk("CPU%d: ", cpu); + print_cpu_info(&cpu_data[cpu]); + Dprintk("CPU has booted.\n"); + } else { + boot_status = 1; + if (*((volatile unsigned char *)phys_to_virt(8192)) + == 0xA5) + /* trampoline started but...? */ + printk("Stuck ??\n"); + else + /* trampoline code not run */ + printk("Not responding.\n"); +#if APIC_DEBUG + inquire_remote_apic(apicid); +#endif + } + } + if (send_status || accept_status || boot_status) { + x86_cpu_to_apicid[cpu] = -1; + x86_apicid_to_cpu[apicid] = -1; + cpucount--; + } + + /* mark "stuck" area as not stuck */ + *((volatile unsigned long *)phys_to_virt(8192)) = 0; +} + +cycles_t cacheflush_time; + +static void smp_tune_scheduling (void) +{ + unsigned long cachesize; /* kB */ + unsigned long bandwidth = 350; /* MB/s */ + /* + * Rough estimation for SMP scheduling, this is the number of + * cycles it takes for a fully memory-limited process to flush + * the SMP-local cache. + * + * (For a P5 this pretty much means we will choose another idle + * CPU almost always at wakeup time (this is due to the small + * L1 cache), on PIIs it's around 50-100 usecs, depending on + * the cache size) + */ + + if (!cpu_khz) { + /* + * this basically disables processor-affinity + * scheduling on SMP without a TSC. + */ + cacheflush_time = 0; + return; + } else { + cachesize = boot_cpu_data.x86_cache_size; + if (cachesize == -1) { + cachesize = 16; /* Pentiums, 2x8kB cache */ + bandwidth = 100; + } + + cacheflush_time = (cpu_khz>>10) * (cachesize<<10) / bandwidth; + } + + printk("per-CPU timeslice cutoff: %ld.%02ld usecs.\n", + (long)cacheflush_time/(cpu_khz/1000), + ((long)cacheflush_time*100/(cpu_khz/1000)) % 100); +} + +/* + * Cycle through the processors sending APIC IPIs to boot each. + */ + +extern int prof_multiplier[NR_CPUS]; +extern int prof_old_multiplier[NR_CPUS]; +extern int prof_counter[NR_CPUS]; + +void __init smp_boot_cpus(void) +{ + int apicid, cpu; + +#ifdef CONFIG_MTRR + /* Must be done before other processors booted */ + mtrr_init_boot_cpu (); +#endif + /* + * Initialize the logical to physical CPU number mapping + * and the per-CPU profiling counter/multiplier + */ + + for (apicid = 0; apicid < NR_CPUS; apicid++) { + x86_apicid_to_cpu[apicid] = -1; + prof_counter[apicid] = 1; + prof_old_multiplier[apicid] = 1; + prof_multiplier[apicid] = 1; + } + + /* + * Setup boot CPU information + */ + smp_store_cpu_info(0); /* Final full version of the data */ + printk("CPU%d: ", 0); + print_cpu_info(&cpu_data[0]); + + /* + * We have the boot CPU online for sure. + */ + set_bit(0, &cpu_online_map); + x86_apicid_to_cpu[boot_cpu_id] = 0; + x86_cpu_to_apicid[0] = boot_cpu_id; + global_irq_holder = 0; + current->processor = 0; + init_idle(); + smp_tune_scheduling(); + + /* + * If we couldnt find an SMP configuration at boot time, + * get out of here now! + */ + if (!smp_found_config) { + printk(KERN_NOTICE "SMP motherboard not detected.\n"); +#ifndef CONFIG_VISWS + io_apic_irqs = 0; +#endif + cpu_online_map = phys_cpu_present_map = 1; + smp_num_cpus = 1; + if (APIC_init_uniprocessor()) + printk(KERN_NOTICE "Local APIC not detected." + " Using dummy APIC emulation.\n"); + goto smp_done; + } + + /* + * Should not be necessary because the MP table should list the boot + * CPU too, but we do it for the sake of robustness anyway. + */ + if (!test_bit(boot_cpu_id, &phys_cpu_present_map)) { + printk("weird, boot CPU (#%d) not listed by the BIOS.\n", + boot_cpu_id); + phys_cpu_present_map |= (1 << hard_smp_processor_id()); + } + + /* + * If we couldn't find a local APIC, then get out of here now! + */ + if (APIC_INTEGRATED(apic_version[boot_cpu_id]) && + !test_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability)) { + printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n", + boot_cpu_id); + printk(KERN_ERR "... forcing use of dummy APIC emulation. (tell your hw vendor)\n"); +#ifndef CONFIG_VISWS + io_apic_irqs = 0; +#endif + cpu_online_map = phys_cpu_present_map = 1; + smp_num_cpus = 1; + goto smp_done; + } + + verify_local_APIC(); + + /* + * If SMP should be disabled, then really disable it! + */ + if (!max_cpus) { + smp_found_config = 0; + printk(KERN_INFO "SMP mode deactivated, forcing use of dummy APIC emulation.\n"); +#ifndef CONFIG_VISWS + io_apic_irqs = 0; +#endif + cpu_online_map = phys_cpu_present_map = 1; + smp_num_cpus = 1; + goto smp_done; + } + + connect_bsp_APIC(); + setup_local_APIC(); + + if (GET_APIC_ID(apic_read(APIC_ID)) != boot_cpu_id) + BUG(); + + /* + * Now scan the CPU present map and fire up the other CPUs. + */ + Dprintk("CPU present map: %lx\n", phys_cpu_present_map); + + for (apicid = 0; apicid < NR_CPUS; apicid++) { + /* + * Don't even attempt to start the boot CPU! + */ + if (apicid == boot_cpu_id) + continue; + + if (!(phys_cpu_present_map & (1 << apicid))) + continue; + if ((max_cpus >= 0) && (max_cpus <= cpucount+1)) + continue; + + do_boot_cpu(apicid); + + /* + * Make sure we unmap all failed CPUs + */ + if ((x86_apicid_to_cpu[apicid] == -1) && + (phys_cpu_present_map & (1 << apicid))) + printk("phys CPU #%d not responding - cannot use it.\n",apicid); + } + + /* + * Cleanup possible dangling ends... + */ +#ifndef CONFIG_VISWS + { + /* + * Install writable page 0 entry to set BIOS data area. + */ + local_flush_tlb(); + + /* + * Paranoid: Set warm reset code and vector here back + * to default values. + */ + CMOS_WRITE(0, 0xf); + + *((volatile long *) phys_to_virt(0x467)) = 0; + } +#endif + + /* + * Allow the user to impress friends. + */ + + Dprintk("Before bogomips.\n"); + if (!cpucount) { + printk(KERN_ERR "Error: only one processor found.\n"); + } else { + unsigned long bogosum = 0; + for (cpu = 0; cpu < NR_CPUS; cpu++) + if (cpu_online_map & (1< +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way Unix traditionally does this, though. + */ +asmlinkage long sys_pipe(unsigned long * fildes) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +long sys_mmap(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long off) +{ + long error; + struct file * file; + + error = -EINVAL; + if (off & ~PAGE_MASK) + goto out; + + error = -EBADF; + file = NULL; + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, off >> PAGE_SHIFT); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) +{ + struct vm_area_struct *vma; + unsigned long end = TASK_SIZE; + + if (flags & MAP_32BIT) + end = 0xffffffff; + if (len > TASK_SIZE) + return -ENOMEM; + if (!addr) { + addr = TASK_UNMAPPED_64; + if ((current->thread.flags & THREAD_IA32) || (flags & MAP_32BIT)) { + addr = TASK_UNMAPPED_32; + } + } + addr = PAGE_ALIGN(addr); + + for (vma = find_vma(current->mm, addr); ; vma = vma->vm_next) { + /* At this point: (!vma || addr < vma->vm_end). */ + if (TASK_SIZE - len < addr) + return -ENOMEM; + if (!vma || addr + len <= vma->vm_start) + return addr; + addr = vma->vm_end; + } +} + +/* + * Old cruft + */ +asmlinkage long sys_uname(struct old_utsname * name) +{ + int err; + if (!name) + return -EFAULT; + down_read(&uts_sem); + err=copy_to_user(name, &system_utsname, sizeof (*name)); + up_read(&uts_sem); + return err?-EFAULT:0; +} + +asmlinkage long sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/syscall.c linux.ac/arch/x86_64/kernel/syscall.c --- linux.vanilla/arch/x86_64/kernel/syscall.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/syscall.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,25 @@ +/* System call table for x86-64. */ + +#include +#include +#include + +#define __NO_STUBS + +#define __SYSCALL(nr, sym) extern asmlinkage void sym(void) ; +#undef _ASM_X86_64_UNISTD_H_ +#include + +#undef __SYSCALL +#define __SYSCALL(nr, sym) [ nr ] = sym, +#undef _ASM_X86_64_UNISTD_H_ + +typedef void (*sys_call_ptr_t)(void); + +extern void sys_ni_syscall(void); + +sys_call_ptr_t sys_call_table[__NR_syscall_max+1] __cacheline_aligned = { + /* Smells like a like a compiler bug -- it doesn't work when the & below is removed. */ + [0 ... __NR_syscall_max] = &sys_ni_syscall, +#include +}; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/time.c linux.ac/arch/x86_64/kernel/time.c --- linux.vanilla/arch/x86_64/kernel/time.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/time.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,685 @@ +/* + * linux/arch/i386/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * + * This file contains the PC-specific time handling details: + * reading the RTC at bootup, etc.. + * 1994-07-02 Alan Modra + * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime + * 1995-03-26 Markus Kuhn + * fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887 + * precision CMOS clock update + * 1996-05-03 Ingo Molnar + * fixed time warps in do_[slow|fast]_gettimeoffset() + * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills + * 1998-09-05 (Various) + * More robust do_fast_gettimeoffset() algorithm implemented + * (works with APM, Cyrix 6x86MX and Centaur C6), + * monotonic gettimeofday() with fast_get_timeoffset(), + * drift-proof precision TSC calibration on boot + * (C. Scott Ananian , Andrew D. + * Balsa , Philip Gladstone ; + * ported from 2.0.35 Jumbo-9 by Michael Krause ). + * 1998-12-16 Andrea Arcangeli + * Fixed Jumbo-9 code in 2.1.131: do_gettimeofday was missing 1 jiffy + * because was not accounting lost_ticks. + * 1998-12-24 Copyright (C) 1998 Andrea Arcangeli + * Fixed a xtime SMP race (we need the xtime_lock rw spinlock to + * serialize accesses to xtime/lost_ticks). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* + * for x86_do_profile() + */ +#include + + +unsigned long cpu_khz; /* Detected as we calibrate the TSC */ + +/* Number of usecs that the last interrupt was delayed */ +int __delay_at_last_interrupt __section_delay_at_last_interrupt; + +unsigned long __last_tsc_low __section_last_tsc_low; /* lsb 32 bits of Time Stamp Counter */ + +/* Cached *multiplier* to convert TSC counts to microseconds. + * (see the equation below). + * Equal to 2^32 * (1 / (clocks per usec) ). + * Initialized in time_init. + */ +unsigned long __fast_gettimeoffset_quotient __section_fast_gettimeoffset_quotient; + +extern rwlock_t xtime_lock; +struct timeval __xtime __section_xtime; +volatile unsigned long __jiffies __section_jiffies; +unsigned long __wall_jiffies __section_wall_jiffies; +struct timezone __sys_tz __section_sys_tz; + +spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; + +static inline unsigned long do_fast_gettimeoffset(void) +{ + register unsigned long eax, edx; + + /* Read the Time Stamp Counter */ + + rdtsc(eax,edx); + + /* .. relative to previous jiffy (32 bits is enough) */ + eax -= last_tsc_low; /* tsc_low delta */ + + /* + * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient + * = (tsc_low delta) * (usecs_per_clock) + * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy) + * + * Using a mull instead of a divl saves up to 31 clock cycles + * in the critical path. + */ + + edx = (eax*fast_gettimeoffset_quotient) >> 32; + + /* our adjusted time offset in microseconds */ + return delay_at_last_interrupt + edx; +} + +#define TICK_SIZE tick + +spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED; + +extern spinlock_t i8259A_lock; + +#ifndef CONFIG_X86_TSC + +/* This function must be called with interrupts disabled + * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs + * + * However, the pc-audio speaker driver changes the divisor so that + * it gets interrupted rather more often - it loads 64 into the + * counter rather than 11932! This has an adverse impact on + * do_gettimeoffset() -- it stops working! What is also not + * good is that the interval that our timer function gets called + * is no longer 10.0002 ms, but 9.9767 ms. To get around this + * would require using a different timing source. Maybe someone + * could use the RTC - I know that this can interrupt at frequencies + * ranging from 8192Hz to 2Hz. If I had the energy, I'd somehow fix + * it so that at startup, the timer code in sched.c would select + * using either the RTC or the 8253 timer. The decision would be + * based on whether there was any other device around that needed + * to trample on the 8253. I'd set up the RTC to interrupt at 1024 Hz, + * and then do some jiggery to have a version of do_timer that + * advanced the clock by 1/1024 s. Every time that reached over 1/100 + * of a second, then do all the old code. If the time was kept correct + * then do_gettimeoffset could just return 0 - there is no low order + * divider that can be accessed. + * + * Ideally, you would be able to use the RTC for the speaker driver, + * but it appears that the speaker driver really needs interrupt more + * often than every 120 us or so. + * + * Anyway, this needs more thought.... pjsg (1993-08-28) + * + * If you are really that interested, you should be reading + * comp.protocols.time.ntp! + */ + +static unsigned long do_slow_gettimeoffset(void) +{ + int count; + + static int count_p = LATCH; /* for the first call after boot */ + static unsigned long jiffies_p = 0; + + /* + * cache volatile jiffies temporarily; we have IRQs turned off. + */ + unsigned long jiffies_t; + + /* gets recalled with irq locally disabled */ + spin_lock(&i8253_lock); + /* timer count may underflow right here */ + outb_p(0x00, 0x43); /* latch the count ASAP */ + + count = inb_p(0x40); /* read the latched count */ + + /* + * We do this guaranteed double memory access instead of a _p + * postfix in the previous port access. Wheee, hackady hack + */ + jiffies_t = jiffies; + + count |= inb_p(0x40) << 8; + + /* VIA686a test code... reset the latch if count > max + 1 */ + if (count > LATCH) { + outb_p(0x34, 0x43); + outb_p(LATCH & 0xff, 0x40); + outb(LATCH >> 8, 0x40); + count = LATCH - 1; + } + + spin_unlock(&i8253_lock); + + /* + * avoiding timer inconsistencies (they are rare, but they happen)... + * there are two kinds of problems that must be avoided here: + * 1. the timer counter underflows + * 2. hardware problem with the timer, not giving us continuous time, + * the counter does small "jumps" upwards on some Pentium systems, + * (see c't 95/10 page 335 for Neptun bug.) + */ + + if( jiffies_t == jiffies_p ) { + if( count > count_p ) { + /* the nutcase */ + + int i; + + spin_lock(&i8259A_lock); + /* + * This is tricky when I/O APICs are used; + * see do_timer_interrupt(). + */ + i = inb(0x20); + spin_unlock(&i8259A_lock); + + /* assumption about timer being IRQ0 */ + if (i & 0x01) { + /* + * We cannot detect lost timer interrupts ... + * well, that's why we call them lost, don't we? :) + * [hmm, on the Pentium and Alpha we can ... sort of] + */ + count -= LATCH; + } else + printk("do_slow_gettimeoffset(): hardware timer problem?\n"); + } + } else + jiffies_p = jiffies_t; + + count_p = count; + + count = ((LATCH-1) - count) * TICK_SIZE; + count = (count + LATCH/2) / LATCH; + + return count; +} + +static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; + +#else + +#define do_gettimeoffset() do_fast_gettimeoffset() + +#endif + +/* + * This version of gettimeofday has microsecond resolution + * and better than microsecond precision on fast x86 machines with TSC. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + unsigned long usec, sec; + + read_lock_irqsave(&xtime_lock, flags); + usec = do_gettimeoffset(); + { + unsigned long lost = jiffies - wall_jiffies; + if (lost) + usec += lost * (1000000 / HZ); + } + sec = xtime.tv_sec; + usec += xtime.tv_usec; + read_unlock_irqrestore(&xtime_lock, flags); + + while (usec >= 1000000) { + usec -= 1000000; + sec++; + } + + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +void do_settimeofday(struct timeval *tv) +{ + write_lock_irq(&xtime_lock); + vxtime_lock(); + /* + * This is revolting. We need to set "xtime" correctly. However, the + * value in this location is the value at the most recent update of + * wall time. Discover what correction gettimeofday() would have + * made, and then undo it! + */ + tv->tv_usec -= do_gettimeoffset(); + tv->tv_usec -= (jiffies - wall_jiffies) * (1000000 / HZ); + + while (tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } + + xtime = *tv; + vxtime_unlock(); + + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_unlock_irq(&xtime_lock); +} + +/* + * In order to set the CMOS clock precisely, set_rtc_mmss has to be + * called 500 ms after the second nowtime has started, because when + * nowtime is written into the registers of the CMOS clock, it will + * jump to the next second precisely 500 ms later. Check the Motorola + * MC146818A or Dallas DS12887 data sheet for details. + * + * BUG: This routine does not handle hour overflow properly; it just + * sets the minutes. Usually you'll only notice that after reboot! + */ +static int set_rtc_mmss(unsigned long nowtime) +{ + int retval = 0; + int real_seconds, real_minutes, cmos_minutes; + unsigned char save_control, save_freq_select; + + /* gets recalled with irq locally disabled */ + spin_lock(&rtc_lock); + save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */ + CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); + + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */ + CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + cmos_minutes = CMOS_READ(RTC_MINUTES); + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + BCD_TO_BIN(cmos_minutes); + + /* + * since we're only adjusting minutes and seconds, + * don't interfere with hour overflow. This avoids + * messing with unknown time zones but requires your + * RTC not to be off by more than 15 minutes + */ + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) + real_minutes += 30; /* correct for half hour time zone */ + real_minutes %= 60; + + if (abs(real_minutes - cmos_minutes) < 30) { + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(real_seconds); + BIN_TO_BCD(real_minutes); + } + CMOS_WRITE(real_seconds,RTC_SECONDS); + CMOS_WRITE(real_minutes,RTC_MINUTES); + } else { + printk(KERN_WARNING + "set_rtc_mmss: can't update from %d to %d\n", + cmos_minutes, real_minutes); + retval = -1; + } + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + spin_unlock(&rtc_lock); + + return retval; +} + +/* last time the cmos clock got updated */ +static long last_rtc_update; + +int timer_ack; + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ +static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +#ifdef CONFIG_X86_IO_APIC + if (timer_ack) { + /* + * Subtle, when I/O APICs are used we have to ack timer IRQ + * manually to reset the IRR bit for do_slow_gettimeoffset(). + * This will also deassert NMI lines for the watchdog if run + * on an 82489DX-based system. + */ + spin_lock(&i8259A_lock); + outb(0x0c, 0x20); + /* Ack the IRQ; AEOI will end it automatically. */ + inb(0x20); + spin_unlock(&i8259A_lock); + } +#endif + +#ifdef CONFIG_VISWS + /* Clear the interrupt */ + co_cpu_write(CO_CPU_STAT,co_cpu_read(CO_CPU_STAT) & ~CO_STAT_TIMEINTR); +#endif + do_timer(regs); +/* + * In the SMP case we use the local APIC timer interrupt to do the + * profiling, except when we simulate SMP mode on a uniprocessor + * system, in that case we have to call the local interrupt handler. + */ +#ifndef CONFIG_X86_LOCAL_APIC + if (!user_mode(regs)) + x86_do_profile(regs->rip); +#else + if (!using_apic_timer) + smp_local_timer_interrupt(regs); +#endif + + /* + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && + xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) { + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + } +} + +static int use_tsc; + +/* + * This is the same as the above, except we _also_ save the current + * Time Stamp Counter value at the time of the timer interrupt, so that + * we later on can estimate the time of day more exactly. + */ +static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int count; + + /* + * Here we are in the timer irq handler. We just have irqs locally + * disabled but we don't know if the timer_bh is running on the other + * CPU. We need to avoid to SMP race with it. NOTE: we don' t need + * the irq version of write_lock because as just said we have irq + * locally disabled. -arca + */ + write_lock(&xtime_lock); + vxtime_lock(); + + if (use_tsc) + { + /* + * It is important that these two operations happen almost at + * the same time. We do the RDTSC stuff first, since it's + * faster. To avoid any inconsistencies, we need interrupts + * disabled locally. + */ + + /* + * Interrupts are just disabled locally since the timer irq + * has the SA_INTERRUPT flag set. -arca + */ + + /* read Pentium cycle counter */ + + rdtscl(last_tsc_low); + + spin_lock(&i8253_lock); + outb_p(0x00, 0x43); /* latch the count ASAP */ + + count = inb_p(0x40); /* read the latched count */ + count |= inb(0x40) << 8; + + + /* VIA686a test code... reset the latch if count > max */ + if (count > LATCH) { + static int last_whine; + outb_p(0x34, 0x43); + outb_p(LATCH & 0xff, 0x40); + outb(LATCH >> 8, 0x40); + count = LATCH - 1; + if(time_after(jiffies, last_whine)) + { + printk(KERN_WARNING "probable hardware bug: clock timer configuration lost - probably a VIA686a motherboard.\n"); + printk(KERN_WARNING "probable hardware bug: restoring chip configuration.\n"); + last_whine = jiffies + HZ; + } + } + + + spin_unlock(&i8253_lock); + + count = ((LATCH-1) - count) * TICK_SIZE; + delay_at_last_interrupt = (count + LATCH/2) / LATCH; + } + + do_timer_interrupt(irq, NULL, regs); + + vxtime_unlock(); + write_unlock(&xtime_lock); + +} + +/* not static: needed by APM */ +unsigned long get_cmos_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + + /* The Linux interpretation of the CMOS clock register contents: + * When the Update-In-Progress (UIP) flag goes from 1 to 0, the + * RTC registers show the second which has precisely just started. + * Let's hope other operating systems interpret the RTC the same way. + */ +#ifndef CONFIG_SIMNOW + int i; + /* FIXME: This would take eons in emulated environment */ + /* read RTC exactly on falling edge of update flag */ + for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ + if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) + break; + for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ + if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) + break; +#endif + do { /* Isn't this overkill ? UIP above should guarantee consistency */ + sec = CMOS_READ(RTC_SECONDS); + min = CMOS_READ(RTC_MINUTES); + hour = CMOS_READ(RTC_HOURS); + day = CMOS_READ(RTC_DAY_OF_MONTH); + mon = CMOS_READ(RTC_MONTH); + year = CMOS_READ(RTC_YEAR); + } while (sec != CMOS_READ(RTC_SECONDS)); + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + if ((year += 1900) < 1970) + year += 100; + return mktime(year, mon, day, hour, min, sec); +} + +static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL}; + +/* ------ Calibrate the TSC ------- + * Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset(). + * Too much 64-bit arithmetic here to do this cleanly in C, and for + * accuracy's sake we want to keep the overhead on the CTC speaker (channel 2) + * output busy loop as low as possible. We avoid reading the CTC registers + * directly because of the awkward 8-bit access mechanism of the 82C54 + * device. + */ + +#define CALIBRATE_LATCH (5 * LATCH) +#define CALIBRATE_TIME (5 * 1000020/HZ) + +static unsigned long __init calibrate_tsc(void) +{ + /* Set the Gate high, disable speaker */ + outb((inb(0x61) & ~0x02) | 0x01, 0x61); + + /* + * Now let's take care of CTC channel 2 + * + * Set the Gate high, program CTC channel 2 for mode 0, + * (interrupt on terminal count mode), binary count, + * load 5 * LATCH count, (LSB and MSB) to begin countdown. + */ + outb(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */ + outb(CALIBRATE_LATCH & 0xff, 0x42); /* LSB of count */ + outb(CALIBRATE_LATCH >> 8, 0x42); /* MSB of count */ + + { + unsigned long start; + unsigned long end; + unsigned long count; + + { + int low, high; + rdtsc(low,high); + start = ((u64)high)<<32 | low; + } + count = 0; + do { + count++; + } while ((inb(0x61) & 0x20) == 0); + + { + int low, high; + rdtsc(low,high); + end = ((u64)high)<<32 | low; + last_tsc_low = low; + } + + + /* Error: ECTCNEVERSET */ + if (count <= 1) + goto bad_ctc; + + end -= start; + + /* Error: ECPUTOOSLOW */ + if (end <= CALIBRATE_TIME) + goto bad_ctc; + + end = (((u64)CALIBRATE_TIME)<<32)/end; + return end; + } + + /* + * The CTC wasn't reliable: we got a hit on the very first read, + * or the CPU was so fast/slow that the quotient wouldn't fit in + * 32 bits.. + */ +bad_ctc: + return 0; +} + +void __init time_init(void) +{ + extern int x86_udelay_tsc; + + xtime.tv_sec = get_cmos_time(); + xtime.tv_usec = 0; + +/* + * If we have APM enabled or the CPU clock speed is variable + * (CPU stops clock on HLT or slows clock to save power) + * then the TSC timestamps may diverge by up to 1 jiffy from + * 'real time' but nothing will break. + * The most frequent case is that the CPU is "woken" from a halt + * state by the timer interrupt itself, so we get 0 error. In the + * rare cases where a driver would "wake" the CPU and request a + * timestamp, the maximum error is < 1 jiffy. But timestamps are + * still perfectly ordered. + * Note that the TSC counter will be reset if APM suspends + * to disk; this won't break the kernel, though, 'cuz we're + * smart. See arch/i386/kernel/apm.c. + */ + /* + * Firstly we have to do a CPU check for chips with + * a potentially buggy TSC. At this point we haven't run + * the ident/bugs checks so we must run this hook as it + * may turn off the TSC flag. + * + * NOTE: this doesnt yet handle SMP 486 machines where only + * some CPU's have a TSC. Thats never worked and nobody has + * moaned if you have the only one in the world - you fix it! + */ + + dodgy_tsc(); + + if (cpu_has_tsc) { + unsigned long tsc_quotient = calibrate_tsc(); + if (tsc_quotient) { + fast_gettimeoffset_quotient = tsc_quotient; + use_tsc = 1; + /* + * We could be more selective here I suspect + * and just enable this for the next intel chips ? + */ + x86_udelay_tsc = 1; +#ifndef do_gettimeoffset + do_gettimeoffset = do_fast_gettimeoffset; +#endif + do_get_fast_time = do_gettimeofday; + + /* report CPU clock rate in Hz. + * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) = + * clock/second. Our precision is about 100 ppm. + */ + { + cpu_khz = ((1000000*(1UL<<32)) / tsc_quotient); /* FIXME: is it right? */ + printk("Detected %ld Hz processor.\n", cpu_khz); + } + } + } + + setup_irq(0, &irq0); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/trampoline.S linux.ac/arch/x86_64/kernel/trampoline.S --- linux.vanilla/arch/x86_64/kernel/trampoline.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/trampoline.S Wed Oct 10 01:47:38 2001 @@ -0,0 +1,72 @@ +/* + * + * Trampoline.S Derived from Setup.S by Linus Torvalds + * + * 4 Jan 1997 Michael Chastain: changed to gnu as. + * + * Entry: CS:IP point to the start of our code, we are + * in real mode with no stack, but the rest of the + * trampoline page to make our stack and everything else + * is a mystery. + * + * In fact we don't actually need a stack so we don't + * set one up. + * + * On entry to trampoline_data, the processor is in real mode + * with 16-bit addressing and 16-bit data. CS has some value + * and IP is zero. Thus, data addresses need to be absolute + * (no relocation) and are taken with regard to r_base. + * + * If you work on this file, check the object module with objdump + * --full-contents --reloc to make sure there are no relocation + * entries. For the GDT entry we do hand relocation in smpboot.c + * because of 64bit linker limitations. + */ + +#include +#include +#include + +.data + +.code16 + +ENTRY(trampoline_data) +r_base = . + + mov %cs, %ax # Code and data in the same place + mov %ax, %ds + + mov $1, %bx # Flag an SMP trampoline + cli # We should be safe anyway + + movl $0xA5A5A5A5, trampoline_data - r_base + # write marker for master knows we're running + + lidt idt_48 - r_base # load idt with 0, 0 + lgdt gdt_48 - r_base # load gdt with whatever is appropriate + + xor %ax, %ax + inc %ax # protected mode (PE) bit + lmsw %ax # into protected mode + jmp flush_instr +flush_instr: + ljmpl $__KERNEL_CS, $0x00100000 + # jump to startup_32 in arch/i386/kernel/head.S + +idt_48: + .word 0 # idt limit = 0 + .word 0, 0 # idt base = 0L + +gdt_48: + .short 0x0800 # gdt limit = 2048, 256 GDT entries + .globl tramp_gdt_ptr +tramp_gdt_ptr: + .long 0 # gdt base = gdt (first SMP CPU) + # this is filled in by C because the 64bit + # linker doesn't support absolute 32bit + # relocations. + + +.globl SYMBOL_NAME(trampoline_end) +SYMBOL_NAME_LABEL(trampoline_end) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/traps.c linux.ac/arch/x86_64/kernel/traps.c --- linux.vanilla/arch/x86_64/kernel/traps.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/traps.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,745 @@ +/* + * linux/arch/x86-64/traps.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 2000 Andi Kleen, SuSE Labs + * + * Pentium III FXSR, SSE support + * Gareth Hughes , May 2000 + */ + +/* + * 'Traps.c' handles hardware traps and faults after we have saved some + * state in 'entry.S'. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +asmlinkage int system_call(void); +asmlinkage int kernel_syscall(void); +extern void ia32_syscall(void); + +char doublefault_stack[4*1024]; +#ifndef CONFIG_SMP +char stackfault_stack[4*1024]; +#endif + +/* We really need to get rid of F0 0F workaround. Setting up new idts + is not an option */ + +struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, + { 0, 0 }, { 0, 0 } }; + +extern struct gate_struct idt_table[256]; + +asmlinkage void divide_error(void); +asmlinkage void debug(void); +asmlinkage void nmi(void); +asmlinkage void int3(void); +asmlinkage void overflow(void); +asmlinkage void bounds(void); +asmlinkage void invalid_op(void); +asmlinkage void device_not_available(void); +asmlinkage void double_fault(void); +asmlinkage void coprocessor_segment_overrun(void); +asmlinkage void invalid_TSS(void); +asmlinkage void segment_not_present(void); +asmlinkage void stack_segment(void); +asmlinkage void general_protection(void); +asmlinkage void page_fault(void); +asmlinkage void coprocessor_error(void); +asmlinkage void simd_coprocessor_error(void); +asmlinkage void reserved(void); +asmlinkage void alignment_check(void); +asmlinkage void spurious_interrupt_bug(void); + +struct notifier_block *die_chain; + +int kstack_depth_to_print = 24; + +/* + * If the address is either in the .text section of the + * kernel, or in the vmalloc'ed module regions, it *may* + * be the address of a calling routine + */ + +#ifdef CONFIG_MODULES + +extern struct module *module_list; +extern struct module kernel_module; + +static int kernel_text_address(unsigned long addr) +{ + int retval = 0; + struct module *mod; + + if (addr >= (unsigned long) &_stext && + addr <= (unsigned long) &_etext) + return 1; + + for (mod = module_list; mod != &kernel_module; mod = mod->next) { + /* mod_bound tests for addr being inside the vmalloc'ed + * module area. Of course it'd be better to test only + * for the .text subset... */ + if (mod_bound(addr, 0, mod)) { + retval = 1; + break; + } + } + + return retval; +} + +#else + +static inline int kernel_text_address(unsigned long addr) +{ + return (addr >= (unsigned long) &_stext && + addr <= (unsigned long) &_etext); +} + +#endif + + +void show_trace(unsigned long *stack) +{ + unsigned long module_start, module_end,addr; + unsigned long *irqstack, *irqstack_end; + /* FIXME: should read the cpuid from the APIC; to still work with bogus %gs */ + const int cpu = smp_processor_id(); + int i; + + printk("\nCall Trace: "); + module_start = VMALLOC_START; + module_end = VMALLOC_END; + + irqstack = (unsigned long *) &(cpu_pda[cpu].irqstack); + irqstack_end = (unsigned long *) ((char *)irqstack + sizeof_field(struct x8664_pda, irqstack)); + + i = 1; + if (stack >= irqstack && stack < irqstack_end) { + while (stack < irqstack_end) { + addr = *stack++; + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (kernel_text_address(addr)) { + if (i && ((i % 6) == 0)) + printk("\n "); + printk("[<%016lx>] ", addr); + i++; + } + } + stack = (unsigned long *) (irqstack_end[-1]); + printk(" "); +#if 1 + if (stack < (unsigned long *)current || (char*)stack > ((char*)current)+THREAD_SIZE) + printk("\n" KERN_DEBUG "no stack at the end of irqstack; stack:%p, cur:%p/%p\n", + stack, current, ((char*)current)+THREAD_SIZE); +#endif + } + + + + while (((long) stack & (THREAD_SIZE-1)) != 0) { + addr = *stack++; + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (kernel_text_address(addr)) { + if (i && ((i % 6) == 0)) + printk("\n "); + printk("[<%016lx>] ", addr); + i++; + } + } + printk("\n"); +} + +void show_trace_task(struct task_struct *tsk) +{ + unsigned long rsp = tsk->thread.rsp; + + /* User space on another CPU? */ + if ((rsp ^ (unsigned long)tsk) & (PAGE_MASK<<1)) + return; + show_trace((unsigned long *)rsp); +} + +void show_stack(unsigned long * esp) +{ + unsigned long *stack; + int i; + + // debugging aid: "show_stack(NULL);" prints the + // back trace for this cpu. + + if(esp==NULL) + esp=(unsigned long*)&esp; + + stack = esp; + for(i=0; i < kstack_depth_to_print; i++) { + if (((long) stack & (THREAD_SIZE-1)) == 0) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%016lx ", *stack++); + } +} + +extern void dump_pagetable(void); + +void show_registers(struct pt_regs *regs) +{ + int i; + int in_kernel = 1; + unsigned long rsp; +#ifdef CONFIG_SMP + /* For SMP should get the APIC id here, just to protect against corrupted GS */ + const int cpu = smp_processor_id(); +#else + const int cpu = 0; +#endif + struct task_struct *cur = cpu_pda[cpu].pcurrent; + + rsp = (unsigned long) (®s->rsp); + if (regs->rsp < TASK_SIZE) { + in_kernel = 0; + rsp = regs->rsp; + } + printk("CPU %d ", cpu); + show_regs(regs); + printk("Process %s (pid: %d, stackpage=%08lx)\n", + cur->comm, cur->pid, 4096+(unsigned long)cur); + + dump_pagetable(); + + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (in_kernel) { + + printk("Stack: "); + show_stack((unsigned long*)rsp); + + printk("\nCode: "); + if(regs->rip < PAGE_OFFSET) + goto bad; + + for(i=0;i<20;i++) + { + unsigned char c; + if(__get_user(c, &((unsigned char*)regs->rip)[i])) { +bad: + printk(" Bad EIP value."); + break; + } + printk("%02x ", c); + } + } + printk("\n"); + show_trace(rsp); +} + +spinlock_t die_lock = SPIN_LOCK_UNLOCKED; + +void die(const char * str, struct pt_regs * regs, long err) +{ + struct die_args args = { regs, str, err }; + console_verbose(); + notifier_call_chain(&die_chain, DIE_DIE, &args); + spin_lock_irq(&die_lock); + printk("%s: %04lx\n", str, err & 0xffff); + show_registers(regs); + + spin_unlock_irq(&die_lock); + do_exit(SIGSEGV); +} + +static inline void die_if_kernel(const char * str, struct pt_regs * regs, long err) +{ + if (!(regs->eflags & VM_MASK) && (regs->rip >= TASK_SIZE)) + die(str, regs, err); +} + +static inline unsigned long get_cr2(void) +{ + unsigned long address; + + /* get the address */ + __asm__("movq %%cr2,%0":"=r" (address)); + return address; +} + +static void inline do_trap(int trapnr, int signr, char *str, int vm86, + struct pt_regs * regs, long error_code, siginfo_t *info) +{ + if (regs->rip >= TASK_SIZE) + goto kernel_trap; + + { + struct task_struct *tsk = current; + tsk->thread.error_code = error_code; + tsk->thread.trap_no = trapnr; + if (info) + force_sig_info(signr, info, tsk); + else + force_sig(signr, tsk); + return; + } + + kernel_trap: { + unsigned long fixup = search_exception_table(regs->rip); + if (fixup) + regs->rip = fixup; + else + die(str, regs, error_code); + return; + } +} + +#define DO_ERROR(trapnr, signr, str, name) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \ +} + +#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + siginfo_t info; \ + info.si_signo = signr; \ + info.si_errno = 0; \ + info.si_code = sicode; \ + info.si_addr = (void *)siaddr; \ + do_trap(trapnr, signr, str, 0, regs, error_code, &info); \ +} + +#define DO_VM86_ERROR(trapnr, signr, str, name) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \ +} + +#define DO_VM86_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + siginfo_t info; \ + info.si_signo = signr; \ + info.si_errno = 0; \ + info.si_code = sicode; \ + info.si_addr = (void *)siaddr; \ + do_trap(trapnr, signr, str, 1, regs, error_code, &info); \ +} + +DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->rip) +DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow) +DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds) +DO_ERROR_INFO( 6, SIGILL, "invalid operand", invalid_op, ILL_ILLOPN, regs->rip) +DO_VM86_ERROR( 7, SIGSEGV, "device not available", device_not_available) +DO_ERROR( 8, SIGSEGV, "double fault", double_fault) +DO_ERROR( 9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun) +DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS) +DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) +DO_ERROR(12, SIGBUS, "stack segment", stack_segment) +DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, get_cr2()) +DO_ERROR(18, SIGSEGV, "reserved", reserved) + +asmlinkage void do_int3(struct pt_regs * regs, long error_code) +{ + struct die_args args = { regs, "int3", error_code }; + notifier_call_chain(&die_chain, DIE_INT3, &args); + do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL); +} + +asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) +{ + if ((regs->cs & 3)==0) + goto gp_in_kernel; + + current->thread.error_code = error_code; + current->thread.trap_no = 13; + force_sig(SIGSEGV, current); + return; + +gp_in_kernel: + { + unsigned long fixup; + fixup = search_exception_table(regs->rip); + if (fixup) { + regs->rip = fixup; + return; + } + die("general protection fault", regs, error_code); + } +} + +static void mem_parity_error(unsigned char reason, struct pt_regs * regs) +{ + printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n"); + printk("You probably have a hardware problem with your RAM chips\n"); + + /* Clear and disable the memory parity error line. */ + reason = (reason & 0xf) | 4; + outb(reason, 0x61); +} + +static void io_check_error(unsigned char reason, struct pt_regs * regs) +{ + printk("NMI: IOCK error (debug interrupt?)\n"); + show_registers(regs); + + /* Re-enable the IOCK line, wait for a few seconds */ + reason = (reason & 0xf) | 8; + outb(reason, 0x61); + mdelay(2000); + reason &= ~8; + outb(reason, 0x61); +} + +static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs) +{ + printk("Uhhuh. NMI received for unknown reason %02x.\n", reason); + printk("Dazed and confused, but trying to continue\n"); + printk("Do you have a strange power saving mode enabled?\n"); +} + +asmlinkage void do_nmi(struct pt_regs * regs) +{ + unsigned char reason = inb(0x61); + + + ++nmi_count(smp_processor_id()); + if (!(reason & 0xc0)) { +#if CONFIG_X86_LOCAL_APIC + /* + * Ok, so this is none of the documented NMI sources, + * so it must be the NMI watchdog. + */ + if (nmi_watchdog) { + nmi_watchdog_tick(regs); + return; + } else + unknown_nmi_error(reason, regs); +#else + unknown_nmi_error(reason, regs); +#endif + return; + } + if (reason & 0x80) + mem_parity_error(reason, regs); + if (reason & 0x40) + io_check_error(reason, regs); + /* + * Reassert NMI in case it became active meanwhile + * as it's edge-triggered. + */ + outb(0x8f, 0x70); + inb(0x71); /* dummy */ + outb(0x0f, 0x70); + inb(0x71); /* dummy */ +} + +asmlinkage void do_debug(struct pt_regs * regs, long error_code) +{ + unsigned long condition; + struct task_struct *tsk = current; + siginfo_t info; + + asm("movq %%db6,%0" : "=r" (condition)); + + /* Mask out spurious debug traps due to lazy DR7 setting */ + if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { + if (!tsk->thread.debugreg[7]) { + goto clear_dr7; + } + } + + tsk->thread.debugreg[6] = condition; + + /* Mask out spurious TF errors due to lazy TF clearing */ + if (condition & DR_STEP) { + /* + * The TF error should be masked out only if the current + * process is not traced and if the TRAP flag has been set + * previously by a tracing process (condition detected by + * the PT_DTRACE flag); remember that the i386 TRAP flag + * can be modified by the process itself in user mode, + * allowing programs to debug themselves without the ptrace() + * interface. + */ + if ((tsk->ptrace & (PT_DTRACE|PT_PTRACED)) == PT_DTRACE) + goto clear_TF; + } + + /* Ok, finally something we can handle */ + /* XXX: add die_chain here */ + tsk->thread.trap_no = 1; + tsk->thread.error_code = error_code; + info.si_signo = SIGTRAP; + info.si_errno = 0; + info.si_code = TRAP_BRKPT; + info.si_addr = ((regs->cs & 3) == 0) ? (void *)tsk->thread.rip : + (void *)regs->rip; + force_sig_info(SIGTRAP, &info, tsk); +clear_dr7: + asm("movq %0,%%db7"::"r"(0UL)); + return; + +clear_TF: + regs->eflags &= ~TF_MASK; + return; +} + +/* + * Note that we play around with the 'TS' bit in an attempt to get + * the correct behaviour even in the presence of the asynchronous + * IRQ13 behaviour + */ +void math_error(void *eip) +{ + struct task_struct * task; + siginfo_t info; + unsigned short cwd, swd; + + /* + * Save the info for the exception handler and clear the error. + */ + task = current; + save_init_fpu(task); + task->thread.trap_no = 16; + task->thread.error_code = 0; + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_code = __SI_FAULT; + info.si_addr = eip; + /* + * (~cwd & swd) will mask out exceptions that are not set to unmasked + * status. 0x3f is the exception bits in these regs, 0x200 is the + * C1 reg you need in case of a stack fault, 0x040 is the stack + * fault bit. We should only be taking one exception at a time, + * so if this combination doesn't produce any single exception, + * then we have a bad program that isn't syncronizing its FPU usage + * and it will suffer the consequences since we won't be able to + * fully reproduce the context of the exception + */ + cwd = get_fpu_cwd(task); + swd = get_fpu_swd(task); + switch (((~cwd) & swd & 0x3f) | (swd & 0x240)) { + case 0x000: + default: + break; + case 0x001: /* Invalid Op */ + case 0x040: /* Stack Fault */ + case 0x240: /* Stack Fault | Direction */ + info.si_code = FPE_FLTINV; + break; + case 0x002: /* Denormalize */ + case 0x010: /* Underflow */ + info.si_code = FPE_FLTUND; + break; + case 0x004: /* Zero Divide */ + info.si_code = FPE_FLTDIV; + break; + case 0x008: /* Overflow */ + info.si_code = FPE_FLTOVF; + break; + case 0x020: /* Precision */ + info.si_code = FPE_FLTRES; + break; + } + force_sig_info(SIGFPE, &info, task); +} + +asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code) +{ + ignore_irq13 = 1; + math_error((void *)regs->rip); +} + +asmlinkage void bad_intr(void) +{ + printk("bad interrupt"); +} + +void simd_math_error(void *eip) +{ + struct task_struct * task; + siginfo_t info; + unsigned short mxcsr; + + /* + * Save the info for the exception handler and clear the error. + */ + task = current; + save_init_fpu(task); + task->thread.trap_no = 19; + task->thread.error_code = 0; + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_code = __SI_FAULT; + info.si_addr = eip; + /* + * The SIMD FPU exceptions are handled a little differently, as there + * is only a single status/control register. Thus, to determine which + * unmasked exception was caught we must mask the exception mask bits + * at 0x1f80, and then use these to mask the exception bits at 0x3f. + */ + mxcsr = get_fpu_mxcsr(task); + switch (~((mxcsr & 0x1f80) >> 7) & (mxcsr & 0x3f)) { + case 0x000: + default: + break; + case 0x001: /* Invalid Op */ + info.si_code = FPE_FLTINV; + break; + case 0x002: /* Denormalize */ + case 0x010: /* Underflow */ + info.si_code = FPE_FLTUND; + break; + case 0x004: /* Zero Divide */ + info.si_code = FPE_FLTDIV; + break; + case 0x008: /* Overflow */ + info.si_code = FPE_FLTOVF; + break; + case 0x020: /* Precision */ + info.si_code = FPE_FLTRES; + break; + } + force_sig_info(SIGFPE, &info, task); +} + +asmlinkage void do_simd_coprocessor_error(struct pt_regs * regs, + long error_code) +{ + if (cpu_has_xmm) { + /* Handle SIMD FPU exceptions on PIII+ processors. */ + ignore_irq13 = 1; + simd_math_error((void *)regs->rip); + } else { + /* + * Handle strange cache flush from user space exception + * in all other cases. This is undocumented behaviour. + */ + die_if_kernel("cache flush denied", regs, error_code); + current->thread.trap_no = 19; + current->thread.error_code = error_code; + force_sig(SIGSEGV, current); + } +} + +asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs, + long error_code) +{ +#if 0 + /* No need to warn about this any longer. */ + printk("Ignoring P6 Local APIC Spurious Interrupt Bug...\n"); +#endif +} + +/* + * 'math_state_restore()' saves the current math information in the + * old math state array, and gets the new ones from the current task + * + * Careful.. There are problems with IBM-designed IRQ13 behaviour. + * Don't touch unless you *really* know how it works. + */ +asmlinkage void math_state_restore(void) +{ + clts(); /* Allow maths ops (or we recurse) */ + + if (current->used_math) { + restore_fpu(current); + } else { + init_fpu(); + } + current->flags |= PF_USEDFPU; /* So we fnsave on switch_to() */ +} + +asmlinkage void math_emulate(void) +{ + printk("math-emulation not enabled and no coprocessor found.\n"); + printk("killing %s.\n",current->comm); + force_sig(SIGFPE,current); + schedule(); +} + +void __init trap_init(void) +{ + set_intr_gate(0,÷_error); + set_intr_gate(1,&debug); + set_intr_gate(2,&nmi); + set_system_gate(3,&int3); /* int3-5 can be called from all */ + set_system_gate(4,&overflow); + set_system_gate(5,&bounds); + set_intr_gate(6,&invalid_op); + set_intr_gate(7,&device_not_available); + set_intr_gate_ist(8,&double_fault, 1); + set_intr_gate(9,&coprocessor_segment_overrun); + set_intr_gate(10,&invalid_TSS); + set_intr_gate(11,&segment_not_present); + set_intr_gate_ist(12,&stack_segment,STACKFAULT_STACK); + set_intr_gate(13,&general_protection); + set_intr_gate(14,&page_fault); + set_intr_gate(15,&spurious_interrupt_bug); + set_intr_gate(16,&coprocessor_error); + set_intr_gate(17,&alignment_check); + set_intr_gate(19,&simd_coprocessor_error); + +#ifdef CONFIG_IA32_EMULATION + set_intr_gate(IA32_SYSCALL_VECTOR, ia32_syscall); +#endif + +#if 0 + /* + * default LDT is a single-entry callgate to lcall7 for iBCS + * and a callgate to lcall27 for Solaris/x86 binaries + */ + set_call_gate(&default_ldt[0],lcall7); + set_call_gate(&default_ldt[4],lcall27); +#endif + + /* + * Should be a barrier for any external CPU state. + */ + cpu_init(); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/vsyscall.c linux.ac/arch/x86_64/kernel/vsyscall.c --- linux.vanilla/arch/x86_64/kernel/vsyscall.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/vsyscall.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,185 @@ +/* + * linux/arch/x86_64/kernel/vsyscall.c + * + * Copyright (C) 2001 Andrea Arcangeli SuSE + * + * Thanks to hpa@transmeta.com for some useful hint. + * + * vsyscall 1 is located at -10Mbyte, vsyscall 2 is located + * at virtual address -10Mbyte+1024bytes etc... There are at max 8192 + * vsyscalls. One vsyscall can reserve more than 1 slot to avoid + * jumping out of line if necessary. + * + * $Id: vsyscall.c,v 1.2 2001/04/20 00:59:30 ak Exp $ + */ + +/* + * TODO 2001-03-20: + * + * 1) make page fault handler detect faults on page1-page-last of the vsyscall + * virtual space, and make it increase %rip and write -ENOSYS in %rax (so + * we'll be able to upgrade to a new glibc without upgrading kernel after + * we add more vsyscalls. + * 2) Possibly we need a fixmap table for the vsyscalls too if we want + * to avoid SIGSEGV and we want to return -EFAULT from the vsyscalls as well. + * Can we segfault inside a "syscall"? We can fix this anytime and those fixes + * won't be visible for userspace. Not fixing this is a noop for correct programs, + * broken programs will segfault and there's no security risk until we choose to + * fix it. + * + * These are not urgent things that we need to address only before shipping the first + * production binary kernels. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define __vsyscall(nr) __attribute__ ((unused,__section__(".vsyscall_" #nr))) + +static inline void timeval_normalize(struct timeval * tv) +{ + time_t __sec; + + __sec = tv->tv_usec / 1000000; + if (__sec) + { + tv->tv_usec %= 1000000; + tv->tv_sec += __sec; + } +} + +long __vxtime_sequence[2] __section_vxtime_sequence; + +static inline void do_vgettimeofday(struct timeval * tv) +{ + long sequence; + unsigned long usec, sec; + + do { + register unsigned long eax, edx, usec; + + sequence = __vxtime_sequence[1]; + rmb(); + + /* Read the Time Stamp Counter */ + rdtsc(eax,edx); + + /* .. relative to previous jiffy (32 bits is enough) */ + eax -= __last_tsc_low; /* tsc_low delta */ + + /* + * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient + * = (tsc_low delta) * (usecs_per_clock) + * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy) + * + * Using a mull instead of a divl saves up to 31 clock cycles + * in the critical path. + */ + + edx = (eax*__fast_gettimeoffset_quotient) >> 32; + + /* our adjusted time offset in microseconds */ + usec = __delay_at_last_interrupt + edx; + + { + unsigned long lost = __jiffies - __wall_jiffies; + if (lost) + usec += lost * (1000000 / HZ); + } + sec = __xtime.tv_sec; + usec += __xtime.tv_usec; + + rmb(); + } while (sequence != __vxtime_sequence[0]); + + tv->tv_sec = sec; + tv->tv_usec = usec; + timeval_normalize(tv); +} + +static inline void do_get_tz(struct timezone * tz) +{ + long sequence; + + do { + sequence = __vxtime_sequence[1]; + rmb(); + + *tz = __sys_tz; + + rmb(); + } while (sequence != __vxtime_sequence[0]); +} + +static int __vsyscall(0) vgettimeofday(struct timeval * tv, struct timezone * tz) +{ + if (tv) + do_vgettimeofday(tv); + if (tz) + do_get_tz(tz); + return 0; +} + +static time_t __vsyscall(1) vtime(time_t * time) +{ + long sequence; + time_t __time; + + do { + sequence = __vxtime_sequence[1]; + rmb(); + + __time = __xtime.tv_sec; + + rmb(); + } while (sequence != __vxtime_sequence[0]); + + if (time) + *time = __time; + return __time; +} + +static long __vsyscall(2) venosys_0(void) +{ + return -ENOSYS; +} + +static long __vsyscall(3) venosys_1(void) +{ + return -ENOSYS; +} + +static void __init map_vsyscall(void) +{ + extern char __vsyscall_0; + unsigned long physaddr_page0 = (unsigned long) &__vsyscall_0 - __START_KERNEL_map; + + __set_fixmap(VSYSCALL_FIRST_PAGE, physaddr_page0, PAGE_KERNEL_VSYSCALL); +} + +static int __init vsyscall_init(void) +{ + printk("VSYSCALL: consistency checks..."); + if ((unsigned long) &vgettimeofday != VSYSCALL_ADDR(__NR_vgettimeofday)) + panic("vgettimeofday link addr broken"); + if ((unsigned long) &vtime != VSYSCALL_ADDR(__NR_vtime)) + panic("vtime link addr broken"); + if (VSYSCALL_ADDR(0) != __fix_to_virt(VSYSCALL_FIRST_PAGE)) + panic("fixmap first vsyscall %lx should be %lx", __fix_to_virt(VSYSCALL_FIRST_PAGE), + VSYSCALL_ADDR(0)); + printk("passed...mapping..."); + map_vsyscall(); + printk("done.\n"); + + return 0; +} + +__initcall(vsyscall_init); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/kernel/x8664_ksyms.c linux.ac/arch/x86_64/kernel/x8664_ksyms.c --- linux.vanilla/arch/x86_64/kernel/x8664_ksyms.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/kernel/x8664_ksyms.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void dump_thread(struct pt_regs *, struct user *); +extern spinlock_t rtc_lock; + +#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) +extern void machine_real_restart(unsigned char *, int); +EXPORT_SYMBOL(machine_real_restart); +#endif + +#ifdef CONFIG_SMP +extern void FASTCALL( __write_lock_failed(rwlock_t *rw)); +extern void FASTCALL( __read_lock_failed(rwlock_t *rw)); +#endif + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) || defined(CONFIG_BLK_DEV_IDE_MODULE) || defined(CONFIG_BLK_DEV_HD_MODULE) +extern struct drive_info_struct drive_info; +EXPORT_SYMBOL(drive_info); +#endif + +extern unsigned long get_cmos_time(void); + +/* platform dependent support */ +EXPORT_SYMBOL(boot_cpu_data); +#ifdef CONFIG_EISA +EXPORT_SYMBOL(EISA_bus); +#endif +EXPORT_SYMBOL(MCA_bus); +EXPORT_SYMBOL(dump_thread); +EXPORT_SYMBOL(dump_fpu); +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(disable_irq_nosync); +EXPORT_SYMBOL(probe_irq_mask); +EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(pm_idle); +EXPORT_SYMBOL(pm_power_off); +EXPORT_SYMBOL(get_cmos_time); +EXPORT_SYMBOL(apm_info); + +#ifdef CONFIG_IO_DEBUG +EXPORT_SYMBOL(__io_virt_debug); +#endif + +EXPORT_SYMBOL_NOVERS(__down_failed); +EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); +EXPORT_SYMBOL_NOVERS(__down_failed_trylock); +EXPORT_SYMBOL_NOVERS(__up_wakeup); +/* Networking helper routines. */ +EXPORT_SYMBOL(csum_partial_copy_nocheck); +/* Delay loops */ +EXPORT_SYMBOL(__udelay); +EXPORT_SYMBOL(__delay); +EXPORT_SYMBOL(__const_udelay); + +EXPORT_SYMBOL_NOVERS(__get_user_1); +EXPORT_SYMBOL_NOVERS(__get_user_2); +EXPORT_SYMBOL_NOVERS(__get_user_4); +EXPORT_SYMBOL_NOVERS(__put_user_1); +EXPORT_SYMBOL_NOVERS(__put_user_2); +EXPORT_SYMBOL_NOVERS(__put_user_4); + +EXPORT_SYMBOL(strtok); +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(simple_strtol); +EXPORT_SYMBOL(strstr); + +EXPORT_SYMBOL(strncpy_from_user); +EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(clear_user); +EXPORT_SYMBOL(__clear_user); +EXPORT_SYMBOL(__generic_copy_from_user); +EXPORT_SYMBOL(__generic_copy_to_user); +EXPORT_SYMBOL(strnlen_user); + +EXPORT_SYMBOL(pci_alloc_consistent); +EXPORT_SYMBOL(pci_free_consistent); + +#ifdef CONFIG_PCI +EXPORT_SYMBOL(pcibios_penalize_isa_irq); +EXPORT_SYMBOL(pci_mem_start); +#endif + +#ifdef CONFIG_X86_USE_3DNOW +EXPORT_SYMBOL(_mmx_memcpy); +EXPORT_SYMBOL(mmx_clear_page); +EXPORT_SYMBOL(mmx_copy_page); +#endif + +#ifdef CONFIG_SMP +EXPORT_SYMBOL(cpu_data); +EXPORT_SYMBOL(kernel_flag); +EXPORT_SYMBOL(smp_num_cpus); +EXPORT_SYMBOL(cpu_online_map); +EXPORT_SYMBOL_NOVERS(__write_lock_failed); +EXPORT_SYMBOL_NOVERS(__read_lock_failed); + +/* Global SMP irq stuff */ +EXPORT_SYMBOL(synchronize_irq); +EXPORT_SYMBOL(global_irq_holder); +EXPORT_SYMBOL(__global_cli); +EXPORT_SYMBOL(__global_sti); +EXPORT_SYMBOL(__global_save_flags); +EXPORT_SYMBOL(__global_restore_flags); +EXPORT_SYMBOL(smp_call_function); + +/* TLB flushing */ +EXPORT_SYMBOL(flush_tlb_page); +#endif + +#ifdef CONFIG_MCA +EXPORT_SYMBOL(machine_id); +#endif + +#ifdef CONFIG_VT +EXPORT_SYMBOL(screen_info); +#endif + +EXPORT_SYMBOL(get_wchan); + +EXPORT_SYMBOL(rtc_lock); + +#undef memcpy +#undef memset +extern void * memset(void *,int,__kernel_size_t); +extern void * memcpy(void *,const void *,__kernel_size_t); +EXPORT_SYMBOL_NOVERS(memcpy); +EXPORT_SYMBOL_NOVERS(memset); + +EXPORT_SYMBOL(empty_zero_page); + +#if defined(CONFIG_SONYPI) || defined(CONFIG_SONYPI_MODULE) +extern int is_sony_vaio_laptop; +EXPORT_SYMBOL(is_sony_vaio_laptop); +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/lib/Makefile linux.ac/arch/x86_64/lib/Makefile --- linux.vanilla/arch/x86_64/lib/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/lib/Makefile Wed Oct 10 01:47:38 2001 @@ -0,0 +1,17 @@ +# +# Makefile for x86_64-specific library files.. +# + +.S.o: + $(CC) $(AFLAGS) -c $< -o $*.o + +L_TARGET = lib.a +obj-y = generic-checksum.o old-checksum.o delay.o \ + usercopy.o getuser.o putuser.o \ + memcpy.o checksum_copy.o rwsem_thunk.o + +obj-$(CONFIG_IO_DEBUG) += iodebug.o +obj-$(CONFIG_X86_USE_3DNOW) += mmx.o +obj-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/lib/checksum_copy.S linux.ac/arch/x86_64/lib/checksum_copy.S --- linux.vanilla/arch/x86_64/lib/checksum_copy.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/lib/checksum_copy.S Wed Oct 10 01:47:38 2001 @@ -0,0 +1,142 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * IP/TCP/UDP checksumming routines + * + * Authors: Jorge Cwik, + * Arnt Gulbrandsen, + * Tom May, + * Pentium Pro/II routines: + * Alexander Kjeldaas + * Finn Arne Gangstad + * Lots of code moved from tcp.c and ip.c; see those files + * for more names. + * + * Changes: Ingo Molnar, converted csum_partial_copy() to 2.1 exception + * handling. + * Andi Kleen, add zeroing on error + * converted to pure assembler + * Andi Kleen initial raw port to x86-64 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + + +/* Version for PentiumII/PPro ported to x86-64. Still very raw and + does not exploit 64bit. */ + +#define SRC(y...) \ + 9999: y; \ + .section __ex_table, "a"; \ + .quad 9999b, 6001f ; \ + .previous + +#define DST(y...) \ + 9999: y; \ + .section __ex_table, "a"; \ + .quad 9999b, 6002f ; \ + .previous + +#define ROUND1(x) \ + SRC(movl x(%rsi), %ebx ) ; \ + addl %ebx, %eax ; \ + DST(movl %ebx, x(%rdi) ) ; + +#define ROUND(x) \ + SRC(movl x(%rsi), %ebx ) ; \ + adcl %ebx, %eax ; \ + DST(movl %ebx, x(%rdi) ) ; + +#define ARGBASE 0 + +/* + asmlinkage unsigned int csum_partial_copy_generic( const char *src, char *dst, int len, int sum, + int *src_err_ptr, int *dst_err_ptr); + rdi .. src + rsi .. dst (copy in r12) + rdx .. len (copy in r10) + rcx .. sum + r8 .. src_err_ptr + r9 .. dst_err_ptr + + OPTIMIZEME: this routine should take advantage of checksumming 64bits at a time +*/ + + .globl csum_partial_copy_generic +csum_partial_copy_generic: + pushq %r10 + pushq %r12 + pushq %rbx + pushq %rbp + xchgq %rsi, %rdi + + movq %rdx, %r10 + movq %rsi, %r12 + + movq %rcx, %rax + movq %rdx, %rcx # And now it looks like PII case + movl %ecx, %ebx + movl %esi, %edx + shrl $6, %ecx + andl $0x3c, %ebx + negq %rbx + subq %rbx, %rsi + subq %rbx, %rdi + lea 3f(%rbx,%rbx), %rbx + testq %rsi, %rsi + jmp *%rbx +1: addq $64,%rsi + addq $64,%rdi + ROUND1(-64) ROUND(-60) ROUND(-56) ROUND(-52) + ROUND (-48) ROUND(-44) ROUND(-40) ROUND(-36) + ROUND (-32) ROUND(-28) ROUND(-24) ROUND(-20) + ROUND (-16) ROUND(-12) ROUND(-8) ROUND(-4) +3: adcl $0,%eax + addl $64,%edx + dec %ecx + jge 1b +4: movq %r10,%rdx + andl $3, %edx + jz 7f + cmpl $2, %edx + jb 5f +SRC( movw (%rsi), %dx ) + leaq 2(%rsi), %rsi +DST( movw %dx, (%rdi) ) + leaq 2(%rdi), %rdi + je 6f + shll $16,%edx +5: +SRC( movb (%rsi), %dl ) +DST( movb %dl, (%rdi) ) +6: addl %edx, %eax + adcl $0, %eax +7: +.section .fixup, "ax" +6001: + movl $-EFAULT, (%r8) + # zero the complete destination (computing the rest is too much work) + movq %r12,%rdi # dst + movq %r10,%rcx # len + xorl %eax,%eax + rep; stosb + jmp 7b +6002: movl $-EFAULT,(%r9) + jmp 7b +.previous + popq %rbp + popq %rbx + popq %r12 + popq %r10 + ret + +#undef ROUND +#undef ROUND1 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/lib/dec_and_lock.c linux.ac/arch/x86_64/lib/dec_and_lock.c --- linux.vanilla/arch/x86_64/lib/dec_and_lock.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/lib/dec_and_lock.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,40 @@ +/* + * x86 version of "atomic_dec_and_lock()" using + * the atomic "cmpxchg" instruction. + * + * (For CPU's lacking cmpxchg, we use the slow + * generic version, and this one never even gets + * compiled). + */ + +#include +#include + +int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) +{ + int counter; + int newcount; + +repeat: + counter = atomic_read(atomic); + newcount = counter-1; + + if (!newcount) + goto slow_path; + + asm volatile("lock; cmpxchgl %1,%2" + :"=a" (newcount) + :"r" (newcount), "m" (atomic->counter), "0" (counter)); + + /* If the above failed, "eax" will have changed */ + if (newcount != counter) + goto repeat; + return 0; + +slow_path: + spin_lock(lock); + if (atomic_dec_and_test(atomic)) + return 1; + spin_unlock(lock); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/lib/delay.c linux.ac/arch/x86_64/lib/delay.c --- linux.vanilla/arch/x86_64/lib/delay.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/lib/delay.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,45 @@ +/* + * Precise Delay Loops for i386 + * + * Copyright (C) 1993 Linus Torvalds + * Copyright (C) 1997 Martin Mares + * + * The __delay function must _NOT_ be inlined as its execution time + * depends wildly on alignment on many x86 processors. + */ + +#include +#include +#include +#include + +#ifdef CONFIG_SMP +#include +#endif + +int x86_udelay_tsc = 0; /* Delay via TSC */ + +void __delay(unsigned long loops) +{ +#ifndef CONFIG_SIMNOW + unsigned long bclock, now; + + rdtscl(bclock); + do + { + __asm__ __volatile__ ("rep;nop\n\t"); + rdtscl(now); + } + while((now-bclock) < loops); +#endif +} + +inline void __const_udelay(unsigned long xloops) +{ + __delay(((xloops * current_cpu_data.loops_per_jiffy) >> 32) * HZ); +} + +void __udelay(unsigned long usecs) +{ + __const_udelay(usecs * 0x000010c6); /* 2**32 / 1000000 */ +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/lib/generic-checksum.c linux.ac/arch/x86_64/lib/generic-checksum.c --- linux.vanilla/arch/x86_64/lib/generic-checksum.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/lib/generic-checksum.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,124 @@ +/* + * arch/x86_64/lib/checksum.c + * + * This file contains network checksum routines that are better done + * in an architecture-specific manner due to speed.. + */ + +#include +#include + +static inline unsigned short from64to16(unsigned long x) +{ + /* add up 32-bit words for 33 bits */ + x = (x & 0xffffffff) + (x >> 32); + /* add up 16-bit and 17-bit words for 17+c bits */ + x = (x & 0xffff) + (x >> 16); + /* add up 16-bit and 2-bit for 16+c bit */ + x = (x & 0xffff) + (x >> 16); + /* add up carry.. */ + x = (x & 0xffff) + (x >> 16); + return x; +} + +/* + * Do a 64-bit checksum on an arbitrary memory area.. + * + * This isn't a great routine, but it's not _horrible_ either. The + * inner loop could be unrolled a bit further, and there are better + * ways to do the carry, but this is reasonable. + */ +static inline unsigned long do_csum(const unsigned char * buff, int len) +{ + int odd, count; + unsigned long result = 0; + + if (len <= 0) + goto out; + odd = 1 & (unsigned long) buff; + if (odd) { + result = *buff << 8; + len--; + buff++; + } + count = len >> 1; /* nr of 16-bit words.. */ + if (count) { + if (2 & (unsigned long) buff) { + result += *(unsigned short *) buff; + count--; + len -= 2; + buff += 2; + } + count >>= 1; /* nr of 32-bit words.. */ + if (count) { + if (4 & (unsigned long) buff) { + result += *(unsigned int *) buff; + count--; + len -= 4; + buff += 4; + } + count >>= 1; /* nr of 64-bit words.. */ + if (count) { + unsigned long carry = 0; + do { + unsigned long w = *(unsigned long *) buff; + count--; + buff += 8; + result += carry; + result += w; + carry = (w > result); + } while (count); + result += carry; + result = (result & 0xffffffff) + (result >> 32); + } + if (len & 4) { + result += *(unsigned int *) buff; + buff += 4; + } + } + if (len & 2) { + result += *(unsigned short *) buff; + buff += 2; + } + } + if (len & 1) + result += *buff; + result = from64to16(result); + if (odd) + result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); +out: + return result; +} + +/* + * computes the checksum of a memory block at buff, length len, + * and adds in "sum" (32-bit) + * + * returns a 32-bit number suitable for feeding into itself + * or csum_tcpudp_magic + * + * this function must be called with even lengths, except + * for the last fragment, which may be odd + * + * it's best to have buff aligned on a 32-bit boundary + */ +unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) +{ + unsigned long result = do_csum(buff, len); + + /* add in old sum, and carry.. */ + result += sum; + /* 32+c bits -> 32 bits */ + result = (result & 0xffffffff) + (result >> 32); + return result; +} + +/* + * this routine is used for miscellaneous IP-like checksums, mainly + * in icmp.c + */ +unsigned short ip_compute_csum(unsigned char * buff, int len) +{ + return ~from64to16(do_csum(buff,len)); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/lib/getuser.S linux.ac/arch/x86_64/lib/getuser.S --- linux.vanilla/arch/x86_64/lib/getuser.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/lib/getuser.S Wed Oct 10 01:47:38 2001 @@ -0,0 +1,90 @@ +/* + * __get_user functions. + * + * (C) Copyright 1998 Linus Torvalds + * + * These functions have a non-standard call interface + * to make them more efficient, especially as they + * return an error value in addition to the "real" + * return value. + */ + +/* + * __get_user_X + * + * Inputs: %rax contains the address + * + * Outputs: %rax is error code (0 or -EFAULT) + * %rdx contains zero-extended value + * + * %rbx is destroyed. + * + * These functions should not modify any other registers, + * as they get called from within inline assembly. + */ + +#include +#include +#include +#include +#include + + .text + .p2align +.globl __get_user_1 +__get_user_1: + GET_CURRENT(%rbx) + cmpq tsk_addr_limit(%rbx),%rax + jae bad_get_user +1: movzb (%rax),%edx + xorq %rax,%rax + ret + + .p2align +.globl __get_user_2 +__get_user_2: + GET_CURRENT(%rbx) + addq $1,%rax + jc bad_get_user + cmpq tsk_addr_limit(%rbx),%rax + jae bad_get_user +2: movzwl -1(%rax),%edx + xorq %rax,%rax + ret + + .p2align +.globl __get_user_4 +__get_user_4: + GET_CURRENT(%rbx) + addq $3,%rax + jc bad_get_user + cmpq tsk_addr_limit(%rbx),%rax + jae bad_get_user +3: movl -3(%rax),%edx + xorq %rax,%rax + ret + + .p2align +.globl __get_user_8 +__get_user_8: + GET_CURRENT(%rbx) + addq $7,%rax + jc bad_get_user + cmpq tsk_addr_limit(%rbx),%rax + jae bad_get_user +4: movq -7(%rax),%rdx + xorq %rax,%rax + ret + +ENTRY(bad_get_user) +bad_get_user: + xorq %rdx,%rdx + movq $(-EFAULT),%rax + ret + +.section __ex_table,"a" + .quad 1b,bad_get_user + .quad 2b,bad_get_user + .quad 3b,bad_get_user + .quad 4b,bad_get_user +.previous diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/lib/iodebug.c linux.ac/arch/x86_64/lib/iodebug.c --- linux.vanilla/arch/x86_64/lib/iodebug.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/lib/iodebug.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,19 @@ +#include + +void * __io_virt_debug(unsigned long x, const char *file, int line) +{ + if (x < PAGE_OFFSET) { + printk("io mapaddr 0x%05lx not valid at %s:%d!\n", x, file, line); + return __va(x); + } + return (void *)x; +} + +unsigned long __io_phys_debug(unsigned long x, const char *file, int line) +{ + if (x < PAGE_OFFSET) { + printk("io mapaddr 0x%05lx not valid at %s:%d!\n", x, file, line); + return x; + } + return __pa(x); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/lib/memcpy.c linux.ac/arch/x86_64/lib/memcpy.c --- linux.vanilla/arch/x86_64/lib/memcpy.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/lib/memcpy.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,3 @@ +/* We rely on gcc internal versions of memcpy and friends. In 2.95+, + they should be good enough. */ + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/lib/mmx.c linux.ac/arch/x86_64/lib/mmx.c --- linux.vanilla/arch/x86_64/lib/mmx.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/lib/mmx.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,377 @@ +#include +#include +#include +#include + +#include +#include + + +/* + * MMX 3DNow! library helper functions + * + * To do: + * We can use MMX just for prefetch in IRQ's. This may be a win. + * (reported so on K6-III) + * We should use a better code neutral filler for the short jump + * leal ebx. [ebx] is apparently best for K6-2, but Cyrix ?? + * We also want to clobber the filler register so we dont get any + * register forwarding stalls on the filler. + * + * Add *user handling. Checksums are not a win with MMX on any CPU + * tested so far for any MMX solution figured. + * + * 22/09/2000 - Arjan van de Ven + * Improved for non-egineering-sample Athlons + * + */ + +#error Don't use these for now, but we'll have to provide optimized functions in future + +void *_mmx_memcpy(void *to, const void *from, size_t len) +{ + void *p; + int i; + + if (in_interrupt()) + return __memcpy(to, from, len); + + p = to; + i = len >> 6; /* len/64 */ + + kernel_fpu_begin(); + + __asm__ __volatile__ ( + "1: prefetch (%0)\n" /* This set is 28 bytes */ + " prefetch 64(%0)\n" + " prefetch 128(%0)\n" + " prefetch 192(%0)\n" + " prefetch 256(%0)\n" + "2: \n" + ".section .fixup, \"ax\"\n" + "3: movw $0x1AEB, 1b\n" /* jmp on 26 bytes */ + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b, 3b\n" + ".previous" + : : "r" (from) ); + + + for(; i>0; i--) + { + __asm__ __volatile__ ( + "1: prefetch 320(%0)\n" + "2: movq (%0), %%mm0\n" + " movq 8(%0), %%mm1\n" + " movq 16(%0), %%mm2\n" + " movq 24(%0), %%mm3\n" + " movq %%mm0, (%1)\n" + " movq %%mm1, 8(%1)\n" + " movq %%mm2, 16(%1)\n" + " movq %%mm3, 24(%1)\n" + " movq 32(%0), %%mm0\n" + " movq 40(%0), %%mm1\n" + " movq 48(%0), %%mm2\n" + " movq 56(%0), %%mm3\n" + " movq %%mm0, 32(%1)\n" + " movq %%mm1, 40(%1)\n" + " movq %%mm2, 48(%1)\n" + " movq %%mm3, 56(%1)\n" + ".section .fixup, \"ax\"\n" + "3: movw $0x05EB, 1b\n" /* jmp on 5 bytes */ + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b, 3b\n" + ".previous" + : : "r" (from), "r" (to) : "memory"); + from+=64; + to+=64; + } + /* + * Now do the tail of the block + */ + __memcpy(to, from, len&63); + kernel_fpu_end(); + return p; +} + +#ifdef CONFIG_MK7 + +/* + * The K7 has streaming cache bypass load/store. The Cyrix III, K6 and + * other MMX using processors do not. + */ + +static void fast_clear_page(void *page) +{ + int i; + + kernel_fpu_begin(); + + __asm__ __volatile__ ( + " pxor %%mm0, %%mm0\n" : : + ); + + for(i=0;i<4096/64;i++) + { + __asm__ __volatile__ ( + " movntq %%mm0, (%0)\n" + " movntq %%mm0, 8(%0)\n" + " movntq %%mm0, 16(%0)\n" + " movntq %%mm0, 24(%0)\n" + " movntq %%mm0, 32(%0)\n" + " movntq %%mm0, 40(%0)\n" + " movntq %%mm0, 48(%0)\n" + " movntq %%mm0, 56(%0)\n" + : : "r" (page) : "memory"); + page+=64; + } + /* since movntq is weakly-ordered, a "sfence" is needed to become + * ordered again. + */ + __asm__ __volatile__ ( + " sfence \n" : : + ); + kernel_fpu_end(); +} + +static void fast_copy_page(void *to, void *from) +{ + int i; + + kernel_fpu_begin(); + + /* maybe the prefetch stuff can go before the expensive fnsave... + * but that is for later. -AV + */ + __asm__ __volatile__ ( + "1: prefetch (%0)\n" + " prefetch 64(%0)\n" + " prefetch 128(%0)\n" + " prefetch 192(%0)\n" + " prefetch 256(%0)\n" + "2: \n" + ".section .fixup, \"ax\"\n" + "3: movw $0x1AEB, 1b\n" /* jmp on 26 bytes */ + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b, 3b\n" + ".previous" + : : "r" (from) ); + + for(i=0; i<(4096-320)/64; i++) + { + __asm__ __volatile__ ( + "1: prefetch 320(%0)\n" + "2: movq (%0), %%mm0\n" + " movntq %%mm0, (%1)\n" + " movq 8(%0), %%mm1\n" + " movntq %%mm1, 8(%1)\n" + " movq 16(%0), %%mm2\n" + " movntq %%mm2, 16(%1)\n" + " movq 24(%0), %%mm3\n" + " movntq %%mm3, 24(%1)\n" + " movq 32(%0), %%mm4\n" + " movntq %%mm4, 32(%1)\n" + " movq 40(%0), %%mm5\n" + " movntq %%mm5, 40(%1)\n" + " movq 48(%0), %%mm6\n" + " movntq %%mm6, 48(%1)\n" + " movq 56(%0), %%mm7\n" + " movntq %%mm7, 56(%1)\n" + ".section .fixup, \"ax\"\n" + "3: movw $0x05EB, 1b\n" /* jmp on 5 bytes */ + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b, 3b\n" + ".previous" + : : "r" (from), "r" (to) : "memory"); + from+=64; + to+=64; + } + for(i=(4096-320)/64; i<4096/64; i++) + { + __asm__ __volatile__ ( + "2: movq (%0), %%mm0\n" + " movntq %%mm0, (%1)\n" + " movq 8(%0), %%mm1\n" + " movntq %%mm1, 8(%1)\n" + " movq 16(%0), %%mm2\n" + " movntq %%mm2, 16(%1)\n" + " movq 24(%0), %%mm3\n" + " movntq %%mm3, 24(%1)\n" + " movq 32(%0), %%mm4\n" + " movntq %%mm4, 32(%1)\n" + " movq 40(%0), %%mm5\n" + " movntq %%mm5, 40(%1)\n" + " movq 48(%0), %%mm6\n" + " movntq %%mm6, 48(%1)\n" + " movq 56(%0), %%mm7\n" + " movntq %%mm7, 56(%1)\n" + : : "r" (from), "r" (to) : "memory"); + from+=64; + to+=64; + } + /* since movntq is weakly-ordered, a "sfence" is needed to become + * ordered again. + */ + __asm__ __volatile__ ( + " sfence \n" : : + ); + kernel_fpu_end(); +} + +#else + +/* + * Generic MMX implementation without K7 specific streaming + */ + +static void fast_clear_page(void *page) +{ + int i; + + kernel_fpu_begin(); + + __asm__ __volatile__ ( + " pxor %%mm0, %%mm0\n" : : + ); + + for(i=0;i<4096/128;i++) + { + __asm__ __volatile__ ( + " movq %%mm0, (%0)\n" + " movq %%mm0, 8(%0)\n" + " movq %%mm0, 16(%0)\n" + " movq %%mm0, 24(%0)\n" + " movq %%mm0, 32(%0)\n" + " movq %%mm0, 40(%0)\n" + " movq %%mm0, 48(%0)\n" + " movq %%mm0, 56(%0)\n" + " movq %%mm0, 64(%0)\n" + " movq %%mm0, 72(%0)\n" + " movq %%mm0, 80(%0)\n" + " movq %%mm0, 88(%0)\n" + " movq %%mm0, 96(%0)\n" + " movq %%mm0, 104(%0)\n" + " movq %%mm0, 112(%0)\n" + " movq %%mm0, 120(%0)\n" + : : "r" (page) : "memory"); + page+=128; + } + + kernel_fpu_end(); +} + +static void fast_copy_page(void *to, void *from) +{ + int i; + + + kernel_fpu_begin(); + + __asm__ __volatile__ ( + "1: prefetch (%0)\n" + " prefetch 64(%0)\n" + " prefetch 128(%0)\n" + " prefetch 192(%0)\n" + " prefetch 256(%0)\n" + "2: \n" + ".section .fixup, \"ax\"\n" + "3: movw $0x1AEB, 1b\n" /* jmp on 26 bytes */ + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b, 3b\n" + ".previous" + : : "r" (from) ); + + for(i=0; i<4096/64; i++) + { + __asm__ __volatile__ ( + "1: prefetch 320(%0)\n" + "2: movq (%0), %%mm0\n" + " movq 8(%0), %%mm1\n" + " movq 16(%0), %%mm2\n" + " movq 24(%0), %%mm3\n" + " movq %%mm0, (%1)\n" + " movq %%mm1, 8(%1)\n" + " movq %%mm2, 16(%1)\n" + " movq %%mm3, 24(%1)\n" + " movq 32(%0), %%mm0\n" + " movq 40(%0), %%mm1\n" + " movq 48(%0), %%mm2\n" + " movq 56(%0), %%mm3\n" + " movq %%mm0, 32(%1)\n" + " movq %%mm1, 40(%1)\n" + " movq %%mm2, 48(%1)\n" + " movq %%mm3, 56(%1)\n" + ".section .fixup, \"ax\"\n" + "3: movw $0x05EB, 1b\n" /* jmp on 5 bytes */ + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b, 3b\n" + ".previous" + : : "r" (from), "r" (to) : "memory"); + from+=64; + to+=64; + } + kernel_fpu_end(); +} + + +#endif + +/* + * Favour MMX for page clear and copy. + */ + +static void slow_zero_page(void * page) +{ + int d0, d1; + __asm__ __volatile__( \ + "cld\n\t" \ + "rep ; stosl" \ + : "=&c" (d0), "=&D" (d1) + :"a" (0),"1" (page),"0" (1024) + :"memory"); +} + +void mmx_clear_page(void * page) +{ + if(in_interrupt()) + slow_zero_page(page); + else + fast_clear_page(page); +} + +static void slow_copy_page(void *to, void *from) +{ + int d0, d1, d2; + __asm__ __volatile__( \ + "cld\n\t" \ + "rep ; movsl" \ + : "=&c" (d0), "=&D" (d1), "=&S" (d2) \ + : "0" (1024),"1" ((long) to),"2" ((long) from) \ + : "memory"); +} + + +void mmx_copy_page(void *to, void *from) +{ + if(in_interrupt()) + slow_copy_page(to, from); + else + fast_copy_page(to, from); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/lib/old-checksum.c linux.ac/arch/x86_64/lib/old-checksum.c --- linux.vanilla/arch/x86_64/lib/old-checksum.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/lib/old-checksum.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,33 @@ +/* + * Temporal C versions of the checksum functions until optimized assembler versions + * can go in. + */ + +#include + +/* + * Copy from userspace and compute checksum. If we catch an exception + * then zero the rest of the buffer. + */ +unsigned int csum_partial_copy_from_user (const char *src, char *dst, + int len, unsigned int sum, + int *err_ptr) +{ + int missing; + + missing = copy_from_user(dst, src, len); + if (missing) { + memset(dst + len - missing, 0, missing); + *err_ptr = -EFAULT; + } + + return csum_partial(dst, len, sum); +} + +unsigned int csum_partial_copy_nocheck(const char *src, char *dst, int len, unsigned int sum) +{ + memcpy(dst,src,len); + return csum_partial(dst,len,sum); +} + +/* Fallback for csum_and_copy_to_user is currently in include/net/checksum.h */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/lib/putuser.S linux.ac/arch/x86_64/lib/putuser.S --- linux.vanilla/arch/x86_64/lib/putuser.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/lib/putuser.S Wed Oct 10 01:47:38 2001 @@ -0,0 +1,88 @@ +/* + * __put_user functions. + * + * (C) Copyright 1998 Linus Torvalds + * + * These functions have a non-standard call interface + * to make them more efficient. + */ + +/* + * __put_user_X + * + * Inputs: %rax contains the address + * %rdx contains the value + * + * Outputs: %rax is error code (0 or -EFAULT) + * %rbx is corrupted (will contain "current_task"). + * + * These functions should not modify any other registers, + * as they get called from within inline assembly. + */ + +/* FIXME: putuser.S should be really merged with getuser.S, and preprocessor should be used to keep code duplication lower */ + +#include +#include +#include +#include +#include + +.text +.p2align +.globl __put_user_1 +__put_user_1: + GET_CURRENT(%rbx) + cmpq tsk_addr_limit(%rbx),%rax + jae bad_put_user +1: movb %dl,(%rax) + xorq %rax,%rax + ret + +.p2align +.globl __put_user_2 +__put_user_2: + GET_CURRENT(%rbx) + addq $1,%rax + jc bad_put_user + cmpq tsk_addr_limit(%rbx),%rax + jae bad_put_user +2: movw %dx,-1(%rax) + xorq %rax,%rax + ret + +.p2align +.globl __put_user_4 +__put_user_4: + GET_CURRENT(%rbx) + addq $3,%rax + jc bad_put_user + cmpq tsk_addr_limit(%rbx),%rax + jae bad_put_user +3: movl %edx,-3(%rax) + xorq %rax,%rax + ret + +.p2align +.globl __put_user_8 +__put_user_8: + GET_CURRENT(%rbx) + addq $7,%rax + jc bad_put_user + cmpq tsk_addr_limit(%rbx),%rax + jae bad_put_user +4: movq %rdx,-7(%rax) + xorq %rax,%rax + ret + +ENTRY(bad_put_user) +bad_put_user: + movq $(-EFAULT),%rax + ret + +.section __ex_table,"a" + .quad 1b,bad_put_user + .quad 2b,bad_put_user + .quad 3b,bad_put_user + .quad 4b,bad_put_user +.previous diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/lib/rwsem_thunk.S linux.ac/arch/x86_64/lib/rwsem_thunk.S --- linux.vanilla/arch/x86_64/lib/rwsem_thunk.S Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/lib/rwsem_thunk.S Wed Oct 10 01:47:38 2001 @@ -0,0 +1,29 @@ + #include + + /* + * Save registers for the slow path of semaphores here; to avoid + * disturbance of register allocation in fast paths with function calls. + * Written 2001 by Andi Kleen. + */ + + .macro rwsem_thunk name,func,fallthrough=0 + .globl \name +\name: + SAVE_ARGS + movq %rax,%rdi + call \func + .ifeq \fallthrough + jmp restore + .endif + .endm + + rwsem_thunk rwsem_down_read_failed_thunk,rwsem_down_read_failed + rwsem_thunk rwsem_down_write_failed_thunk,rwsem_down_write_failed + rwsem_thunk rwsem_wake_thunk,rwsem_wake,1 + /* This does not really belong here, but the macros are so + convenient. */ + rwsem_thunk do_softirq_thunk,do_softirq + +restore: + RESTORE_ARGS + ret diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/lib/usercopy.c linux.ac/arch/x86_64/lib/usercopy.c --- linux.vanilla/arch/x86_64/lib/usercopy.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/lib/usercopy.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,147 @@ +/* + * User address space access functions. + * The non inlined parts of asm-i386/uaccess.h are here. + * + * Copyright 1997 Andi Kleen + * Copyright 1997 Linus Torvalds + */ +#include +#include +#include + +unsigned long +__generic_copy_to_user(void *to, const void *from, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __copy_user(to,from,n); + return n; +} + +unsigned long +__generic_copy_from_user(void *to, const void *from, unsigned long n) +{ + if (access_ok(VERIFY_READ, from, n)) + __copy_user_zeroing(to,from,n); + else + memset(to, 0, n); + return n; +} + +/* + * Copy a null terminated string from userspace. + */ + +#define __do_strncpy_from_user(dst,src,count,res) \ +do { \ + long __d0, __d1, __d2; \ + __asm__ __volatile__( \ + " testq %1,%1\n" \ + " jz 2f\n" \ + "0: lodsb\n" \ + " stosb\n" \ + " testb %%al,%%al\n" \ + " jz 1f\n" \ + " decq %1\n" \ + " jnz 0b\n" \ + "1: subq %1,%0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: movq %5,%0\n" \ + " jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .quad 0b,3b\n" \ + ".previous" \ + : "=r"(res), "=c"(count), "=&a" (__d0), "=&S" (__d1), \ + "=&D" (__d2) \ + : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), "4"(dst) \ + : "memory"); \ +} while (0) + +long +__strncpy_from_user(char *dst, const char *src, long count) +{ + long res; + __do_strncpy_from_user(dst, src, count, res); + return res; +} + +long +strncpy_from_user(char *dst, const char *src, long count) +{ + long res = -EFAULT; + if (access_ok(VERIFY_READ, src, 1)) + __do_strncpy_from_user(dst, src, count, res); + return res; +} + + +/* + * Zero Userspace + */ + +/* This may not be working, and certainly is not optimal. + FIXME: this can be generically implemented using put_user. */ +#define __do_clear_user(addr,size) \ +do { \ + long __d0; \ + __asm__ __volatile__( \ + "cld\n" \ + "0: rep; stosl\n" \ + " movq %2,%0\n" \ + "1: rep; stosb\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: lea 0(%2,%0,4),%0\n" \ + " jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .quad 0b,3b\n" \ + " .quad 1b,2b\n" \ + ".previous" \ + : "=&c"(size), "=&D" (__d0) \ + : "r"(size & 3), "0"(size / 4), "1"(addr), "a"(0)); \ +} while (0) + +unsigned long +clear_user(void *to, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __do_clear_user(to, n); + return n; +} + +unsigned long +__clear_user(void *to, unsigned long n) +{ + __do_clear_user(to, n); + return n; +} + +/* + * Return the size of a string (including the ending 0) + * + * Return 0 on exception, a value greater than N if too long + */ + +long strnlen_user(const char *s, long n) +{ + unsigned long res = 0, tmp; + char c; + + if (!access_ok(VERIFY_READ, s, n)) + return 0; + + while (1) { + if (get_user(c, s)) + return 0; + if (!c) + return res+1; + if (res>n) + return n+1; + res++; + s++; + } +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/mm/Makefile linux.ac/arch/x86_64/mm/Makefile --- linux.vanilla/arch/x86_64/mm/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/mm/Makefile Wed Oct 10 01:47:38 2001 @@ -0,0 +1,13 @@ +# +# Makefile for the linux i386-specific parts of the memory manager. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := mm.o +obj-y := init.o fault.o ioremap.o extable.o + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/mm/extable.c linux.ac/arch/x86_64/mm/extable.c --- linux.vanilla/arch/x86_64/mm/extable.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/mm/extable.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,62 @@ +/* + * linux/arch/i386/mm/extable.c + */ + +#include +#include +#include +#include + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline unsigned long +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) +{ + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return 0; +} + +extern spinlock_t modlist_lock; + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret = 0; + unsigned long flags; + +#ifndef CONFIG_MODULES + /* There is only the kernel to search. */ + ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); + return ret; +#else + /* The kernel is the last "module" -- no need to treat it special. */ + struct module *mp; + + spin_lock_irqsave(&modlist_lock, flags); + for (mp = module_list; mp != NULL; mp = mp->next) { + if (mp->ex_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING))) + continue; + ret = search_one_table(mp->ex_table_start, + mp->ex_table_end - 1, addr); + if (ret) + break; + } + spin_unlock_irqrestore(&modlist_lock, flags); + return ret; +#endif +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/mm/fault.c linux.ac/arch/x86_64/mm/fault.c --- linux.vanilla/arch/x86_64/mm/fault.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/mm/fault.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,305 @@ +/* + * linux/arch/x86-64/mm/fault.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +extern void die(const char *,struct pt_regs *,long); + +asmlinkage void do_invalid_op(struct pt_regs *, unsigned long); +extern unsigned long idt; + +extern spinlock_t timerlist_lock; + +/* + * Unlock any spinlocks which will prevent us from getting the + * message out (timerlist_lock is acquired through the + * console unblank code) + */ +void bust_spinlocks(int yes) +{ + spin_lock_init(&timerlist_lock); + if (yes) { + oops_in_progress = 1; +#ifdef CONFIG_SMP + global_irq_lock = 0; /* Many serial drivers do __global_cli() */ +#endif + } else { + int loglevel_save = console_loglevel; +#ifdef CONFIG_VT + unblank_screen(); +#endif + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console up */ + printk(" "); + console_loglevel = loglevel_save; + } +} + +void dump_pagetable(unsigned long address) +{ + static char *name[] = { "PML4", "PGD", "PDE", "PTE" }; + int i, shift; + unsigned long page; + + asm("movq %%cr3,%0":"=r" (page)); + shift = 9+9+9+12; + address &= ~0xFFFF000000000000UL; + for (i = 0; i < 4; i++) { + page = ((unsigned long *) __va(page))[(address >> shift) & 0x1FFU]; + printk("%s: %016lx ", name[i], page); + if ((page & (1 | (1<<7))) != 1) /* Not present or 2MB page */ + break; + page &= ~0xFFFUL; + shift -= 9; + } + printk("\n"); +} + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * error_code: + * bit 0 == 0 means no page found, 1 means protection fault + * bit 1 == 0 means read, 1 means write + * bit 2 == 0 means kernel, 1 means user-mode + */ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) +{ + struct task_struct *tsk; + struct mm_struct *mm; + struct vm_area_struct * vma; + unsigned long address; + unsigned long fixup; + int write; + siginfo_t info; + + /* get the address */ + __asm__("movq %%cr2,%0":"=r" (address)); + + tsk = current; + mm = tsk->mm; + info.si_code = SEGV_MAPERR; + + if (address >= TASK_SIZE) + goto vmalloc_fault; + + + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_interrupt() || !mm) + goto no_context; + + down_read(&mm->mmap_sem); + + vma = find_vma(mm, address); + if (!vma) + goto bad_area; + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + if (error_code & 4) { + // XXX: align red zone size with ABI + if (address + 128 < regs->rsp) + goto bad_area; + } + if (expand_stack(vma, address)) + goto bad_area; +/* + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ +good_area: + info.si_code = SEGV_ACCERR; + write = 0; + switch (error_code & 3) { + default: /* 3: write, present */ + /* fall through */ + case 2: /* write, not present */ + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + write++; + break; + case 1: /* read, present */ + goto bad_area; + case 0: /* read, not present */ + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + switch (handle_mm_fault(mm, vma, address, write)) { + case 1: + tsk->min_flt++; + break; + case 2: + tsk->maj_flt++; + break; + case 0: + goto do_sigbus; + default: + goto out_of_memory; + } + + up_read(&mm->mmap_sem); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + up_read(&mm->mmap_sem); + +bad_area_nosemaphore: + + /* User mode accesses just cause a SIGSEGV */ + if (error_code & 4) { + +#ifdef CONFIG_USER_LEVEL_OOPS + printk("********* PROCESS %s segfaulted. Printing user level oops:\n\n", current->comm); + + show_registers(regs); +#endif + + tsk->thread.cr2 = address; + tsk->thread.error_code = error_code; + tsk->thread.trap_no = 14; + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* info.si_code has been set above */ + info.si_addr = (void *)address; + force_sig_info(SIGSEGV, &info, tsk); + return; + } + +no_context: + + /* Are we prepared to handle this kernel fault? */ + if ((fixup = search_exception_table(regs->rip)) != 0) { + regs->rip = fixup; + return; + } + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + + bust_spinlocks(1); + + if (address < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + else + printk(KERN_ALERT "Unable to handle kernel paging request"); + printk(" at virtual address %016lx\n",address); + printk(" printing eip:\n"); + printk("%016lx\n", regs->rip); + dump_pagetable(address); + die("Oops", regs, error_code); + do_exit(SIGKILL); + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: + up_read(&mm->mmap_sem); + printk("VM: killing process %s\n", tsk->comm); + if (error_code & 4) + do_exit(SIGKILL); + goto no_context; + +do_sigbus: + up_read(&mm->mmap_sem); + + /* + * Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ + tsk->thread.cr2 = address; + tsk->thread.error_code = error_code; + tsk->thread.trap_no = 14; + info.si_code = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *)address; + force_sig_info(SIGBUS, &info, tsk); + + /* Kernel mode? Handle exceptions or die */ + if (!(error_code & 4)) + goto no_context; + + +vmalloc_fault: + { + /* + * Synchronize the kernel space top level page-table + * with the 'reference' page table. + * Currently it only works for first and last 512 GB of + * kernel memory FIXME + * + */ + level4_t *l4pd = level4_offset_k(address); + int offset = __pgd_offset(address); + pgd_t *pgd, *pgd_k; + pmd_t *pmd, *pmd_k; + + if (! level4_val(*l4pd)) { + printk(KERN_ERR "fatal - no entry in level4_page for %lx\n", + address); + goto bad_area_nosemaphore; + } + pgd = level3_offset_k(l4pd, address); + pgd_k = init_mm.pgd + offset; + + if (!pgd_present(*pgd)) { + if (!pgd_present(*pgd_k)) + goto bad_area_nosemaphore; + set_pgd(pgd, *pgd_k); + return; + } + + pmd = pmd_offset(pgd, address); + pmd_k = pmd_offset(pgd_k, address); + + if (pmd_present(*pmd) || !pmd_present(*pmd_k)) + goto bad_area_nosemaphore; + set_pmd(pmd, *pmd_k); + return; + } +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/mm/init.c linux.ac/arch/x86_64/mm/init.c --- linux.vanilla/arch/x86_64/mm/init.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/mm/init.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,389 @@ +/* + * linux/arch/i386/mm/init.c + * + * Copyright (C) 1995 Linus Torvalds + * Copyright (C) 2000 Pavel Machek + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_BLK_DEV_INITRD +#include +#endif +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +mmu_gather_t mmu_gathers[NR_CPUS]; + +static unsigned long totalram_pages; +static unsigned long totalhigh_pages; + +int do_check_pgt_cache(int low, int high) +{ + int freed = 0; + if(read_pda(pgtable_cache_sz) > high) { + do { + if (read_pda(pgd_quick)) { + pgd_free_slow(pgd_alloc_one_fast()); + freed++; + } + if (read_pda(pmd_quick)) { + pmd_free_slow(pmd_alloc_one_fast(NULL, 0)); + freed++; + } + if (read_pda(pte_quick)) { + pte_free_slow(pte_alloc_one_fast(NULL, 0)); + freed++; + } + } while(read_pda(pgtable_cache_sz) > low); + } + return freed; +} + +/* + * NOTE: pagetable_init alloc all the fixmap pagetables contiguous on the + * physical space so we can cache the place of the first one and move + * around without checking the pgd every time. + */ + +void show_mem(void) +{ + int i, total = 0, reserved = 0; + int shared = 0, cached = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageReserved(mem_map+i)) + reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; + else if (page_count(mem_map+i)) + shared += page_count(mem_map+i) - 1; + } + printk("%d pages of RAM\n", total); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%ld pages in page table cache\n",read_pda(pgtable_cache_sz)); + show_buffers(); +} + +/* References to section boundaries */ + +extern char _text, _etext, _edata, __bss_start, _end; +extern char __init_begin, __init_end; + +int after_bootmem; + +static void *spp_getpage(void) +{ + void *ptr; + if (after_bootmem) + ptr = (void *) get_free_page(GFP_ATOMIC); + else + ptr = alloc_bootmem_low(PAGE_SIZE); + if (!ptr) + panic("set_pte_phys: cannot allocate page data %s\n", after_bootmem?"after bootmem":""); + return ptr; +} + +static void set_pte_phys(unsigned long vaddr, + unsigned long phys, pgprot_t prot) +{ + level4_t *level4; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + level4 = level4_offset_k(vaddr); + if (level4_none(*level4)) { + printk("LEVEL4 FIXMAP MISSING, it should be setup in head.S!\n"); + return; + } + pgd = level3_offset_k(level4, vaddr); + if (pgd_none(*pgd)) { + pmd = (pmd_t *) spp_getpage(); + set_pgd(pgd, __pgd(__pa(pmd) + 0x7)); + if (pmd != pmd_offset(pgd, 0)) { + printk("PAGETABLE BUG #01!\n"); + return; + } + } + pmd = pmd_offset(pgd, vaddr); + if (pmd_none(*pmd)) { + pte = (pte_t *) spp_getpage(); + set_pmd(pmd, __pmd(__pa(pte) + 0x7)); + if (pte != pte_offset(pmd, 0)) { + printk("PAGETABLE BUG #02!\n"); + return; + } + } + pte = pte_offset(pmd, vaddr); + if (pte_val(*pte)) + pte_ERROR(*pte); + set_pte(pte, mk_pte_phys(phys, prot)); + + /* + * It's enough to flush this one mapping. + * (PGE mappings get flushed as well) + */ + __flush_tlb_one(vaddr); +} + +/* NOTE: this is meant to be run only at boot */ +void __set_fixmap (enum fixed_addresses idx, unsigned long phys, pgprot_t prot) +{ + unsigned long address = __fix_to_virt(idx); + + if (idx >= __end_of_fixed_addresses) { + printk("Invalid __set_fixmap\n"); + return; + } + set_pte_phys(address, phys, prot); +} + +static void __init pagetable_init (void) +{ + unsigned long paddr, end; + pgd_t *pgd; + int i, j; + pmd_t *pmd; + + /* + * This can be zero as well - no problem, in that case we exit + * the loops anyway due to the PTRS_PER_* conditions. + */ + end = (unsigned long) max_low_pfn*PAGE_SIZE; + if (end > 0x8000000000) { + printk("Temporary supporting only 512G of global RAM\n"); + end = 0x8000000000; + max_low_pfn = 0x8000000000 >> PAGE_SHIFT; + } + + i = __pgd_offset(PAGE_OFFSET); + pgd = level3_physmem_pgt + i; + + for (; i < PTRS_PER_PGD; pgd++, i++) { + paddr = i*PGDIR_SIZE; + if (paddr >= end) + break; + if (i) + pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE); + else + pmd = level2_kernel_pgt; + + set_pgd(pgd, __pgd(__pa(pmd) + 0x7)); + for (j = 0; j < PTRS_PER_PMD; pmd++, j++) { + unsigned long __pe; + + paddr = i*PGDIR_SIZE + j*PMD_SIZE; + if (paddr >= end) + break; + + __pe = _KERNPG_TABLE + _PAGE_PSE + paddr + _PAGE_GLOBAL; + set_pmd(pmd, __pmd(__pe)); + } + } + + /* + * Add low memory identity-mappings - SMP needs it when + * starting up on an AP from real-mode. In the non-PAE + * case we already have these mappings through head.S. + * All user-space mappings are explicitly cleared after + * SMP startup. + */ +#ifdef FIXME + pgd_base [0] is not what you think, this needs to be rewritten for SMP. + pgd_base[0] = pgd_base[USER_PTRS_PER_PGD]; +#endif +} + +void __init zap_low_mappings (void) +{ + int i; + /* + * Zap initial low-memory mappings. + * + * Note that "pgd_clear()" doesn't do it for + * us in this case, because pgd_clear() is a + * no-op in the 2-level case (pmd_clear() is + * the thing that clears the page-tables in + * that case). + */ + for (i = 0; i < USER_PTRS_PER_PGD; i++) + pgd_clear(swapper_pg_dir+i); + flush_tlb_all(); +} + +/* + * paging_init() sets up the page tables - note that the first 4MB are + * already mapped by head.S. + * + * This routines also unmaps the page at virtual kernel address 0, so + * that we can trap those pesky NULL-reference errors in the kernel. + */ +void __init paging_init(void) +{ + asm volatile("movq %%cr4,%0" : "=r" (mmu_cr4_features)); + + pagetable_init(); + + __flush_tlb_all(); + + { + unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; + unsigned int max_dma, low; + + max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; + low = max_low_pfn; + + if (low < max_dma) + zones_size[ZONE_DMA] = low; + else { + zones_size[ZONE_DMA] = max_dma; + zones_size[ZONE_NORMAL] = low - max_dma; + } + free_area_init(zones_size); + } + return; +} + + +static inline int page_is_ram (unsigned long pagenr) +{ + int i; + + for (i = 0; i < e820.nr_map; i++) { + unsigned long addr, end; + + if (e820.map[i].type != E820_RAM) /* not usable memory */ + continue; + /* + * !!!FIXME!!! Some BIOSen report areas as RAM that + * are not. Notably the 640->1Mb area. We need a sanity + * check here. + */ + addr = (e820.map[i].addr+PAGE_SIZE-1) >> PAGE_SHIFT; + end = (e820.map[i].addr+e820.map[i].size) >> PAGE_SHIFT; + if ((pagenr >= addr) && (pagenr < end)) + return 1; + } + return 0; +} + +void __init mem_init(void) +{ + int codesize, reservedpages, datasize, initsize; + int tmp; + + if (!mem_map) + BUG(); + + max_mapnr = num_physpages = max_low_pfn; + high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); + + /* clear the zero-page */ + memset(empty_zero_page, 0, PAGE_SIZE); + + /* this will put all low memory onto the freelists */ + totalram_pages += free_all_bootmem(); + + after_bootmem = 1; + + reservedpages = 0; + for (tmp = 0; tmp < max_low_pfn; tmp++) + /* + * Only count reserved RAM pages + */ + if (page_is_ram(tmp) && PageReserved(mem_map+tmp)) + reservedpages++; + codesize = (unsigned long) &_etext - (unsigned long) &_text; + datasize = (unsigned long) &_edata - (unsigned long) &_etext; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + + printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init)\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), + max_mapnr << (PAGE_SHIFT-10), + codesize >> 10, + reservedpages << (PAGE_SHIFT-10), + datasize >> 10, + initsize >> 10); + + /* + * Subtle. SMP is doing it's boot stuff late (because it has to + * fork idle threads) - but it also needs low mappings for the + * protected-mode entry to work. We zap these entries only after + * the WP-bit has been tested. + */ +#ifndef CONFIG_SMP + zap_low_mappings(); +#endif +} + +void free_initmem(void) +{ + unsigned long addr; + + addr = (unsigned long)(&__init_begin); + for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { + ClearPageReserved(virt_to_page(addr)); + set_page_count(virt_to_page(addr), 1); + free_page(addr); + totalram_pages++; + } + printk ("Freeing unused kernel memory: %luk freed\n", (&__init_end - &__init_begin) >> 10); +} + +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long start, unsigned long end) +{ + if (start < (unsigned long)&_end) + return; + printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10); + for (; start < end; start += PAGE_SIZE) { + ClearPageReserved(virt_to_page(start)); + set_page_count(virt_to_page(start), 1); + free_page(start); + totalram_pages++; + } +} +#endif + +void si_meminfo(struct sysinfo *val) +{ + val->totalram = totalram_pages; + val->sharedram = atomic_read(&shmem_nrpages); + val->freeram = nr_free_pages(); + val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = 0; + val->freehigh = nr_free_highpages(); + val->mem_unit = PAGE_SIZE; + return; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/mm/ioremap.c linux.ac/arch/x86_64/mm/ioremap.c --- linux.vanilla/arch/x86_64/mm/ioremap.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/mm/ioremap.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,163 @@ +/* + * arch/i386/mm/ioremap.c + * + * Re-map IO memory to kernel address space so that we can access it. + * This is needed for high PCI addresses that aren't mapped in the + * 640k-1MB IO memory area on PC's + * + * (C) Copyright 1995 1996 Linus Torvalds + */ + +#include +#include +#include + +static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + if (address >= end) + BUG(); + do { + if (!pte_none(*pte)) { + printk("remap_area_pte: page already exists\n"); + BUG(); + } + set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | _PAGE_RW | + _PAGE_DIRTY | _PAGE_ACCESSED | flags))); + address += PAGE_SIZE; + phys_addr += PAGE_SIZE; + pte++; + } while (address && (address < end)); +} + +static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + phys_addr -= address; + if (address >= end) + BUG(); + do { + pte_t * pte = pte_alloc(&init_mm, pmd, address); + if (!pte) + return -ENOMEM; + remap_area_pte(pte, address, end - address, address + phys_addr, flags); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address && (address < end)); + return 0; +} + +static int remap_area_pages(unsigned long address, unsigned long phys_addr, + unsigned long size, unsigned long flags) +{ + int error; + pgd_t * dir; + unsigned long end = address + size; + + phys_addr -= address; + dir = pgd_offset(&init_mm, address); + flush_cache_all(); + if (address >= end) + BUG(); + spin_lock(&init_mm.page_table_lock); + do { + pmd_t *pmd; + pmd = pmd_alloc(&init_mm, dir, address); + error = -ENOMEM; + if (!pmd) + break; + if (remap_area_pmd(pmd, address, end - address, + phys_addr + address, flags)) + break; + error = 0; + address = (address + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } while (address && (address < end)); + spin_unlock(&init_mm.page_table_lock); + flush_tlb_all(); + return error; +} + +/* + * Generic mapping function (not visible outside): + */ + +/* + * Remap an arbitrary physical address space into the kernel virtual + * address space. Needed when the kernel wants to access high addresses + * directly. + * + * NOTE! We need to allow non-page-aligned mappings too: we will obviously + * have to convert them into an offset in a page-aligned mapping, but the + * caller shouldn't need to know that small detail. + */ +void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +{ + void * addr; + struct vm_struct * area; + unsigned long offset, last_addr; + + /* Don't allow wraparound or zero size */ + last_addr = phys_addr + size - 1; + if (!size || last_addr < phys_addr) + return NULL; + + /* + * Don't remap the low PCI/ISA area, it's always mapped.. + */ + if (phys_addr >= 0xA0000 && last_addr < 0x100000) + return phys_to_virt(phys_addr); + + /* + * Don't allow anybody to remap normal RAM that we're using.. + */ + if (phys_addr < virt_to_phys(high_memory)) { + char *t_addr, *t_end; + struct page *page; + + t_addr = __va(phys_addr); + t_end = t_addr + (size - 1); + + for(page = virt_to_page(t_addr); page <= virt_to_page(t_end); page++) + if(!PageReserved(page)) + return NULL; + } + + /* + * Mappings have to be page-aligned + */ + offset = phys_addr & ~PAGE_MASK; + phys_addr &= PAGE_MASK; + size = PAGE_ALIGN(last_addr) - phys_addr; + + /* + * Ok, go for it.. + */ + area = get_vm_area(size, VM_IOREMAP); + if (!area) + return NULL; + addr = area->addr; + if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags)) { + vfree(addr); + return NULL; + } + return (void *) (offset + (char *)addr); +} + +void iounmap(void *addr) +{ + if (addr > high_memory) + return vfree((void *) (PAGE_MASK & (unsigned long) addr)); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/tools/Makefile linux.ac/arch/x86_64/tools/Makefile --- linux.vanilla/arch/x86_64/tools/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/tools/Makefile Wed Oct 10 01:47:38 2001 @@ -0,0 +1,25 @@ + +TARGET = $(TOPDIR)/include/asm-x86_64/offset.h + +all: $(TARGET) + +.PHONY: all + +$(TARGET): offset.h + cmp -s $^ $@ || (cp $^ $(TARGET).new && mv $(TARGET).new $(TARGET)) + +.PHONY : offset.h + +offset.h: + $(CC) $(CFLAGS) -S -o offset.tmp offset.c + sed -n -f offset.sed < offset.tmp > offset.h + +clean: + rm -f offset.[hs] $(TARGET).new + +mrproper: + rm -f offset.[hs] $(TARGET) + rm -f $(TARGET) + +include $(TOPDIR)/Rules.make + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/tools/offset.c linux.ac/arch/x86_64/tools/offset.c --- linux.vanilla/arch/x86_64/tools/offset.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/tools/offset.c Wed Oct 10 01:47:38 2001 @@ -0,0 +1,67 @@ +/* Written 2000 by Andi Kleen */ +/* This program is never executed, just its assembly is examined for offsets + (this trick is needed to get cross compiling right) */ +/* $Id: offset.c,v 1.12 2001/07/05 23:43:47 ak Exp $ */ +#define ASM_OFFSET_H 1 +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#include +#include +#include +#include +#include +#include + +#define output(x) asm volatile ("--- " x) +#define outconst(x,y) asm volatile ("--- " x : : "i" (y)) + +int main(void) +{ + output("/* Auto generated by arch/../tools/offset.c at " __DATE__ ". Do not edit. */\n"); + output("#ifndef ASM_OFFSET_H\n"); + output("#define ASM_OFFSET_H 1\n"); + + // task struct entries needed by entry.S +#define ENTRY(entry) outconst("#define tsk_" #entry " %0", offsetof(struct task_struct, entry)) + ENTRY(state); + ENTRY(flags); + ENTRY(sigpending); + ENTRY(addr_limit); + ENTRY(need_resched); + ENTRY(exec_domain); + ENTRY(ptrace); + ENTRY(processor); + ENTRY(need_resched); + ENTRY(thread); +#undef ENTRY +#define ENTRY(entry) outconst("#define pda_" #entry " %0", offsetof(struct x8664_pda, entry)) + ENTRY(kernelstack); + ENTRY(oldrsp); + ENTRY(pcurrent); + ENTRY(irqrsp); + ENTRY(irqcount); + ENTRY(irqstack); + ENTRY(pgd_quick); + ENTRY(pmd_quick); + ENTRY(pte_quick); + ENTRY(pgtable_cache_sz); + ENTRY(cpunumber); + ENTRY(irqstackptr); +#undef ENTRY + // various stuff needed by entry.S + outconst("#define PT_TRACESYS %0", PT_TRACESYS); + outconst("#define TASK_SIZE %0", TASK_SIZE); + outconst("#define SIGCHLD %0", SIGCHLD); + outconst("#define CLONE_VFORK %0", CLONE_VFORK); + outconst("#define CLONE_VM %0", CLONE_VM); + + + outconst("#define thread_flags %0" , offsetof(struct thread_struct, flags)); + outconst("#define ASM_THREAD_IA32 %0", THREAD_IA32); + + output("#endif\n"); + + return(0); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/tools/offset.sed linux.ac/arch/x86_64/tools/offset.sed --- linux.vanilla/arch/x86_64/tools/offset.sed Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/tools/offset.sed Wed Oct 10 01:47:38 2001 @@ -0,0 +1,7 @@ +/---/ { + s/---// + s/\$// + s/^ // + s/^ // + p +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/x86_64/vmlinux.lds linux.ac/arch/x86_64/vmlinux.lds --- linux.vanilla/arch/x86_64/vmlinux.lds Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/x86_64/vmlinux.lds Wed Oct 10 01:47:38 2001 @@ -0,0 +1,105 @@ +/* ld script to make i386 Linux kernel + * Written by Martin Mares ; + */ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT_ARCH(i386) +ENTRY(_start) +SECTIONS +{ + . = 0xffffffff80100000; + _text = .; /* Text and read-only data */ + .text : { + *(.text) + *(.fixup) + *(.gnu.warning) + } = 0x9090 + .text.lock : { *(.text.lock) } /* out-of-line lock text */ + + _etext = .; /* End of text section */ + + .rodata : { *(.rodata) *(.rodata.*) } + .kstrtab : { *(.kstrtab) } + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + + .data : { /* Data */ + *(.data) + CONSTRUCTORS + } + + _edata = .; /* End of data section */ + + __bss_start = .; /* BSS */ + .bss : { + *(.bss) + } + __bss_end = .; + + . = ALIGN(64); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + .vsyscall_0 -10*1024*1024: AT ((LOADADDR(.data.cacheline_aligned) + SIZEOF(.data.cacheline_aligned) + 4095) & ~(4095)) { *(.vsyscall_0) } + __vsyscall_0 = LOADADDR(.vsyscall_0); + . = ALIGN(64); + .vxtime_sequence : AT ((LOADADDR(.vsyscall_0) + SIZEOF(.vsyscall_0) + 63) & ~(63)) { *(.vxtime_sequence) } + vxtime_sequence = LOADADDR(.vxtime_sequence); + .last_tsc_low : AT (LOADADDR(.vxtime_sequence) + SIZEOF(.vxtime_sequence)) { *(.last_tsc_low) } + last_tsc_low = LOADADDR(.last_tsc_low); + .delay_at_last_interrupt : AT (LOADADDR(.last_tsc_low) + SIZEOF(.last_tsc_low)) { *(.delay_at_last_interrupt) } + delay_at_last_interrupt = LOADADDR(.delay_at_last_interrupt); + .fast_gettimeoffset_quotient : AT (LOADADDR(.delay_at_last_interrupt) + SIZEOF(.delay_at_last_interrupt)) { *(.fast_gettimeoffset_quotient) } + fast_gettimeoffset_quotient = LOADADDR(.fast_gettimeoffset_quotient); + .wall_jiffies : AT (LOADADDR(.fast_gettimeoffset_quotient) + SIZEOF(.fast_gettimeoffset_quotient)) { *(.wall_jiffies) } + wall_jiffies = LOADADDR(.wall_jiffies); + .sys_tz : AT (LOADADDR(.wall_jiffies) + SIZEOF(.wall_jiffies)) { *(.sys_tz) } + sys_tz = LOADADDR(.sys_tz); + .jiffies : AT (LOADADDR(.sys_tz) + SIZEOF(.sys_tz)) { *(.jiffies) } + jiffies = LOADADDR(.jiffies); + . = ALIGN(16); + .xtime : AT ((LOADADDR(.jiffies) + SIZEOF(.jiffies) + 15) & ~(15)) { *(.xtime) } + xtime = LOADADDR(.xtime); + .vsyscall_1 ADDR(.vsyscall_0) + 1024: AT (LOADADDR(.vsyscall_0) + 1024) { *(.vsyscall_1) } + . = LOADADDR(.vsyscall_0) + 4096; + + . = ALIGN(8192); /* init_task */ + .data.init_task : { *(.data.init_task) } + + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(16); + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { *(.initcall.init) } + __initcall_end = .; + . = ALIGN(4096); + __init_end = .; + + _end = . ; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.text.exit) + *(.data.exit) + *(.exitcall.exit) + } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/Makefile linux.ac/drivers/Makefile --- linux.vanilla/drivers/Makefile Mon Jul 16 00:15:44 2001 +++ linux.ac/drivers/Makefile Wed Oct 10 01:47:38 2001 @@ -7,7 +7,7 @@ mod-subdirs := dio mtd sbus video macintosh usb input telephony sgi ide \ - i2o message/fusion scsi md ieee1394 pnp isdn atm \ + message/i2o message/fusion scsi md ieee1394 pnp isdn atm \ fc4 net/hamradio i2c acpi bluetooth subdir-y := parport char block net sound misc media cdrom @@ -31,7 +31,7 @@ subdir-$(CONFIG_SGI) += sgi subdir-$(CONFIG_IDE) += ide subdir-$(CONFIG_SCSI) += scsi -subdir-$(CONFIG_I2O) += i2o +subdir-$(CONFIG_I2O) += message/i2o subdir-$(CONFIG_FUSION) += message/fusion subdir-$(CONFIG_MD) += md subdir-$(CONFIG_IEEE1394) += ieee1394 @@ -48,4 +48,3 @@ subdir-$(CONFIG_BLUEZ) += bluetooth include $(TOPDIR)/Rules.make - diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/char/mouse_rpc.c linux.ac/drivers/acorn/char/mouse_rpc.c --- linux.vanilla/drivers/acorn/char/mouse_rpc.c Wed Jun 27 22:12:04 2001 +++ linux.ac/drivers/acorn/char/mouse_rpc.c Thu Oct 11 09:43:20 2001 @@ -83,3 +83,8 @@ module_init(mouse_rpc_init); module_exit(mouse_rpc_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("RiscPC mouse driver"); +MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/acornscsi.c linux.ac/drivers/acorn/scsi/acornscsi.c --- linux.vanilla/drivers/acorn/scsi/acornscsi.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/acorn/scsi/acornscsi.c Wed Oct 10 01:47:39 2001 @@ -3159,5 +3159,7 @@ module_init(acornscsi_init); module_exit(acornscsi_exit); +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("AcornSCSI driver"); MODULE_LICENSE("GPL"); EXPORT_NO_SYMBOLS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/arxescsi.c linux.ac/drivers/acorn/scsi/arxescsi.c --- linux.vanilla/drivers/acorn/scsi/arxescsi.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/acorn/scsi/arxescsi.c Wed Oct 10 01:47:39 2001 @@ -439,4 +439,7 @@ module_init(init_arxe_scsi_driver); module_exit(exit_arxe_scsi_driver); +MODULE_AUTHOR("Stefan Hanske"); +MODULE_DESCRIPTION("ARXESCSI driver for Acorn machines"); MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/cumana_2.c linux.ac/drivers/acorn/scsi/cumana_2.c --- linux.vanilla/drivers/acorn/scsi/cumana_2.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/acorn/scsi/cumana_2.c Wed Oct 10 01:47:39 2001 @@ -82,12 +82,6 @@ static struct expansion_card *ecs[MAX_ECARDS]; -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Cumana SCSI II driver"); -MODULE_PARM(term, "1-8i"); -MODULE_PARM_DESC(term, "SCSI bus termination"); -MODULE_LICENSE("GPL"); - /* * Use term=0,1,0,0,0 to turn terminators on/off */ @@ -600,3 +594,10 @@ module_init(cumanascsi2_init); module_exit(cumanascsi2_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Cumana SCSI-2 driver for Acorn machines"); +MODULE_PARM(term, "1-8i"); +MODULE_PARM_DESC(term, "SCSI bus termination"); +MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/ecoscsi.c linux.ac/drivers/acorn/scsi/ecoscsi.c --- linux.vanilla/drivers/acorn/scsi/ecoscsi.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/acorn/scsi/ecoscsi.c Wed Oct 10 01:47:39 2001 @@ -293,5 +293,7 @@ module_init(ecoscsi_init); module_exit(ecoscsi_exit); -EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Econet-SCSI driver for Acorn machines"); MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/eesox.c linux.ac/drivers/acorn/scsi/eesox.c --- linux.vanilla/drivers/acorn/scsi/eesox.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/acorn/scsi/eesox.c Wed Oct 10 01:47:39 2001 @@ -80,11 +80,6 @@ static struct expansion_card *ecs[MAX_ECARDS]; -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("EESOX SCSI driver"); -MODULE_PARM(term, "1-8i"); -MODULE_PARM_DESC(term, "SCSI bus termination"); - /* * Use term=0,1,0,0,0 to turn terminators on/off */ @@ -602,5 +597,9 @@ module_init(eesox_init); module_exit(eesox_exit); -EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("EESOX 'Fast' SCSI driver for Acorn machines"); +MODULE_PARM(term, "1-8i"); +MODULE_PARM_DESC(term, "SCSI bus termination"); MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/fas216.c linux.ac/drivers/acorn/scsi/fas216.c --- linux.vanilla/drivers/acorn/scsi/fas216.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/acorn/scsi/fas216.c Wed Oct 10 01:47:39 2001 @@ -60,9 +60,6 @@ #include "../../scsi/hosts.h" #include "fas216.h" -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Generic FAS216/NCR53C9x driver"); - #define VER_MAJOR 0 #define VER_MINOR 0 #define VER_PATCH 5 @@ -2767,15 +2764,6 @@ EXPORT_SYMBOL(fas216_print_stats); EXPORT_SYMBOL(fas216_print_device); -#ifdef MODULE -int __init init_module(void) -{ - return 0; -} - -void __exit cleanup_module(void) -{ -} -#endif - +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Generic FAS216/NCR53C9x driver core"); MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/msgqueue.c linux.ac/drivers/acorn/scsi/msgqueue.c --- linux.vanilla/drivers/acorn/scsi/msgqueue.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/acorn/scsi/msgqueue.c Wed Oct 10 01:47:39 2001 @@ -16,10 +16,6 @@ #include "msgqueue.h" -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("SCSI message queue handling"); -MODULE_LICENSE("GPL"); - /* * Function: struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq) * Purpose : Allocate a message queue entry @@ -170,13 +166,6 @@ EXPORT_SYMBOL(msgqueue_addmsg); EXPORT_SYMBOL(msgqueue_flush); -#ifdef MODULE -int __init init_module(void) -{ - return 0; -} - -void __exit cleanup_module(void) -{ -} -#endif +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("SCSI message queue handling"); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/oak.c linux.ac/drivers/acorn/scsi/oak.c --- linux.vanilla/drivers/acorn/scsi/oak.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/acorn/scsi/oak.c Wed Oct 10 01:47:39 2001 @@ -286,5 +286,7 @@ module_init(oakscsi_init); module_exit(oakscsi_exit); +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Oak SCSI driver"); MODULE_LICENSE("GPL"); EXPORT_NO_SYMBOLS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/powertec.c linux.ac/drivers/acorn/scsi/powertec.c --- linux.vanilla/drivers/acorn/scsi/powertec.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/acorn/scsi/powertec.c Wed Oct 10 01:47:39 2001 @@ -77,12 +77,6 @@ #define VER_MINOR 0 #define VER_PATCH 5 -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Powertec SCSI driver"); -MODULE_PARM(term, "1-8i"); -MODULE_PARM_DESC(term, "SCSI bus termination"); -MODULE_LICENSE("GPL"); - static struct expansion_card *ecs[MAX_ECARDS]; /* @@ -502,3 +496,10 @@ module_init(powertecscsi_init); module_exit(powertecscsi_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Powertec SCSI driver"); +MODULE_PARM(term, "1-8i"); +MODULE_PARM_DESC(term, "SCSI bus termination"); +MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/queue.c linux.ac/drivers/acorn/scsi/queue.c --- linux.vanilla/drivers/acorn/scsi/queue.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/acorn/scsi/queue.c Wed Oct 10 01:47:39 2001 @@ -25,10 +25,6 @@ #include "../../scsi/scsi.h" -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("SCSI command queueing"); -MODULE_LICENSE("GPL"); - #define DEBUG typedef struct queue_entry { @@ -296,13 +292,6 @@ EXPORT_SYMBOL(queue_remove_cmd); EXPORT_SYMBOL(queue_probetgtlun); -#ifdef MODULE -int __init init_module (void) -{ - return 0; -} - -void __exit cleanup_module (void) -{ -} -#endif +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("SCSI command queueing"); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acpi/hardware/hwregs.c linux.ac/drivers/acpi/hardware/hwregs.c --- linux.vanilla/drivers/acpi/hardware/hwregs.c Sun Sep 23 17:42:32 2001 +++ linux.ac/drivers/acpi/hardware/hwregs.c Wed Oct 10 01:47:39 2001 @@ -29,6 +29,7 @@ #include "acpi.h" #include "achware.h" #include "acnamesp.h" +#include #define _COMPONENT ACPI_HARDWARE MODULE_NAME ("hwregs") @@ -162,6 +163,24 @@ if ((sleep_state > ACPI_S_STATES_MAX) || !slp_typ_a || !slp_typ_b) { return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + if (dmi_broken & BROKEN_ACPI_Sx) { + printk("Attempted to enter %d\n", sleep_state); + switch (sleep_state) { + /* 0,3,4,6,7,8,11,15 is nop */ + /* 5 is nop, too? No. 5 is some valid state, just not S1 nor S3 (strange). */ + /* 13 is lockup when used in S1 or S3 context */ + /* 14 is "keep monitor on and than all goes off when power is pressed in S1 context */ + /* 1,2,10,9,14 are known working */ + /* 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 */ + case 1: *slp_typ_a = *slp_typ_b =12; return_ACPI_STATUS (status); /* Hey, seems okay! */ + case 3: *slp_typ_a = *slp_typ_b =15; return_ACPI_STATUS (status); /* Not known, yet */ + case 4: *slp_typ_a = *slp_typ_b = 1; return_ACPI_STATUS (status); /* Seems ok, but it times somehow long to wakeup. Is it possible this is S3? (same as 9) */ + case 5: *slp_typ_a = *slp_typ_b =10; return_ACPI_STATUS (status); /* Seems ok, (same as 2) */ + default: return_ACPI_STATUS (AE_BAD_PARAMETER); + } + } /* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acpi/include/platform/acgcc.h linux.ac/drivers/acpi/include/platform/acgcc.h --- linux.vanilla/drivers/acpi/include/platform/acgcc.h Sun Sep 23 17:42:32 2001 +++ linux.ac/drivers/acpi/include/platform/acgcc.h Wed Oct 10 01:47:39 2001 @@ -101,7 +101,6 @@ #define disable() __cli() #define enable() __sti() #define halt() __asm__ __volatile__ ("sti; hlt":::"memory") -#define wbinvd() __asm__ __volatile__ ("wbinvd":::"memory") /*! [Begin] no source code translation * diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acpi/ospm/processor/pr_osl.c linux.ac/drivers/acpi/ospm/processor/pr_osl.c --- linux.vanilla/drivers/acpi/ospm/processor/pr_osl.c Sun Sep 23 17:42:32 2001 +++ linux.ac/drivers/acpi/ospm/processor/pr_osl.c Wed Oct 10 01:47:39 2001 @@ -144,7 +144,7 @@ PR_CONTEXT *processor) { u32 i = 0; - struct proc_dir_entry *proc_entry = NULL; + struct proc_dir_entry *proc_entry = NULL, *proc; char processor_uid[16]; if (!processor) { @@ -170,15 +170,18 @@ sprintf(processor_uid, "%d", processor->uid); proc_entry = proc_mkdir(processor_uid, pr_proc_root); - if (!proc_entry) { + if (!proc_entry) return(AE_ERROR); - } - create_proc_read_entry(PR_PROC_STATUS, S_IFREG | S_IRUGO, - proc_entry, pr_osl_proc_read_status, (void*)processor); + proc = create_proc_read_entry(PR_PROC_STATUS, S_IFREG | S_IRUGO, + proc_entry, pr_osl_proc_read_status, (void*)processor); + if (!proc_entry) + return(AE_ERROR); - create_proc_read_entry(PR_PROC_INFO, S_IFREG | S_IRUGO, - proc_entry, pr_osl_proc_read_info, (void*)processor); + proc = create_proc_read_entry(PR_PROC_INFO, S_IFREG | S_IRUGO, + proc_entry, pr_osl_proc_read_info, (void*)processor); + if (!proc_entry) + return(AE_ERROR); return(AE_OK); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acpi/ospm/processor/prpower.c linux.ac/drivers/acpi/ospm/processor/prpower.c --- linux.vanilla/drivers/acpi/ospm/processor/prpower.c Sun Sep 23 17:42:32 2001 +++ linux.ac/drivers/acpi/ospm/processor/prpower.c Wed Oct 10 01:47:39 2001 @@ -274,7 +274,7 @@ * by this state's promotion policy, prevents * promotions from occuring. */ - if (bm_control && !(processor->power.bm_activity & + if (!bm_control || !(processor->power.bm_activity & c_state->promotion.bm_threshold)) { next_state = c_state->promotion.target_state; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acpi/ospm/system/sm_osl.c linux.ac/drivers/acpi/ospm/system/sm_osl.c --- linux.vanilla/drivers/acpi/ospm/system/sm_osl.c Sun Sep 23 17:42:32 2001 +++ linux.ac/drivers/acpi/ospm/system/sm_osl.c Wed Oct 10 01:47:39 2001 @@ -719,6 +719,11 @@ /* TODO: resume devices and restore their state */ enable(); + + if (dmi_broken & BROKEN_INIT_AFTER_S1) { + printk("Broken toshiba laptop -> kicking interrupts\n"); + init_8259A(0); + } return status; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acpi/ospm/thermal/tz_osl.c linux.ac/drivers/acpi/ospm/thermal/tz_osl.c --- linux.vanilla/drivers/acpi/ospm/thermal/tz_osl.c Sun Sep 23 17:42:32 2001 +++ linux.ac/drivers/acpi/ospm/thermal/tz_osl.c Wed Oct 10 01:47:39 2001 @@ -177,7 +177,7 @@ tz_osl_add_device( TZ_CONTEXT *thermal_zone) { - struct proc_dir_entry *proc_entry = NULL; + struct proc_dir_entry *proc_entry = NULL, *proc; if (!thermal_zone) { return(AE_BAD_PARAMETER); @@ -186,15 +186,18 @@ printk("Thermal Zone: found\n"); proc_entry = proc_mkdir(thermal_zone->uid, tz_proc_root); - if (!proc_entry) { + if (!proc_entry) return(AE_ERROR); - } - create_proc_read_entry(TZ_PROC_STATUS, S_IFREG | S_IRUGO, + proc = create_proc_read_entry(TZ_PROC_STATUS, S_IFREG | S_IRUGO, proc_entry, tz_osl_proc_read_status, (void*)thermal_zone); + if (!proc) + return(AE_ERROR); - create_proc_read_entry(TZ_PROC_INFO, S_IFREG | S_IRUGO, + proc = create_proc_read_entry(TZ_PROC_INFO, S_IFREG | S_IRUGO, proc_entry, tz_osl_proc_read_info, (void*)thermal_zone); + if (!proc) + return(AE_ERROR); return(AE_OK); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/atm/Config.in linux.ac/drivers/atm/Config.in --- linux.vanilla/drivers/atm/Config.in Sat Feb 17 00:02:35 2001 +++ linux.ac/drivers/atm/Config.in Wed Oct 10 01:47:39 2001 @@ -7,6 +7,7 @@ tristate 'ATM over TCP' CONFIG_ATM_TCP fi if [ "$CONFIG_PCI" = "y" ]; then + tristate 'Efficient Networks Speedstream 3010' CONFIG_ATM_LANAI tristate 'Efficient Networks ENI155P' CONFIG_ATM_ENI if [ "$CONFIG_ATM_ENI" != "n" ]; then bool ' Enable extended debugging' CONFIG_ATM_ENI_DEBUG diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/atm/Makefile linux.ac/drivers/atm/Makefile --- linux.vanilla/drivers/atm/Makefile Mon Aug 27 17:01:33 2001 +++ linux.ac/drivers/atm/Makefile Wed Oct 10 01:47:39 2001 @@ -26,6 +26,7 @@ obj-$(CONFIG_ATM_TCP) += atmtcp.o obj-$(CONFIG_ATM_IA) += iphase.o suni.o obj-$(CONFIG_ATM_FIRESTREAM) += firestream.o +obj-$(CONFIG_ATM_LANAI) += lanai.o ifeq ($(CONFIG_ATM_FORE200E_PCA),y) FORE200E_FW_OBJS += fore200e_pca_fw.o diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/atm/atmdev_init.c linux.ac/drivers/atm/atmdev_init.c --- linux.vanilla/drivers/atm/atmdev_init.c Fri Dec 29 22:35:47 2000 +++ linux.ac/drivers/atm/atmdev_init.c Wed Oct 10 01:47:39 2001 @@ -25,6 +25,9 @@ #ifdef CONFIG_ATM_FORE200E extern int fore200e_detect(void); #endif +#ifdef CONFIG_ATM_LANAI +extern int lanai_detect(void); +#endif /* @@ -55,6 +58,9 @@ #endif #ifdef CONFIG_ATM_FORE200E devs += fore200e_detect(); +#endif +#ifdef CONFIG_ATM_LANAI + devs += lanai_detect(); #endif return devs; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/atm/lanai.c linux.ac/drivers/atm/lanai.c --- linux.vanilla/drivers/atm/lanai.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/atm/lanai.c Wed Oct 10 01:47:39 2001 @@ -0,0 +1,2916 @@ +/* lanai.c -- Copyright 1999 by Mitchell Blank Jr + * + * 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 driver supports ATM cards based on the Efficient "Lanai" + * chipset such as the Speedstream 3010 and the ENI-25p. The + * Speedstream 3060 is currently not supported since we don't + * have the code to drive the on-board Alcatel DSL chipset (yet). + * + * Thanks to Efficient for supporting this project with hardware, + * documentation, and by answering my questions. + * + * Things not working yet: + * + * o We're only set up to compile as a module currently. i.e. + * you should put the source in drivers/atm/lanai.c and then + * just do "make drivers/atm/lanai.o" from the main + * source directory. This will produce a drivers/atm/lanai.o + * file suitable for insmod'ing + * + * o We don't support the Speedstream 3060 yet - this card has + * an on-board DSL modem chip by Alcatel and the driver will + * need some extra code added to handle it + * + * o Note that due to limitations of the Lanai only one VCC can be + * in CBR at once + * + * o We don't currently parse the EEPROM at all. The code is all + * there as per the spec, but it doesn't actually work. I think + * there may be some issues with the docs. Anyway, do NOT + * enable it yet - bugs in that code may actually damage your + * hardware! Because of this you should hardware an ESI before + * trying to use this in a LANE or MPOA environment. + * + * o AAL0 is stubbed in but the actual rx/tx path isn't written yet: + * vcc_tx_aal0() needs to send or queue a SKB + * vcc_tx_unqueue_aal0() needs to attempt to send queued SKBs + * vcc_rx_aal0() needs to handle AAL0 interrupts + * This isn't too much work - I just wanted to get other things + * done first. + * + * o lanai_change_qos() isn't written yet + * + * o There aren't any ioctl's yet -- I'd like to eventually support + * setting loopback and LED modes that way. (see lanai_ioctl) + * + * o If the segmentation engine or DMA gets shut down we should restart + * card as per section 17.0i. (see lanai_reset) + * + * o setsockopt(SO_CIRANGE) isn't done (although despite what the + * API says it isn't exactly commonly implemented) + */ + +/* Version history: + * v.0.02 -- 11-JAN-2000 -- Endian fixes + * v.0.01 -- 30-NOV-1999 -- Initial release + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef PCI_VENDOR_ID_EF_ATM_LANAI2 +/* These need to eventually go into - they're here for now */ +#define PCI_VENDOR_ID_EF_ATM_LANAI2 0x0003 +#define PCI_VENDOR_ID_EF_ATM_LANAIHB 0x0005 +#endif + +/* -------------------- TUNABLE PARAMATERS: */ + +/* + * Maximum number of VCIs per card. Setting it lower could theoretically + * save some memory, but since we allocate our vcc list with get_free_pages, + * it's not really likely for most architectures + */ +#define NUM_VCI (1024) + +/* + * Enable extra debugging + */ +#define DEBUG +/* + * Debug _all_ register operations with card, except the memory test. + * Also disables the timed poll to prevent extra chattiness. This + * isn't for normal use + */ +#undef DEBUG_RW + +/* + * The programming guide specifies a full test of the on-board SRAM + * at initialization time. Undefine to remove this + */ +#define FULL_MEMORY_TEST + +/* + * This is the number of (4 byte) service entries that we will + * try to allocate at startup. Note that we will end up with + * one PAGE_SIZE's worth regardless of what this is set to + */ +#define SERVICE_ENTRIES (1024) +/* TODO: make above a module load-time option */ + +/* + * We normally read the onboard EEPROM in order to discover our MAC + * address. Undefine to _not_ do this + */ +/* #define READ_EEPROM */ /* ***DONT ENABLE YET*** */ +/* TODO: make above a module load-time option (also) */ + +/* + * Depth of TX fifo (in 128 byte units; range 2-31) + * Smaller numbers are better for network latency + * Larger numbers are better for PCI latency + * I'm really sure where the best tradeoff is, but the BSD driver uses + * 7 and it seems to work ok. + */ +#define TX_FIFO_DEPTH (7) +/* TODO: make above a module load-time option */ + +/* + * How often (in jiffies) we will try to unstick stuck connections - + * shouldn't need to happen much + */ +#define LANAI_POLL_PERIOD (10*HZ) +/* TODO: make above a module load-time option */ + +/* + * When allocating an AAL5 receiving buffer, try to make it at least + * large enough to hold this many max_sdu sized PDUs + */ +#define AAL5_RX_MULTIPLIER (3) +/* TODO: make above a module load-time option */ + +/* + * Same for transmitting buffer + */ +#define AAL5_TX_MULTIPLIER (3) +/* TODO: make above a module load-time option */ + +/* + * When allocating an AAL0 transmiting buffer, how many cells should fit. + * Remember we'll end up with a PAGE_SIZE of them anyway, so this isn't + * really critical + */ +#define AAL0_TX_MULTIPLIER (40) +/* TODO: make above a module load-time option */ + +/* + * How large should we make the AAL0 receiving buffer. Remember that this + * is shared between all AAL0 VC's + */ +#define AAL0_RX_BUFFER_SIZE (PAGE_SIZE) +/* TODO: make above a module load-time option */ + +/* + * Should we use Lanai's "powerdown" feature when no vcc's are bound? + */ +/* #define USE_POWERDOWN */ +/* TODO: make above a module load-time option (also) */ + +/* -------------------- DEBUGGING AIDS: */ + +#define DEV_LABEL "lanai" + +#ifdef DEBUG + +#define DPRINTK(format, args...) \ + printk(KERN_DEBUG DEV_LABEL ": " format, ##args) +#define APRINTK(truth, format, args...) \ + do { \ + if (!(truth)) \ + printk(KERN_ERR DEV_LABEL ": " format, ##args); \ + } while (0) + +#else /* !DEBUG */ + +#define DPRINTK(format, args...) +#define APRINTK(truth, format, args...) + +#endif /* DEBUG */ + +#ifdef DEBUG_RW +#define RWDEBUG(format, args...) \ + printk(KERN_DEBUG DEV_LABEL ": " format, ##args) +#else /* !DEBUG_RW */ +#define RWDEBUG(format, args...) +#endif + +/* -------------------- DATA DEFINITIONS: */ + +#define LANAI_MAPPING_SIZE (0x40000) +#define LANAI_EEPROM_SIZE (128) + +typedef int vci_t; +typedef unsigned long bus_addr_t; + +/* A bitfield large enough for NUM_VCI */ +#define VCI_BITFIELD_NELEM ((NUM_VCI + BITS_PER_LONG - 1) / BITS_PER_LONG) +typedef struct { + unsigned long ul[VCI_BITFIELD_NELEM]; +} vci_bitfield; + +/* DMA buffer in host memory for TX, RX, or service list. */ +struct lanai_buffer { + u32 *start; /* From get_free_pages */ + u32 *end; /* One past last byte */ + u32 *ptr; /* Pointer to current host location */ + int order; /* log2(size/PAGE_SIZE) */ +}; + +struct lanai_vcc_stats { + unsigned rx_nomem; + union { + struct { + unsigned rx_badlen; + unsigned service_trash; + unsigned service_stream; + unsigned service_rxcrc; + } aal5; + struct { + } aal0; + } x; +}; + +struct lanai_dev; /* Forward declaration */ + +/* + * This is the card-specific per-vcc data. Note that unlike some other + * drivers there is NOT a 1-to-1 correspondance between these and + * atm_vcc's - each one of these represents an actual 2-way vcc, but + * an atm_vcc can be 1-way and share with a 1-way vcc in the other + * direction. To make it weirder, there can even be 0-way vccs + * bound to us, waiting to do a change_qos + */ +struct lanai_vcc { + bus_addr_t vbase; /* Base of VCC's registers */ + struct lanai_vcc_stats stats; + int nref; /* # of atm_vcc's who reference us */ + vci_t vci; + struct { + struct lanai_buffer buf; + struct atm_vcc *atmvcc; /* atm_vcc who is receiver */ + } rx; + struct { + struct lanai_buffer buf; + struct atm_vcc *atmvcc; /* atm_vcc who is transmitter */ + int endptr; /* last endptr from service entry */ + struct sk_buff_head backlog; + struct sk_buff *inprogress; /* We're streaming this PDU */ + unsigned char *pptr; /* Where we are in above */ + int inprogleft; /* Bytes left to send "inprogress" */ + void (*unqueue)(struct lanai_dev *, struct lanai_vcc *, int); + } tx; +}; + +enum lanai_type { + lanai2 = PCI_VENDOR_ID_EF_ATM_LANAI2, + lanaihb = PCI_VENDOR_ID_EF_ATM_LANAIHB +}; + +struct lanai_dev_stats { + unsigned ovfl_trash; /* # of cells dropped - buffer overflow */ + unsigned vci_trash; /* # of cells dropped - closed vci */ + unsigned hec_err; /* # of cells dropped - bad HEC */ + unsigned atm_ovfl; /* # of cells dropped - rx fifo overflow */ + unsigned pcierr_parity_detect; + unsigned pcierr_serr_set; + unsigned pcierr_master_abort; + unsigned pcierr_m_target_abort; + unsigned pcierr_s_target_abort; + unsigned pcierr_master_parity; + unsigned service_novcc_rx; + unsigned service_novcc_tx; + unsigned service_notx; + unsigned service_norx; + unsigned service_rxnotaal5; + unsigned dma_reenable; + unsigned card_reset; +}; + +struct lanai_dev { + bus_addr_t base; + struct lanai_dev_stats stats; + struct lanai_buffer service; + struct lanai_vcc **vccs; +#ifdef USE_POWERDOWN + int nbound; /* number of bound vccs */ +#endif + enum lanai_type type; + vci_t num_vci; /* Currently just NUM_VCI */ + u8 eeprom[LANAI_EEPROM_SIZE]; + u32 serialno, magicno; + struct pci_dev *pci; + vci_bitfield backlog_vccs; /* VCCs that are backlogged */ + vci_bitfield transmit_ready; /* VCCs that have transmit space */ + struct timer_list timer; + int naal0; + struct lanai_buffer aal0buf; /* AAL0 RX buffers */ + u32 conf1, conf2; /* CONFIG[12] registers */ + u32 status; /* STATUS register */ + spinlock_t txlock; + spinlock_t servicelock; + struct atm_vcc *cbrvcc; + int number; + int board_rev; + u8 pci_revision; +/* TODO - look at race conditions with maintence of conf1/conf2 */ +/* TODO - transmit locking: should we use _irq not _irqsave? */ +/* TODO - organize above in some rational fashion (see ) */ +}; + +/* -------------------- VCI_BITFIELD UTILITIES: */ + +/* + * These functions assume that BITS_PER_LONG is a power of two, which + * should be safe + */ +#if (BITS_PER_LONG & (BITS_PER_LONG - 1)) +#error lanai driver requires type long to have a power of two number of bits +#endif + +/* + * In vci_bitfield_{set,clear} we do the operation in three + * parts to ensure that gcc doesn't cast anything down to + * 32 bits (and then sign extend them later) on 64-bit + * platforms like the alpha + */ +static inline void vci_bitfield_set(vci_bitfield *bf, vci_t vci) +{ + unsigned long bit = 1; + bit <<= (unsigned long) (vci & (BITS_PER_LONG - 1)); + bf->ul[vci / BITS_PER_LONG] |= bit; +} + +static inline void vci_bitfield_clear(vci_bitfield *bf, vci_t vci) +{ + unsigned long bit = 1; + bit <<= (unsigned long) (vci & (BITS_PER_LONG - 1)); + bf->ul[vci / BITS_PER_LONG] &= ~bit; +} + +static inline void vci_bitfield_init(vci_bitfield *bf) +{ + memset(bf, 0, sizeof(*bf)); +} + +static void vci_bitfield_iterate(struct lanai_dev *lanai, + const vci_bitfield *bf, void (*func)(struct lanai_dev *,vci_t vci)) +{ + vci_t vci; + unsigned long mask; + const unsigned long *lp = &(bf->ul[0]); + for (vci = 0; vci < NUM_VCI; lp++) + if (*lp == 0) + vci += BITS_PER_LONG; + else + for (mask = 1; mask != 0; mask <<= 1, vci++) + if (*lp & mask) + func(lanai, vci); +} + +/* -------------------- BUFFER UTILITIES: */ + +/* + * Lanai needs DMA buffers aligned to 256 bytes of at least 1024 bytes - + * we assume that any page allocation will do. I'm sure this is + * never going to be a problem, but it's good to document assumtions + */ +#if PAGE_SIZE < 1024 +#error PAGE_SIZE too small to support LANAI chipset +#endif +/* + * We also assume that the maximum buffer size will be some number + * of whole pages, although that wouldn't be too hard to fix + */ +#if PAGE_SIZE > (128 * 1024) +#error PAGE_SIZE too large to support LANAI chipset +#endif + +/* Convert a size to "order" for __get_free_pages */ +static int bytes_to_order(int bytes) +{ + int order = 0; + if (bytes > (128 * 1024)) + bytes = 128 * 1024; /* Max buffer size for lanai */ + while ((PAGE_SIZE << order) < bytes) + order++; + return order; +} + +/* + * Allocate a buffer in host RAM for service list, RX, or TX + * Returns buf->order<0 if no memory + * Note that the size will be rounded up to an "order" of pages, and + * if we can't allocate that we'll settle for something smaller + * until minbytes + * + * NOTE: buffer must be 32-bit DMA capable - when linux can + * make distinction, this will need tweaking for this + * to work on BIG memory machines. + */ +static void lanai_buf_allocate(struct lanai_buffer *buf, + int bytes, int minbytes) +{ + unsigned long address; + int order = bytes_to_order(bytes); + do { + address = __get_free_pages(GFP_KERNEL, order); + if (address != 0) { /* Success */ + bytes = PAGE_SIZE << order; + buf->start = buf->ptr = (u32 *) address; + buf->end = (u32 *) (address + bytes); + memset((void *) address, 0, bytes); + break; + } + if ((PAGE_SIZE << --order) < minbytes) + order = -1; /* Too small - give up */ + } while (order >= 0); + buf->order = order; +} + +static inline void lanai_buf_deallocate(struct lanai_buffer *buf) +{ + if (buf->order >= 0) { + APRINTK(buf->start != 0, "lanai_buf_deallocate: start==0!\n"); + free_pages((unsigned long) buf->start, buf->order); + buf->start = buf->end = buf->ptr = 0; + } +} + +/* size of buffer in bytes */ +static inline int lanai_buf_size(const struct lanai_buffer *buf) +{ + return ((unsigned long) buf->end) - ((unsigned long) buf->start); +} + +/* size of buffer as "card order" (0=1k .. 7=128k) */ +static inline int lanai_buf_size_cardorder(const struct lanai_buffer *buf) +{ + return buf->order + PAGE_SHIFT - 10; +} + +/* DMA-able address for this buffer */ +static unsigned long lanai_buf_dmaaddr(const struct lanai_buffer *buf) +{ + unsigned long r = virt_to_bus(buf->start); + APRINTK((r & ~0xFFFFFF00) == 0, "bad dmaaddr: 0x%lx\n", (long) r); + return r; +} + +/* -------------------- HANDLE BACKLOG_VCCS BITFIELD: */ + +static inline void vcc_mark_backlogged(struct lanai_dev *lanai, + const struct lanai_vcc *lvcc) +{ + APRINTK(lvcc->vbase != 0, "vcc_mark_backlogged: zero vbase!\n"); + vci_bitfield_set(&lanai->backlog_vccs, lvcc->vci); +} + +static inline void vcc_unmark_backlogged(struct lanai_dev *lanai, + const struct lanai_vcc *lvcc) +{ + APRINTK(lvcc->vbase != 0, "vcc_unmark_backlogged: zero vbase!\n"); + vci_bitfield_clear(&lanai->backlog_vccs, lvcc->vci); +} + +static inline void vcc_backlog_init(struct lanai_dev *lanai) +{ + vci_bitfield_init(&lanai->backlog_vccs); +} + +static inline int vcc_is_backlogged(/*const*/ struct lanai_vcc *lvcc) +{ + return lvcc->tx.inprogress != NULL || + !skb_queue_empty(&lvcc->tx.backlog); +} + +/* -------------------- PORT I/O UTILITIES: */ + +/* Registers (and their bit-fields) */ +enum lanai_register { + Reset_Reg = 0x00, /* Reset; read for chip type; bits: */ +#define RESET_GET_BOARD_REV(x) (((x)>> 0)&0x03) /* Board revision */ +#define RESET_GET_BOARD_ID(x) (((x)>> 2)&0x03) /* Board ID */ +#define BOARD_ID_LANAI256 (0) /* 25.6M adaptor card */ + Endian_Reg = 0x04, /* Endian setting */ + IntStatus_Reg = 0x08, /* Interrupt status */ + IntStatusMasked_Reg = 0x0C, /* Interrupt status (masked) */ + IntAck_Reg = 0x10, /* Interrupt acknowledge */ + IntAckMasked_Reg = 0x14, /* Interrupt acknowledge (masked) */ + IntStatusSet_Reg = 0x18, /* Get status + enable/disable */ + IntStatusSetMasked_Reg = 0x1C, /* Get status + en/di (masked) */ + IntControlEna_Reg = 0x20, /* Interrupt control enable */ + IntControlDis_Reg = 0x24, /* Interrupt control disable */ + Status_Reg = 0x28, /* Status */ +#define STATUS_PROMDATA (0x00000001) /* PROM_DATA pin */ +#define STATUS_WAITING (0x00000002) /* Interrupt being delayed */ +#define STATUS_SOOL (0x00000004) /* SOOL alarm */ +#define STATUS_LOCD (0x00000008) /* LOCD alarm */ +#define STATUS_LED (0x00000010) /* LED (HAPPI) output */ +#define STATUS_GPIN (0x00000020) /* GPIN pin */ +#define STATUS_BUTTBUSY (0x00000040) /* Butt register is pending */ + Config1_Reg = 0x2C, /* Config word 1; bits: */ +#define CONFIG1_PROMDATA (0x00000001) /* PROM_DATA pin */ +#define CONFIG1_PROMCLK (0x00000002) /* PROM_CLK pin */ +#define CONFIG1_SET_READMODE(x) ((x)*0x004) /* PCI BM reads; values: */ +#define READMODE_PLAIN (0) /* Plain memory read */ +#define READMODE_LINE (2) /* Memory read line */ +#define READMODE_MULTIPLE (3) /* Memory read multiple */ +#define CONFIG1_DMA_ENABLE (0x00000010) /* Turn on DMA */ +#define CONFIG1_POWERDOWN (0x00000020) /* Turn off clocks */ +#define CONFIG1_SET_LOOPMODE(x) ((x)*0x080) /* Clock&loop mode; values: */ +#define LOOPMODE_NORMAL (0) /* Normal - no loop */ +#define LOOPMODE_TIME (1) +#define LOOPMODE_DIAG (2) +#define LOOPMODE_LINE (3) +#define CONFIG1_MASK_LOOPMODE (0x00000180) +#define CONFIG1_SET_LEDMODE(x) ((x)*0x0200) /* Mode of LED; values: */ +#define LEDMODE_NOT_SOOL (0) /* !SOOL */ +#define LEDMODE_OFF (1) /* 0 */ +#define LEDMODE_ON (2) /* 1 */ +#define LEDMODE_NOT_LOCD (3) /* !LOCD */ +#define LEDMORE_GPIN (4) /* GPIN */ +#define LEDMODE_NOT_GPIN (7) /* !GPIN */ +#define CONFIG1_MASK_LEDMODE (0x00000E00) +#define CONFIG1_GPOUT1 (0x00001000) /* Toggle for reset */ +#define CONFIG1_GPOUT2 (0x00002000) /* Loopback PHY */ +#define CONFIG1_GPOUT3 (0x00004000) /* Loopback lanai */ + Config2_Reg = 0x30, /* Config word 2; bits: */ +#define CONFIG2_HOWMANY (0x00000001) /* >512 VCIs? */ +#define CONFIG2_PTI7_MODE (0x00000002) /* Make PTI=7 RM, not OAM */ +#define CONFIG2_VPI_CHK_DIS (0x00000004) /* Ignore RX VPI value */ +#define CONFIG2_HEC_DROP (0x00000008) /* Drop cells w/ HEC errors */ +#define CONFIG2_VCI0_NORMAL (0x00000010) /* Treat VCI=0 normally */ +#define CONFIG2_CBR_ENABLE (0x00000020) /* Deal with CBR traffic */ +#define CONFIG2_TRASH_ALL (0x00000040) /* Trashing incoming cells */ +#define CONFIG2_TX_DISABLE (0x00000080) /* Trashing outgoing cells */ +#define CONFIG2_SET_TRASH (0x00000100) /* Turn trashing on */ + Statistics_Reg = 0x34, /* Statistics; bits: */ +#define STATS_GET_FIFO_OVFL(x) (((x)>> 0)&0xFF) /* FIFO overflowed */ +#define STATS_GET_HEC_ERR(x) (((x)>> 8)&0xFF) /* HEC was bad */ +#define STATS_GET_BAD_VCI(x) (((x)>>16)&0xFF) /* VCI not open */ +#define STATS_GET_BUF_OVFL(x) (((x)>>24)&0xFF) /* VCC buffer full */ + ServiceStuff_Reg = 0x38, /* Service stuff; bits: */ +#define SSTUFF_SET_SIZE(x) ((x)*0x20000000) /* size of service buffer */ +#define SSTUFF_SET_ADDR(x) ((x)>>8) /* set address of buffer */ + ServWrite_Reg = 0x3C, /* ServWrite Pointer */ + ServRead_Reg = 0x40, /* ServRead Pointer */ + TxDepth_Reg = 0x44, /* FIFO Transmit Depth */ + Butt_Reg = 0x48, /* Butt register */ + CBR_ICG_Reg = 0x50, + CBR_PTR_Reg = 0x54, + PingCount_Reg = 0x58, /* Ping count */ + DMA_Addr_Reg = 0x5C /* DMA address */ +}; + +static inline bus_addr_t reg_addr(const struct lanai_dev *lanai, + enum lanai_register reg) +{ + return lanai->base + (bus_addr_t) reg; +} + + +static inline u32 reg_read(const struct lanai_dev *lanai, + enum lanai_register reg) +{ + u32 t; + t = readl(reg_addr(lanai, reg)); + RWDEBUG("R [0x%08X] 0x%02X = 0x%08X\n", (unsigned int) lanai->base, + (int) reg, t); + return t; +} + +static inline void reg_write(const struct lanai_dev *lanai, u32 val, + enum lanai_register reg) +{ + RWDEBUG("W [0x%08X] 0x%02X < 0x%08X\n", (unsigned int) lanai->base, + (int) reg, val); + writel(val, reg_addr(lanai, reg)); + mdelay(1); +} + +static inline void conf1_write(const struct lanai_dev *lanai) +{ + reg_write(lanai, lanai->conf1, Config1_Reg); +} + +static inline void conf2_write(const struct lanai_dev *lanai) +{ + reg_write(lanai, lanai->conf2, Config2_Reg); +} + +static inline void reset_board(const struct lanai_dev *lanai) +{ + DPRINTK("about to reset board\n"); + reg_write(lanai, 0, Reset_Reg); + /* + * If we don't delay a little while here then we can end up + * leaving the card in a VERY weird state and lock up the + * PCI bus. This isn't documented anywhere but I've convinced + * myself after a lot of painful experimentation + */ + udelay(5); +} + +/* -------------------- VCC LIST LOCK: */ + +/* + * The linux-atm code disables local IRQs while managing the list of + * VCCs on a card. This is good, but it doesn't save us against + * SMP. Unfortunately, fixing this will require changes in the + * API which will have to wait a little bit. It's a hard race to + * trigger accidentally, so it isn't TOO horrible so far. + * + * One possible solution would be to have an rwlock which is + * always grabbed _irq-style on writing. This would automatically + * be grabbed (for writing) by the higher layers on things that + * would result in a change in the vcc list (_open, _close, + * probably _change_qos) - thus it would also protect the + * higher-level list of vccs on each device (atm_dev->vccs). + * The driver would be responsible for grabbing it as a read_lock + * anytime it wants to consult its table of vccs - for instance + * when handling an incoming PDU. This also explains why we would + * probably want the write_lock while in _change_qos - to prevent + * handling of PDUs while possibly in an inconsistant state. + * Also, _send would grab the lock for reading. + * + * One problem with this is that _open and _close could no longer + * do anything that might provoke a schedule. First, it would + * force us to use GFP_ATOMIC memory (which is bad), but also + * some devices pretty much require scheduling due to long + * delays (see lanai_close for an example). So in this case + * we need a way to schedule without losing the spinlock. + * The cleanest way to do this is probably have a way to mark a + * VCC as "in progress" so that the interrupt handler can + * still disregard any traffic for it while _open or _close + * are sleeping on it. Then it will need to be _open and + * _close's job to relinquish the write_lock. Thus, the + * lock could be dropped around the times that scheduling + * might occur. Perhaps the _READY flag can be used for + * this purpose. + * + * One short note about this "upper layer grabs, driver + * relinquishes" write lock - since this needs to be + * an _irq lock we're going to have problem saving + * and restoring flags (_irqsave/_irqrestore). This + * shouldn't be a problem, however - we must just + * require that those syscalls are never called with + * interrupts disabled so we can use the non-flags-saving + * versions. + * + * Anyway, all of the above is vaporware currently - fixing + * this right will require changes in the API and all of + * the drivers - this will wait until 2.5.x most likely. + * The following NOP macros are just here to mark where + * the locks will be needed in the future. + */ +#define vcclist_read_lock() do {} while (0) +#define vcclist_read_unlock() do {} while (0) +#define vcclist_write_lock() do {} while (0) +#define vcclist_write_unlock() do {} while (0) + +/* -------------------- CARD SRAM UTILITIES: */ + +/* The SRAM is mapped into normal PCI memory space - the only catch is + * that it is only 16-bits wide but must be accessed as 32-bit. The + * 16 high bits will be zero. We don't hide this, since they get + * programmed mostly like discrete registers anyway + */ +#define SRAM_START (0x20000) +#define SRAM_BYTES (0x20000) /* Again, half don't really exist */ + +static inline bus_addr_t sram_addr(const struct lanai_dev *lanai, int offset) +{ + return lanai->base + SRAM_START + offset; +} + +static inline u32 sram_read(const struct lanai_dev *lanai, int offset) +{ + return readl(sram_addr(lanai, offset)); +} + +static inline void sram_write(const struct lanai_dev *lanai, + u32 val, int offset) +{ + writel(val, sram_addr(lanai, offset)); +} + +static int __init sram_test_word( + const struct lanai_dev *lanai, int offset, u32 pattern) +{ + u32 readback; + sram_write(lanai, pattern, offset); + readback = sram_read(lanai, offset); + if (readback == pattern) + return 0; + printk(KERN_ERR DEV_LABEL + "(itf %d): SRAM word at %d bad: wrote 0x%X, read 0x%X\n", + lanai->number, offset, pattern, readback); + return -EIO; +} + +static int __init sram_test_pass(const struct lanai_dev *lanai, u32 pattern) +{ + int offset, result = 0; + for (offset = 0; offset < SRAM_BYTES && result == 0; offset += 4) + result = sram_test_word(lanai, offset, pattern); + return result; +} + +static int __init sram_test_and_clear(const struct lanai_dev *lanai) +{ +#ifdef FULL_MEMORY_TEST + int result; + DPRINTK("testing SRAM\n"); + if ((result = sram_test_pass(lanai, 0x5555)) != 0) + return result; + if ((result = sram_test_pass(lanai, 0xAAAA)) != 0) + return result; +#endif + DPRINTK("clearing SRAM\n"); + return sram_test_pass(lanai, 0x0000); +} + +/* -------------------- CARD-BASED VCC TABLE UTILITIES: */ + +/* vcc table */ +enum lanai_vcc_offset { + vcc_rxaddr1 = 0x00, /* Location1, plus bits: */ +#define RXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of RX buffer */ +#define RXADDR1_SET_RMMODE(x) ((x)*0x00800) /* RM cell action; values: */ +#define RMMODE_TRASH (0) /* discard */ +#define RMMODE_PRESERVE (1) /* input as AAL0 */ +#define RMMODE_PIPE (2) /* pipe to coscheduler */ +#define RMMODE_PIPEALL (3) /* pipe non-RM too */ +#define RXADDR1_OAM_PRESERVE (0x00002000) /* Input OAM cells as AAL0 */ +#define RXADDR1_SET_MODE(x) ((x)*0x0004000) /* Reassembly mode */ +#define RXMODE_TRASH (0) /* discard */ +#define RXMODE_AAL0 (1) /* non-AAL5 mode */ +#define RXMODE_AAL5 (2) /* AAL5, intr. each PDU */ +#define RXMODE_AAL5_STREAM (3) /* AAL5 w/o per-PDU intr */ + vcc_rxaddr2 = 0x04, /* Location2 */ + vcc_rxcrc1 = 0x08, /* RX CRC claculation space */ + vcc_rxcrc2 = 0x0C, + vcc_rxwriteptr = 0x10, /* RX writeptr, plus bits: */ +#define RXWRITEPTR_LASTEFCI (0x00002000) /* Last PDU had EFCI bit */ +#define RXWRITEPTR_DROPPING (0x00004000) /* Had error, dropping */ +#define RXWRITEPTR_TRASHING (0x00008000) /* Trashing */ + vcc_rxbufstart = 0x14, /* RX bufstart, plus bits: */ +#define RXBUFSTART_CLP (0x00004000) +#define RXBUFSTART_CI (0x00008000) + vcc_rxreadptr = 0x18, /* RX readptr */ + vcc_txicg = 0x1C, /* TX ICG */ + vcc_txaddr1 = 0x20, /* Location1, plus bits: */ +#define TXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of TX buffer */ +#define TXADDR1_ABR (0x00008000) /* use ABR (doesn't work) */ + vcc_txaddr2 = 0x24, /* Location2 */ + vcc_txcrc1 = 0x28, /* TX CRC claculation space */ + vcc_txcrc2 = 0x2C, + vcc_txreadptr = 0x30, /* TX Readptr, plus bits: */ +#define TXREADPTR_GET_PTR(x) ((x)&0x01FFF) +#define TXREADPTR_MASK_DELTA (0x0000E000) /* ? */ + vcc_txendptr = 0x34, /* TX Endptr, plus bits: */ +#define TXENDPTR_CLP (0x00002000) +#define TXENDPTR_MASK_PDUMODE (0x0000C000) /* PDU mode; values: */ +#define PDUMODE_AAL0 (0*0x04000) +#define PDUMODE_AAL5 (2*0x04000) +#define PDUMODE_AAL5STREAM (3*0x04000) + vcc_txwriteptr = 0x38, /* TX Writeptr */ +#define TXWRITEPTR_GET_PTR(x) ((x)&0x1FFF) + vcc_txcbr_next = 0x3C /* # of next CBR VCI in ring */ +#define TXCBR_NEXT_BOZO (0x00008000) /* "bozo bit" */ +}; + +#define CARDVCC_SIZE (0x40) + +static inline bus_addr_t cardvcc_addr(const struct lanai_dev *lanai, + vci_t vci) +{ + return sram_addr(lanai, vci * CARDVCC_SIZE); +} + +static inline u32 cardvcc_read(const struct lanai_vcc *lvcc, + enum lanai_vcc_offset offset) +{ + u32 val; + APRINTK(lvcc->vbase != 0, "cardvcc_read: unbound vcc!\n"); + val= readl(lvcc->vbase + (bus_addr_t) offset); + RWDEBUG("VR vci=%04d 0x%02X = 0x%08X\n", + lvcc->vci, (int) offset, val); + return val; +} + +static inline void cardvcc_write(const struct lanai_vcc *lvcc, + u32 val, enum lanai_vcc_offset offset) +{ + APRINTK(lvcc->vbase != 0, "cardvcc_write: unbound vcc!\n"); + APRINTK((val & ~0xFFFF) == 0, + "cardvcc_write: bad val 0x%X (vci=%d, addr=0x%02X)\n", + val, lvcc->vci, (int) offset); + RWDEBUG("VW vci=%04d 0x%02X > 0x%08X\n", + lvcc->vci, (int) offset, val); + writel(val, lvcc->vbase + (bus_addr_t) offset); +} + +/* -------------------- COMPUTE SIZE OF AN AAL5 PDU: */ + +/* How many bytes will an AAL5 PDU take to transmit - remember that: + * o we need to add 8 bytes for length, CPI, UU, and CRC + * o we need to round up to 48 bytes for cells + */ +static inline int aal5_size(int size) +{ + int cells = (size + 8 + 47) / 48; + return cells * 48; +} + +/* How many bytes can we send if we have "space" space, assuming we have + * to send full cells + */ +static inline int aal5_spacefor(int space) +{ + int cells = space / 48; + return cells * 48; +} + +/* -------------------- FREE AN ATM SKB: */ + +static inline void lanai_free_skb(struct atm_vcc *atmvcc, struct sk_buff *skb) +{ + if (atmvcc->pop != NULL) + atmvcc->pop(atmvcc, skb); + else + dev_kfree_skb_any(skb); +} + +/* -------------------- TURN VCCS ON AND OFF: */ + +static void host_vcc_start_rx(const struct lanai_vcc *lvcc) +{ + u32 addr1; + if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) { + unsigned long dmaaddr = lanai_buf_dmaaddr(&lvcc->rx.buf); + cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc1); + cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc2); + cardvcc_write(lvcc, 0, vcc_rxwriteptr); + cardvcc_write(lvcc, 0, vcc_rxbufstart); + cardvcc_write(lvcc, 0, vcc_rxreadptr); + cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_rxaddr2); + addr1 = ((dmaaddr >> 8) & 0xFF) | + RXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->rx.buf))| + RXADDR1_SET_RMMODE(RMMODE_TRASH) | /* ??? */ + /* RXADDR1_OAM_PRESERVE | --- no OAM support yet */ + RXADDR1_SET_MODE(RXMODE_AAL5); + } else + addr1 = RXADDR1_SET_RMMODE(RMMODE_PRESERVE) | /* ??? */ + RXADDR1_OAM_PRESERVE | /* ??? */ + RXADDR1_SET_MODE(RXMODE_AAL0); + /* This one must be last! */ + cardvcc_write(lvcc, addr1, vcc_rxaddr1); +} + +static void host_vcc_start_tx(const struct lanai_vcc *lvcc) +{ + unsigned long dmaaddr = lanai_buf_dmaaddr(&lvcc->tx.buf); + cardvcc_write(lvcc, 0, vcc_txicg); + cardvcc_write(lvcc, 0xFFFF, vcc_txcrc1); + cardvcc_write(lvcc, 0xFFFF, vcc_txcrc2); + cardvcc_write(lvcc, 0, vcc_txreadptr); + cardvcc_write(lvcc, 0, vcc_txendptr); + cardvcc_write(lvcc, 0, vcc_txwriteptr); + cardvcc_write(lvcc, + (lvcc->tx.atmvcc->qos.txtp.traffic_class == ATM_CBR) ? + TXCBR_NEXT_BOZO | lvcc->vci : 0, vcc_txcbr_next); + cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_txaddr2); + cardvcc_write(lvcc, + ((dmaaddr >> 8) & 0xFF) | + TXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->tx.buf)), + vcc_txaddr1); +} + +/* Shutdown receiving on card */ +static void lanai_shutdown_rx_vci(const struct lanai_vcc *lvcc) +{ + if (lvcc->vbase == 0) /* We were never bound to a VCI */ + return; + /* 15.1.1 - set to trashing, wait one cell time (15us) */ + cardvcc_write(lvcc, + RXADDR1_SET_RMMODE(RMMODE_TRASH) | + RXADDR1_SET_MODE(RXMODE_TRASH), vcc_rxaddr1); + udelay(15); + /* 15.1.2 - clear rest of entries */ + cardvcc_write(lvcc, 0, vcc_rxaddr2); + cardvcc_write(lvcc, 0, vcc_rxcrc1); + cardvcc_write(lvcc, 0, vcc_rxcrc2); + cardvcc_write(lvcc, 0, vcc_rxwriteptr); + cardvcc_write(lvcc, 0, vcc_rxbufstart); + cardvcc_write(lvcc, 0, vcc_rxreadptr); +} + +/* Shutdown transmitting on card. + * Unfortunately the lanai needs us to wait until all the data + * drains out of the buffer before we can dealloc it, so this + * can take awhile -- up to 370ms for a full 128KB buffer + * assuming everone else is quiet. In theory the time is + * boundless if there's a CBR VCC holding things up. + */ +static void lanai_shutdown_tx_vci(struct lanai_dev *lanai, + struct lanai_vcc *lvcc) +{ + struct sk_buff *skb; + unsigned long flags, timeout; + int read, write, lastread = -1; + APRINTK(!in_interrupt(), + "lanai_shutdown_tx_vci called w/o process context!\n"); + if (lvcc->vbase == 0) /* We were never bound to a VCI */ + return; + /* 15.2.1 - wait for queue to drain */ + spin_lock_irqsave(&lanai->txlock, flags); + if (lvcc->tx.inprogress != NULL) { + lanai_free_skb(lvcc->tx.atmvcc, lvcc->tx.inprogress); + lvcc->tx.inprogress = NULL; + } + while ((skb = skb_dequeue(&lvcc->tx.backlog)) != NULL) + lanai_free_skb(lvcc->tx.atmvcc, skb); + vcc_unmark_backlogged(lanai, lvcc); + spin_unlock_irqrestore(&lanai->txlock, flags); + timeout = jiffies + ((lanai_buf_size(&lvcc->tx.buf) * HZ) >> 17); + write = TXWRITEPTR_GET_PTR(cardvcc_read(lvcc, vcc_txwriteptr)); + goto start; + while (time_before_eq(jiffies, timeout)) { + schedule_timeout(HZ / 25); + start: + read = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)); + if (read == write && /* Is TX buffer empty? */ + (lvcc->tx.atmvcc->qos.txtp.traffic_class != ATM_CBR || + (cardvcc_read(lvcc, vcc_txcbr_next) & + TXCBR_NEXT_BOZO) == 0)) + goto done; + if (read != lastread) { /* Has there been any progress? */ + lastread = read; + timeout += HZ / 10; + } + } + printk(KERN_ERR DEV_LABEL "(itf %d): Timed out on backlog closing " + "vci %d\n", lvcc->tx.atmvcc->dev->number, lvcc->vci); + DPRINTK("read, write = %d, %d\n", read, write); + done: + /* 15.2.2 - clear out all tx registers */ + cardvcc_write(lvcc, 0, vcc_txreadptr); + cardvcc_write(lvcc, 0, vcc_txwriteptr); + cardvcc_write(lvcc, 0, vcc_txendptr); + cardvcc_write(lvcc, 0, vcc_txcrc1); + cardvcc_write(lvcc, 0, vcc_txcrc2); + cardvcc_write(lvcc, 0, vcc_txaddr2); + cardvcc_write(lvcc, 0, vcc_txaddr1); +} + +/* -------------------- MANAGING AAL0 RX BUFFER: */ + +static inline int aal0_buffer_allocate(struct lanai_dev *lanai) +{ + DPRINTK("aal0_buffer_allocate: allocating AAL0 RX buffer\n"); + lanai_buf_allocate(&lanai->aal0buf, AAL0_RX_BUFFER_SIZE, 80); + return (lanai->aal0buf.order < 0) ? -ENOMEM : 0; +} + +static inline void aal0_buffer_free(struct lanai_dev *lanai) +{ + DPRINTK("aal0_buffer_allocate: freeing AAL0 RX buffer\n"); + lanai_buf_deallocate(&lanai->aal0buf); +} + +/* -------------------- EEPROM UTILITIES: */ + +/* Offsets of data in the EEPROM */ +#define EEPROM_COPYRIGHT (0) +#define EEPROM_COPYRIGHT_LEN (44) +#define EEPROM_CHECKSUM (62) +#define EEPROM_CHECKSUM_REV (63) +#define EEPROM_MAC (64) +#define EEPROM_MAC_REV (70) +#define EEPROM_SERIAL (112) +#define EEPROM_SERIAL_REV (116) +#define EEPROM_MAGIC (120) +#define EEPROM_MAGIC_REV (124) + +#define EEPROM_MAGIC_VALUE (0x5AB478D2) + +#ifndef READ_EEPROM + +/* Stub functions to use if EEPROM reading is disabled */ +static int __init eeprom_read(struct lanai_dev *lanai) +{ + printk(KERN_INFO DEV_LABEL "(itf %d): *NOT* reading EEPROM\n", + lanai->number); + memset(&lanai->eeprom[EEPROM_MAC], 0, 6); + return 0; +} + +static int __init eeprom_validate(struct lanai_dev *lanai) +{ + lanai->serialno = 0; + lanai->magicno = EEPROM_MAGIC_VALUE; + return 0; +} + +#else /* READ_EEPROM */ + +static int __init eeprom_read(struct lanai_dev *lanai) +{ + int i, address; + u8 data; + u32 tmp; +#define set_config1(x) do { lanai->conf1 = x; conf1_write(lanai); \ + } while (0) +#define clock_h() set_config1(lanai->conf1 | CONFIG1_PROMCLK) +#define clock_l() set_config1(lanai->conf1 &~ CONFIG1_PROMCLK) +#define data_h() set_config1(lanai->conf1 | CONFIG1_PROMDATA) +#define data_l() set_config1(lanai->conf1 &~ CONFIG1_PROMDATA) +#define pre_read() do { data_h(); clock_h(); udelay(5); } while (0) +#define read_pin() (reg_read(lanai, Status_Reg) & STATUS_PROMDATA) +#define send_stop() do { data_l(); udelay(5); clock_h(); udelay(5); \ + data_h(); udelay(5); } while (0) + /* start with both clock and data high */ + data_h(); clock_h(); udelay(5); + for (address = 0; address < LANAI_EEPROM_SIZE; address++) { + data = (address << 1) | 1; /* Command=read + address */ + /* send start bit */ + data_l(); udelay(5); + clock_l(); udelay(5); + for (i = 128; i != 0; i >>= 1) { /* write command out */ + tmp = (lanai->conf1 & ~CONFIG1_PROMDATA) | + (data & i) ? CONFIG1_PROMDATA : 0; + if (lanai->conf1 != tmp) { + set_config1(tmp); + udelay(5); /* Let new data settle */ + } + clock_h(); udelay(5); clock_l(); udelay(5); + } + /* look for ack */ + data_h(); clock_h(); udelay(5); + if (read_pin() != 0) + goto error; /* No ack seen */ + clock_l(); udelay(5); + /* read back result */ + for (data = 0, i = 7; i >= 0; i--) { + data_h(); clock_h(); udelay(5); + data = (data << 1) | !!read_pin(); + clock_l(); udelay(5); + } + /* look again for ack */ + data_h(); clock_h(); udelay(5); + if (read_pin() == 0) + goto error; /* Spurious ack */ + clock_l(); udelay(5); + send_stop(); + lanai->eeprom[address] = data; + DPRINTK("EEPROM 0x%04X %02X\n", address, data); + } + return 0; + error: + clock_l(); udelay(5); /* finish read */ + send_stop(); + printk(KERN_ERR DEV_LABEL "(itf %d): error reading EEPROM byte %d\n", + lanai->number, address); + return -EIO; +#undef set_config1 +#undef clock_h +#undef clock_l +#undef data_h +#undef data_l +#undef pre_read +#undef read_pin +#undef send_stop +} + +/* read a big-endian 4-byte value out of eeprom */ +static inline u32 eeprom_be4(const struct lanai_dev *lanai, int address) +{ + return be32_to_cpup((u32 *) (&lanai->eeprom[address])); +} + +/* Checksum/validate EEPROM contents */ +static int __init eeprom_validate(struct lanai_dev *lanai) +{ + int i, s; + u32 v; + const u8 *e = lanai->eeprom; +#ifdef DEBUG + /* First, see if we can get an ASCIIZ string out of the copyright */ + for (i = EEPROM_COPYRIGHT; + i < (EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN); i++) + if (e[i] < 0x20 || e[i] > 0x7E) + break; + if ( i != EEPROM_COPYRIGHT && + i != EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN && e[i] == '\0') + DPRINTK("eeprom: copyright = \"%s\"\n", + (char *) &e[EEPROM_COPYRIGHT]); + else + DPRINTK("eeprom: copyright not found\n"); +#endif + /* Validate checksum */ + for (i = s = 0; i < EEPROM_CHECKSUM; i++) + s += e[i]; + s &= 0xFF; + if (s != e[EEPROM_CHECKSUM]) { + printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM checksum bad " + "(wanted 0x%02X, got 0x%02X)\n", lanai->number, + s, e[EEPROM_CHECKSUM]); + return -EIO; + } + s ^= 0xFF; + if (s != e[EEPROM_CHECKSUM_REV]) { + printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM inverse checksum " + "bad (wanted 0x%02X, got 0x%02X)\n", lanai->number, + s, e[EEPROM_CHECKSUM_REV]); + return -EIO; + } + /* Verify MAC address */ + for (i = 0; i < 6; i++) + if ((e[EEPROM_MAC + i] ^ e[EEPROM_MAC_REV + i]) != 0xFF) { + printk(KERN_ERR DEV_LABEL + "(itf %d) : EEPROM MAC addresses don't match " + "(0x%02X, inverse 0x%02X)\n", lanai->number, + e[EEPROM_MAC + i], e[EEPROM_MAC_REV + i]); + return -EIO; + } + DPRINTK("eeprom: MAC address = %02X:%02X:%02X:%02X:%02X:%02X\n", + e[EEPROM_MAC + 0], e[EEPROM_MAC + 1], e[EEPROM_MAC + 2], + e[EEPROM_MAC + 3], e[EEPROM_MAC + 4], e[EEPROM_MAC + 5]); + /* Verify serial number */ + lanai->serialno = eeprom_be4(lanai, EEPROM_SERIAL); + v = eeprom_be4(lanai, EEPROM_SERIAL_REV); + if ((lanai->serialno ^ v) != 0xFFFFFFFF) { + printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM serial numbers " + "don't match (0x%08X, inverse 0x%08X)\n", lanai->number, + lanai->serialno, v); + return -EIO; + } + DPRINTK("eeprom: Serial number = %d\n", lanai->serialno); + /* Verify magic number */ + lanai->magicno = eeprom_be4(lanai, EEPROM_MAGIC); + v = eeprom_be4(lanai, EEPROM_MAGIC_REV); + if ((lanai->magicno ^ v) != 0xFFFFFFFF) { + printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM magic numbers " + "don't match (0x%08X, inverse 0x%08X)\n", lanai->number, + lanai->magicno, v); + return -EIO; + } + DPRINTK("eeprom: Magic number = 0x%08X\n", lanai->magicno); + if (lanai->magicno != EEPROM_MAGIC_VALUE) + printk(KERN_WARNING DEV_LABEL "(itf %d): warning - EEPROM " + "magic not what expected (got 0x%08X, not 0x%08X)\n", + lanai->number, lanai->magicno, EEPROM_MAGIC_VALUE); + return 0; +} + +#endif /* READ_EEPROM */ + +static inline const u8 *eeprom_mac(const struct lanai_dev *lanai) +{ + return &lanai->eeprom[EEPROM_MAC]; +} + +/* -------------------- INTERRUPT HANDLING UTILITIES: */ + +/* Interrupt types */ +#define INT_STATS (0x00000002) /* Statistics counter overflow */ +#define INT_SOOL (0x00000004) /* SOOL changed state */ +#define INT_LOCD (0x00000008) /* LOCD changed state */ +#define INT_LED (0x00000010) /* LED (HAPPI) changed state */ +#define INT_GPIN (0x00000020) /* GPIN changed state */ +#define INT_PING (0x00000040) /* PING_COUNT fulfilled */ +#define INT_WAKE (0x00000080) /* Lanai wants bus */ +#define INT_CBR0 (0x00000100) /* CBR sched hit VCI 0 */ +#define INT_LOCK (0x00000200) /* Service list overflow */ +#define INT_MISMATCH (0x00000400) /* TX magic list mismatch */ +#define INT_AAL0_STR (0x00000800) /* Non-AAL5 buffer half filled */ +#define INT_AAL0 (0x00001000) /* Non-AAL5 data available */ +#define INT_SERVICE (0x00002000) /* Service list entries available */ +#define INT_TABORTSENT (0x00004000) /* Target abort sent by lanai */ +#define INT_TABORTBM (0x00008000) /* Abort rcv'd as bus master */ +#define INT_TIMEOUTBM (0x00010000) /* No response to bus master */ +#define INT_PCIPARITY (0x00020000) /* Parity error on PCI */ + +/* Sets of the above */ +#define INT_ALL (0x0003FFFE) /* All interrupts */ +#define INT_STATUS (0x0000003C) /* Some status pin changed */ +#define INT_DMASHUT (0x00038000) /* DMA engine got shut down */ +#define INT_SEGSHUT (0x00000700) /* Segmentation got shut down */ + +static inline u32 intr_pending(const struct lanai_dev *lanai) +{ + return reg_read(lanai, IntStatusMasked_Reg); +} + +static inline void intr_enable(const struct lanai_dev *lanai, u32 i) +{ + reg_write(lanai, i, IntControlEna_Reg); +} + +static inline void intr_disable(const struct lanai_dev *lanai, u32 i) +{ + reg_write(lanai, i, IntControlDis_Reg); +} + +/* -------------------- CARD/PCI STATUS: */ + +static void status_message(int itf, const char *name, int status) +{ + static const char *onoff[2] = { "off to on", "on to off" }; + printk(KERN_INFO DEV_LABEL "(itf %d): %s changed from %s\n", + itf, name, onoff[!status]); +} + +static void lanai_check_status(struct lanai_dev *lanai) +{ + u32 new = reg_read(lanai, Status_Reg); + u32 changes = new ^ lanai->status; + lanai->status = new; +#define e(flag, name) \ + if (changes & flag) \ + status_message(lanai->number, name, new & flag) + e(STATUS_SOOL, "SOOL"); + e(STATUS_LOCD, "LOCD"); + e(STATUS_LED, "LED"); + e(STATUS_GPIN, "GPIN"); +#undef e +} + +static void pcistatus_got(int itf, const char *name) +{ + printk(KERN_INFO DEV_LABEL "(itf %d): PCI got %s error\n", itf, name); +} + +static void pcistatus_check(struct lanai_dev *lanai, int clearonly) +{ + u16 s; + int result; + result = pci_read_config_word(lanai->pci, PCI_STATUS, &s); + if (result != PCIBIOS_SUCCESSFUL) { + printk(KERN_ERR DEV_LABEL "(itf %d): can't read PCI_STATUS: " + "%d\n", lanai->number, result); + return; + } + s &= PCI_STATUS_DETECTED_PARITY | PCI_STATUS_SIG_SYSTEM_ERROR | + PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT | + PCI_STATUS_SIG_TARGET_ABORT | PCI_STATUS_PARITY; + if (s == 0) + return; + result = pci_write_config_word(lanai->pci, PCI_STATUS, s); + if (result != PCIBIOS_SUCCESSFUL) + printk(KERN_ERR DEV_LABEL "(itf %d): can't write PCI_STATUS: " + "%d\n", lanai->number, result); + if (clearonly) + return; +#define e(flag, name, stat) \ + if (s & flag) { \ + pcistatus_got(lanai->number, name); \ + ++lanai->stats.pcierr_##stat; \ + } + e(PCI_STATUS_DETECTED_PARITY, "parity", parity_detect); + e(PCI_STATUS_SIG_SYSTEM_ERROR, "signalled system", serr_set); + e(PCI_STATUS_REC_MASTER_ABORT, "master", master_abort); + e(PCI_STATUS_REC_TARGET_ABORT, "master target", m_target_abort); + e(PCI_STATUS_SIG_TARGET_ABORT, "slave", s_target_abort); + e(PCI_STATUS_PARITY, "master parity", master_parity); +#undef e +} + +/* -------------------- VCC TX BUFFER UTILITIES: */ + +/* space left in tx buffer in bytes */ +static inline int vcc_tx_space(const struct lanai_vcc *lvcc, int endptr) +{ + int r; + r = endptr * 16; + r -= ((unsigned long) lvcc->tx.buf.ptr) - + ((unsigned long) lvcc->tx.buf.start); + r -= 16; /* Leave "bubble" - if start==end it looks empty */ + if (r < 0) + r += lanai_buf_size(&lvcc->tx.buf); + return r; +} + +/* Bit fields in the segmentation buffer descriptor */ +#define DESCRIPTOR_MAGIC (0xD0000000) +#define DESCRIPTOR_AAL5 (0x00008000) +#define DESCRIPTOR_AAL5_STREAM (0x00004000) +#define DESCRIPTOR_CLP (0x00002000) + +/* Add 32-bit descriptor with it's padding */ +static inline void vcc_tx_add_aal5_descriptor(struct lanai_vcc *lvcc, + u32 flags, int len) +{ + int pos; + APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 0, + "vcc_tx_add_aal5_descriptor: bad ptr=%p\n", lvcc->tx.buf.ptr); + lvcc->tx.buf.ptr += 4; /* Hope the values REALLY don't matter */ + pos = ((unsigned char *) lvcc->tx.buf.ptr) - + (unsigned char *) lvcc->tx.buf.start; + APRINTK((pos & ~0x0001FFF0) == 0, + "vcc_tx_add_aal5_descriptor: bad pos (%d) before, vci=%d, " + "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci, + lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end); + pos = (pos + len) & (lanai_buf_size(&lvcc->tx.buf) - 1); + APRINTK((pos & ~0x0001FFF0) == 0, + "vcc_tx_add_aal5_descriptor: bad pos (%d) after, vci=%d, " + "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci, + lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end); + lvcc->tx.buf.ptr[-1] = + cpu_to_le32(DESCRIPTOR_MAGIC | DESCRIPTOR_AAL5 | + ((lvcc->tx.atmvcc->atm_options & ATM_ATMOPT_CLP) ? + DESCRIPTOR_CLP : 0) | flags | pos >> 4); + if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end) + lvcc->tx.buf.ptr = lvcc->tx.buf.start; +} + +/* Add 32-bit AAL5 trailer and leave room for its CRC */ +static inline void vcc_tx_add_aal5trailer(struct lanai_vcc *lvcc, + int len, int cpi, int uu) +{ + APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 8, + "vcc_tx_add_aal5_descriptor: bad ptr=%p\n", lvcc->tx.buf.ptr); + lvcc->tx.buf.ptr += 2; + lvcc->tx.buf.ptr[-2] = cpu_to_be32((uu << 24) | (cpi << 16) | len); + if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end) + lvcc->tx.buf.ptr = lvcc->tx.buf.start; +} + +static inline void vcc_tx_memcpy(struct lanai_vcc *lvcc, + const unsigned char *src, int n) +{ + unsigned char *e; + int m; + e = ((unsigned char *) lvcc->tx.buf.ptr) + n; + m = e - (unsigned char *) lvcc->tx.buf.end; + if (m < 0) + m = 0; + memcpy(lvcc->tx.buf.ptr, src, n - m); + if (m != 0) { + memcpy(lvcc->tx.buf.start, src + n - m, m); + e = ((unsigned char *) lvcc->tx.buf.start) + m; + } + lvcc->tx.buf.ptr = (u32 *) e; +} + +static inline void vcc_tx_memzero(struct lanai_vcc *lvcc, int n) +{ + unsigned char *e; + int m; + if (n == 0) + return; + e = ((unsigned char *) lvcc->tx.buf.ptr) + n; + m = e - (unsigned char *) lvcc->tx.buf.end; + if (m < 0) + m = 0; + memset(lvcc->tx.buf.ptr, 0, n - m); + if (m != 0) { + memset(lvcc->tx.buf.start, 0, m); + e = ((unsigned char *) lvcc->tx.buf.start) + m; + } + lvcc->tx.buf.ptr = (u32 *) e; +} + +/* Update "butt" register to specify new WritePtr */ +static inline void lanai_endtx(const struct lanai_dev *lanai, + const struct lanai_vcc *lvcc) +{ + int i, ptr = ((unsigned char *) lvcc->tx.buf.ptr) - + (unsigned char *) lvcc->tx.buf.start; + APRINTK((ptr & ~0x0001FFF0) == 0, + "lanai_endtx: bad ptr (%d), vci=%d, start,ptr,end=%p,%p,%p\n", + ptr, lvcc->vci, lvcc->tx.buf.start, lvcc->tx.buf.ptr, + lvcc->tx.buf.end); + /* + * We need to check if the "butt busy" bit is set before + * updating the butt register. In theory this should + * never happen because the ATM card is plenty fast at + * updating the register. Still, we should make sure + */ + for (i = 0; reg_read(lanai, Status_Reg) & STATUS_BUTTBUSY; i++) { + if (i > 50) { + printk(KERN_ERR DEV_LABEL "(itf %d): butt register " + "always busy!\n", lanai->number); + break; + } + udelay(5); + } + reg_write(lanai, (ptr << 12) | lvcc->vci, Butt_Reg); +} + +/* Try to fill the buffer - don't call unless there is backlog */ +static void vcc_tx_unqueue_aal5(struct lanai_dev *lanai, + struct lanai_vcc *lvcc, int endptr) +{ + int pad, n; + struct sk_buff *skb; + int space = vcc_tx_space(lvcc, endptr); + APRINTK(vcc_is_backlogged(lvcc), + "vcc_tx_unqueue() called with empty backlog (vci=%d)\n", + lvcc->vci); + if (space < 64) + return; /* No space for even 1 cell+descriptor */ + if (lvcc->tx.inprogress != NULL) { + APRINTK((lvcc->tx.inprogleft % 48) == 0, + "vcc_tx_unqueue_aal5: bad progleft=%d\n", + lvcc->tx.inprogleft); + if (lvcc->tx.inprogleft + 16 > space) { /* Can't send all? */ + n = aal5_spacefor(space - 16); /* Bytes to send */ + vcc_tx_add_aal5_descriptor(lvcc, + DESCRIPTOR_AAL5_STREAM, n); + pad = lvcc->tx.pptr + n - lvcc->tx.inprogress->tail; + if (pad < 0) + pad = 0; + vcc_tx_memcpy(lvcc, lvcc->tx.pptr, n - pad); + vcc_tx_memzero(lvcc, pad); + lvcc->tx.pptr += n; + lvcc->tx.inprogleft -= n; + goto end; /* Buffer is now full */ + } + /* OK, there's at least space for all of "inprogress" skb */ + vcc_tx_add_aal5_descriptor(lvcc, 0, + lvcc->tx.inprogleft); + pad = lvcc->tx.pptr + lvcc->tx.inprogleft - + lvcc->tx.inprogress->tail; + if (pad >= lvcc->tx.inprogleft) { /* Nothing but pad left */ + APRINTK(lvcc->tx.inprogleft == 48, + "vcc_tx_unqueue_aal5: bad pure-pad=%d\n", + lvcc->tx.inprogleft); + pad = 48; + } else + vcc_tx_memcpy(lvcc, lvcc->tx.pptr, + lvcc->tx.inprogleft - pad); + vcc_tx_memzero(lvcc, pad - 8); + vcc_tx_add_aal5trailer(lvcc, lvcc->tx.inprogress->len, 0, 0); + lanai_free_skb(lvcc->tx.atmvcc, lvcc->tx.inprogress); + lvcc->tx.inprogress = NULL; + space -= lvcc->tx.inprogleft + 16; + atomic_inc(&lvcc->tx.atmvcc->stats->tx); + } + while (space >= 64) { + if ((skb = skb_dequeue(&lvcc->tx.backlog)) == NULL) + break; + n = aal5_size(skb->len); + if (n + 16 > space) { /* Can only send part */ + int m = aal5_spacefor(space - 16); /* Bytes to send */ + vcc_tx_add_aal5_descriptor(lvcc, + DESCRIPTOR_AAL5_STREAM, m); + lvcc->tx.pptr = skb->data + m; + pad = lvcc->tx.pptr - skb->tail; + if (pad < 0) + pad = 0; + vcc_tx_memcpy(lvcc, skb->data, m - pad); + vcc_tx_memzero(lvcc, pad); + lvcc->tx.inprogleft = n - m; + lvcc->tx.inprogress = skb; + goto end; + } + vcc_tx_add_aal5_descriptor(lvcc, 0, n); + pad = n - skb->len - 8; + vcc_tx_memcpy(lvcc, skb->data, skb->len); + vcc_tx_memzero(lvcc, pad); + lanai_free_skb(lvcc->tx.atmvcc, skb); + vcc_tx_add_aal5trailer(lvcc, skb->len, 0, 0); + space -= n + 16; + atomic_inc(&lvcc->tx.atmvcc->stats->tx); + } + if (skb_queue_empty(&lvcc->tx.backlog)) + vcc_unmark_backlogged(lanai, lvcc); + end: + lanai_endtx(lanai, lvcc); +} + +/* Given an skb that we want to transmit either send it now or queue */ +static void vcc_tx_aal5(struct lanai_dev *lanai, struct lanai_vcc *lvcc, + struct sk_buff *skb) +{ + int space, n, pad; + if (vcc_is_backlogged(lvcc)) /* Already backlogged */ + goto queue_it; + space = vcc_tx_space(lvcc, TXREADPTR_GET_PTR(cardvcc_read(lvcc, + vcc_txreadptr))); + if (space < 64) { + vcc_mark_backlogged(lanai, lvcc); /* No space */ + goto queue_it; + } + if (space >= 16 + (n = aal5_size(skb->len))) { + /* We can send the whole thing now */ + vcc_tx_add_aal5_descriptor(lvcc, 0, n); + pad = n - skb->len; + vcc_tx_memcpy(lvcc, skb->data, skb->len); + vcc_tx_memzero(lvcc, pad - 8); + vcc_tx_add_aal5trailer(lvcc, skb->len, 0, 0); + lanai_free_skb(lvcc->tx.atmvcc, skb); + atomic_inc(&lvcc->tx.atmvcc->stats->tx); + } else { /* Space for only part of skb */ + int bytes = aal5_spacefor(space - 16); /* Bytes to send */ + vcc_tx_add_aal5_descriptor(lvcc, + DESCRIPTOR_AAL5_STREAM, bytes); + pad = bytes - skb->len; + if (pad < 0) + pad = 0; + vcc_tx_memcpy(lvcc, skb->data, bytes - pad); + vcc_tx_memzero(lvcc, pad); + lvcc->tx.inprogress = skb; + lvcc->tx.inprogleft = n - bytes; + lvcc->tx.pptr = skb->data + bytes; + vcc_mark_backlogged(lanai, lvcc); + } + lanai_endtx(lanai, lvcc); + return; + queue_it: + skb_queue_tail(&lvcc->tx.backlog, skb); +} + +static void vcc_tx_unqueue_aal0(struct lanai_dev *lanai, + struct lanai_vcc *lvcc, int endptr) +{ + printk(KERN_INFO DEV_LABEL + ": vcc_tx_unqueue_aal0: not implemented\n"); +} + +static void vcc_tx_aal0(struct lanai_dev *lanai, struct lanai_vcc *lvcc, + struct sk_buff *skb) +{ + printk(KERN_INFO DEV_LABEL ": vcc_tx_aal0: not implemented\n"); + /* Remember to increment lvcc->tx.atmvcc->stats->tx */ + lanai_free_skb(lvcc->tx.atmvcc, skb); +} + +/* Try to undequeue 1 backlogged vcc */ +static void iter_dequeue(struct lanai_dev *lanai, vci_t vci) +{ + struct lanai_vcc *lvcc = lanai->vccs[vci]; + int endptr; + if (lvcc == NULL || !vcc_is_backlogged(lvcc)) { + vci_bitfield_clear(&lanai->backlog_vccs, vci); + return; + } + endptr = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)); + lvcc->tx.unqueue(lanai, lvcc, endptr); +} + +/* Try a dequeue on all backlogged connections */ +static inline void vcc_tx_dequeue_all(struct lanai_dev *lanai) +{ + unsigned long flags; + spin_lock_irqsave(&lanai->txlock, flags); + vci_bitfield_iterate(lanai, &lanai->backlog_vccs, iter_dequeue); + spin_unlock_irqrestore(&lanai->txlock, flags); +} + +/* -------------------- VCC RX BUFFER UTILITIES: */ + +/* unlike the _tx_ cousins, this doesn't update ptr */ +static inline void vcc_rx_memcpy(unsigned char *dest, + const struct lanai_vcc *lvcc, int n) +{ + int m = ((const unsigned char *) lvcc->rx.buf.ptr) + n - + ((const unsigned char *) (lvcc->rx.buf.end)); + if (m < 0) + m = 0; + memcpy(dest, lvcc->rx.buf.ptr, n - m); + memcpy(dest + n - m, lvcc->rx.buf.start, m); +} + +/* Receive AAL5 data on a VCC with a particular endptr */ +static void vcc_rx_aal5(struct lanai_vcc *lvcc, int endptr) +{ + int size; + struct sk_buff *skb; + /*const*/ u32 *x, *end = &lvcc->rx.buf.start[endptr * 4]; + int n = ((unsigned long) end) - ((unsigned long) lvcc->rx.buf.ptr); + if (n < 0) + n += lanai_buf_size(&lvcc->rx.buf); + APRINTK(n >= 0 && n < lanai_buf_size(&lvcc->rx.buf) && !(n & 15), + "vcc_rx_aal5: n out of range (%d/%d)\n", + n, lanai_buf_size(&lvcc->rx.buf)); + /* Recover the second-to-last word to get true pdu length */ + if ((x = &end[-2]) < lvcc->rx.buf.start) + x = &lvcc->rx.buf.end[-2]; + size = be32_to_cpup(x) & 0xffff; + if (n != aal5_size(size)) { /* Make sure size matches padding */ + printk(KERN_INFO DEV_LABEL "(itf %d): Got bad AAL5 length " + "on vci=%d - size=%d n=%d\n", + lvcc->rx.atmvcc->dev->number, lvcc->vci, size, n); + lvcc->stats.x.aal5.rx_badlen++; + goto out; + } + skb = atm_alloc_charge(lvcc->rx.atmvcc, size, GFP_ATOMIC); + if (skb == NULL) { + lvcc->stats.rx_nomem++; + goto out; + } + skb_put(skb, size); + ATM_SKB(skb)->vcc = lvcc->rx.atmvcc; + skb->stamp = xtime; + vcc_rx_memcpy(skb->data, lvcc, size); + lvcc->rx.atmvcc->push(lvcc->rx.atmvcc, skb); + atomic_inc(&lvcc->rx.atmvcc->stats->rx); + out: + lvcc->rx.buf.ptr = end; + cardvcc_write(lvcc, endptr, vcc_rxreadptr); +} + +static void vcc_rx_aal0(struct lanai_dev *lanai) +{ + printk(KERN_INFO DEV_LABEL ": vcc_rx_aal0: not implemented\n"); + /* Remember to get vcclist_read_lock while looking up VC */ + /* Remember to increment lvcc->rx.atmvcc->stats->rx */ +} + +/* -------------------- MANAGING HOST-BASED VCC TABLE: */ + +/* Decide whether to use vmalloc or get_free_page for VCC table */ +#if (NUM_VCI * BITS_PER_LONG) <= PAGE_SIZE +#define VCCTABLE_GETFREEPAGE +#else +#include +#endif + +static int __init vcc_table_allocate(struct lanai_dev *lanai) +{ +#ifdef VCCTABLE_GETFREEPAGE + APRINTK((lanai->num_vci) * sizeof(struct lanai_vcc *) <= PAGE_SIZE, + "vcc table > PAGE_SIZE!"); + lanai->vccs = (struct lanai_vcc **) get_free_page(GFP_KERNEL); + return (lanai->vccs == NULL) ? -ENOMEM : 0; +#else + int bytes = (lanai->num_vci) * sizeof(struct lanai_vcc *); + lanai->vccs = (struct lanai_vcc **) vmalloc(bytes); + if (lanai->vccs == NULL) + return -ENOMEM; + memset(lanai->vccs, 0, bytes); + return 0; +#endif +} + +static inline void vcc_table_deallocate(const struct lanai_dev *lanai) +{ +#ifdef VCCTABLE_GETFREEPAGE + free_page((unsigned long) lanai->vccs); +#else + vfree(lanai->vccs); +#endif +} + +/* Allocate a fresh lanai_vcc, with the appropriate things cleared */ +static inline struct lanai_vcc *new_lanai_vcc(void) +{ + struct lanai_vcc *lvcc; + lvcc = (struct lanai_vcc *) kmalloc(sizeof(*lvcc), GFP_KERNEL); + if (lvcc != NULL) { + lvcc->vbase = 0; + lvcc->rx.atmvcc = lvcc->tx.atmvcc = NULL; + lvcc->nref = 0; + memset(&lvcc->stats, 0, sizeof lvcc->stats); + lvcc->rx.buf.start = lvcc->tx.buf.start = NULL; + skb_queue_head_init(&lvcc->tx.backlog); + lvcc->tx.inprogress = NULL; +#ifdef DEBUG + lvcc->tx.unqueue = NULL; + lvcc->vci = -1; +#endif + } + return lvcc; +} + +static int lanai_get_sized_buffer(int number, struct lanai_buffer *buf, + int max_sdu, int multiplier, int min, const char *name) +{ + int size; + if (max_sdu < 1) + max_sdu = 1; + max_sdu = aal5_size(max_sdu); + size = (max_sdu + 16) * multiplier + 16; + lanai_buf_allocate(buf, size, min); + if (buf->order < 0) + return -ENOMEM; + if (lanai_buf_size(buf) < size) + printk(KERN_WARNING DEV_LABEL "(itf %d): wanted %d bytes " + "for %s buffer, got only %d\n", number, size, name, + lanai_buf_size(buf)); + DPRINTK("Allocated %d byte %s buffer\n", lanai_buf_size(buf), name); + return 0; +} + +/* Setup a RX buffer for a currently unbound AAL5 vci */ +static inline int lanai_setup_rx_vci_aal5(int number, struct lanai_vcc *lvcc, + const struct atm_qos *qos) +{ + return lanai_get_sized_buffer(number, &lvcc->rx.buf, + qos->rxtp.max_sdu, AAL5_RX_MULTIPLIER, qos->rxtp.max_sdu + 32, + "RX"); +} + +/* Setup a TX buffer for a currently unbound AAL5 vci */ +static int lanai_setup_tx_vci(int number, struct lanai_vcc *lvcc, + const struct atm_qos *qos) +{ + int max_sdu, multiplier; + if (qos->aal == ATM_AAL0) { + lvcc->tx.unqueue = vcc_tx_unqueue_aal0; + max_sdu = ATM_CELL_SIZE - 1; + multiplier = AAL0_TX_MULTIPLIER; + } else { + lvcc->tx.unqueue = vcc_tx_unqueue_aal5; + max_sdu = qos->txtp.max_sdu; + multiplier = AAL5_TX_MULTIPLIER; + } + return lanai_get_sized_buffer(number, &lvcc->tx.buf, max_sdu, + multiplier, 80, "TX"); +} + +static inline void host_vcc_bind(struct lanai_dev *lanai, + struct lanai_vcc *lvcc, vci_t vci) +{ + if (lvcc->vbase != 0) + return; /* We already were bound in the other direction */ + DPRINTK("Binding vci %d\n", vci); +#ifdef USE_POWERDOWN + if (lanai->nbound++ == 0) { + DPRINTK("Coming out of powerdown\n"); + lanai->conf1 &= ~CONFIG1_POWERDOWN; + conf1_write(lanai); + conf2_write(lanai); + } +#endif + lvcc->vbase = cardvcc_addr(lanai, vci); + lanai->vccs[lvcc->vci = vci] = lvcc; +} + +static inline void host_vcc_unbind(struct lanai_dev *lanai, + struct lanai_vcc *lvcc) +{ + if (lvcc->vbase == 0) + return; /* This vcc was never bound */ + DPRINTK("Unbinding vci %d\n", lvcc->vci); + lvcc->vbase = 0; + lanai->vccs[lvcc->vci] = NULL; +#ifdef USE_POWERDOWN + if (--lanai->nbound == 0) { + DPRINTK("Going into powerdown\n"); + lanai->conf1 |= CONFIG1_POWERDOWN; + conf1_write(lanai); + } +#endif +} + +/* -------------------- RESET CARD: */ + +static void lanai_reset(struct lanai_dev *lanai) +{ + printk(KERN_CRIT DEV_LABEL "(itf %d): *NOT* reseting - not " + "implemented\n", lanai->number); + /* TODO */ + /* The following is just a hack until we write the real + * resetter - at least ack whatever interrupt sent us + * here + */ + reg_write(lanai, INT_ALL, IntAck_Reg); + lanai->stats.card_reset++; +} + +/* -------------------- SERVICE LIST UTILITIES: */ + +/* + * Allocate service buffer and tell card about it + */ +static int __init service_buffer_allocate(struct lanai_dev *lanai) +{ + lanai_buf_allocate(&lanai->service, SERVICE_ENTRIES * 4, 0); + if (lanai->service.order < 0) + return -ENOMEM; + DPRINTK("allocated service buffer at 0x%08lX, size %d(%d)\n", + (unsigned long) lanai->service.start, + lanai_buf_size(&lanai->service), + lanai_buf_size_cardorder(&lanai->service)); + /* Clear ServWrite register to be safe */ + reg_write(lanai, 0, ServWrite_Reg); + /* ServiceStuff register contains size and address of buffer */ + reg_write(lanai, + SSTUFF_SET_SIZE(lanai_buf_size_cardorder(&lanai->service)) | + SSTUFF_SET_ADDR(lanai_buf_dmaaddr(&lanai->service)), + ServiceStuff_Reg); + return 0; +} + +static inline void service_buffer_deallocate(struct lanai_dev *lanai) +{ + lanai_buf_deallocate(&lanai->service); +} + +/* Bitfields in service list */ +#define SERVICE_TX (0x80000000) /* Was from transmission */ +#define SERVICE_TRASH (0x40000000) /* RXed PDU was trashed */ +#define SERVICE_CRCERR (0x20000000) /* RXed PDU had CRC error */ +#define SERVICE_CI (0x10000000) /* RXed PDU had CI set */ +#define SERVICE_CLP (0x08000000) /* RXed PDU had CLP set */ +#define SERVICE_STREAM (0x04000000) /* RX Stream mode */ +#define SERVICE_GET_VCI(x) (((x)>>16)&0x3FF) +#define SERVICE_GET_END(x) ((x)&0x1FFF) + +/* Handle one thing from the service list - returns true if it marked a + * VCC ready for xmit + */ +static int handle_service(struct lanai_dev *lanai, u32 s) +{ + vci_t vci = SERVICE_GET_VCI(s); + struct lanai_vcc *lvcc; + vcclist_read_lock(); + lvcc = lanai->vccs[vci]; + if (lvcc == NULL) { + vcclist_read_unlock(); + DPRINTK("(itf %d) got service entry 0x%X for nonexistent " + "vcc %d\n", lanai->number, s, vci); + if (s & SERVICE_TX) + lanai->stats.service_novcc_tx++; + else + lanai->stats.service_novcc_rx++; + return 0; + } + if (s & SERVICE_TX) { /* segmentation interrupt */ + if (lvcc->tx.atmvcc == NULL) { + vcclist_read_unlock(); + DPRINTK("(itf %d) got service entry 0x%X for non-TX " + "vcc %d\n", lanai->number, s, vci); + lanai->stats.service_notx++; + return 0; + } + vci_bitfield_set(&lanai->transmit_ready, vci); + lvcc->tx.endptr = SERVICE_GET_END(s); + vcclist_read_unlock(); + return 1; + } + if (lvcc->rx.atmvcc == NULL) { + vcclist_read_unlock(); + DPRINTK("(itf %d) got service entry 0x%X for non-RX " + "vcc %d\n", lanai->number, s, vci); + lanai->stats.service_norx++; + return 0; + } + if (lvcc->rx.atmvcc->qos.aal != ATM_AAL5) { + vcclist_read_unlock(); + DPRINTK("(itf %d) got RX service entry 0x%X for non-AAL5 " + "vcc %d\n", lanai->number, s, vci); + lanai->stats.service_rxnotaal5++; + atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); + return 0; + } + if ((s & (SERVICE_TRASH | SERVICE_STREAM | SERVICE_CRCERR)) == 0) { + vcc_rx_aal5(lvcc, SERVICE_GET_END(s)); + vcclist_read_unlock(); + return 0; + } + if (s & SERVICE_TRASH) { + int bytes; + vcclist_read_unlock(); + DPRINTK("got trashed rx pdu on vci %d\n", vci); + atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); + lvcc->stats.x.aal5.service_trash++; + bytes = (SERVICE_GET_END(s) * 16) - + (((unsigned long) lvcc->rx.buf.ptr) - + ((unsigned long) lvcc->rx.buf.start)) + 47; + if (bytes < 0) + bytes += lanai_buf_size(&lvcc->rx.buf); + lanai->stats.ovfl_trash += (bytes / 48); + return 0; + } + if (s & SERVICE_STREAM) { + vcclist_read_unlock(); + atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); + lvcc->stats.x.aal5.service_stream++; + printk(KERN_ERR DEV_LABEL "(itf %d): Got AAL5 stream " + "PDU on VCI %d!\n", lanai->number, vci); + lanai_reset(lanai); + return 0; + } + DPRINTK("got rx crc error on vci %d\n", vci); + atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); + lvcc->stats.x.aal5.service_rxcrc++; + lvcc->rx.buf.ptr = &lvcc->rx.buf.start[SERVICE_GET_END(s) * 4]; + cardvcc_write(lvcc, SERVICE_GET_END(s), vcc_rxreadptr); + vcclist_read_unlock(); + return 0; +} + +/* Try transmitting on all VCIs that we marked ready to serve */ +static void iter_transmit(struct lanai_dev *lanai, vci_t vci) +{ + struct lanai_vcc *lvcc = lanai->vccs[vci]; + if (!vcc_is_backlogged(lvcc)) + return; + lvcc->tx.unqueue(lanai, lvcc, lvcc->tx.endptr); +} + +/* Run service queue -- called from interrupt context or with + * interrupts otherwise disabled and with the lanai->servicelock + * lock held + */ +static void run_service(struct lanai_dev *lanai) +{ + int ntx = 0; + u32 wreg = reg_read(lanai, ServWrite_Reg); + const u32 *end = lanai->service.start + wreg; + while (lanai->service.ptr != end) { + ntx += handle_service(lanai, + le32_to_cpup(lanai->service.ptr++)); + if (lanai->service.ptr >= lanai->service.end) + lanai->service.ptr = lanai->service.start; + } + reg_write(lanai, wreg, ServRead_Reg); + if (ntx != 0) { + spin_lock(&lanai->txlock); + vcclist_read_lock(); + vci_bitfield_iterate(lanai, &lanai->transmit_ready, + iter_transmit); + vci_bitfield_init(&lanai->transmit_ready); + vcclist_read_unlock(); + spin_unlock(&lanai->txlock); + } +} + +/* -------------------- GATHER STATISTICS: */ + +static void get_statistics(struct lanai_dev *lanai) +{ + u32 statreg = reg_read(lanai, Statistics_Reg); + lanai->stats.atm_ovfl += STATS_GET_FIFO_OVFL(statreg); + lanai->stats.hec_err += STATS_GET_HEC_ERR(statreg); + lanai->stats.vci_trash += STATS_GET_BAD_VCI(statreg); + lanai->stats.ovfl_trash += STATS_GET_BUF_OVFL(statreg); +} + +/* -------------------- POLLING TIMER: */ + +static void lanai_timed_poll(unsigned long arg) +{ +#ifndef DEBUG_RW + struct lanai_dev *lanai = (struct lanai_dev *) arg; + unsigned long flags; +#ifdef USE_POWERDOWN + if (lanai->conf1 & CONFIG1_POWERDOWN) + return; +#endif + spin_lock_irqsave(&lanai->servicelock, flags); + run_service(lanai); + spin_unlock_irqrestore(&lanai->servicelock, flags); + vcc_tx_dequeue_all(lanai); + get_statistics(lanai); + mod_timer(&lanai->timer, jiffies + LANAI_POLL_PERIOD); +#endif /* DEBUG_RW */ +} + +static inline void lanai_timed_poll_start(struct lanai_dev *lanai) +{ + init_timer(&lanai->timer); + lanai->timer.expires = jiffies + LANAI_POLL_PERIOD; + lanai->timer.data = (unsigned long) lanai; + lanai->timer.function = lanai_timed_poll; + add_timer(&lanai->timer); +} + +static inline void lanai_timed_poll_stop(struct lanai_dev *lanai) +{ + del_timer(&lanai->timer); +} + +/* -------------------- INTERRUPT SERVICE: */ + +static inline void lanai_int_1(struct lanai_dev *lanai, u32 reason) +{ + u32 ack = 0; + if (reason & INT_SERVICE) { + ack = INT_SERVICE; + spin_lock(&lanai->servicelock); + run_service(lanai); + spin_unlock(&lanai->servicelock); + } + if (reason & (INT_AAL0_STR | INT_AAL0)) { + ack |= reason & (INT_AAL0_STR | INT_AAL0); + vcc_rx_aal0(lanai); + } + if (reason & INT_STATS) { + reason &= ~INT_STATS; /* No need to ack */ + get_statistics(lanai); + } + if (reason & INT_STATUS) { + ack |= reason & INT_STATUS; + lanai_check_status(lanai); + } + if (reason & INT_DMASHUT) { + printk(KERN_ERR DEV_LABEL "(itf %d): driver error - DMA " + "shutdown, reason=0x%08X, address=0x%08X\n", + lanai->number, reason & INT_DMASHUT, + reg_read(lanai, DMA_Addr_Reg)); + if (reason & INT_TABORTBM) { + lanai_reset(lanai); + return; + } + ack |= (reason & INT_DMASHUT); + printk(KERN_ERR DEV_LABEL "(itf %d): re-enabling DMA\n", + lanai->number); + conf1_write(lanai); + lanai->stats.dma_reenable++; + pcistatus_check(lanai, 0); + } + if (reason & INT_TABORTSENT) { + ack |= (reason & INT_TABORTSENT); + printk(KERN_ERR DEV_LABEL "(itf %d): sent PCI target abort\n", + lanai->number); + pcistatus_check(lanai, 0); + } + if (reason & INT_SEGSHUT) { + printk(KERN_ERR DEV_LABEL "(itf %d): driver error - " + "segmentation shutdown, reason=0x%08X\n", lanai->number, + reason & INT_SEGSHUT); + lanai_reset(lanai); + return; + } + if (reason & (INT_PING | INT_WAKE)) { + printk(KERN_ERR DEV_LABEL "(itf %d): driver error - " + "unexpected interrupt 0x%08X, resetting\n", + lanai->number, reason & (INT_PING | INT_WAKE)); + lanai_reset(lanai); + return; + } +#ifdef DEBUG + if (ack != reason) { + DPRINTK("unacked ints: 0x%08X\n", reason & ~ack); + ack = reason; + } +#endif + if (ack != 0) + reg_write(lanai, ack, IntAck_Reg); +} + +static void lanai_int(int irq, void *devid, struct pt_regs *regs) +{ + struct lanai_dev *lanai = (struct lanai_dev *) devid; + u32 reason; + (void) irq; (void) regs; /* unused variables */ +#ifdef USE_POWERDOWN + if (lanai->conf1 & CONFIG1_POWERDOWN) { + lanai->conf1 &= ~CONFIG1_POWERDOWN; + conf1_write(lanai); + printk(KERN_WARNING DEV_LABEL "(itf %d): Got interrupt " + "0x%08X while in POWERDOWN, powering up\n", lanai->conf1, + intr_pending(lanai)); + conf2_write(lanai); + } +#endif + while ((reason = intr_pending(lanai)) != 0) + lanai_int_1(lanai, reason); +} + +/* TODO - it would be nice if we could use the "delayed interrupt" system + * to some advantage + */ + +/* -------------------- CHECK BOARD ID/REV: */ + +/* + * The board id and revision are stored both in the reset register and + * in the PCI configuration space - the documentation says to check + * each of them. If revp!=NULL we store the revision there + */ +static int check_board_id_and_rev(const char *name, u32 val, int *revp) +{ + DPRINTK("%s says board_id=%d, board_rev=%d\n", name, + RESET_GET_BOARD_ID(val), RESET_GET_BOARD_REV(val)); + if (RESET_GET_BOARD_ID(val) != BOARD_ID_LANAI256) { + printk(KERN_ERR DEV_LABEL ": Found %s board-id %d -- not a " + "Lanai 25.6\n", name, RESET_GET_BOARD_ID(val)); + return -ENODEV; + } + if (revp != NULL) + *revp = RESET_GET_BOARD_REV(val); + return 0; +} + +/* -------------------- PCI INITIALIZATION/SHUTDOWN: */ + +static inline int __init lanai_pci_start(struct lanai_dev *lanai) +{ + struct pci_dev *pci = lanai->pci; + int result; + u16 w; + /* Get the pci revision byte */ + result = pci_read_config_byte(pci, PCI_REVISION_ID, + &lanai->pci_revision); + if (result != PCIBIOS_SUCCESSFUL) { + printk(KERN_ERR DEV_LABEL "(itf %d): can't read " + "PCI_REVISION_ID: %d\n", lanai->number, result); + return -EINVAL; + } + result = pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &w); + if (result != PCIBIOS_SUCCESSFUL) { + printk(KERN_ERR DEV_LABEL "(itf %d): can't read "" + PCI_SUBSYSTEM_ID: %d\n", lanai->number, result); + return -EINVAL; + } + if ((result = check_board_id_and_rev("PCI", w, NULL)) != 0) + return result; + /* Set latency timer to zero as per lanai docs */ + result = pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0); + if (result != PCIBIOS_SUCCESSFUL) { + printk(KERN_ERR DEV_LABEL "(itf %d): can't write " + "PCI_LATENCY_TIMER: %d\n", lanai->number, result); + return -EINVAL; + } + result = pci_read_config_word(pci, PCI_COMMAND, &w); + if (result != PCIBIOS_SUCCESSFUL) { + printk(KERN_ERR DEV_LABEL "(itf %d): can't read " + "PCI_COMMAND: %d\n", lanai->number, result); + return -EINVAL; + } + w |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_SERR | + PCI_COMMAND_PARITY); + result = pci_write_config_word(pci, PCI_COMMAND, w); + if (result != PCIBIOS_SUCCESSFUL) { + printk(KERN_ERR DEV_LABEL "(itf %d): can't " + "write PCI_COMMAND: %d\n", lanai->number, result); + return -EINVAL; + } + pcistatus_check(lanai, 1); + pcistatus_check(lanai, 0); + return 0; +} + +static void lanai_pci_stop(struct lanai_dev *lanai) +{ + struct pci_dev *pci = lanai->pci; + int result; + u16 pci_command; + result = pci_read_config_word(pci, PCI_COMMAND, &pci_command); + if (result != PCIBIOS_SUCCESSFUL) { + printk(KERN_ERR DEV_LABEL "(itf %d): can't " + "read PCI_COMMAND: %d\n", lanai->number, result); + return; + } + pci_command &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + result = pci_write_config_word(pci, PCI_COMMAND, pci_command); + if (result != PCIBIOS_SUCCESSFUL) + printk(KERN_ERR DEV_LABEL "(itf %d): can't " + "write PCI_COMMAND: %d\n", lanai->number, result); +} + +/* -------------------- VPI/VCI ALLOCATION: */ + +/* + * We _can_ use VCI==0 for normal traffic, but only for UBR (or we'll + * get a CBRZERO interrupt), and we can use it only if noone is receiving + * AAL0 traffic (since they will use the same queue) - according to the + * docs we shouldn't even use it for AAL0 traffic + */ +static inline int vci0_is_ok(struct lanai_dev *lanai, + const struct atm_qos *qos) +{ + if (qos->txtp.traffic_class == ATM_CBR || qos->aal == ATM_AAL0) + return 0; + if (qos->rxtp.traffic_class != ATM_NONE) { + if (lanai->naal0 != 0) + return 0; + lanai->conf2 |= CONFIG2_VCI0_NORMAL; +#ifdef USE_POWERDOWN + if ((lanai->conf1 & CONFIG1_POWERDOWN) == 0) +#endif + conf2_write(lanai); + } + return 1; +} + +/* return true if vci is currently unused, or if requested qos is + * compatible + */ +static int vci_is_ok(struct lanai_dev *lanai, vci_t vci, + const struct atm_vcc *atmvcc) +{ + const struct atm_qos *qos = &atmvcc->qos; + const struct lanai_vcc *lvcc = lanai->vccs[vci]; + if (vci == 0 && !vci0_is_ok(lanai, qos)) + return 0; + if (lvcc != NULL) { + if (qos->rxtp.traffic_class != ATM_NONE && + lvcc->rx.atmvcc != NULL && lvcc->rx.atmvcc != atmvcc) + return 0; + if (qos->txtp.traffic_class != ATM_NONE && + lvcc->tx.atmvcc != NULL && lvcc->tx.atmvcc != atmvcc) + return 0; + if (qos->txtp.traffic_class == ATM_CBR && + lanai->cbrvcc != NULL && lanai->cbrvcc != atmvcc) + return 0; + } + if (qos->aal == ATM_AAL0 && lanai->naal0 == 0 && + qos->rxtp.traffic_class != ATM_NONE) { + const struct lanai_vcc *vci0 = lanai->vccs[0]; + if (vci0 != NULL && vci0->rx.atmvcc != NULL) + return 0; + lanai->conf2 &= ~CONFIG2_VCI0_NORMAL; +#ifdef USE_POWERDOWN + if ((lanai->conf1 & CONFIG1_POWERDOWN) == 0) +#endif + conf2_write(lanai); + } + return 1; +} + +static int lanai_normalize_ci(struct lanai_dev *lanai, + const struct atm_vcc *atmvcc, short *vpip, vci_t *vcip) +{ + switch (*vpip) { + case ATM_VPI_ANY: + *vpip = 0; + /* FALLTHROUGH */ + case 0: + break; + default: + return -EADDRINUSE; + } + switch (*vcip) { + case ATM_VCI_ANY: + for (*vcip = ATM_NOT_RSV_VCI; *vcip < lanai->num_vci; + (*vcip)++) + if (vci_is_ok(lanai, *vcip, atmvcc)) + return 0; + return -EADDRINUSE; + default: + if (*vcip >= lanai->num_vci || *vcip < 0 || + !vci_is_ok(lanai, *vcip, atmvcc)) + return -EADDRINUSE; + } + return 0; +} + +/* -------------------- MANAGE CBR: */ + +/* + * CBR ICG is stored as a fixed-point number with 4 fractional bits. + * Note that storing a number greater than 2046.0 will result in + * incorrect shaping + */ +#define CBRICG_FRAC_BITS (4) +#define CBRICG_MAX (2046 << CBRICG_FRAC_BITS) + +/* + * ICG is related to PCR with the formula PCR = MAXPCR / (ICG + 1) + * where MAXPCR is (according to the docs) 25600000/(54*8), + * which is equal to (3125<<9)/27. + * + * Solving for ICG, we get: + * ICG = MAXPCR/PCR - 1 + * ICG = (3125<<9)/(27*PCR) - 1 + * ICG = ((3125<<9) - (27*PCR)) / (27*PCR) + * + * The end result is supposed to be a fixed-point number with FRAC_BITS + * bits of a fractional part, so we keep everything in the numerator + * shifted by that much as we compute + * + */ +static int pcr_to_cbricg(/*const*/ struct atm_qos *qos) +{ + int rounddown = 0; /* 1 = Round PCR down, i.e. round ICG _up_ */ + int x, icg, pcr = atm_pcr_goal(&qos->txtp); + if (pcr == 0) /* Use maximum bandwidth */ + return 0; + if (pcr < 0) { + rounddown = 1; + pcr = -pcr; + } + x = pcr * 27; + icg = (3125 << (9 + CBRICG_FRAC_BITS)) - (x << CBRICG_FRAC_BITS); + if (rounddown) + icg += x - 1; + icg /= x; + if (icg > CBRICG_MAX) + icg = CBRICG_MAX; + DPRINTK("pcr_to_cbricg: pcr=%d rounddown=%c icg=%d\n", + pcr, rounddown ? 'Y' : 'N', icg); + return icg; +} + +static inline void lanai_cbr_setup(struct lanai_dev *lanai) +{ + reg_write(lanai, pcr_to_cbricg(&lanai->cbrvcc->qos), CBR_ICG_Reg); + reg_write(lanai, lanai->cbrvcc->vci, CBR_PTR_Reg); + lanai->conf2 |= CONFIG2_CBR_ENABLE; + conf2_write(lanai); +} + +static inline void lanai_cbr_shutdown(struct lanai_dev *lanai) +{ + lanai->conf2 &= ~CONFIG2_CBR_ENABLE; + conf2_write(lanai); +} + +/* -------------------- OPERATIONS: */ + +/* setup a newly detected device */ +static int __init lanai_dev_open(struct atm_dev *atmdev) +{ + struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; + unsigned long raw_base; + int result; + + DPRINTK("In lanai_dev_open()\n"); + /* Basic device fields */ + lanai->number = atmdev->number; + lanai->num_vci = NUM_VCI; + vci_bitfield_init(&lanai->backlog_vccs); + vci_bitfield_init(&lanai->transmit_ready); + lanai->naal0 = 0; +#ifdef USE_POWERDOWN + lanai->nbound = 0; +#endif + lanai->cbrvcc = NULL; + memset(&lanai->stats, 0, sizeof lanai->stats); + spin_lock_init(&lanai->txlock); + spin_lock_init(&lanai->servicelock); + atmdev->ci_range.vpi_bits = 0; + atmdev->ci_range.vci_bits = 0; + while (1 << atmdev->ci_range.vci_bits < lanai->num_vci) + atmdev->ci_range.vci_bits++; + atmdev->link_rate = ((25600000 / 8 - 8000) / 54); + + /* 3.2: PCI initialization */ + if ((result = lanai_pci_start(lanai)) != 0) + goto error; + raw_base = (bus_addr_t) lanai->pci->resource[0].start; + lanai->base = (bus_addr_t) ioremap(raw_base, LANAI_MAPPING_SIZE); + if (lanai->base == 0) { + printk(KERN_ERR DEV_LABEL ": couldn't remap I/O space\n"); + goto error_pci; + } + /* 3.3: Reset lanai and PHY */ + reset_board(lanai); + lanai->conf1 = reg_read(lanai, Config1_Reg); + lanai->conf1 &= ~(CONFIG1_GPOUT1 | CONFIG1_POWERDOWN | + CONFIG1_MASK_LEDMODE); + lanai->conf1 |= CONFIG1_SET_LEDMODE(LEDMODE_NOT_SOOL); + reg_write(lanai, lanai->conf1 | CONFIG1_GPOUT1, Config1_Reg); + udelay(1000); + conf1_write(lanai); + + /* + * 3.4: Turn on endian mode for big-endian hardware + * We don't actually want to do this - the actual bit fields + * in the endian register are not documented anywhere. + * Instead we do the bit-flipping ourselves on big-endian + * hardware. + * + * 3.5: get the board ID/rev by reading the reset register + */ + result = check_board_id_and_rev("register", + reg_read(lanai, Reset_Reg), &lanai->board_rev); + if (result != 0) + goto error_unmap; + + /* 3.6: read EEPROM */ + if ((result = eeprom_read(lanai)) != 0) + goto error_unmap; + if ((result = eeprom_validate(lanai)) != 0) + goto error_unmap; + + /* 3.7: re-reset PHY, do loopback tests, setup PHY */ + reg_write(lanai, lanai->conf1 | CONFIG1_GPOUT1, Config1_Reg); + udelay(1000); + conf1_write(lanai); + /* TODO - loopback tests */ + lanai->conf1 |= (CONFIG1_GPOUT2 | CONFIG1_GPOUT3 | CONFIG1_DMA_ENABLE); + conf1_write(lanai); + + /* 3.8/3.9: test and initialize card SRAM */ + if ((result = sram_test_and_clear(lanai)) != 0) + goto error_unmap; + + /* 3.10: initialize lanai registers */ + lanai->conf1 |= CONFIG1_DMA_ENABLE; + conf1_write(lanai); + if ((result = service_buffer_allocate(lanai)) != 0) + goto error_unmap; + if ((result = vcc_table_allocate(lanai)) != 0) + goto error_service; + lanai->conf2 = (lanai->num_vci >= 512 ? CONFIG2_HOWMANY : 0) | + CONFIG2_HEC_DROP | /* ??? */ CONFIG2_PTI7_MODE; + conf2_write(lanai); + reg_write(lanai, TX_FIFO_DEPTH, TxDepth_Reg); + reg_write(lanai, 0, CBR_ICG_Reg); /* CBR defaults to no limit */ + if ((result = request_irq(lanai->pci->irq, lanai_int, SA_SHIRQ, + "lanai", lanai)) != 0) { + printk(KERN_ERR DEV_LABEL ": can't allocate interrupt\n"); + goto error_vcctable; + } + MOD_INC_USE_COUNT; /* At this point we can't fail */ + intr_enable(lanai, INT_ALL & ~(INT_PING | INT_WAKE)); + /* 3.11: initialize loop mode (i.e. turn looping off) */ + lanai->conf1 = (lanai->conf1 & ~CONFIG1_MASK_LOOPMODE) | + CONFIG1_SET_LOOPMODE(LOOPMODE_NORMAL) | + CONFIG1_GPOUT2 | CONFIG1_GPOUT3; + conf1_write(lanai); + lanai->status = reg_read(lanai, Status_Reg); + /* We're now done initializing this card */ +#ifdef USE_POWERDOWN + lanai->conf1 |= CONFIG1_POWERDOWN; + conf1_write(lanai); +#endif + memcpy(atmdev->esi, eeprom_mac(lanai), ESI_LEN); + lanai_timed_poll_start(lanai); + printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d, base=0x%lx, irq=%d " + "(%02X-%02X-%02X-%02X-%02X-%02X)\n", lanai->number, + lanai->pci_revision, (long) lanai->base, lanai->pci->irq, + atmdev->esi[0], atmdev->esi[1], atmdev->esi[2], + atmdev->esi[3], atmdev->esi[4], atmdev->esi[5]); + printk(KERN_NOTICE DEV_LABEL "(itf %d): LANAI%s, serialno=%d(0x%X), " + "board_rev=%d\n", lanai->number, + lanai->type==lanai2 ? "2" : "HB", lanai->serialno, + lanai->serialno, lanai->board_rev); + return 0; + + error_vcctable: + vcc_table_deallocate(lanai); + error_service: + service_buffer_deallocate(lanai); + error_unmap: + reset_board(lanai); +#ifdef USE_POWERDOWN + lanai->conf1 = reg_read(lanai, Config1_Reg) | CONFIG1_POWERDOWN; + conf1_write(lanai); +#endif + iounmap((void *) lanai->base); + error_pci: + lanai_pci_stop(lanai); + error: + return result; +} + +/* called when device is being shutdown, and all vcc's are gone - higher + * levels will deallocate the atm device for us + */ +static void lanai_dev_close(struct atm_dev *atmdev) +{ + struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; + printk(KERN_INFO DEV_LABEL "(itf %d): shutting down interface\n", + lanai->number); + lanai_timed_poll_stop(lanai); +#ifdef USE_POWERDOWN + lanai->conf1 = reg_read(lanai, Config1_Reg) & ~CONFIG1_POWERDOWN; + conf1_write(lanai); +#endif + intr_disable(lanai, INT_ALL); + free_irq(lanai->pci->irq, lanai); + reset_board(lanai); +#ifdef USE_POWERDOWN + lanai->conf1 |= CONFIG1_POWERDOWN; + conf1_write(lanai); +#endif + lanai_pci_stop(lanai); + vcc_table_deallocate(lanai); + service_buffer_deallocate(lanai); + iounmap((void *) lanai->base); + kfree(lanai); + MOD_DEC_USE_COUNT; +} + +/* close a vcc */ +static void lanai_close(struct atm_vcc *atmvcc) +{ + struct lanai_vcc *lvcc = (struct lanai_vcc *) atmvcc->dev_data; + struct lanai_dev *lanai = (struct lanai_dev *) atmvcc->dev->dev_data; + if (lvcc == NULL) + return; + clear_bit(ATM_VF_READY, &atmvcc->flags); + clear_bit(ATM_VF_PARTIAL, &atmvcc->flags); + if (lvcc->rx.atmvcc == atmvcc) { + lanai_shutdown_rx_vci(lvcc); + if (atmvcc->qos.aal == ATM_AAL0) { + if (--lanai->naal0 <= 0) + aal0_buffer_free(lanai); + } else + lanai_buf_deallocate(&lvcc->rx.buf); + lvcc->rx.atmvcc = NULL; + } + if (lvcc->tx.atmvcc == atmvcc) { + if (atmvcc == lanai->cbrvcc) { + if (lvcc->vbase != 0) + lanai_cbr_shutdown(lanai); + lanai->cbrvcc = NULL; + } + lanai_shutdown_tx_vci(lanai, lvcc); + lanai_buf_deallocate(&lvcc->tx.buf); + lvcc->tx.atmvcc = NULL; + } + if (--lvcc->nref == 0) { + host_vcc_unbind(lanai, lvcc); + kfree(lvcc); + } + atmvcc->dev_data = NULL; + clear_bit(ATM_VF_ADDR, &atmvcc->flags); +} + +/* open a vcc on the card to vpi/vci */ +static int lanai_open(struct atm_vcc *atmvcc, short vpi, int vci) +{ + struct lanai_dev *lanai; + struct lanai_vcc *lvcc; + int result = 0; + /* we don't support partial open - it's not really useful anyway */ + if ((test_bit(ATM_VF_PARTIAL, &atmvcc->flags)) || + (vpi == ATM_VPI_UNSPEC) || (vci == ATM_VCI_UNSPEC)) + return -EINVAL; + lanai = (struct lanai_dev *) atmvcc->dev->dev_data; + if ((result = lanai_normalize_ci(lanai, atmvcc, &vpi, &vci)) != 0) + goto out; + atmvcc->vpi = vpi; + atmvcc->vci = vci; + set_bit(ATM_VF_ADDR, &atmvcc->flags); + lvcc = lanai->vccs[vci]; + if (atmvcc->qos.aal != ATM_AAL0 && atmvcc->qos.aal != ATM_AAL5) + return -EINVAL; +#if 0 + DPRINTK(DEV_LABEL "(itf %d): open %d.%d flags=0x%X\n", + lanai->number, vpi, vci, (unsigned long) atmvcc->flags); +#else + DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n", lanai->number, vpi, vci); +#endif + if (lvcc == NULL && (lvcc = new_lanai_vcc()) == NULL) + return -ENOMEM; + atmvcc->dev_data = lvcc; + lvcc->nref++; + if (atmvcc->qos.rxtp.traffic_class != ATM_NONE) { + APRINTK(lvcc->rx.atmvcc == NULL, "rx.atmvcc!=NULL, vci=%d\n", + vci); + if (atmvcc->qos.aal == ATM_AAL0) { + if (lanai->naal0 == 0) + result = aal0_buffer_allocate(lanai); + } else + result = lanai_setup_rx_vci_aal5( + lanai->number, lvcc, &atmvcc->qos); + if (result != 0) + goto out_free; + lvcc->rx.atmvcc = atmvcc; + lvcc->stats.rx_nomem = 0; + lvcc->stats.x.aal5.rx_badlen = 0; + lvcc->stats.x.aal5.service_trash = 0; + lvcc->stats.x.aal5.service_stream = 0; + lvcc->stats.x.aal5.service_rxcrc = 0; + if (atmvcc->qos.aal == ATM_AAL0) + lanai->naal0++; + } + if (atmvcc->qos.txtp.traffic_class != ATM_NONE) { + APRINTK(lvcc->tx.atmvcc == NULL, "tx.atmvcc!=NULL, vci=%d\n", + vci); + result = lanai_setup_tx_vci(lanai->number, lvcc, &atmvcc->qos); + if (result != 0) + goto out_free; + lvcc->tx.atmvcc = atmvcc; + if (atmvcc->qos.txtp.traffic_class == ATM_CBR) { + APRINTK(lanai->cbrvcc == NULL, + "cbrvcc!=NULL, vci=%d\n", vci); + lanai->cbrvcc = atmvcc; + } + } + host_vcc_bind(lanai, lvcc, vci); + if (atmvcc == lvcc->rx.atmvcc) + host_vcc_start_rx(lvcc); + if (atmvcc == lvcc->tx.atmvcc) { + host_vcc_start_tx(lvcc); + if (lanai->cbrvcc == atmvcc) + lanai_cbr_setup(lanai); + } + set_bit(ATM_VF_READY, &atmvcc->flags); + return 0; + out_free: + lanai_close(atmvcc); + out: + return result; +} + +/* ioctl operations for card */ +/* NOTE: these are all DEBUGGING ONLY currently */ +static int lanai_ioctl(struct atm_dev *atmdev, unsigned int cmd, void *arg) +{ + int result = 0; + struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; + switch(cmd) { + case 2106275: + shutdown_atm_dev(atmdev); + return 0; + case 2200000: { + unsigned long flags; + spin_lock_irqsave(&lanai->servicelock, flags); + run_service(lanai); + spin_unlock_irqrestore(&lanai->servicelock, flags); + return 0; } + case 2200001: + vcc_tx_dequeue_all(lanai); + return 0; + case 2200002: + get_statistics(lanai); + return 0; + case 2200003: { + int i; + for (i = 0; i <= 0x5C ; i += 4) { + if (i==0x48) /* Write-only butt reg */ + continue; + printk(KERN_CRIT DEV_LABEL " 0x%02X: " + "0x%08X\n", i, + (u32) readl(lanai->base + i)); + barrier(); mb(); + pcistatus_check(lanai, 0); + barrier(); mb(); + } + return 0; } + case 2200004: { + u8 b; + u16 w; + u32 dw; + struct pci_dev *pci = lanai->pci; + (void) pci_read_config_word(pci, PCI_VENDOR_ID, &w); + DPRINTK("vendor = 0x%X\n", w); + (void) pci_read_config_word(pci, PCI_DEVICE_ID, &w); + DPRINTK("device = 0x%X\n", w); + (void) pci_read_config_word(pci, PCI_COMMAND, &w); + DPRINTK("command = 0x%X\n", w); + (void) pci_read_config_word(pci, PCI_STATUS, &w); + DPRINTK("status = 0x%X\n", w); + (void) pci_read_config_dword(pci, + PCI_CLASS_REVISION, &dw); + DPRINTK("class/revision = 0x%X\n", dw); + (void) pci_read_config_byte(pci, + PCI_CACHE_LINE_SIZE, &b); + DPRINTK("cache line size = 0x%X\n", b); + (void) pci_read_config_byte(pci, PCI_LATENCY_TIMER, &b); + DPRINTK("latency = %d (0x%X)\n", b, b); + (void) pci_read_config_byte(pci, PCI_HEADER_TYPE, &b); + DPRINTK("header type = 0x%X\n", b); + (void) pci_read_config_byte(pci, PCI_BIST, &b); + DPRINTK("bist = 0x%X\n", b); + /* skipping a few here */ + (void) pci_read_config_byte(pci, + PCI_INTERRUPT_LINE, &b); + DPRINTK("pci_int_line = 0x%X\n", b); + (void) pci_read_config_byte(pci, + PCI_INTERRUPT_PIN, &b); + DPRINTK("pci_int_pin = 0x%X\n", b); + (void) pci_read_config_byte(pci, PCI_MIN_GNT, &b); + DPRINTK("min_gnt = 0x%X\n", b); + (void) pci_read_config_byte(pci, PCI_MAX_LAT, &b); + DPRINTK("max_lat = 0x%X\n", b); } + return 0; +#ifdef USE_POWERDOWN + case 2200005: + DPRINTK("Coming out of powerdown\n"); + lanai->conf1 &= ~CONFIG1_POWERDOWN; + conf1_write(lanai); + return 0; +#endif + default: + result = -EINVAL; + } + return result; +} + +static int lanai_send(struct atm_vcc *atmvcc, struct sk_buff *skb) +{ + struct lanai_vcc *lvcc = (struct lanai_vcc *) atmvcc->dev_data; + struct lanai_dev *lanai = (struct lanai_dev *) atmvcc->dev->dev_data; + unsigned long flags; + if (lvcc == NULL || lvcc->vbase == 0 || lvcc->tx.atmvcc != atmvcc) + goto einval; +#ifdef DEBUG + if (skb == NULL) { + DPRINTK("lanai_send: skb==NULL for vci=%d\n", atmvcc->vci); + goto einval; + } + if (lanai == NULL) { + DPRINTK("lanai_send: lanai==NULL for vci=%d\n", atmvcc->vci); + goto einval; + } +#endif + ATM_SKB(skb)->vcc = atmvcc; + switch (atmvcc->qos.aal) { + case ATM_AAL5: + spin_lock_irqsave(&lanai->txlock, flags); + vcc_tx_aal5(lanai, lvcc, skb); + spin_unlock_irqrestore(&lanai->txlock, flags); + return 0; + case ATM_AAL0: + if (skb->len != ATM_CELL_SIZE-1) + goto einval; + /* NOTE - this next line is technically invalid - we haven't unshared skb */ + cpu_to_be32s((u32 *) skb->data); + spin_lock_irqsave(&lanai->txlock, flags); + vcc_tx_aal0(lanai, lvcc, skb); + spin_unlock_irqrestore(&lanai->txlock, flags); + return 0; + } + DPRINTK("lanai_send: bad aal=%d on vci=%d\n", atmvcc->qos.aal, + atmvcc->vci); + einval: + lanai_free_skb(atmvcc, skb); + return -EINVAL; +} + +static int lanai_change_qos(struct atm_vcc *atmvcc, + /*const*/ struct atm_qos *qos, int flags) +{ + return -EBUSY; /* TODO: need to write this */ +} + +#ifndef CONFIG_PROC_FS +#define lanai_proc_read NULL +#else +static int lanai_proc_read(struct atm_dev *atmdev, loff_t *pos, char *page) +{ + struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; + loff_t left = *pos; + struct lanai_vcc *lvcc; + if (left-- == 0) + return sprintf(page, DEV_LABEL "(itf %d): chip=LANAI%s, " + "serial=%d, magic=0x%08X, num_vci=%d\n", + atmdev->number, lanai->type==lanai2 ? "2" : "HB", + lanai->serialno, lanai->magicno, lanai->num_vci); + if (left-- == 0) + return sprintf(page, "revision: board=%d, pci_if=%d\n", + lanai->board_rev, lanai->pci_revision); + if (left-- == 0) + return sprintf(page, "EEPROM ESI: " + "%02X:%02X:%02X:%02X:%02X:%02X\n", + lanai->eeprom[EEPROM_MAC + 0], + lanai->eeprom[EEPROM_MAC + 1], + lanai->eeprom[EEPROM_MAC + 2], + lanai->eeprom[EEPROM_MAC + 3], + lanai->eeprom[EEPROM_MAC + 4], + lanai->eeprom[EEPROM_MAC + 5]); + if (left-- == 0) + return sprintf(page, "status: SOOL=%d, LOCD=%d, LED=%d, " + "GPIN=%d\n", (lanai->status & STATUS_SOOL) ? 1 : 0, + (lanai->status & STATUS_LOCD) ? 1 : 0, + (lanai->status & STATUS_LED) ? 1 : 0, + (lanai->status & STATUS_GPIN) ? 1 : 0); + if (left-- == 0) + return sprintf(page, "global buffer sizes: service=%d, " + "aal0_rx=%d\n", lanai_buf_size(&lanai->service), + lanai->naal0 ? lanai_buf_size(&lanai->aal0buf) : 0); + if (left-- == 0) { + get_statistics(lanai); + return sprintf(page, "cells in error: overflow=%d, " + "closed_vci=%d, bad_HEC=%d, rx_fifo=%d\n", + lanai->stats.ovfl_trash, lanai->stats.vci_trash, + lanai->stats.hec_err, lanai->stats.atm_ovfl); + } + if (left-- == 0) + return sprintf(page, "PCI errors: parity_detect=%d, " + "master_abort=%d, master_target_abort=%d,\n", + lanai->stats.pcierr_parity_detect, + lanai->stats.pcierr_serr_set, + lanai->stats.pcierr_m_target_abort); + if (left-- == 0) + return sprintf(page, " slave_target_abort=%d, " + "master_parity=%d\n", lanai->stats.pcierr_s_target_abort, + lanai->stats.pcierr_master_parity); + if (left-- == 0) + return sprintf(page, "service list errors: no_vcc_rx=%d, " + "no_vcc_tx=%d,\n", lanai->stats.service_novcc_rx, + lanai->stats.service_novcc_tx); + if (left-- == 0) + return sprintf(page, " no_tx=%d, " + "no_rx=%d, bad_rx_aal=%d\n", lanai->stats.service_norx, + lanai->stats.service_notx, + lanai->stats.service_rxnotaal5); + if (left-- == 0) + return sprintf(page, "resets: dma=%d, card=%d\n", + lanai->stats.dma_reenable, lanai->stats.card_reset); + /* At this point, "left" should be the VCI we're looking for */ + vcclist_read_lock(); + for (; ; left++) { + if (left >= NUM_VCI) { + left = 0; + goto out; + } + if ((lvcc = lanai->vccs[left]) != NULL) + break; + (*pos)++; + } + /* Note that we re-use "left" here since we're done with it */ + left = sprintf(page, "VCI %4d: nref=%d, rx_nomem=%d", (vci_t) left, + lvcc->nref, lvcc->stats.rx_nomem); + if (lvcc->rx.atmvcc != NULL) { + left += sprintf(&page[left], ",\n rx_AAL=%d", + lvcc->rx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0); + if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) + left += sprintf(&page[left], ", rx_buf_size=%d, " + "rx_bad_len=%d,\n rx_service_trash=%d, " + "rx_service_stream=%d, rx_bad_crc=%d", + lanai_buf_size(&lvcc->rx.buf), + lvcc->stats.x.aal5.rx_badlen, + lvcc->stats.x.aal5.service_trash, + lvcc->stats.x.aal5.service_stream, + lvcc->stats.x.aal5.service_rxcrc); + } + if (lvcc->tx.atmvcc != NULL) + left += sprintf(&page[left], ",\n tx_AAL=%d, " + "tx_buf_size=%d, tx_qos=%cBR, tx_backlogged=%c", + lvcc->tx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0, + lanai_buf_size(&lvcc->tx.buf), + lvcc->tx.atmvcc == lanai->cbrvcc ? 'C' : 'U', + vcc_is_backlogged(lvcc) ? 'Y' : 'N'); + page[left++] = '\n'; + page[left] = '\0'; + out: + vcclist_read_unlock(); + return left; +} +#endif /* CONFIG_PROC_FS */ + +/* -------------------- HOOKS: */ + +static const struct atmdev_ops ops = { + dev_close: lanai_dev_close, + open: lanai_open, + close: lanai_close, + ioctl: lanai_ioctl, + getsockopt: NULL, + setsockopt: NULL, + send: lanai_send, + sg_send: NULL, /* no scatter-gather on card */ + send_oam: NULL, /* OAM support not in linux yet */ + phy_put: NULL, + phy_get: NULL, + feedback: NULL, + change_qos: lanai_change_qos, + free_rx_skb: NULL, + proc_read: lanai_proc_read +}; + +/* detect one type of card LANAI2 or LANAIHB */ +static int __init lanai_detect_1(unsigned int vendor, unsigned int device) +{ + struct pci_dev *pci = NULL; + struct lanai_dev *lanai; + struct atm_dev *atmdev; + int count = 0, result; + while ((pci = pci_find_device(vendor, device, pci)) != NULL) { + lanai = (struct lanai_dev *) + kmalloc(sizeof *lanai, GFP_KERNEL); + if (lanai == NULL) { + printk(KERN_ERR DEV_LABEL ": couldn't allocate " + "dev_data structure!\n"); + break; + } + atmdev = atm_dev_register(DEV_LABEL, &ops, -1, 0); + if (atmdev == NULL) { + printk(KERN_ERR DEV_LABEL ": couldn't register " + "atm device!\n"); + kfree(lanai); + break; + } + atmdev->dev_data = lanai; + lanai->pci = pci; + lanai->type = (enum lanai_type) device; + if ((result = lanai_dev_open(atmdev)) != 0) { + DPRINTK("lanai_start() failed, err=%d\n", -result); + atm_dev_deregister(atmdev); + kfree(lanai); + continue; + } + count++; + } + return count; +} + +#ifdef MODULE +static +#endif +int __init lanai_detect(void) +{ + return lanai_detect_1(PCI_VENDOR_ID_EF, PCI_VENDOR_ID_EF_ATM_LANAI2) + + lanai_detect_1(PCI_VENDOR_ID_EF, PCI_VENDOR_ID_EF_ATM_LANAIHB); +} + +#ifdef MODULE + +int init_module(void) +{ + if (lanai_detect() == 0) { + printk(KERN_ERR DEV_LABEL ": no adaptor found\n"); + return -ENODEV; + } + return 0; +} + +void cleanup_module(void) +{ + /* We'll only get called when all the interfaces are already + * gone, so there isn't much to do + */ + DPRINTK("cleanup_module()\n"); +} + +MODULE_AUTHOR("Mitchell Blank Jr "); +MODULE_DESCRIPTION("Efficient Networks Speedstream 3010 driver"); + +#endif /* MODULE */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/Config.in linux.ac/drivers/block/Config.in --- linux.vanilla/drivers/block/Config.in Fri Sep 14 22:04:06 2001 +++ linux.ac/drivers/block/Config.in Thu Oct 11 09:12:04 2001 @@ -35,6 +35,7 @@ fi dep_tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA $CONFIG_PCI dep_tristate 'Compaq Smart Array 5xxx support' CONFIG_BLK_CPQ_CISS_DA $CONFIG_PCI +dep_mbool ' SCSI tape drive support for Smart Array 5xxx' CONFIG_CISS_SCSI_TAPE $CONFIG_BLK_CPQ_CISS_DA $CONFIG_SCSI dep_tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960 $CONFIG_PCI tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/DAC960.c linux.ac/drivers/block/DAC960.c --- linux.vanilla/drivers/block/DAC960.c Mon Sep 10 20:42:31 2001 +++ linux.ac/drivers/block/DAC960.c Wed Oct 10 01:47:40 2001 @@ -508,16 +508,20 @@ void *DataPointer) { DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); - DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox; - DAC960_V1_CommandStatus_T CommandStatus; - DAC960_V1_ClearCommand(Command); - Command->CommandType = DAC960_ImmediateCommand; - CommandMailbox->Type3.CommandOpcode = CommandOpcode; - CommandMailbox->Type3.BusAddress = Virtual_to_Bus32(DataPointer); - DAC960_ExecuteCommand(Command); - CommandStatus = Command->V1.CommandStatus; - DAC960_DeallocateCommand(Command); - return (CommandStatus == DAC960_V1_NormalCompletion); + if (Command == NULL) { + return false; + } else { + DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox; + DAC960_V1_CommandStatus_T CommandStatus; + DAC960_V1_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->Type3.CommandOpcode = CommandOpcode; + CommandMailbox->Type3.BusAddress = Virtual_to_Bus32(DataPointer); + DAC960_ExecuteCommand(Command); + CommandStatus = Command->V1.CommandStatus; + DAC960_DeallocateCommand(Command); + return (CommandStatus == DAC960_V1_NormalCompletion); + } } @@ -534,18 +538,22 @@ void *DataPointer) { DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); - DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox; - DAC960_V1_CommandStatus_T CommandStatus; - DAC960_V1_ClearCommand(Command); - Command->CommandType = DAC960_ImmediateCommand; - CommandMailbox->Type3D.CommandOpcode = CommandOpcode; - CommandMailbox->Type3D.Channel = Channel; - CommandMailbox->Type3D.TargetID = TargetID; - CommandMailbox->Type3D.BusAddress = Virtual_to_Bus32(DataPointer); - DAC960_ExecuteCommand(Command); - CommandStatus = Command->V1.CommandStatus; - DAC960_DeallocateCommand(Command); - return (CommandStatus == DAC960_V1_NormalCompletion); + if (Command == NULL) { + return false; + } else { + DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox; + DAC960_V1_CommandStatus_T CommandStatus; + DAC960_V1_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->Type3D.CommandOpcode = CommandOpcode; + CommandMailbox->Type3D.Channel = Channel; + CommandMailbox->Type3D.TargetID = TargetID; + CommandMailbox->Type3D.BusAddress = Virtual_to_Bus32(DataPointer); + DAC960_ExecuteCommand(Command); + CommandStatus = Command->V1.CommandStatus; + DAC960_DeallocateCommand(Command); + return (CommandStatus == DAC960_V1_NormalCompletion); + } } @@ -561,29 +569,33 @@ unsigned int DataByteCount) { DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); - DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; - DAC960_V2_CommandStatus_T CommandStatus; - DAC960_V2_ClearCommand(Command); - Command->CommandType = DAC960_ImmediateCommand; - CommandMailbox->Common.CommandOpcode = DAC960_V2_IOCTL; - CommandMailbox->Common.CommandControlBits + if (Command == NULL) { + return false; + } else { + DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; + DAC960_V2_CommandStatus_T CommandStatus; + DAC960_V2_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->Common.CommandOpcode = DAC960_V2_IOCTL; + CommandMailbox->Common.CommandControlBits .DataTransferControllerToHost = true; - CommandMailbox->Common.CommandControlBits + CommandMailbox->Common.CommandControlBits .NoAutoRequestSense = true; - CommandMailbox->Common.DataTransferSize = DataByteCount; - CommandMailbox->Common.IOCTL_Opcode = IOCTL_Opcode; - CommandMailbox->Common.DataTransferMemoryAddress + CommandMailbox->Common.DataTransferSize = DataByteCount; + CommandMailbox->Common.IOCTL_Opcode = IOCTL_Opcode; + CommandMailbox->Common.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentDataPointer = - Virtual_to_Bus64(DataPointer); - CommandMailbox->Common.DataTransferMemoryAddress + Virtual_to_Bus64(DataPointer); + CommandMailbox->Common.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentByteCount = - CommandMailbox->Common.DataTransferSize; - DAC960_ExecuteCommand(Command); - CommandStatus = Command->V2.CommandStatus; - DAC960_DeallocateCommand(Command); - return (CommandStatus == DAC960_V2_NormalCompletion); + CommandMailbox->Common.DataTransferSize; + DAC960_ExecuteCommand(Command); + CommandStatus = Command->V2.CommandStatus; + DAC960_DeallocateCommand(Command); + return (CommandStatus == DAC960_V2_NormalCompletion); + } } @@ -599,30 +611,34 @@ unsigned int DataByteCount) { DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); - DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; - DAC960_V2_CommandStatus_T CommandStatus; - DAC960_V2_ClearCommand(Command); - Command->CommandType = DAC960_ImmediateCommand; - CommandMailbox->ControllerInfo.CommandOpcode = DAC960_V2_IOCTL; - CommandMailbox->ControllerInfo.CommandControlBits + if (Command == NULL) { + return false; + } else { + DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; + DAC960_V2_CommandStatus_T CommandStatus; + DAC960_V2_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->ControllerInfo.CommandOpcode = DAC960_V2_IOCTL; + CommandMailbox->ControllerInfo.CommandControlBits .DataTransferControllerToHost = true; - CommandMailbox->ControllerInfo.CommandControlBits + CommandMailbox->ControllerInfo.CommandControlBits .NoAutoRequestSense = true; - CommandMailbox->ControllerInfo.DataTransferSize = DataByteCount; - CommandMailbox->ControllerInfo.ControllerNumber = 0; - CommandMailbox->ControllerInfo.IOCTL_Opcode = IOCTL_Opcode; - CommandMailbox->ControllerInfo.DataTransferMemoryAddress + CommandMailbox->ControllerInfo.DataTransferSize = DataByteCount; + CommandMailbox->ControllerInfo.ControllerNumber = 0; + CommandMailbox->ControllerInfo.IOCTL_Opcode = IOCTL_Opcode; + CommandMailbox->ControllerInfo.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentDataPointer = - Virtual_to_Bus64(DataPointer); - CommandMailbox->ControllerInfo.DataTransferMemoryAddress + Virtual_to_Bus64(DataPointer); + CommandMailbox->ControllerInfo.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentByteCount = - CommandMailbox->ControllerInfo.DataTransferSize; - DAC960_ExecuteCommand(Command); - CommandStatus = Command->V2.CommandStatus; - DAC960_DeallocateCommand(Command); - return (CommandStatus == DAC960_V2_NormalCompletion); + CommandMailbox->ControllerInfo.DataTransferSize; + DAC960_ExecuteCommand(Command); + CommandStatus = Command->V2.CommandStatus; + DAC960_DeallocateCommand(Command); + return (CommandStatus == DAC960_V2_NormalCompletion); + } } @@ -641,31 +657,35 @@ unsigned int DataByteCount) { DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); - DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; - DAC960_V2_CommandStatus_T CommandStatus; - DAC960_V2_ClearCommand(Command); - Command->CommandType = DAC960_ImmediateCommand; - CommandMailbox->LogicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL; - CommandMailbox->LogicalDeviceInfo.CommandControlBits + if (Command == NULL) { + return false; + } else { + DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; + DAC960_V2_CommandStatus_T CommandStatus; + DAC960_V2_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->LogicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL; + CommandMailbox->LogicalDeviceInfo.CommandControlBits .DataTransferControllerToHost = true; - CommandMailbox->LogicalDeviceInfo.CommandControlBits + CommandMailbox->LogicalDeviceInfo.CommandControlBits .NoAutoRequestSense = true; - CommandMailbox->LogicalDeviceInfo.DataTransferSize = DataByteCount; - CommandMailbox->LogicalDeviceInfo.LogicalDevice.LogicalDeviceNumber = - LogicalDeviceNumber; - CommandMailbox->LogicalDeviceInfo.IOCTL_Opcode = IOCTL_Opcode; - CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress + CommandMailbox->LogicalDeviceInfo.DataTransferSize = DataByteCount; + CommandMailbox->LogicalDeviceInfo.LogicalDevice.LogicalDeviceNumber = + LogicalDeviceNumber; + CommandMailbox->LogicalDeviceInfo.IOCTL_Opcode = IOCTL_Opcode; + CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentDataPointer = - Virtual_to_Bus64(DataPointer); - CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress + Virtual_to_Bus64(DataPointer); + CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentByteCount = - CommandMailbox->LogicalDeviceInfo.DataTransferSize; - DAC960_ExecuteCommand(Command); - CommandStatus = Command->V2.CommandStatus; - DAC960_DeallocateCommand(Command); - return (CommandStatus == DAC960_V2_NormalCompletion); + CommandMailbox->LogicalDeviceInfo.DataTransferSize; + DAC960_ExecuteCommand(Command); + CommandStatus = Command->V2.CommandStatus; + DAC960_DeallocateCommand(Command); + return (CommandStatus == DAC960_V2_NormalCompletion); + } } @@ -685,32 +705,36 @@ unsigned int DataByteCount) { DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); - DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; - DAC960_V2_CommandStatus_T CommandStatus; - DAC960_V2_ClearCommand(Command); - Command->CommandType = DAC960_ImmediateCommand; - CommandMailbox->PhysicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL; - CommandMailbox->PhysicalDeviceInfo.CommandControlBits + if (Command == NULL) { + return false; + } else { + DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; + DAC960_V2_CommandStatus_T CommandStatus; + DAC960_V2_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->PhysicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL; + CommandMailbox->PhysicalDeviceInfo.CommandControlBits .DataTransferControllerToHost = true; - CommandMailbox->PhysicalDeviceInfo.CommandControlBits + CommandMailbox->PhysicalDeviceInfo.CommandControlBits .NoAutoRequestSense = true; - CommandMailbox->PhysicalDeviceInfo.DataTransferSize = DataByteCount; - CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.LogicalUnit = LogicalUnit; - CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.TargetID = TargetID; - CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.Channel = Channel; - CommandMailbox->PhysicalDeviceInfo.IOCTL_Opcode = IOCTL_Opcode; - CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress + CommandMailbox->PhysicalDeviceInfo.DataTransferSize = DataByteCount; + CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.LogicalUnit = LogicalUnit; + CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.TargetID = TargetID; + CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.Channel = Channel; + CommandMailbox->PhysicalDeviceInfo.IOCTL_Opcode = IOCTL_Opcode; + CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentDataPointer = - Virtual_to_Bus64(DataPointer); - CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress + Virtual_to_Bus64(DataPointer); + CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentByteCount = - CommandMailbox->PhysicalDeviceInfo.DataTransferSize; - DAC960_ExecuteCommand(Command); - CommandStatus = Command->V2.CommandStatus; - DAC960_DeallocateCommand(Command); - return (CommandStatus == DAC960_V2_NormalCompletion); + CommandMailbox->PhysicalDeviceInfo.DataTransferSize; + DAC960_ExecuteCommand(Command); + CommandStatus = Command->V2.CommandStatus; + DAC960_DeallocateCommand(Command); + return (CommandStatus == DAC960_V2_NormalCompletion); + } } @@ -726,21 +750,25 @@ OperationDevice) { DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); - DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; - DAC960_V2_CommandStatus_T CommandStatus; - DAC960_V2_ClearCommand(Command); - Command->CommandType = DAC960_ImmediateCommand; - CommandMailbox->DeviceOperation.CommandOpcode = DAC960_V2_IOCTL; - CommandMailbox->DeviceOperation.CommandControlBits + if (Command == NULL) { + return false; + } else { + DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; + DAC960_V2_CommandStatus_T CommandStatus; + DAC960_V2_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->DeviceOperation.CommandOpcode = DAC960_V2_IOCTL; + CommandMailbox->DeviceOperation.CommandControlBits .DataTransferControllerToHost = true; - CommandMailbox->DeviceOperation.CommandControlBits + CommandMailbox->DeviceOperation.CommandControlBits .NoAutoRequestSense = true; - CommandMailbox->DeviceOperation.IOCTL_Opcode = IOCTL_Opcode; - CommandMailbox->DeviceOperation.OperationDevice = OperationDevice; - DAC960_ExecuteCommand(Command); - CommandStatus = Command->V2.CommandStatus; - DAC960_DeallocateCommand(Command); - return (CommandStatus == DAC960_V2_NormalCompletion); + CommandMailbox->DeviceOperation.IOCTL_Opcode = IOCTL_Opcode; + CommandMailbox->DeviceOperation.OperationDevice = OperationDevice; + DAC960_ExecuteCommand(Command); + CommandStatus = Command->V2.CommandStatus; + DAC960_DeallocateCommand(Command); + return (CommandStatus == DAC960_V2_NormalCompletion); + } } @@ -1425,20 +1453,27 @@ kmalloc(sizeof(DAC960_V2_PhysicalDeviceInfo_T), GFP_ATOMIC); if (PhysicalDeviceInfo == NULL) return DAC960_Failure(Controller, "PHYSICAL DEVICE ALLOCATION"); + InquiryUnitSerialNumber = (DAC960_SCSI_Inquiry_UnitSerialNumber_T *) + kmalloc(sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T), GFP_ATOMIC); + if (InquiryUnitSerialNumber == NULL) { + kfree(PhysicalDeviceInfo); + return DAC960_Failure(Controller, "SERIAL NUMBER ALLOCATION"); + } + Command = DAC960_AllocateCommand(Controller); + if (Command == NULL) { + kfree(PhysicalDeviceInfo); + kfree(InquiryUnitSerialNumber); + return DAC960_Failure(Controller, "COMMAND ALLOCATION"); + } Controller->V2.PhysicalDeviceInformation[PhysicalDeviceIndex] = PhysicalDeviceInfo; memcpy(PhysicalDeviceInfo, NewPhysicalDeviceInfo, sizeof(DAC960_V2_PhysicalDeviceInfo_T)); - InquiryUnitSerialNumber = (DAC960_SCSI_Inquiry_UnitSerialNumber_T *) - kmalloc(sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T), GFP_ATOMIC); - if (InquiryUnitSerialNumber == NULL) - return DAC960_Failure(Controller, "SERIAL NUMBER ALLOCATION"); Controller->V2.InquiryUnitSerialNumber[PhysicalDeviceIndex] = InquiryUnitSerialNumber; memset(InquiryUnitSerialNumber, 0, sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T)); InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F; - Command = DAC960_AllocateCommand(Controller); CommandMailbox = &Command->V2.CommandMailbox; DAC960_V2_ClearCommand(Command); Command->CommandType = DAC960_ImmediateCommand; @@ -4102,7 +4137,8 @@ if (InquiryUnitSerialNumber == NULL && PhysicalDeviceInfo != NULL) { - kfree(PhysicalDeviceInfo); + if (PhysicalDeviceInfo) + kfree(PhysicalDeviceInfo); PhysicalDeviceInfo = NULL; } DAC960_Critical("Physical Device %d:%d Now Exists%s\n", @@ -6606,5 +6642,5 @@ DAC960_Finalize(&DAC960_NotifierBlock, SYS_RESTART, NULL); } - +MODULE_LICENSE("GPL"); #endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/Makefile linux.ac/drivers/block/Makefile --- linux.vanilla/drivers/block/Makefile Sun Sep 9 20:00:55 2001 +++ linux.ac/drivers/block/Makefile Wed Oct 10 01:47:40 2001 @@ -10,7 +10,9 @@ O_TARGET := block.o -export-objs := ll_rw_blk.o blkpg.o loop.o DAC960.o genhd.o +mod-subdirs := paride + +export-objs := ll_rw_blk.o blkpg.o elevator.o loop.o DAC960.o genhd.o obj-y := ll_rw_blk.o blkpg.o genhd.o elevator.o @@ -27,11 +29,17 @@ obj-$(CONFIG_BLK_DEV_PS2) += ps2esdi.o obj-$(CONFIG_BLK_DEV_XD) += xd.o obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o -obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o +obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o subdir-$(CONFIG_PARIDE) += paride + +ifeq ($(CONFIG_ARCH_ACORN),y) +mod-subdirs += ../acorn/block +subdir-y += ../acorn/block +obj-y += ../acorn/block/acorn-block.o +endif include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/cciss.c linux.ac/drivers/block/cciss.c --- linux.vanilla/drivers/block/cciss.c Tue Sep 18 22:10:43 2001 +++ linux.ac/drivers/block/cciss.c Thu Oct 11 09:12:04 2001 @@ -1,5 +1,5 @@ /* - * Disk Array driver for Compaq SMART2 Controllers + * Disk Array driver for Compaq SA53xx Controllers * Copyright 2000 Compaq Computer Corporation * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,20 @@ * */ +/* +Thu Oct 4 14:29:15 CDT 2001 Changelog begins. +v. 2.4.21 * Added support for SCSI tape drives (Steve Cameron) + * Added support for dynamically adding and removing + logical volumes, This implies that the disk index + ("x" in /dev/cciss/c*b*dx*) is no longer a contiguous + sequential series (e.g. 0,1,2,3) as it now maps to logical + volume numbers directly and thus there may be gaps. + (Charles White) + + (Note, due to bug in sd driver, tape support will not work + with 2.4.10 kernel, 2.4.11-pre3 or better is required) +*/ + #include /* CONFIG_PROC_FS */ #include #include @@ -44,12 +58,12 @@ #include #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) -#define DRIVER_NAME "Compaq CISS Driver (v 2.4.5)" -#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,4,5) +#define DRIVER_NAME "Compaq CISS Driver (v 2.4.21)" +#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,4,21) /* Embedded module documentation macros - see modules.h */ MODULE_AUTHOR("Charles M. White III - Compaq Computer Corporation"); -MODULE_DESCRIPTION("Driver for Compaq Smart Array Controller 5300"); +MODULE_DESCRIPTION("Driver for Compaq SA53xx Controllers version 2.4.21"); MODULE_LICENSE("GPL"); #include "cciss_cmd.h" @@ -64,6 +78,8 @@ 0x0E11, 0x4080, 0, 0, 0}, { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSB, 0x0E11, 0x4082, 0, 0, 0}, + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSB, + 0x0E11, 0x4083, 0, 0, 0}, {0,} }; MODULE_DEVICE_TABLE(pci, cciss_pci_device_id); @@ -78,6 +94,7 @@ { 0x40700E11, "Smart Array 5300", &SA5_access }, { 0x40800E11, "Smart Array 5i", &SA5B_access}, { 0x40820E11, "Smart Array 532", &SA5B_access}, + { 0x40830E11, "Smart Array 5312", &SA5B_access}, }; /* How long to wait (in millesconds) for board to go into simple mode */ @@ -87,9 +104,14 @@ #define NR_CMDS 128 /* #commands that can be outstanding */ #define MAX_CTLR 8 -#define CCISS_DMA_MASK 0xFFFFFFFF /* 32 bit DMA */ +#if CONFIG_IA64 +#define CCISS_DMA_MASK 0xFFFFFFFFFFFFFFFF /* 64 bit DMA */ +#else /* X86 */ +#define CCISS_DMA_MASK 0xFFFFFFFF /* 32 bit DMA */ +#endif -static ctlr_info_t *hba[MAX_CTLR]; +static ctlr_info_t *hba[MAX_CTLR] = + { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static struct proc_dir_entry *proc_cciss; @@ -102,6 +124,8 @@ static int revalidate_allvol(kdev_t dev); static int revalidate_logvol(kdev_t dev, int maxusage); static int frevalidate_logvol(kdev_t dev); +static int deregister_disk(int ctlr, int logvol); +static int register_new_disk(int cltr,int logvol); static void cciss_getgeometry(int cntl_num); @@ -125,6 +149,8 @@ revalidate: frevalidate_logvol, }; +#include "cciss_scsi.c" /* For SCSI tape support */ + /* * Report information about this controller. */ @@ -145,6 +171,7 @@ " Memory Address: 0x%08lx\n" " IRQ: %d\n" " Logical drives: %d\n" + " Highest Logical Volume ID: %d\n" " Current Q depth: %d\n" " Current # commands on controller %d\n" " Max Q depth since init: %d\n" @@ -157,12 +184,16 @@ (unsigned long)h->vaddr, (unsigned int)h->intr, h->num_luns, + h->highest_lun, h->Qdepth, h->commands_outstanding, h->maxQsinceinit, h->max_outstanding, h->maxSG); pos += size; len += size; - for(i=0; inum_luns; i++) { + cciss_proc_tape_report(ctlr, buffer, &pos, &len); + for(i=0; i<=h->highest_lun; i++) { drv = &h->drv[i]; + if(drv->block_size == 0) + continue; size = sprintf(buffer+len, "cciss/c%dd%d: blksz=%d nr_blocks=%d\n", ctlr, i, drv->block_size, drv->nr_blocks); pos += size; len += size; @@ -180,20 +211,53 @@ return len; } +static int +cciss_proc_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned char cmd[80]; + int len; +#ifdef CONFIG_CISS_SCSI_TAPE + ctlr_info_t *h = (ctlr_info_t *) data; + int rc; +#endif + + if (count > sizeof(cmd)-1) return -EINVAL; + if (copy_from_user(cmd, buffer, count)) return -EFAULT; + cmd[count] = '\0'; + len = strlen(cmd); // safe??? + if (cmd[len-1] == '\n') + cmd[--len] = '\0'; +# ifdef CONFIG_CISS_SCSI_TAPE + if (strcmp("engage scsi", cmd)==0) { + rc = cciss_engage_scsi(h->ctlr); + if (rc != 0) return -rc; + return count; + } + /* might be nice to have "disengage" too, but it's not + safely possible. (only 1 module use count, lock issues.) */ +# endif + return -EINVAL; +} + /* * Get us a file in /proc/cciss that says something about each controller. * Create /proc/cciss if it doesn't exist yet. */ static void __init cciss_procinit(int i) { + struct proc_dir_entry *pde; + if (proc_cciss == NULL) { proc_cciss = proc_mkdir("cciss", proc_root_driver); if (!proc_cciss) return; } - create_proc_read_entry(hba[i]->devname, 0, proc_cciss, - cciss_proc_get_info, hba[i]); + pde = create_proc_read_entry(hba[i]->devname, + S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, + proc_cciss, cciss_proc_get_info, hba[i]); + pde->write_proc = cciss_proc_write; } #endif /* CONFIG_PROC_FS */ @@ -214,7 +278,7 @@ if (!get_from_pool) { c = (CommandList_struct *) pci_alloc_consistent( - h->pdev, sizeof(CommandList_struct), &cmd_dma_handle); + NULL, sizeof(CommandList_struct), &cmd_dma_handle); if(c==NULL) return NULL; memset(c, 0, sizeof(CommandList_struct)); @@ -225,7 +289,7 @@ if (c->err_info == NULL) { - pci_free_consistent(h->pdev, + pci_free_consistent(NULL, sizeof(CommandList_struct), c, cmd_dma_handle); return NULL; } @@ -277,7 +341,7 @@ temp64.val32.upper = c->ErrDesc.Addr.upper; pci_free_consistent(h->pdev, sizeof(ErrorInfo_struct), c->err_info, (dma_addr_t) temp64.val); - pci_free_consistent(h->pdev, sizeof(CommandList_struct), + pci_free_consistent(NULL, sizeof(CommandList_struct), c, (dma_addr_t) c->busaddr); } else { @@ -313,8 +377,8 @@ hba[ctlr]->hardsizes[ (i<block_size; } - hba[ctlr]->gendisk.nr_real++; } + hba[ctlr]->gendisk.nr_real = hba[ctlr]->highest_lun+1; } /* * Open. Make sure the device is really there. @@ -378,8 +442,6 @@ { int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR; int dsk = MINOR(inode->i_rdev) >> NWD_SHIFT; - int diskinfo[4]; - struct hd_geometry *geo = (struct hd_geometry *)arg; #ifdef CCISS_DEBUG printk(KERN_DEBUG "cciss_ioctl: Called with cmd=%x %lx\n", cmd, arg); @@ -387,19 +449,43 @@ switch(cmd) { case HDIO_GETGEO: - if (hba[ctlr]->drv[dsk].cylinders) { - diskinfo[0] = hba[ctlr]->drv[dsk].heads; - diskinfo[1] = hba[ctlr]->drv[dsk].sectors; - diskinfo[2] = hba[ctlr]->drv[dsk].cylinders; - } else { - diskinfo[0] = 0xff; - diskinfo[1] = 0x3f; - diskinfo[2] = hba[ctlr]->drv[dsk].nr_blocks / (0xff*0x3f); } - put_user(diskinfo[0], &geo->heads); - put_user(diskinfo[1], &geo->sectors); - put_user(diskinfo[2], &geo->cylinders); - put_user(hba[ctlr]->hd[MINOR(inode->i_rdev)].start_sect, &geo->start); - return 0; + { + struct hd_geometry driver_geo; + if (hba[ctlr]->drv[dsk].cylinders) { + driver_geo.heads = hba[ctlr]->drv[dsk].heads; + driver_geo.sectors = hba[ctlr]->drv[dsk].sectors; + driver_geo.cylinders = hba[ctlr]->drv[dsk].cylinders; + } else { + driver_geo.heads = 0xff; + driver_geo.sectors = 0x3f; + driver_geo.cylinders = hba[ctlr]->drv[dsk].nr_blocks / (0xff*0x3f); + } + driver_geo.start= + hba[ctlr]->hd[MINOR(inode->i_rdev)].start_sect; + if (copy_to_user((void *) arg, &driver_geo, + sizeof( struct hd_geometry))) + return -EFAULT; + return(0); + } + case HDIO_GETGEO_BIG: + { + struct hd_big_geometry driver_geo; + if (hba[ctlr]->drv[dsk].cylinders) { + driver_geo.heads = hba[ctlr]->drv[dsk].heads; + driver_geo.sectors = hba[ctlr]->drv[dsk].sectors; + driver_geo.cylinders = hba[ctlr]->drv[dsk].cylinders; + } else { + driver_geo.heads = 0xff; + driver_geo.sectors = 0x3f; + driver_geo.cylinders = hba[ctlr]->drv[dsk].nr_blocks / (0xff*0x3f); + } + driver_geo.start= + hba[ctlr]->hd[MINOR(inode->i_rdev)].start_sect; + if (copy_to_user((void *) arg, &driver_geo, + sizeof( struct hd_big_geometry))) + return -EFAULT; + return(0); + } case BLKGETSIZE: put_user(hba[ctlr]->hd[MINOR(inode->i_rdev)].nr_sects, (long*)arg); return 0; @@ -584,7 +670,15 @@ case CCISS_REVALIDVOLS: return( revalidate_allvol(inode->i_rdev)); - + + case CCISS_DEREGDISK: + return( deregister_disk(ctlr,dsk)); + + case CCISS_REGNEWDISK: + { + int logvol = arg; + return(register_new_disk(ctlr, logvol)); + } case CCISS_PASSTHRU: { IOCTL_Command_struct iocommand; @@ -817,8 +911,454 @@ return 0; } +static int deregister_disk(int ctlr, int logvol) +{ + unsigned long flags; + struct gendisk *gdev = &(hba[ctlr]->gendisk); + ctlr_info_t *h = hba[ctlr]; + int start, max_p, i; + + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + spin_lock_irqsave(&io_request_lock, flags); + /* make sure logical volume is NOT is use */ + if( h->drv[logvol].usage_count > 1) + { + spin_unlock_irqrestore(&io_request_lock, flags); + return -EBUSY; + } + h->drv[logvol].usage_count++; + spin_unlock_irqrestore(&io_request_lock, flags); + + /* invalidate the devices and deregister the disk */ + max_p = gdev->max_p; + start = logvol << gdev->minor_shift; + for (i=max_p-1; i>=0; i--) + { + int minor = start+i; + // printk("invalidating( %d %d)\n", ctlr, minor); + invalidate_device(MKDEV(MAJOR_NR+ctlr, minor), 1); + /* so open will now fail */ + h->sizes[minor] = 0; + } + /* check to see if it was the last disk */ + if (logvol == h->highest_lun) + { + /* if so, find the new hightest lun */ + int i, newhighest =-1; + for(i=0; ihighest_lun; i++) + { + /* if the disk has size > 0, it is available */ + if (h->sizes[i << gdev->minor_shift] != 0) + newhighest = i; + } + h->highest_lun = newhighest; + + } + --h->num_luns; + gdev->nr_real = h->highest_lun+1; + /* zero out the disk size info */ + h->drv[logvol].nr_blocks = 0; + h->drv[logvol].block_size = 0; + return(0); +} +static int sendcmd_withirq(__u8 cmd, + int ctlr, + void *buff, + size_t size, + unsigned int use_unit_num, + unsigned int log_unit, + __u8 page_code ) +{ + ctlr_info_t *h = hba[ctlr]; + CommandList_struct *c; + u64bit buff_dma_handle; + unsigned long flags; + int return_status = IO_OK; + + if ((c = cmd_alloc(h , 0)) == NULL) + { + return -ENOMEM; + } + // Fill in the command type + c->cmd_type = CMD_IOCTL_PEND; + // Fill in Command Header + c->Header.ReplyQueue = 0; // unused in simple mode + if( buff != NULL) // buffer to fill + { + c->Header.SGList = 1; + c->Header.SGTotal= 1; + } else // no buffers to fill + { + c->Header.SGList = 0; + c->Header.SGTotal= 0; + } + c->Header.Tag.lower = c->busaddr; // use the kernel address the cmd block for tag + // Fill in Request block + switch(cmd) + { + case CISS_INQUIRY: + /* If the logical unit number is 0 then, this is going + to controller so It's a physical command + mode = 0 target = 0. + So we have nothing to write. + Otherwise + mode = 1 target = LUNID + */ + if(use_unit_num != 0) + { + c->Header.LUN.LogDev.VolId= + hba[ctlr]->drv[log_unit].LunID; + c->Header.LUN.LogDev.Mode = 1; + } + if(page_code != 0) + { + c->Request.CDB[1] = 0x01; + c->Request.CDB[2] = page_code; + } + c->Request.CDBLen = 6; + c->Request.Type.Type = TYPE_CMD; // It is a command. + c->Request.Type.Attribute = ATTR_SIMPLE; + c->Request.Type.Direction = XFER_READ; // Read + c->Request.Timeout = 0; // Don't time out + c->Request.CDB[0] = CISS_INQUIRY; + c->Request.CDB[4] = size & 0xFF; + break; + case CISS_REPORT_LOG: + /* Talking to controller so It's a physical command + mode = 00 target = 0. + So we have nothing to write. + */ + c->Request.CDBLen = 12; + c->Request.Type.Type = TYPE_CMD; // It is a command. + c->Request.Type.Attribute = ATTR_SIMPLE; + c->Request.Type.Direction = XFER_READ; // Read + c->Request.Timeout = 0; // Don't time out + c->Request.CDB[0] = CISS_REPORT_LOG; + c->Request.CDB[6] = (size >> 24) & 0xFF; //MSB + c->Request.CDB[7] = (size >> 16) & 0xFF; + c->Request.CDB[8] = (size >> 8) & 0xFF; + c->Request.CDB[9] = size & 0xFF; + break; + case CCISS_READ_CAPACITY: + c->Header.LUN.LogDev.VolId= + hba[ctlr]->drv[log_unit].LunID; + c->Header.LUN.LogDev.Mode = 1; + c->Request.CDBLen = 10; + c->Request.Type.Type = TYPE_CMD; // It is a command. + c->Request.Type.Attribute = ATTR_SIMPLE; + c->Request.Type.Direction = XFER_READ; // Read + c->Request.Timeout = 0; // Don't time out + c->Request.CDB[0] = CCISS_READ_CAPACITY; + break; + default: + printk(KERN_WARNING + "cciss: Unknown Command 0x%c sent attempted\n", cmd); + cmd_free(h, c, 1); + return(IO_ERROR); + }; + + // Fill in the scatter gather information + if (size > 0 ) + { + buff_dma_handle.val = (__u64) pci_map_single( h->pdev, + buff, size, PCI_DMA_BIDIRECTIONAL); + c->SG[0].Addr.lower = buff_dma_handle.val32.lower; + c->SG[0].Addr.upper = buff_dma_handle.val32.upper; + c->SG[0].Len = size; + c->SG[0].Ext = 0; // we are not chaining + } + + /* Put the request on the tail of the queue and send it */ + spin_lock_irqsave(&io_request_lock, flags); + addQ(&h->reqQ, c); + h->Qdepth++; + start_io(h); + spin_unlock_irqrestore(&io_request_lock, flags); + + /* wait for completion */ + while(c->cmd_type != CMD_IOCTL_DONE) + schedule_timeout(1); + /* unlock the buffers from DMA */ + pci_unmap_single( h->pdev, (dma_addr_t) buff_dma_handle.val, + size, PCI_DMA_BIDIRECTIONAL); + + if(c->err_info->CommandStatus != 0) + { /* an error has occurred */ + switch(c->err_info->CommandStatus) + { + case CMD_TARGET_STATUS: + printk(KERN_WARNING "cciss: cmd %p has " + " completed with errors\n", c); + if( c->err_info->ScsiStatus) + { + printk(KERN_WARNING "cciss: cmd %p " + "has SCSI Status = %x\n", + c, + c->err_info->ScsiStatus); + } + + break; + case CMD_DATA_UNDERRUN: + case CMD_DATA_OVERRUN: + /* expected for inquire and report lun commands */ + break; + case CMD_INVALID: + printk(KERN_WARNING "cciss: cmd %p is " + "reported invalid\n", c); + return_status = IO_ERROR; + break; + case CMD_PROTOCOL_ERR: + printk(KERN_WARNING "cciss: cmd %p has " + "protocol error \n", c); + return_status = IO_ERROR; + break; +case CMD_HARDWARE_ERR: + printk(KERN_WARNING "cciss: cmd %p had " + " hardware error\n", c); + return_status = IO_ERROR; + break; + case CMD_CONNECTION_LOST: + printk(KERN_WARNING "cciss: cmd %p had " + "connection lost\n", c); + return_status = IO_ERROR; + break; + case CMD_ABORTED: + printk(KERN_WARNING "cciss: cmd %p was " + "aborted\n", c); + return_status = IO_ERROR; + break; + case CMD_ABORT_FAILED: + printk(KERN_WARNING "cciss: cmd %p reports " + "abort failed\n", c); + return_status = IO_ERROR; + break; + case CMD_UNSOLICITED_ABORT: + printk(KERN_WARNING "cciss: cmd %p aborted " + "do to an unsolicited abort\n", c); + return_status = IO_ERROR; + + + break; + default: + printk(KERN_WARNING "cciss: cmd %p returned " + "unknown status %x\n", c, + c->err_info->CommandStatus); + return_status = IO_ERROR; + } + } + cmd_free(h, c, 0); + return(return_status); + +} +static int register_new_disk(int ctlr, int logvol) +{ + struct gendisk *gdev = &(hba[ctlr]->gendisk); + ctlr_info_t *h = hba[ctlr]; + int start, max_p, i; + int num_luns; + int vol_found = 0; + ReportLunData_struct *ld_buff; + ReadCapdata_struct *size_buff; + InquiryData_struct *inq_buff; + int return_code; + int listlength = 0; + int lunid = 0; + unsigned int block_size; + unsigned int total_size; + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + if( (logvol < 0) || (logvol >= CISS_MAX_LUN)) + return -EINVAL; + + if(h->sizes[logvol << gdev->minor_shift] != 0 ) + return -EINVAL; + + ld_buff = kmalloc(sizeof(ReportLunData_struct), GFP_KERNEL); + if (ld_buff == NULL) + { + printk(KERN_ERR "cciss: out of memory\n"); + return -1; + } + memset(ld_buff, 0, sizeof(ReportLunData_struct)); + size_buff = kmalloc(sizeof( ReadCapdata_struct), GFP_KERNEL); + if (size_buff == NULL) + { + printk(KERN_ERR "cciss: out of memory\n"); + kfree(ld_buff); + return -1; + } + inq_buff = kmalloc(sizeof( InquiryData_struct), GFP_KERNEL); + if (inq_buff == NULL) + { + printk(KERN_ERR "cciss: out of memory\n"); + kfree(ld_buff); + kfree(size_buff); + return -1; + } + + return_code = sendcmd_withirq(CISS_REPORT_LOG, ctlr, ld_buff, + sizeof(ReportLunData_struct), 0, 0, 0 ); + + if( return_code == IO_OK) + { + + // printk("LUN Data\n--------------------------\n"); + + listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[0])) << 24; + listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[1])) << 16; + listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[2])) << 8; + listlength |= 0xff & (unsigned int)(ld_buff->LUNListLength[3]); + } else /* reading number of logical volumes failed */ + { + printk(KERN_WARNING "cciss: report logical volume" + " command failed\n"); + listlength = 0; + return -1; + } + num_luns = listlength / 8; // 8 bytes pre entry + if (num_luns > CISS_MAX_LUN) + { + num_luns = CISS_MAX_LUN; + } +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "Length = %x %x %x %x = %d\n", ld_buff->LUNListLength[0], + ld_buff->LUNListLength[1], ld_buff->LUNListLength[2], + ld_buff->LUNListLength[3], num_luns); +#endif + for(i=0; i< num_luns; i++) + { + int read_logvol; + lunid = (0xff & (unsigned int)(ld_buff->LUN[i][3])) << 24; + lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][2])) << 16; + lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][1])) << 8; + lunid |= 0xff & (unsigned int)(ld_buff->LUN[i][0]); + read_logvol = lunid & 0xff; + if (read_logvol > 15) + { + printk(KERN_WARNING "Logical Volume %d is out of range\n", + logvol); + continue; + } + if (read_logvol == logvol) + { +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "Logical Volume %d found \n", + logvol); +#endif + vol_found = 1; + break; + } + } + if (!vol_found) + { + printk(KERN_WARNING "Logical Volume %d not found\n", logvol); + return -1; + } + hba[ctlr]->drv[logvol].LunID = lunid; + /* there could be gaps in lun numbers, track hightest */ + if(hba[ctlr]->highest_lun < lunid) + hba[ctlr]->highest_lun = logvol; + + memset(size_buff, 0, sizeof(ReadCapdata_struct)); + return_code = sendcmd_withirq(CCISS_READ_CAPACITY, ctlr, size_buff, sizeof( ReadCapdata_struct), 1, logvol, 0 ); + if (return_code == IO_OK) + { + total_size = (0xff & + (unsigned int)(size_buff->total_size[0])) << 24; total_size |= (0xff & + (unsigned int)(size_buff->total_size[1])) << 16; total_size |= (0xff & + (unsigned int)(size_buff->total_size[2])) << 8; + total_size |= (0xff & (unsigned int) + (size_buff->total_size[3])); + total_size++; // command returns highest block address + block_size = (0xff & + (unsigned int)(size_buff->block_size[0])) << 24; block_size |= (0xff & + (unsigned int)(size_buff->block_size[1])) << 16; block_size |= (0xff & + (unsigned int)(size_buff->block_size[2])) << 8; + block_size |= (0xff & + (unsigned int)(size_buff->block_size[3])); + } else /* read capacity command failed */ + { + printk(KERN_WARNING "cciss: read capacity failed\n"); + total_size = block_size = 0; + } + printk(KERN_INFO " blocks= %d block_size= %d\n", + total_size, block_size); + /* Execute the command to read the disk geometry */ + memset(inq_buff, 0, sizeof(InquiryData_struct)); + return_code = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buff, + sizeof(InquiryData_struct), 1, logvol ,0xC1 ); + if (return_code == IO_OK) + { + if(inq_buff->data_byte[8] == 0xFF) + { + printk(KERN_WARNING "cciss: reading geometry failed, volume does not support reading geometry\n"); + + hba[ctlr]->drv[logvol].block_size = block_size; + hba[ctlr]->drv[logvol].nr_blocks = total_size; + hba[ctlr]->drv[logvol].heads = 255; + hba[ctlr]->drv[logvol].sectors = 32; // Sectors per track + hba[ctlr]->drv[logvol].cylinders = total_size / 255 / 32; + } else + { + + hba[ctlr]->drv[logvol].block_size = block_size; + hba[ctlr]->drv[logvol].nr_blocks = total_size; + hba[ctlr]->drv[logvol].heads = + inq_buff->data_byte[6]; + hba[ctlr]->drv[logvol].sectors = + inq_buff->data_byte[7]; + hba[ctlr]->drv[logvol].cylinders = + (inq_buff->data_byte[4] & 0xff) << 8; + hba[ctlr]->drv[logvol].cylinders += + inq_buff->data_byte[5]; + } + } + else /* Get geometry failed */ + { + + printk(KERN_WARNING "cciss: reading geometry failed, continuing with default geometry\n"); + + hba[ctlr]->drv[logvol].block_size = block_size; + hba[ctlr]->drv[logvol].nr_blocks = total_size; + hba[ctlr]->drv[logvol].heads = 255; + hba[ctlr]->drv[logvol].sectors = 32; // Sectors per track + hba[ctlr]->drv[logvol].cylinders = total_size / 255 / 32; + } + printk(KERN_INFO " heads= %d, sectors= %d, cylinders= %d\n\n", + hba[ctlr]->drv[logvol].heads, + hba[ctlr]->drv[logvol].sectors, + hba[ctlr]->drv[logvol].cylinders); + hba[ctlr]->drv[logvol].usage_count = 0; + max_p = gdev->max_p; + start = logvol<< gdev->minor_shift; + + for(i=max_p-1; i>=0; i--) { + int minor = start+i; + invalidate_device(MKDEV(MAJOR_NR + ctlr, minor), 1); + gdev->part[minor].start_sect = 0; + gdev->part[minor].nr_sects = 0; + + /* reset the blocksize so we can read the partition table */ + blksize_size[MAJOR_NR+ctlr][minor] = 1024; + } + + ++hba[ctlr]->num_luns; + gdev->nr_real = hba[ctlr]->highest_lun + 1; + /* setup partitions per disk */ + grok_partitions(gdev, logvol, MAX_PART, + hba[ctlr]->drv[logvol].nr_blocks); + + kfree(ld_buff); + kfree(size_buff); + kfree(inq_buff); + return (0); +} /* * Wait polling for a command to complete. * The memory mapped FIFO is polled for the completion. @@ -850,9 +1390,12 @@ int ctlr, void *buff, size_t size, - unsigned int use_unit_num, + unsigned int use_unit_num, /* 0: address the controller, + 1: address logical volume log_unit, + 2: periph device address is scsi3addr */ unsigned int log_unit, - __u8 page_code ) + __u8 page_code, + unsigned char *scsi3addr) { CommandList_struct *c; int i; @@ -886,15 +1429,23 @@ to controller so It's a physical command mode = 0 target = 0. So we have nothing to write. - Otherwise - mode = 1 target = LUNID + otherwise, if use_unit_num == 1, + mode = 1(volume set addressing) target = LUNID + otherwise, if use_unit_num == 2, + mode = 0(periph dev addr) target = scsi3addr */ - if(use_unit_num != 0) + if(use_unit_num == 1) { c->Header.LUN.LogDev.VolId= hba[ctlr]->drv[log_unit].LunID; c->Header.LUN.LogDev.Mode = 1; } + else if (use_unit_num == 2) + { + memcpy(c->Header.LUN.LunAddrBytes,scsi3addr,8); + c->Header.LUN.LogDev.Mode = 0; // phys dev addr + } + /* are we trying to read a vital product page */ if(page_code != 0) { @@ -910,6 +1461,7 @@ c->Request.CDB[4] = size & 0xFF; break; case CISS_REPORT_LOG: + case CISS_REPORT_PHYS: /* Talking to controller so It's a physical command mode = 00 target = 0. So we have nothing to write. @@ -919,7 +1471,7 @@ c->Request.Type.Attribute = ATTR_SIMPLE; c->Request.Type.Direction = XFER_READ; // Read c->Request.Timeout = 0; // Don't time out - c->Request.CDB[0] = CISS_REPORT_LOG; + c->Request.CDB[0] = cmd; c->Request.CDB[6] = (size >> 24) & 0xFF; //MSB c->Request.CDB[7] = (size >> 16) & 0xFF; c->Request.CDB[8] = (size >> 8) & 0xFF; @@ -937,6 +1489,15 @@ c->Request.Timeout = 0; // Don't time out c->Request.CDB[0] = CCISS_READ_CAPACITY; break; + case CCISS_CACHE_FLUSH: + c->Request.CDBLen = 12; + c->Request.Type.Type = TYPE_CMD; // It is a command. + c->Request.Type.Attribute = ATTR_SIMPLE; + c->Request.Type.Direction = XFER_WRITE; // No data + c->Request.Timeout = 0; // Don't time out + c->Request.CDB[0] = BMIC_WRITE; // BMIC Passthru + c->Request.CDB[6] = BMIC_CACHE_FLUSH; + break; default: printk(KERN_WARNING "cciss: Unknown Command 0x%c sent attempted\n", @@ -997,6 +1558,7 @@ ignore it */ if (((c->Request.CDB[0] == CISS_REPORT_LOG) || + (c->Request.CDB[0] == CISS_REPORT_PHYS) || (c->Request.CDB[0] == CISS_INQUIRY)) && ((c->err_info->CommandStatus == CMD_DATA_OVERRUN) || @@ -1146,17 +1708,32 @@ { /* an error has occurred */ switch(cmd->err_info->CommandStatus) { + unsigned char sense_key; case CMD_TARGET_STATUS: - printk(KERN_WARNING "cciss: cmd %p has " - " completed with errors\n", cmd); - if( cmd->err_info->ScsiStatus) - { - printk(KERN_WARNING "cciss: cmd %p " - "has SCSI Status = %x\n", - cmd, - cmd->err_info->ScsiStatus); - } - + status = 0; + + if( cmd->err_info->ScsiStatus == 0x02) + { + printk(KERN_WARNING "cciss: cmd %p " + "has CHECK CONDITION " + " byte 2 = 0x%x\n", cmd, + cmd->err_info->SenseInfo[2] + ); + /* check the sense key */ + sense_key = 0xf & + cmd->err_info->SenseInfo[2]; + /* no status or recovered error */ + if((sense_key == 0x0) || + (sense_key == 0x1)) + { + status = 1; + } + } else + { + printk(KERN_WARNING "cciss: cmd %p " + "has SCSI Status 0x%x\n", + cmd, cmd->err_info->ScsiStatus); + } break; case CMD_DATA_UNDERRUN: printk(KERN_WARNING "cciss: cmd %p has" @@ -1447,6 +2024,11 @@ } else if (c->cmd_type == CMD_IOCTL_PEND) { c->cmd_type = CMD_IOCTL_DONE; } +# ifdef CONFIG_CISS_SCSI_TAPE + else if (c->cmd_type == CMD_SCSI) { + complete_scsi_command(c, 0, a1); + } +# endif continue; } } @@ -1497,6 +2079,15 @@ } #endif /* CCISS_DEBUG */ +static void release_io_mem(ctlr_info_t *c) +{ + /* if IO mem was not protected do nothing */ + if( c->io_mem_addr == 0) + return; + release_region(c->io_mem_addr, c->io_mem_length); + c->io_mem_addr = 0; + c->io_mem_length = 0; +} static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) { ushort vendor_id, device_id, command; @@ -1526,16 +2117,48 @@ printk(KERN_ERR "cciss: Unable to set DMA mask\n"); return(-1); } - + (void) pci_read_config_word(pdev, PCI_COMMAND,&command); - (void) pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); - (void) pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, - &cache_line_size); - (void) pci_read_config_byte(pdev, PCI_LATENCY_TIMER, - &latency_timer); + (void) pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); + (void) pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, + &cache_line_size); + (void) pci_read_config_byte(pdev, PCI_LATENCY_TIMER, + &latency_timer); + (void) pci_read_config_dword(pdev, PCI_SUBSYSTEM_VENDOR_ID, + &board_id); - (void) pci_read_config_dword(pdev, PCI_SUBSYSTEM_VENDOR_ID, - &board_id); + /* check to see if controller has been disabled */ + if(!(command & 0x02)) + { + printk(KERN_WARNING "cciss: controller appears to be disabled\n"); + return(-1); + } + + /* search for our IO range so we can protect it */ + for(i=0; i<6; i++) + { + /* is this an IO range */ + if( pdev->resource[i].flags & 0x01 ) + { + c->io_mem_addr = pdev->resource[i].start; + c->io_mem_length = pdev->resource[i].end - + pdev->resource[i].start +1; +#ifdef CCISS_DEBUG + printk("IO value found base_addr[%d] %lx %lx\n", i, + c->io_mem_addr, c->io_mem_length); +#endif /* CCISS_DEBUG */ + /* register the IO range */ + if(!request_region( c->io_mem_addr, + c->io_mem_length, "cciss")) + { + printk(KERN_WARNING "cciss I/O memory range already in use addr=%lx length=%ld\n", + c->io_mem_addr, c->io_mem_length); + c->io_mem_addr= 0; + c->io_mem_length = 0; + } + break; + } + } #ifdef CCISS_DEBUG printk("vendor_id = %x\n", vendor_id); @@ -1557,7 +2180,7 @@ * table */ - c->paddr = addr[0] & 0xfffffff0; /* remove the addressing mode bits */ + c->paddr = addr[0] ; /* addressing mode bits already removed */ #ifdef CCISS_DEBUG printk("address 0 = %x\n", c->paddr); #endif /* CCISS_DEBUG */ @@ -1657,6 +2280,7 @@ int lunid = 0; int block_size; int total_size; + int max_luns; ld_buff = kmalloc(sizeof(ReportLunData_struct), GFP_KERNEL); if (ld_buff == NULL) @@ -1682,7 +2306,7 @@ } /* Get the firmware version */ return_code = sendcmd(CISS_INQUIRY, cntl_num, inq_buff, - sizeof(InquiryData_struct), 0, 0 ,0 ); + sizeof(InquiryData_struct), 0, 0 ,0, NULL); if (return_code == IO_OK) { hba[cntl_num]->firm_ver[0] = inq_buff->data_byte[32]; @@ -1696,7 +2320,7 @@ } /* Get the number of logical volumes */ return_code = sendcmd(CISS_REPORT_LOG, cntl_num, ld_buff, - sizeof(ReportLunData_struct), 0, 0, 0 ); + sizeof(ReportLunData_struct), 0, 0, 0, NULL ); if( return_code == IO_OK) { @@ -1726,23 +2350,40 @@ ld_buff->LUNListLength[1], ld_buff->LUNListLength[2], ld_buff->LUNListLength[3], hba[cntl_num]->num_luns); #endif /* CCISS_DEBUG */ - for(i=0; i< hba[cntl_num]->num_luns ; i++) + max_luns = hba[cntl_num]->num_luns; + hba[cntl_num]->highest_lun = -1; + + for(i=0; i< max_luns; i++) { + int logvol; + lunid = (0xff & (unsigned int)(ld_buff->LUN[i][3])) << 24; lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][2])) << 16; lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][1])) << 8; lunid |= 0xff & (unsigned int)(ld_buff->LUN[i][0]); - hba[cntl_num]->drv[i].LunID = lunid; + logvol = lunid & 0xff; + if (logvol > 15) + { + printk(KERN_WARNING "Logical Volume %d is out of range\n", + logvol); + --hba[cntl_num]->num_luns; + continue; + } + hba[cntl_num]->drv[logvol].LunID = lunid; + + /* there could be gaps in lun numbers, track hightest */ + if(hba[cntl_num]->highest_lun < lunid) + hba[cntl_num]->highest_lun = logvol; #ifdef CCISS_DEBUG printk(KERN_DEBUG "LUN[%d]: %x %x %x %x = %x\n", i, ld_buff->LUN[i][0], ld_buff->LUN[i][1],ld_buff->LUN[i][2], - ld_buff->LUN[i][3], hba[cntl_num]->drv[i].LunID); + ld_buff->LUN[i][3], hba[cntl_num]->drv[logvol].LunID); #endif /* CCISS_DEBUG */ memset(size_buff, 0, sizeof(ReadCapdata_struct)); return_code = sendcmd(CCISS_READ_CAPACITY, cntl_num, size_buff, - sizeof( ReadCapdata_struct), 1, i, 0 ); + sizeof( ReadCapdata_struct), 1, logvol, 0, NULL); if (return_code == IO_OK) { total_size = (0xff & @@ -1774,29 +2415,29 @@ /* Execute the command to read the disk geometry */ memset(inq_buff, 0, sizeof(InquiryData_struct)); return_code = sendcmd(CISS_INQUIRY, cntl_num, inq_buff, - sizeof(InquiryData_struct), 1, i ,0xC1 ); + sizeof(InquiryData_struct), 1, logvol ,0xC1, NULL ); if (return_code == IO_OK) { if(inq_buff->data_byte[8] == 0xFF) { printk(KERN_WARNING "cciss: reading geometry failed, volume does not support reading geometry\n"); - hba[cntl_num]->drv[i].block_size = block_size; - hba[cntl_num]->drv[i].nr_blocks = total_size; - hba[cntl_num]->drv[i].heads = 255; - hba[cntl_num]->drv[i].sectors = 32; // Sectors per track - hba[cntl_num]->drv[i].cylinders = total_size / 255 / 32; } else + hba[cntl_num]->drv[logvol].block_size = block_size; + hba[cntl_num]->drv[logvol].nr_blocks = total_size; + hba[cntl_num]->drv[logvol].heads = 255; + hba[cntl_num]->drv[logvol].sectors = 32; // Sectors per track + hba[cntl_num]->drv[logvol].cylinders = total_size / 255 / 32; } else { - hba[cntl_num]->drv[i].block_size = block_size; - hba[cntl_num]->drv[i].nr_blocks = total_size; - hba[cntl_num]->drv[i].heads = + hba[cntl_num]->drv[logvol].block_size = block_size; + hba[cntl_num]->drv[logvol].nr_blocks = total_size; + hba[cntl_num]->drv[logvol].heads = inq_buff->data_byte[6]; - hba[cntl_num]->drv[i].sectors = + hba[cntl_num]->drv[logvol].sectors = inq_buff->data_byte[7]; - hba[cntl_num]->drv[i].cylinders = + hba[cntl_num]->drv[logvol].cylinders = (inq_buff->data_byte[4] & 0xff) << 8; - hba[cntl_num]->drv[i].cylinders += + hba[cntl_num]->drv[logvol].cylinders += inq_buff->data_byte[5]; } } @@ -1804,20 +2445,21 @@ { printk(KERN_WARNING "cciss: reading geometry failed, continuing with default geometry\n"); - hba[cntl_num]->drv[i].block_size = block_size; - hba[cntl_num]->drv[i].nr_blocks = total_size; - hba[cntl_num]->drv[i].heads = 255; - hba[cntl_num]->drv[i].sectors = 32; // Sectors per track - hba[cntl_num]->drv[i].cylinders = total_size / 255 / 32; + hba[cntl_num]->drv[logvol].block_size = block_size; + hba[cntl_num]->drv[logvol].nr_blocks = total_size; + hba[cntl_num]->drv[logvol].heads = 255; + hba[cntl_num]->drv[logvol].sectors = 32; // Sectors per track + hba[cntl_num]->drv[logvol].cylinders = total_size / 255 / 32; } printk(KERN_INFO " heads= %d, sectors= %d, cylinders= %d\n\n", - hba[cntl_num]->drv[i].heads, - hba[cntl_num]->drv[i].sectors, - hba[cntl_num]->drv[i].cylinders); + hba[cntl_num]->drv[logvol].heads, + hba[cntl_num]->drv[logvol].sectors, + hba[cntl_num]->drv[logvol].cylinders); } kfree(ld_buff); kfree(size_buff); + kfree(inq_buff); } /* Function to find the first free pointer into our hba[] array */ @@ -1871,6 +2513,7 @@ memset(hba[i], 0, sizeof(ctlr_info_t)); if (cciss_pci_init(hba[i], pdev) != 0) { + release_io_mem(hba[i]); free_hba(i); return (-1); } @@ -1882,6 +2525,7 @@ { printk(KERN_ERR "cciss: Unable to get major number " "%d for %s\n", MAJOR_NR+i, hba[i]->devname); + release_io_mem(hba[i]); free_hba(i); return(-1); } @@ -1893,13 +2537,14 @@ printk(KERN_ERR "ciss: Unable to get irq %d for %s\n", hba[i]->intr, hba[i]->devname); unregister_blkdev( MAJOR_NR+i, hba[i]->devname); + release_io_mem(hba[i]); free_hba(i); return(-1); } hba[i]->cmd_pool_bits = (__u32*)kmalloc( ((NR_CMDS+31)/32)*sizeof(__u32), GFP_KERNEL); hba[i]->cmd_pool = (CommandList_struct *)pci_alloc_consistent( - hba[i]->pdev, NR_CMDS * sizeof(CommandList_struct), + NULL, NR_CMDS * sizeof(CommandList_struct), &(hba[i]->cmd_pool_dhandle)); hba[i]->errinfo_pool = (ErrorInfo_struct *)pci_alloc_consistent( hba[i]->pdev, NR_CMDS * sizeof( ErrorInfo_struct), @@ -1911,7 +2556,7 @@ if(hba[i]->cmd_pool_bits) kfree(hba[i]->cmd_pool_bits); if(hba[i]->cmd_pool) - pci_free_consistent(hba[i]->pdev, + pci_free_consistent(NULL, NR_CMDS * sizeof(CommandList_struct), hba[i]->cmd_pool, hba[i]->cmd_pool_dhandle); if(hba[i]->errinfo_pool) @@ -1921,6 +2566,7 @@ hba[i]->errinfo_pool_dhandle); free_irq(hba[i]->intr, hba[i]); unregister_blkdev(MAJOR_NR+i, hba[i]->devname); + release_io_mem(hba[i]); free_hba(i); printk( KERN_ERR "cciss: out of memory"); return(-1); @@ -1939,6 +2585,8 @@ cciss_getgeometry(i); + cciss_find_non_disk_devices(i); /* find our tape drives, if any */ + /* Turn the interrupts on so we can service requests */ hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON); @@ -1967,7 +2615,7 @@ hba[i]->gendisk.max_p = MAX_PART; hba[i]->gendisk.part = hba[i]->hd; hba[i]->gendisk.sizes = hba[i]->sizes; - hba[i]->gendisk.nr_real = hba[i]->num_luns; + hba[i]->gendisk.nr_real = hba[i]->highest_lun+1; /* Get on the disk list */ add_gendisk(&(hba[i]->gendisk)); @@ -1979,6 +2627,8 @@ MAX_PART, &cciss_fops, hba[i]->drv[j].nr_blocks); + cciss_register_scsi(i, 1); /* hook ourself into SCSI subsystem */ + return(1); } @@ -1986,6 +2636,8 @@ { ctlr_info_t *tmp_ptr; int i; + char flush_buf[4]; + int return_code; if (pdev->driver_data == NULL) { @@ -2000,11 +2652,20 @@ "already be removed \n"); return; } - /* Turn board interrupts off */ - hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF); + /* Turn board interrupts off and send the flush cache command */ + /* sendcmd will turn off interrupt, and send the flush... + * To write all data in the battery backed cache to disks */ + memset(flush_buf, 0, 4); + return_code = sendcmd(CCISS_CACHE_FLUSH, i, flush_buf, 4, 0, 0, 0, NULL); + if(return_code != IO_OK) + { + printk(KERN_WARNING "Error Flushing cache on controller %d\n", + i); + } free_irq(hba[i]->intr, hba[i]); pdev->driver_data = NULL; iounmap((void*)hba[i]->vaddr); + cciss_unregister_scsi(i); /* unhook from SCSI subsystem */ unregister_blkdev(MAJOR_NR+i, hba[i]->devname); remove_proc_entry(hba[i]->devname, proc_cciss); @@ -2012,11 +2673,12 @@ /* remove it from the disk list */ del_gendisk(&(hba[i]->gendisk)); - pci_free_consistent(hba[i]->pdev, NR_CMDS * sizeof(CommandList_struct), + pci_free_consistent(NULL, NR_CMDS * sizeof(CommandList_struct), hba[i]->cmd_pool, hba[i]->cmd_pool_dhandle); pci_free_consistent(hba[i]->pdev, NR_CMDS * sizeof( ErrorInfo_struct), hba[i]->errinfo_pool, hba[i]->errinfo_pool_dhandle); kfree(hba[i]->cmd_pool_bits); + release_io_mem(hba[i]); free_hba(i); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/cciss.h linux.ac/drivers/block/cciss.h --- linux.vanilla/drivers/block/cciss.h Tue May 22 18:23:16 2001 +++ linux.ac/drivers/block/cciss.h Fri Oct 12 12:43:38 2001 @@ -51,6 +51,8 @@ __u32 board_id; ulong vaddr; __u32 paddr; + unsigned long io_mem_addr; + unsigned long io_mem_length; CfgTable_struct *cfgtable; int intr; @@ -58,6 +60,7 @@ int commands_outstanding; int max_outstanding; /* Debug */ int num_luns; + int highest_lun; int usage_count; /* number of opens all all minor devices */ // information about each logical volume @@ -88,6 +91,9 @@ int sizes[256]; int blocksizes[256]; int hardsizes[256]; +#ifdef CONFIG_CISS_SCSI_TAPE + void *scsi_ctlr; /* ptr to structure containing scsi related stuff */ +#endif }; /* Defining the diffent access_menthods */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/cciss_cmd.h linux.ac/drivers/block/cciss_cmd.h --- linux.vanilla/drivers/block/cciss_cmd.h Tue May 22 18:23:16 2001 +++ linux.ac/drivers/block/cciss_cmd.h Thu Oct 11 09:12:04 2001 @@ -89,6 +89,7 @@ //STRUCTURES //########################################################################### #define CISS_MAX_LUN 16 +#define CISS_MAX_PHYS_LUN 1024 // SCSI-3 Cmmands #pragma pack(1) @@ -101,6 +102,7 @@ } InquiryData_struct; #define CISS_REPORT_LOG 0xc2 /* Report Logical LUNs */ +#define CISS_REPORT_PHYS 0xc3 /* Report Physical LUNs */ // Data returned typedef struct _ReportLUNdata_struct { @@ -122,6 +124,12 @@ #define CCISS_READ 0x28 // Read(10) #define CCISS_WRITE 0x2a // Write(10) +// BMIC commands +#define BMIC_READ 0x26 +#define BMIC_WRITE 0x27 +#define BMIC_CACHE_FLUSH 0xc2 +#define CCISS_CACHE_FLUSH 0x01 //C2 was already being used by CCISS + //Command List Structure typedef union _SCSI3Addr_struct { struct { @@ -215,6 +223,9 @@ #define CMD_RWREQ 0x00 #define CMD_IOCTL_PEND 0x01 #define CMD_IOCTL_DONE 0x02 +#define CMD_SCSI 0x03 +#define CMD_MSG_DONE 0x04 +#define CMD_MSG_TIMEOUT 0x05 typedef struct _CommandList_struct { CommandListHeader_struct Header; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/cciss_scsi.c linux.ac/drivers/block/cciss_scsi.c --- linux.vanilla/drivers/block/cciss_scsi.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/block/cciss_scsi.c Thu Oct 11 09:12:04 2001 @@ -0,0 +1,1606 @@ +/* + * Disk Array driver for Compaq SA53xx Controllers, SCSI Tape module + * Copyright 2001 Compaq Computer 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Questions/Comments/Bugfixes to arrays@compaq.com + * + * Author: Stephen M. Cameron + */ +#ifdef CONFIG_CISS_SCSI_TAPE + +/* Here we have code to present the driver as a scsi driver + as it is simultaneously presented as a block driver. The + reason for doing this is to allow access to SCSI tape drives + through the array controller. Note in particular, neither + physical nor logical disks are presented through the scsi layer. */ + +#include "../scsi/scsi.h" +#include "../scsi/hosts.h" +#include +#include + +#include "cciss_scsi.h" + +/* some prototypes... */ +static int sendcmd( + __u8 cmd, + int ctlr, + void *buff, + size_t size, + unsigned int use_unit_num, /* 0: address the controller, + 1: address logical volume log_unit, + 2: address is in scsi3addr */ + unsigned int log_unit, + __u8 page_code, + unsigned char *scsi3addr ); + + +int __init cciss_scsi_detect(Scsi_Host_Template *tpnt); +int cciss_scsi_release(struct Scsi_Host *sh); +const char *cciss_scsi_info(struct Scsi_Host *sa); + +int cciss_scsi_proc_info( + char *buffer, /* data buffer */ + char **start, /* where data in buffer starts */ + off_t offset, /* offset from start of imaginary file */ + int length, /* length of data in buffer */ + int hostnum, /* which host adapter (always zero for me) */ + int func); /* 0 == read, 1 == write */ + +int cciss_scsi_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)); +#if 0 +int cciss_scsi_abort(Scsi_Cmnd *cmd); +#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS +int cciss_scsi_reset(Scsi_Cmnd *cmd, unsigned int reset_flags); +#else +int cciss_scsi_reset(Scsi_Cmnd *cmd); +#endif +#endif + +static struct cciss_scsi_hba_t ccissscsi[MAX_CTLR] = { + { name: "cciss0", ndevices: 0 }, + { name: "cciss1", ndevices: 0 }, + { name: "cciss2", ndevices: 0 }, + { name: "cciss3", ndevices: 0 }, + { name: "cciss4", ndevices: 0 }, + { name: "cciss5", ndevices: 0 }, + { name: "cciss6", ndevices: 0 }, + { name: "cciss7", ndevices: 0 }, +}; + +/* We need one Scsi_Host_Template *per controller* instead of + the usual one Scsi_Host_Template per controller *type*. This + is so PCI hot plug could have a remote possibility of still + working even with the SCSI system. It's so + scsi_unregister_module will differentiate the controllers. + When register_scsi_module is called, each host template is + customized (name change) in cciss_register_scsi() + (that's called from cciss.c:cciss_init_one()) */ + +static +Scsi_Host_Template driver_template[MAX_CTLR] = +{ + CCISS_SCSI, CCISS_SCSI, CCISS_SCSI, CCISS_SCSI, + CCISS_SCSI, CCISS_SCSI, CCISS_SCSI, CCISS_SCSI, +}; + +#pragma pack(1) +struct cciss_scsi_cmd_stack_elem_t { + CommandList_struct cmd; + ErrorInfo_struct Err; +}; + +#pragma pack() + +#define CMD_STACK_SIZE (SCSI_CCISS_CAN_QUEUE * \ + CCISS_MAX_SCSI_DEVS_PER_HBA + 2) + // plus two for init time usage + +#pragma pack(1) +struct cciss_scsi_cmd_stack_t { + struct cciss_scsi_cmd_stack_elem_t *pool; + struct cciss_scsi_cmd_stack_elem_t *elem[CMD_STACK_SIZE]; + dma_addr_t cmd_pool_handle; + int top; +}; +#pragma pack() + +struct cciss_scsi_adapter_data_t { + struct Scsi_Host *scsi_host; + struct cciss_scsi_cmd_stack_t cmd_stack; + int registered; + spinlock_t lock; // to protect ccissscsi[ctlr]; +}; +#if 1 +#define CPQ_TAPE_LOCK(ctlr, flags) spin_lock_irqsave( \ + &(((struct cciss_scsi_adapter_data_t *) \ + hba[ctlr]->scsi_ctlr)->lock), flags); +#define CPQ_TAPE_UNLOCK(ctlr, flags) spin_unlock_irqrestore( \ + &(((struct cciss_scsi_adapter_data_t *) \ + hba[ctlr]->scsi_ctlr)->lock), flags); +#else +#define CPQ_TAPE_LOCK(x,y) +#define CPQ_TAPE_UNLOCK(x,y) +#endif + +static CommandList_struct * +scsi_cmd_alloc(ctlr_info_t *h) +{ + /* assume only one process in here at a time, locking done by caller. */ + + /* take the top memory chunk off the stack and return it, if any. */ + struct cciss_scsi_cmd_stack_elem_t *c; + struct cciss_scsi_adapter_data_t *sa; + struct cciss_scsi_cmd_stack_t *stk; + u64bit temp64; + + sa = (struct cciss_scsi_adapter_data_t *) h->scsi_ctlr; + stk = &sa->cmd_stack; + + if (stk->top < 0) + return NULL; + c = stk->elem[stk->top]; + memset(c, 0, sizeof(*c)); + /* set physical addr of cmd and addr of scsi parameters */ + c->cmd.busaddr = (__u32) (stk->cmd_pool_handle + + (sizeof(struct cciss_scsi_cmd_stack_elem_t)*stk->top)); + + temp64.val = (__u64) (stk->cmd_pool_handle + + (sizeof(struct cciss_scsi_cmd_stack_elem_t)*stk->top) + + sizeof(CommandList_struct)); + stk->top--; + c->cmd.ErrDesc.Addr.lower = temp64.val32.lower; + c->cmd.ErrDesc.Addr.upper = temp64.val32.upper; + c->cmd.ErrDesc.Len = sizeof(ErrorInfo_struct); + + c->cmd.ctlr = h->ctlr; + c->cmd.err_info = &c->Err; + + return (CommandList_struct *) c; +} + +static void +scsi_cmd_free(ctlr_info_t *h, CommandList_struct *cmd) +{ + /* assume only one process in here at a time, locking done by caller. */ + /* drop the free memory chunk on top of the stack. */ + + struct cciss_scsi_adapter_data_t *sa; + struct cciss_scsi_cmd_stack_t *stk; + + sa = (struct cciss_scsi_adapter_data_t *) h->scsi_ctlr; + stk = &sa->cmd_stack; + if (stk->top >= CMD_STACK_SIZE) { + printk("cciss: scsi_cmd_free called too many times.\n"); + BUG(); + } + stk->top++; + stk->elem[stk->top] = (struct cciss_scsi_cmd_stack_elem_t *) cmd; +} + +static int +scsi_cmd_stack_setup(int ctlr) +{ + int i; + struct cciss_scsi_adapter_data_t *sa; + struct cciss_scsi_cmd_stack_t *stk; + size_t size; + + sa = (struct cciss_scsi_adapter_data_t *) hba[ctlr]->scsi_ctlr; + stk = &sa->cmd_stack; + size = sizeof(struct cciss_scsi_cmd_stack_elem_t) * CMD_STACK_SIZE; + stk->pool = (struct cciss_scsi_cmd_stack_elem_t *) + pci_alloc_consistent(hba[ctlr]->pdev, size, + &stk->cmd_pool_handle); + if (stk->pool == NULL) + { + printk("stk->pool is null\n"); + return -1; + } + + for (i=0; ielem[i] = &stk->pool[i]; + stk->top = CMD_STACK_SIZE-1; + return 0; +} + +static void +scsi_cmd_stack_free(int ctlr) +{ + struct cciss_scsi_adapter_data_t *sa; + struct cciss_scsi_cmd_stack_t *stk; + size_t size; + + sa = (struct cciss_scsi_adapter_data_t *) hba[ctlr]->scsi_ctlr; + stk = &sa->cmd_stack; + if (stk->top != CMD_STACK_SIZE-1) { + printk( "cciss: %d scsi commands are still outstanding.\n", + CMD_STACK_SIZE - stk->top); + // BUG(); + printk("WE HAVE A BUG HERE!!! stk=0x%08x\n", + (unsigned int) stk); + } + size = sizeof(struct cciss_scsi_cmd_stack_elem_t) * CMD_STACK_SIZE; + + pci_free_consistent(hba[ctlr]->pdev, size, stk->pool, + stk->cmd_pool_handle); + stk->pool = NULL; +} + +/* scsi_device_types comes from scsi.h */ +#define DEVICETYPE(n) (n<0 || n>MAX_SCSI_DEVICE_CODE) ? \ + "Unknown" : scsi_device_types[n] + +#if 0 +static int xmargin=8; +static int amargin=60; + +static void +print_bytes (unsigned char *c, int len, int hex, int ascii) +{ + + int i; + unsigned char *x; + + if (hex) + { + x = c; + for (i=0;i0) printk("\n"); + if ((i % xmargin) == 0) printk("0x%04x:", i); + printk(" %02x", *x); + x++; + } + printk("\n"); + } + if (ascii) + { + x = c; + for (i=0;i0) printk("\n"); + if ((i % amargin) == 0) printk("0x%04x:", i); + if (*x > 26 && *x < 128) printk("%c", *x); + else printk("."); + x++; + } + printk("\n"); + } +} + +static void +print_cmd(CommandList_struct *cp) +{ + printk("queue:%d\n", cp->Header.ReplyQueue); + printk("sglist:%d\n", cp->Header.SGList); + printk("sgtot:%d\n", cp->Header.SGTotal); + printk("Tag:0x%08x/0x%08x\n", cp->Header.Tag.upper, + cp->Header.Tag.lower); + printk("LUN:0x%02x%02x%02x%02x%02x%02x%02x%02x\n", + cp->Header.LUN.LunAddrBytes[0], + cp->Header.LUN.LunAddrBytes[1], + cp->Header.LUN.LunAddrBytes[2], + cp->Header.LUN.LunAddrBytes[3], + cp->Header.LUN.LunAddrBytes[4], + cp->Header.LUN.LunAddrBytes[5], + cp->Header.LUN.LunAddrBytes[6], + cp->Header.LUN.LunAddrBytes[7]); + printk("CDBLen:%d\n", cp->Request.CDBLen); + printk("Type:%d\n",cp->Request.Type.Type); + printk("Attr:%d\n",cp->Request.Type.Attribute); + printk(" Dir:%d\n",cp->Request.Type.Direction); + printk("Timeout:%d\n",cp->Request.Timeout); + printk( "CDB: %02x %02x %02x %02x %02x %02x %02x %02x" + " %02x %02x %02x %02x %02x %02x %02x %02x\n", + cp->Request.CDB[0], cp->Request.CDB[1], + cp->Request.CDB[2], cp->Request.CDB[3], + cp->Request.CDB[4], cp->Request.CDB[5], + cp->Request.CDB[6], cp->Request.CDB[7], + cp->Request.CDB[8], cp->Request.CDB[9], + cp->Request.CDB[10], cp->Request.CDB[11], + cp->Request.CDB[12], cp->Request.CDB[13], + cp->Request.CDB[14], cp->Request.CDB[15]), + printk("edesc.Addr: 0x%08x/0%08x, Len = %d\n", + cp->ErrDesc.Addr.upper, cp->ErrDesc.Addr.lower, + cp->ErrDesc.Len); + printk("sgs..........Errorinfo:\n"); + printk("scsistatus:%d\n", cp->err_info->ScsiStatus); + printk("senselen:%d\n", cp->err_info->SenseLen); + printk("cmd status:%d\n", cp->err_info->CommandStatus); + printk("resid cnt:%d\n", cp->err_info->ResidualCnt); + printk("offense size:%d\n", cp->err_info->MoreErrInfo.Invalid_Cmd.offense_size); + printk("offense byte:%d\n", cp->err_info->MoreErrInfo.Invalid_Cmd.offense_num); + printk("offense value:%d\n", cp->err_info->MoreErrInfo.Invalid_Cmd.offense_value); + +} + +#endif + +static int +find_bus_target_lun(int ctlr, int *bus, int *target, int *lun) +{ + /* finds an unused bus, target, lun for a new device */ + /* assumes hba[ctlr]->scsi_ctlr->lock is held */ + int i, found=0; + unsigned char target_taken[CCISS_MAX_SCSI_DEVS_PER_HBA]; + + memset(&target_taken[0], 0, CCISS_MAX_SCSI_DEVS_PER_HBA); + + target_taken[SELF_SCSI_ID] = 1; + for (i=0;iscsi_ctlr->lock is held */ + int n = ccissscsi[ctlr].ndevices; + struct cciss_scsi_dev_t *sd; + + if (n >= CCISS_MAX_SCSI_DEVS_PER_HBA) { + printk("cciss%d: Too many devices, " + "some will be inaccessible.\n", ctlr); + return -1; + } + sd = &ccissscsi[ctlr].dev[n]; + if (find_bus_target_lun(ctlr, &sd->bus, &sd->target, &sd->lun) != 0) + return -1; + memcpy(&sd->scsi3addr[0], scsi3addr, 8); + sd->devtype = devtype; + ccissscsi[ctlr].ndevices++; + + /* initially, (before registering with scsi layer) we don't + know our hostno and we don't want to print anything first + time anyway (the scsi layer's inquiries will show that info) */ + if (hostno != -1) + printk("cciss%d: %s device c%db%dt%dl%d added.\n", + ctlr, DEVICETYPE(sd->devtype), hostno, + sd->bus, sd->target, sd->lun); + return 0; +} + +static void +cciss_scsi_remove_entry(int ctlr, int hostno, int entry) +{ + /* assumes hba[ctlr]->scsi_ctlr->lock is held */ + int i; + struct cciss_scsi_dev_t sd; + + if (entry < 0 || entry >= CCISS_MAX_SCSI_DEVS_PER_HBA) return; + sd = ccissscsi[ctlr].dev[entry]; + for (i=entry;iscsi3addr)) { + if (sd[j].devtype == csd->devtype) + found=2; + else + found=1; + break; + } + } + + if (found == 0) { /* device no longer present. */ + changes++; + /* printk("cciss%d: %s device c%db%dt%dl%d removed.\n", + ctlr, DEVICETYPE(csd->devtype), hostno, + csd->bus, csd->target, csd->lun); */ + cciss_scsi_remove_entry(ctlr, hostno, i); + /* note, i not incremented */ + } + else if (found == 1) { /* device is different kind */ + changes++; + printk("cciss%d: device c%db%dt%dl%d type changed " + "(device type now %s).\n", + ctlr, hostno, csd->bus, csd->target, csd->lun, + DEVICETYPE(csd->devtype)); + csd->devtype = sd[j].devtype; + i++; /* so just move along. */ + } else /* device is same as it ever was, */ + i++; /* so just move along. */ + } + + /* Now, make sure every device listed in sd[] is also + listed in ccissscsi[], adding them if they aren't found */ + + for (i=0;iscsi3addr)) { + if (sd[i].devtype == csd->devtype) + found=2; /* found device */ + else + found=1; /* found a bug. */ + break; + } + } + if (!found) { + changes++; + if (cciss_scsi_add_entry(ctlr, hostno, + &sd[i].scsi3addr[0], sd[i].devtype) != 0) + break; + } else if (found == 1) { + /* should never happen... */ + changes++; + printk("cciss%d: device unexpectedly changed type\n", + ctlr); + /* but if it does happen, we just ignore that device */ + } + } + CPQ_TAPE_UNLOCK(ctlr, flags); + + if (!changes) + printk("cciss%d: No device changes detected.\n", ctlr); + + return 0; +} + +static int +lookup_scsi3addr(int ctlr, int bus, int target, int lun, char *scsi3addr) +{ + int i; + struct cciss_scsi_dev_t *sd; + unsigned long flags; + + CPQ_TAPE_LOCK(ctlr, flags); + for (i=0;ibus == bus && + sd->target == target && + sd->lun == lun) { + memcpy(scsi3addr, &sd->scsi3addr[0], 8); + CPQ_TAPE_UNLOCK(ctlr, flags); + return 0; + } + } + CPQ_TAPE_UNLOCK(ctlr, flags); + return -1; +} + + +static void +cciss_find_non_disk_devices(int cntl_num) +{ + ReportLunData_struct *ld_buff; + InquiryData_struct *inq_buff; + int return_code; + int i; + int listlength = 0; + int num_luns; + unsigned char scsi3addr[8]; + unsigned long flags; + int reportlunsize = sizeof(*ld_buff) + CISS_MAX_PHYS_LUN * 8; + + hba[cntl_num]->scsi_ctlr = (void *) + kmalloc(sizeof(struct cciss_scsi_adapter_data_t), + GFP_KERNEL); + if (hba[cntl_num]->scsi_ctlr == NULL) + return; + + ((struct cciss_scsi_adapter_data_t *) + hba[cntl_num]->scsi_ctlr)->scsi_host = NULL; + ((struct cciss_scsi_adapter_data_t *) + hba[cntl_num]->scsi_ctlr)->lock = SPIN_LOCK_UNLOCKED; + ((struct cciss_scsi_adapter_data_t *) + hba[cntl_num]->scsi_ctlr)->registered = 0; + + if (scsi_cmd_stack_setup(cntl_num) != 0) { + printk("Trouble, returned non-zero!\n"); + return; + } + + ld_buff = kmalloc(reportlunsize, GFP_KERNEL); + if (ld_buff == NULL) { + printk(KERN_ERR "cciss: out of memory\n"); + return; + } + memset(ld_buff, 0, sizeof(ReportLunData_struct)); + inq_buff = kmalloc(sizeof( InquiryData_struct), GFP_KERNEL); + if (inq_buff == NULL) { + printk(KERN_ERR "cciss: out of memory\n"); + kfree(ld_buff); + return; + } + + /* Get the physical luns */ + return_code = sendcmd(CISS_REPORT_PHYS, cntl_num, ld_buff, + reportlunsize, 0, 0, 0, NULL ); + + if( return_code == IO_OK) { + unsigned char *c = &ld_buff->LUNListLength[0]; + listlength = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; + } + else { /* getting report of physical luns failed */ + printk(KERN_WARNING "cciss: report physical luns" + " command failed\n"); + listlength = 0; + } + + CPQ_TAPE_LOCK(cntl_num, flags); + ccissscsi[cntl_num].ndevices = 0; + num_luns = listlength / 8; // 8 bytes pre entry + /* printk("Found %d LUNs\n", num_luns); */ + + if (num_luns > CISS_MAX_PHYS_LUN) + { + printk(KERN_WARNING + "cciss: Maximum physical LUNs (%d) exceeded. " + "%d LUNs ignored.\n", CISS_MAX_PHYS_LUN, + num_luns - CISS_MAX_PHYS_LUN); + num_luns = CISS_MAX_PHYS_LUN; + } + + for(i=0; iLUN[i], 8); /* ugly... */ + return_code = sendcmd(CISS_INQUIRY, cntl_num, inq_buff, + sizeof(InquiryData_struct), 2, 0 ,0, scsi3addr ); + if (return_code == IO_OK) { + if(inq_buff->data_byte[8] == 0xFF) + { + printk(KERN_WARNING "cciss: inquiry failed\n"); + } else { + int devtype; + + /* printk("Inquiry...\n"); + print_bytes((unsigned char *) inq_buff, 36, 1, 1); */ + devtype = (inq_buff->data_byte[0] & 0x1f); + + switch (devtype) + { + case 0x01: /* sequential access, (tape) */ + case 0x08: /* medium changer */ + /* this is the only kind of dev */ + /* we want to expose here. */ + if (cciss_scsi_add_entry(cntl_num, -1, + (unsigned char *) ld_buff->LUN[i], + devtype) != 0) + i=num_luns; // leave loop + break; + default: + break; + } + + } + } + else printk("cciss: inquiry failed.\n"); + } +#if 0 + for (i=0;ierr_info; + + /* First, see if it was a message rather than a command */ + if (cp->Request.Type.Type == TYPE_MSG) { + cp->cmd_type = CMD_MSG_DONE; + return; + } + + /* we stored ptr to scsi cmd in the buffer head pointer */ + cmd = (Scsi_Cmnd *) cp->bh; + ctlr = hba[cp->ctlr]; + + /* undo the DMA mappings */ + + if (cmd->use_sg) { + pci_unmap_sg(ctlr->pdev, + cmd->buffer, cmd->use_sg, + scsi_to_pci_dma_dir(cmd->sc_data_direction)); + } + else if (cmd->request_bufflen) { + addr64.val32.lower = cp->SG[0].Addr.lower; + addr64.val32.upper = cp->SG[0].Addr.upper; + pci_unmap_single(ctlr->pdev, (dma_addr_t) addr64.val, + cmd->request_bufflen, + scsi_to_pci_dma_dir(cmd->sc_data_direction)); + } + + cmd->result = (DID_OK << 16); /* host byte */ + cmd->result |= (COMMAND_COMPLETE << 8); /* msg byte */ + /* cmd->result |= (GOOD < 1); */ /* status byte */ + + cmd->result |= (ei->ScsiStatus); + /* printk("Scsistatus is 0x%02x\n", ei->ScsiStatus); */ + + /* copy the sense data whether we need to or not. */ + + memcpy(cmd->sense_buffer, ei->SenseInfo, + ei->SenseLen > SCSI_SENSE_BUFFERSIZE ? + SCSI_SENSE_BUFFERSIZE : + ei->SenseLen); + cmd->resid = ei->ResidualCnt; + + if(ei->CommandStatus != 0) + { /* an error has occurred */ + switch(ei->CommandStatus) + { + case CMD_TARGET_STATUS: + /* Pass it up to the upper layers... */ + if( ei->ScsiStatus) + { +#if 0 + printk(KERN_WARNING "cciss: cmd %p " + "has SCSI Status = %x\n", + cp, + ei->ScsiStatus); +#endif + cmd->result |= (ei->ScsiStatus < 1); + } + else { /* scsi status is zero??? How??? */ + + /* Ordinarily, this case should never happen, but there is a bug + in some released firmware revisions that allows it to happen + if, for example, a 4100 backplane loses power and the tape + drive is in it. We assume that it's a fatal error of some + kind because we can't show that it wasn't. We will make it + look like selection timeout since that is the most common + reason for this to occur, and it's severe enough. */ + + cmd->result = DID_NO_CONNECT << 16; + } + break; + case CMD_DATA_UNDERRUN: /* let mid layer handle it. */ + break; + case CMD_DATA_OVERRUN: + printk(KERN_WARNING "cciss: cp %p has" + " completed with data overrun " + "reported\n", cp); + break; + case CMD_INVALID: { + /* print_bytes(cp, sizeof(*cp), 1, 0); + print_cmd(cp); */ + /* We get CMD_INVALID if you address a non-existent tape drive instead + of a selection timeout (no response). You will see this if you yank + out a tape drive, then try to access it. This is kind of a shame + because it means that any other CMD_INVALID (e.g. driver bug) will + get interpreted as a missing target. */ + cmd->result = DID_NO_CONNECT << 16; + } + break; + case CMD_PROTOCOL_ERR: + printk(KERN_WARNING "cciss: cp %p has " + "protocol error \n", cp); + break; + case CMD_HARDWARE_ERR: + cmd->result = DID_ERROR << 16; + printk(KERN_WARNING "cciss: cp %p had " + " hardware error\n", cp); + break; + case CMD_CONNECTION_LOST: + cmd->result = DID_ERROR << 16; + printk(KERN_WARNING "cciss: cp %p had " + "connection lost\n", cp); + break; + case CMD_ABORTED: + cmd->result = DID_ABORT << 16; + printk(KERN_WARNING "cciss: cp %p was " + "aborted\n", cp); + break; + case CMD_ABORT_FAILED: + cmd->result = DID_ERROR << 16; + printk(KERN_WARNING "cciss: cp %p reports " + "abort failed\n", cp); + break; + case CMD_UNSOLICITED_ABORT: + cmd->result = DID_ABORT << 16; + printk(KERN_WARNING "cciss: cp %p aborted " + "do to an unsolicited abort\n", cp); + break; + case CMD_TIMEOUT: + cmd->result = DID_TIME_OUT << 16; + printk(KERN_WARNING "cciss: cp %p timedout\n", + cp); + break; + default: + cmd->result = DID_ERROR << 16; + printk(KERN_WARNING "cciss: cp %p returned " + "unknown status %x\n", cp, + ei->CommandStatus); + } + } + cmd->scsi_done(cmd); + scsi_cmd_free(ctlr, cp); +} + +/* cciss_scsi_detect is called from the scsi mid layer. + The scsi mid layer (scsi_register_module) is + called from cciss.c:cciss_init_one(). */ + +int __init +cciss_scsi_detect(Scsi_Host_Template *tpnt) +{ + int i; + struct Scsi_Host *sh; + + /* Tell the kernel we want to be a SCSI driver... */ + sh = scsi_register(tpnt, sizeof(struct ctlr_info *)); + if (sh == NULL) return 0; + + sh->io_port = 0; // good enough? FIXME, + sh->n_io_port = 0; // I don't think we use these two... + + sh->this_id = SELF_SCSI_ID; + + /* This is a bit kludgey, using the adapter name to figure out */ + /* which scsi host template we've got, won't scale beyond 9 ctlrs. */ + i = tpnt->name[5] - '0'; + +# if MAX_CTLR > 9 +# error "cciss_scsi.c: MAX_CTLR > 9, code maintenance needed." +# endif + + if (i<0 || i>=MAX_CTLR || hba[i] == NULL) { + /* we didn't find ourself... we shouldn't get here. */ + printk("cciss_scsi_detect: could not find ourself in hba[]\n"); + return 0; + } + + ((struct cciss_scsi_adapter_data_t *) + hba[i]->scsi_ctlr)->scsi_host = (void *) sh; + sh->hostdata[0] = (unsigned long) hba[i]; + sh->irq = hba[i]->intr; + sh->unique_id = sh->irq; + scsi_set_pci_device(sh, hba[i]->pdev); + + return 1; /* Say we have 1 scsi adapter, this will be */ + /* called multiple times, once for each adapter */ + /* from cciss.c:cciss_init_one(). We do it this */ + /* way for PCI-hot plug reasons. (we don't know how */ + /* many adapters we have total, so we say we have */ + /* 1, each of a unique type.) */ +} + +static void __exit cleanup_cciss_module(void); +int +cciss_scsi_release(struct Scsi_Host *sh) +{ + return 0; +} + +static void +cciss_unmap_one(struct pci_dev *pdev, + CommandList_struct *cp, + size_t buflen, + int data_direction) +{ + u64bit addr64; + + addr64.val32.lower = cp->SG[0].Addr.lower; + addr64.val32.upper = cp->SG[0].Addr.upper; + pci_unmap_single(pdev, (dma_addr_t) addr64.val, buflen, data_direction); +} + +static void +cciss_map_one(struct pci_dev *pdev, + CommandList_struct *cp, + unsigned char *buf, + size_t buflen, + int data_direction) +{ + __u64 addr64; + + addr64 = (__u64) pci_map_single(pdev, buf, buflen, data_direction); + cp->SG[0].Addr.lower = + (__u32) (addr64 & (__u64) 0x00000000FFFFFFFF); + cp->SG[0].Addr.upper = + (__u32) ((addr64 >> 32) & (__u64) 0x00000000FFFFFFFF); + cp->SG[0].Len = buflen; + cp->Header.SGList = (__u8) 1; /* no. SGs contig in this cmd */ + cp->Header.SGTotal = (__u16) 1; /* total sgs in this cmd list */ +} + +static int +cciss_scsi_do_simple_cmd(ctlr_info_t *c, + CommandList_struct *cp, + unsigned char *scsi3addr, + unsigned char *cdb, + unsigned char cdblen, + unsigned char *buf, int bufsize, + int direction) +{ + unsigned long flags; + + cp->cmd_type = CMD_IOCTL_PEND; // treat this like an ioctl + cp->bh = NULL; + cp->Header.ReplyQueue = 0; // unused in simple mode + memcpy(&cp->Header.LUN, scsi3addr, sizeof(cp->Header.LUN)); + cp->Header.Tag.lower = cp->busaddr; // Use k. address of cmd as tag + // Fill in the request block... + + /* printk("Using scsi3addr 0x%02x%0x2%0x2%0x2%0x2%0x2%0x2%0x2\n", + scsi3addr[0], scsi3addr[1], scsi3addr[2], scsi3addr[3], + scsi3addr[4], scsi3addr[5], scsi3addr[6], scsi3addr[7]); */ + + memset(cp->Request.CDB, 0, sizeof(cp->Request.CDB)); + memcpy(cp->Request.CDB, cdb, cdblen); + cp->Request.Timeout = 1000; // guarantee completion. + cp->Request.CDBLen = cdblen; + cp->Request.Type.Type = TYPE_CMD; + cp->Request.Type.Attribute = ATTR_SIMPLE; + cp->Request.Type.Direction = direction; + + /* Fill in the SG list and do dma mapping */ + cciss_map_one(c->pdev, cp, + (unsigned char *) buf, bufsize, + scsi_to_pci_dma_dir(SCSI_DATA_READ)); + + /* Put the request on the tail of the request queue */ + spin_lock_irqsave(&io_request_lock, flags); + addQ(&c->reqQ, cp); + c->Qdepth++; + start_io(c); + spin_unlock_irqrestore(&io_request_lock, flags); + + /* Wait for the request to complete */ + while(cp->cmd_type != CMD_IOCTL_DONE) + schedule_timeout(1); + + /* undo the dma mapping */ + cciss_unmap_one(c->pdev, cp, bufsize, + scsi_to_pci_dma_dir(SCSI_DATA_READ)); + + return(0); +} + +static void +cciss_scsi_interpret_error(CommandList_struct *cp) +{ + ErrorInfo_struct *ei; + + ei = cp->err_info; + switch(ei->CommandStatus) + { + case CMD_TARGET_STATUS: + printk(KERN_WARNING "cciss: cmd %p has " + "completed with errors\n", cp); + printk(KERN_WARNING "cciss: cmd %p " + "has SCSI Status = %x\n", + cp, + ei->ScsiStatus); + if (ei->ScsiStatus == 0) + printk(KERN_WARNING + "cciss:SCSI status is abnormally zero. " + "(probably indicates selection timeout " + "reported incorrectly due to a known " + "firmware bug, circa July, 2001.)\n"); + break; + case CMD_DATA_UNDERRUN: /* let mid layer handle it. */ + printk("UNDERRUN\n"); + break; + case CMD_DATA_OVERRUN: + printk(KERN_WARNING "cciss: cp %p has" + " completed with data overrun " + "reported\n", cp); + break; + case CMD_INVALID: { + /* controller unfortunately reports SCSI passthru's */ + /* to non-existent targets as invalid commands. */ + printk(KERN_WARNING "cciss: cp %p is " + "reported invalid (probably means " "target device no longer present)\n", + cp); + /* print_bytes((unsigned char *) cp, sizeof(*cp), 1, 0); + print_cmd(cp); */ + } + break; + case CMD_PROTOCOL_ERR: + printk(KERN_WARNING "cciss: cp %p has " + "protocol error \n", cp); + break; + case CMD_HARDWARE_ERR: + /* cmd->result = DID_ERROR << 16; */ + printk(KERN_WARNING "cciss: cp %p had " + " hardware error\n", cp); + break; + case CMD_CONNECTION_LOST: + printk(KERN_WARNING "cciss: cp %p had " + "connection lost\n", cp); + break; + case CMD_ABORTED: + printk(KERN_WARNING "cciss: cp %p was " + "aborted\n", cp); + break; + case CMD_ABORT_FAILED: + printk(KERN_WARNING "cciss: cp %p reports " + "abort failed\n", cp); + break; + case CMD_UNSOLICITED_ABORT: + printk(KERN_WARNING "cciss: cp %p aborted " + "do to an unsolicited abort\n", cp); + break; + case CMD_TIMEOUT: + printk(KERN_WARNING "cciss: cp %p timedout\n", + cp); + break; + default: + printk(KERN_WARNING "cciss: cp %p returned " + "unknown status %x\n", cp, + ei->CommandStatus); + } +} + +static int +cciss_scsi_do_inquiry(ctlr_info_t *c, unsigned char *scsi3addr, + InquiryData_struct *buf) +{ + int rc; + CommandList_struct *cp; + char cdb[6]; + ErrorInfo_struct *ei; + + cp = scsi_cmd_alloc(c); + ei = cp->err_info; + + if (cp == NULL) { /* trouble... */ + printk("cmd_alloc returned NULL!\n"); + return -1; + } + + cdb[0] = CISS_INQUIRY; + cdb[1] = 0; + cdb[2] = 0; + cdb[3] = 0; + cdb[4] = sizeof(*buf) & 0xff; + cdb[5] = 0; + rc = cciss_scsi_do_simple_cmd(c, cp, scsi3addr, cdb, + 6, (unsigned char *) buf, + sizeof(*buf), XFER_READ); + + if (rc != 0) return rc; /* something went wrong */ + + if (ei->CommandStatus != 0 && + ei->CommandStatus != CMD_DATA_UNDERRUN) { + cciss_scsi_interpret_error(cp); + scsi_cmd_free(c, cp); + return -1; + } + scsi_cmd_free(c, cp); + return 0; +} + +static int +cciss_scsi_do_report_phys_luns(ctlr_info_t *c, + ReportLunData_struct *buf, int bufsize) +{ + int rc; + CommandList_struct *cp; + unsigned char cdb[12]; + unsigned char scsi3addr[8]; + ErrorInfo_struct *ei; + + cp = scsi_cmd_alloc(c); + if (cp == NULL) { /* trouble... */ + printk("cmd_alloc returned NULL!\n"); + return -1; + } + + memset(&scsi3addr[0], 0, 8); /* address the controller */ + cdb[0] = CISS_REPORT_PHYS; + cdb[1] = 0; + cdb[2] = 0; + cdb[3] = 0; + cdb[4] = 0; + cdb[5] = 0; + cdb[6] = (sizeof(*buf) >> 24) & 0xFF; //MSB + cdb[7] = (sizeof(*buf) >> 16) & 0xFF; + cdb[8] = (sizeof(*buf) >> 8) & 0xFF; + cdb[9] = sizeof(*buf) & 0xFF; + cdb[10] = 0; + cdb[11] = 0; + + rc = cciss_scsi_do_simple_cmd(c, cp, scsi3addr, + cdb, 12, + (unsigned char *) buf, + bufsize, XFER_READ); + + if (rc != 0) return rc; /* something went wrong */ + + ei = cp->err_info; + if (ei->CommandStatus != 0 && + ei->CommandStatus != CMD_DATA_UNDERRUN) { + cciss_scsi_interpret_error(cp); + scsi_cmd_free(c, cp); + return -1; + } + scsi_cmd_free(c, cp); + return 0; +} + +static void +cciss_update_non_disk_devices(int cntl_num, int hostno) +{ + /* the idea here is we could get notified from /proc + that some devices have changed, so we do a report + physical luns cmd, and adjust our list of devices + accordingly. (We can't rely on the scsi-mid layer just + doing inquiries, because the "busses" that the scsi + mid-layer probes are totally fabricated by this driver, + so new devices wouldn't show up. + + the scsi3addr's of devices won't change so long as the + adapter is not reset. That means we can rescan and + tell which devices we already know about, vs. new + devices, vs. disappearing devices. + + Also, if you yank out a tape drive, then put in a disk + in it's place, (say, a configured volume from another + array controller for instance) _don't_ poke this driver + (so it thinks it's still a tape, but _do_ poke the scsi + mid layer, so it does an inquiry... the scsi mid layer + will see the physical disk. This would be bad. Need to + think about how to prevent that. One idea would be to + snoop all scsi responses and if an inquiry repsonse comes + back that reports a disk, chuck it an return selection + timeout instead and adjust our table... Not sure i like + that though. + + */ + + ReportLunData_struct *ld_buff; + InquiryData_struct *inq_buff; + unsigned char scsi3addr[8]; + ctlr_info_t *c; + __u32 num_luns=0; + unsigned char *ch; + /* unsigned char found[CCISS_MAX_SCSI_DEVS_PER_HBA]; */ + struct cciss_scsi_dev_t currentsd[CCISS_MAX_SCSI_DEVS_PER_HBA]; + int ncurrent=0; + int reportlunsize = sizeof(*ld_buff) + CISS_MAX_PHYS_LUN * 8; + int i; + + c = (ctlr_info_t *) hba[cntl_num]; + ld_buff = kmalloc(reportlunsize, GFP_KERNEL); + if (ld_buff == NULL) { + printk(KERN_ERR "cciss: out of memory\n"); + return; + } + memset(ld_buff, 0, reportlunsize); + inq_buff = kmalloc(sizeof( InquiryData_struct), GFP_KERNEL); + if (inq_buff == NULL) { + printk(KERN_ERR "cciss: out of memory\n"); + kfree(ld_buff); + return; + } + + if (cciss_scsi_do_report_phys_luns(c, ld_buff, reportlunsize) == 0) { + ch = &ld_buff->LUNListLength[0]; + num_luns = ((ch[0]<<24) | (ch[1]<<16) | (ch[2]<<8) | ch[3]) / 8; + if (num_luns > CISS_MAX_PHYS_LUN) { + printk(KERN_WARNING + "cciss: Maximum physical LUNs (%d) exceeded. " + "%d LUNs ignored.\n", CISS_MAX_PHYS_LUN, + num_luns - CISS_MAX_PHYS_LUN); + num_luns = CISS_MAX_PHYS_LUN; + } + } + else { + printk(KERN_ERR "cciss: Report physical LUNs failed.\n"); + return; + } + + + /* adjust our table of devices */ + for(i=0; iLUN[i][0], 8); + + if (cciss_scsi_do_inquiry(hba[cntl_num], + scsi3addr, inq_buff) != 0) + { + /* Inquiry failed (msg printed already) */ + devtype = 0; /* so we will skip this device. */ + } else /* what kind of device is this? */ + devtype = (inq_buff->data_byte[0] & 0x1f); + + switch (devtype) + { + case 0x01: /* sequential access, (tape) */ + case 0x08: /* medium changer */ + memcpy(¤tsd[ncurrent].scsi3addr[0], + &scsi3addr[0], 8); + currentsd[ncurrent].devtype = devtype; + currentsd[ncurrent].bus = -1; + currentsd[ncurrent].target = -1; + currentsd[ncurrent].lun = -1; + ncurrent++; + break; + default: + break; + } + } + + adjust_cciss_scsi_table(cntl_num, hostno, currentsd, ncurrent); + + kfree(inq_buff); + kfree(ld_buff); + return; +} + +static int +is_keyword(char *ptr, int len, char *verb) // Thanks to ncr53c8xx.c +{ + int verb_len = strlen(verb); + if (len >= verb_len && !memcmp(verb,ptr,verb_len)) + return verb_len; + else + return 0; +} + +static int +cciss_scsi_user_command(int ctlr, int hostno, char *buffer, int length) +{ + int arg_len; + + if ((arg_len = is_keyword(buffer, length, "rescan")) != 0) + cciss_update_non_disk_devices(ctlr, hostno); + else + return -EINVAL; + return length; +} + +/* It's a pity that we need this, but, we do... */ +extern struct Scsi_Host *scsi_hostlist; /* from ../scsi/hosts.c */ + +int +cciss_scsi_proc_info(char *buffer, /* data buffer */ + char **start, /* where data in buffer starts */ + off_t offset, /* offset from start of imaginary file */ + int length, /* length of data in buffer */ + int hostnum, /* which host adapter (always zero for me) */ + int func) /* 0 == read, 1 == write */ +{ + + int buflen, datalen; + struct Scsi_Host *sh; + int found; + ctlr_info_t *ci; + int cntl_num; + + /* Lets see if we can find our Scsi_Host... + this might be kind of "bad", searching scis_hostlist this way + but how else can we find the scsi host? I think I've seen + this coded both ways, (circular list and null terminated list) + I coded it to work either way, since I wasn't sure. */ + + sh = scsi_hostlist; + found=0; + do { + if (sh == NULL) break; + if (sh->host_no == hostnum) { + found++; + break; + } + sh = sh->next; + } while (sh != scsi_hostlist && sh != NULL); + + if (sh == NULL || found == 0) /* This really shouldn't ever happen. */ + return -EINVAL; + + ci = (ctlr_info_t *) sh->hostdata[0]; + if (ci == NULL) /* This really shouldn't ever happen. */ + return -EINVAL; + + cntl_num = ci->ctlr; /* Get our index into the hba[] array */ + + if (func == 0) { /* User is reading from /proc/scsi/ciss*?/?* */ + buflen = sprintf(buffer, "hostnum=%d\n", hostnum); + + datalen = buflen - offset; + if (datalen < 0) { /* they're reading past EOF. */ + datalen = 0; + *start = buffer+buflen; + } else + *start = buffer + offset; + return(datalen); + } else /* User is writing to /proc/scsi/cciss*?/?* ... */ + return cciss_scsi_user_command(cntl_num, hostnum, + buffer, length); +} + +/* this is via the generic proc support */ +const char * +cciss_scsi_info(struct Scsi_Host *sa) +{ + static char buf[300]; + ctlr_info_t *ci; + + /* probably need to work on putting a bit more info in here... */ + /* this is output via the /proc filesystem. */ + + ci = (ctlr_info_t *) sa->hostdata[0]; + + sprintf(buf, "%s %c%c%c%c\n", + ci->product_name, + ci->firm_ver[0], + ci->firm_ver[1], + ci->firm_ver[2], + ci->firm_ver[3]); + + return buf; +} + + +/* cciss_scatter_gather takes a Scsi_Cmnd, (cmd), and does the pci + dma mapping and fills in the scatter gather entries of the + cciss command, cp. */ + +static void +cciss_scatter_gather(struct pci_dev *pdev, + CommandList_struct *cp, + Scsi_Cmnd *cmd) +{ + unsigned int use_sg, nsegs=0, len; + struct scatterlist *scatter = (struct scatterlist *) cmd->buffer; + __u64 addr64; + + /* is it just one virtual address? */ + if (!cmd->use_sg) { + if (cmd->request_bufflen) { /* anything to xfer? */ + + addr64 = (__u64) pci_map_single(pdev, + cmd->request_buffer, + cmd->request_bufflen, + scsi_to_pci_dma_dir(cmd->sc_data_direction)); + + cp->SG[0].Addr.lower = + (__u32) (addr64 & (__u64) 0x00000000FFFFFFFF); + cp->SG[0].Addr.upper = + (__u32) ((addr64 >> 32) & (__u64) 0x00000000FFFFFFFF); + cp->SG[0].Len = cmd->request_bufflen; + nsegs=1; + } + } /* else, must be a list of virtual addresses.... */ + else if (cmd->use_sg <= MAXSGENTRIES) { /* not too many addrs? */ + + use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, + scsi_to_pci_dma_dir(cmd->sc_data_direction)); + + for (nsegs=0; nsegs < use_sg; nsegs++) { + addr64 = (__u64) sg_dma_address(&scatter[nsegs]); + len = sg_dma_len(&scatter[nsegs]); + cp->SG[nsegs].Addr.lower = + (__u32) (addr64 & (__u64) 0x00000000FFFFFFFF); + cp->SG[nsegs].Addr.upper = + (__u32) ((addr64 >> 32) & (__u64) 0x00000000FFFFFFFF); + cp->SG[nsegs].Len = len; + cp->SG[nsegs].Ext = 0; // we are not chaining + } + } else BUG(); + + cp->Header.SGList = (__u8) nsegs; /* no. SGs contig in this cmd */ + cp->Header.SGTotal = (__u16) nsegs; /* total sgs in this cmd list */ + return; +} + + +int +cciss_scsi_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) +{ + ctlr_info_t **c; + int ctlr, rc; + unsigned char scsi3addr[8]; + CommandList_struct *cp; + + // Get the ptr to our adapter structure (hba[i]) out of cmd->host. + // We violate cmd->host privacy here. (Is there another way?) + c = (ctlr_info_t **) &cmd->host->hostdata[0]; + ctlr = (*c)->ctlr; + + rc = lookup_scsi3addr(ctlr, cmd->channel, cmd->target, cmd->lun, + scsi3addr); + if (rc != 0) { + /* the scsi nexus does not match any that we presented... */ + /* pretend to mid layer that we got selection timeout */ + cmd->result = DID_NO_CONNECT << 16; + done(cmd); + /* we might want to think about registering controller itself + as a processor device on the bus so sg binds to it. */ + return 0; + } + + // printk("cciss_queue_command, p=%p, cmd=0x%02x, c%db%dt%dl%d\n", + // cmd, cmd->cmnd[0], ctlr, cmd->channel, cmd->target, cmd->lun); + + /* Ok, we have a reasonable scsi nexus, so send the cmd down, and + see what the device thinks of it. */ + + cp = scsi_cmd_alloc(*c); + if (cp == NULL) { /* trouble... */ + printk("scsi_cmd_alloc returned NULL!\n"); + /* FIXME: next 3 lines are -> BAD! <- */ + cmd->result = DID_NO_CONNECT << 16; + done(cmd); + return 0; + } + + // Fill in the command list header + + cmd->scsi_done = done; // save this for use by completion code + + // save cp in case we have to abort it + cmd->host_scribble = (unsigned char *) cp; + + cp->cmd_type = CMD_SCSI; + cp->bh = (struct buffer_head *) cmd; + cp->Header.ReplyQueue = 0; // unused in simple mode + memcpy(&cp->Header.LUN.LunAddrBytes[0], &scsi3addr[0], 8); + cp->Header.Tag.lower = cp->busaddr; // Use k. address of cmd as tag + + // Fill in the request block... + + cp->Request.Timeout = 1000; // guarantee completion + memset(cp->Request.CDB, 0, sizeof(cp->Request.CDB)); + if (cmd->cmd_len > sizeof(cp->Request.CDB)) BUG(); + cp->Request.CDBLen = cmd->cmd_len; + memcpy(cp->Request.CDB, cmd->cmnd, cmd->cmd_len); + cp->Request.Type.Type = TYPE_CMD; + cp->Request.Type.Attribute = ATTR_SIMPLE; + switch(cmd->sc_data_direction) + { + case SCSI_DATA_WRITE: cp->Request.Type.Direction = XFER_WRITE; break; + case SCSI_DATA_READ: cp->Request.Type.Direction = XFER_READ; break; + case SCSI_DATA_NONE: cp->Request.Type.Direction = XFER_NONE; break; + + case SCSI_DATA_UNKNOWN: + // This can happen if a buggy application does a scsi passthru + // and sets both inlen and outlen to non-zero. ( see + // ../scsi/scsi_ioctl.c:scsi_ioctl_send_command() ) + + cp->Request.Type.Direction = XFER_RSVD; + // This is technically wrong, and cciss controllers should + // reject it with CMD_INVALID, which is the most correct + // response, but non-fibre backends appear to let it + // slide by, and give the same results as if this field + // were set correctly. Either way is acceptable for + // our purposes here. + + break; + + default: + printk("cciss: unknown data direction: %d\n", + cmd->sc_data_direction); + BUG(); + break; + } + + cciss_scatter_gather((*c)->pdev, cp, cmd); // Fill the SG list + + /* Put the request on the tail of the request queue */ + + addQ(&(*c)->reqQ, cp); + (*c)->Qdepth++; + start_io(*c); + + /* the cmd'll come back via intr handler in complete_scsi_command() */ + return 0; +} + +static void +init_driver_template(int ctlr) +{ + memset(&driver_template[ctlr], 0, sizeof(driver_template[ctlr])); + driver_template[ctlr].name = ccissscsi[ctlr].name; + driver_template[ctlr].proc_name = ccissscsi[ctlr].name; + driver_template[ctlr].detect = cciss_scsi_detect; + driver_template[ctlr].release = cciss_scsi_release; + driver_template[ctlr].proc_info = cciss_scsi_proc_info; + driver_template[ctlr].queuecommand = cciss_scsi_queue_command; + driver_template[ctlr].eh_abort_handler = NULL; + driver_template[ctlr].eh_device_reset_handler = NULL; + driver_template[ctlr].bios_param = scsicam_bios_param; + driver_template[ctlr].can_queue = SCSI_CCISS_CAN_QUEUE; + driver_template[ctlr].this_id = 7; + driver_template[ctlr].sg_tablesize = MAXSGENTRIES; + driver_template[ctlr].cmd_per_lun = 1; + driver_template[ctlr].use_new_eh_code = 1; + driver_template[ctlr].use_clustering = DISABLE_CLUSTERING; + driver_template[ctlr].module = THIS_MODULE; + + /* set scsi_host to NULL so our detect routine will + find us on register */ + + ((struct cciss_scsi_adapter_data_t *) + hba[ctlr]->scsi_ctlr)->scsi_host = NULL; + +} + +static void +cciss_unregister_scsi(int ctlr) +{ + struct cciss_scsi_adapter_data_t *sa; + struct cciss_scsi_cmd_stack_t *stk; + unsigned long flags; + + /* we are being forcibly unloaded, and may not refuse. */ + + spin_lock_irqsave(&io_request_lock, flags); + sa = (struct cciss_scsi_adapter_data_t *) hba[ctlr]->scsi_ctlr; + stk = &sa->cmd_stack; + + /* if we weren't ever actually registered, don't unregister */ + if (((struct cciss_scsi_adapter_data_t *) + hba[ctlr]->scsi_ctlr)->registered) { + spin_unlock_irqrestore(&io_request_lock, flags); + scsi_unregister_module(MODULE_SCSI_HA, &driver_template[ctlr]); + spin_lock_irqsave(&io_request_lock, flags); + } + init_driver_template(ctlr); + scsi_cmd_stack_free(ctlr); + kfree(hba[ctlr]->scsi_ctlr); + spin_unlock_irqrestore(&io_request_lock, flags); +} + +static int +cciss_register_scsi(int ctlr, int this_is_init_time) +{ + unsigned long flags; + + CPQ_TAPE_LOCK(ctlr, flags); + driver_template[ctlr].name = ccissscsi[ctlr].name; + driver_template[ctlr].proc_name = ccissscsi[ctlr].name; + driver_template[ctlr].module = THIS_MODULE;; + + /* Since this is really a block driver, the SCSI core may not be + initialized yet, in which case, calling scsi_register_module + would hang. instead, we will do it later, via /proc filesystem + and rc scripts, when we know SCSI core is good to go. */ + + if (this_is_init_time) { + CPQ_TAPE_UNLOCK(ctlr, flags); + return 0; + } + + /* Only register if SCSI devices are detected. */ + if (ccissscsi[ctlr].ndevices != 0) { + ((struct cciss_scsi_adapter_data_t *) + hba[ctlr]->scsi_ctlr)->registered = 1; + CPQ_TAPE_UNLOCK(ctlr, flags); + return scsi_register_module(MODULE_SCSI_HA, + &driver_template[ctlr]); + } + CPQ_TAPE_UNLOCK(ctlr, flags); + printk(KERN_INFO + "cciss%d: No appropriate SCSI device detected, " + "SCSI subsystem not engaged.\n", ctlr); + return 0; +} + +static int +cciss_engage_scsi(int ctlr) +{ + struct cciss_scsi_adapter_data_t *sa; + struct cciss_scsi_cmd_stack_t *stk; + unsigned long flags; + + spin_lock_irqsave(&io_request_lock, flags); + sa = (struct cciss_scsi_adapter_data_t *) hba[ctlr]->scsi_ctlr; + stk = &sa->cmd_stack; + + if (((struct cciss_scsi_adapter_data_t *) + hba[ctlr]->scsi_ctlr)->registered) { + printk("cciss%d: SCSI subsystem already engaged.\n", ctlr); + spin_unlock_irqrestore(&io_request_lock, flags); + return ENXIO; + } + spin_unlock_irqrestore(&io_request_lock, flags); + cciss_update_non_disk_devices(ctlr, -1); + cciss_register_scsi(ctlr, 0); + return 0; +} + +static void +cciss_proc_tape_report(int ctlr, unsigned char *buffer, off_t *pos, off_t *len) +{ + int size; + unsigned int flags; + + *pos = *pos -1; *len = *len - 1; // cut off the last trailing newline + + CPQ_TAPE_LOCK(ctlr, flags); + size = sprintf(buffer + *len, + " Sequential access devices: %d\n\n", + ccissscsi[ctlr].ndevices); + CPQ_TAPE_UNLOCK(ctlr, flags); + *pos += size; *len += size; +} + +#else /* no CONFIG_CISS_SCSI_TAPE */ + +/* If no tape support, then these become defined out of existence */ + +#define cciss_find_non_disk_devices(cntl_num) +#define cciss_unregister_scsi(ctlr) +#define cciss_register_scsi(ctlr, this_is_init_time) +#define cciss_proc_tape_report(ctlr, buffer, pos, len) + +#endif /* CONFIG_CISS_SCSI_TAPE */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/cciss_scsi.h linux.ac/drivers/block/cciss_scsi.h --- linux.vanilla/drivers/block/cciss_scsi.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/block/cciss_scsi.h Thu Oct 11 09:12:04 2001 @@ -0,0 +1,99 @@ +/* + * Disk Array driver for Compaq SA53xx Controllers, SCSI Tape module + * Copyright 2001 Compaq Computer 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Questions/Comments/Bugfixes to arrays@compaq.com + * + */ +#ifdef CONFIG_CISS_SCSI_TAPE +#ifndef _CCISS_SCSI_H_ +#define _CCISS_SCSI_H_ + +#include /* possibly irrelevant, since we don't show disks */ + + // the scsi id of the adapter... +#define SELF_SCSI_ID 15 + // 15 is somewhat arbitrary, since the scsi-2 bus + // that's presented by the driver to the OS is + // fabricated. The "real" scsi-3 bus the + // hardware presents is fabricated too. + // The actual, honest-to-goodness physical + // bus that the devices are attached to is not + // addressible natively, and may in fact turn + // out to be not scsi at all. + +#define SCSI_CCISS_CAN_QUEUE 2 + +/* this notation works fine for static initializations (as is the usual + case for linux scsi drivers), but not so well for dynamic settings, + so, if you change this, you also have to change cciss_unregister_scsi() + in cciss_scsi.c */ +#define CCISS_SCSI { \ + name: "", \ + detect: cciss_scsi_detect, \ + release: cciss_scsi_release, \ + proc_info: cciss_scsi_proc_info, \ + queuecommand: cciss_scsi_queue_command, \ + bios_param: scsicam_bios_param, \ + can_queue: SCSI_CCISS_CAN_QUEUE, \ + this_id: 7, \ + sg_tablesize: MAXSGENTRIES, \ + cmd_per_lun: 1, \ + use_new_eh_code: 1, \ + use_clustering: DISABLE_CLUSTERING,\ +} + +/* + info: cciss_scsi_info, \ + +Note, cmd_per_lun could give us some trouble, so I'm setting it very low. +Likewise, SCSI_CCISS_CAN_QUEUE is set very conservatively. + +If the upper scsi layer tries to track how many commands we have +outstanding, it will be operating under the misapprehension that it is +the only one sending us requests. We also have the block interface, +which is where most requests must surely come from, so the upper layer's +notion of how many requests we have outstanding will be wrong most or +all of the time. + +Note, the normal SCSI mid-layer error handling doesn't work well +for this driver because 1) it takes the io_request_lock before +calling error handlers and uses a local variable to store flags, +so the io_request_lock cannot be released and interrupts enabled +inside the error handlers, and, the error handlers cannot poll +for command completion because they might get commands from the +block half of the driver completing, and not know what to do +with them. That's what we get for making a hybrid scsi/block +driver, I suppose. + +*/ + +struct cciss_scsi_dev_t { + int devtype; + int bus, target, lun; /* as presented to the OS */ + unsigned char scsi3addr[8]; /* as presented to the HW */ +}; + +struct cciss_scsi_hba_t { + char *name; + int ndevices; +#define CCISS_MAX_SCSI_DEVS_PER_HBA 16 + struct cciss_scsi_dev_t dev[CCISS_MAX_SCSI_DEVS_PER_HBA]; +}; + +#endif /* _CCISS_SCSI_H_ */ +#endif /* CONFIG_CISS_SCSI_TAPE */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/cpqarray.c linux.ac/drivers/block/cpqarray.c --- linux.vanilla/drivers/block/cpqarray.c Tue Sep 18 22:10:43 2001 +++ linux.ac/drivers/block/cpqarray.c Thu Oct 11 09:20:16 2001 @@ -41,13 +41,13 @@ #define SMART2_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) -#define DRIVER_NAME "Compaq SMART2 Driver (v 2.4.5)" -#define DRIVER_VERSION SMART2_DRIVER_VERSION(2,4,5) +#define DRIVER_NAME "Compaq SMART2 Driver (v 2.4.20)" +#define DRIVER_VERSION SMART2_DRIVER_VERSION(2,4,20) /* Embedded module documentation macros - see modules.h */ /* Original author Chris Frantz - Compaq Computer Corporation */ MODULE_AUTHOR("Compaq Computer Corporation"); -MODULE_DESCRIPTION("Driver for Compaq Smart2 Array Controllers"); +MODULE_DESCRIPTION("Driver for Compaq Smart2 Array Controllers version 2.4.20"); MODULE_LICENSE("GPL"); #define MAJOR_NR COMPAQ_SMART2_MAJOR @@ -64,12 +64,11 @@ #define NR_CMDS 128 /* This could probably go as high as ~400 */ #define MAX_CTLR 8 -#define CTLR_SHIFT 8 #define CPQARRAY_DMA_MASK 0xFFFFFFFF /* 32 bit DMA */ -static int nr_ctlr; -static ctlr_info_t *hba[MAX_CTLR]; +static ctlr_info_t *hba[MAX_CTLR] = + { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static int eisa[8]; @@ -97,11 +96,33 @@ { 0x40580E11, "Smart Array 431", &smart4_access }, }; -static struct hd_struct * ida; -static int * ida_sizes; -static int * ida_blocksizes; -static int * ida_hardsizes; -static struct gendisk ida_gendisk[MAX_CTLR]; +/* define the PCI info for the PCI cards this driver can control */ +const struct pci_device_id cpqarray_pci_device_id[] = +{ + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_COMPAQ_42XX, + 0x0E11, 0x4058, 0, 0, 0}, /* SA431 */ + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_COMPAQ_42XX, + 0x0E11, 0x4051, 0, 0, 0}, /* SA4250ES */ + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_COMPAQ_42XX, + 0x0E11, 0x4050, 0, 0, 0}, /* SA4200 */ + { PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C1510, + 0x0E11, 0x4048, 0, 0, 0}, /* LC2 */ + { PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C1510, + 0x0E11, 0x4040, 0, 0, 0}, /* Integrated Array */ + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_SMART2P, + 0x0E11, 0x4034, 0, 0, 0}, /* SA 221 */ + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_SMART2P, + 0x0E11, 0x4033, 0, 0, 0}, /* SA 3100ES*/ + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_SMART2P, + 0x0E11, 0x4032, 0, 0, 0}, /* SA 3200*/ + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_SMART2P, + 0x0E11, 0x4031, 0, 0, 0}, /* SA 2SL*/ + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_SMART2P, + 0x0E11, 0x4030, 0, 0, 0}, /* SA 2P */ + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, cpqarray_pci_device_id); static struct proc_dir_entry *proc_array; @@ -115,7 +136,6 @@ #define DBGPX(s) do { } while(0) int cpqarray_init(void); -static int cpqarray_pci_detect(void); static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev); static void *remap_pci_mem(ulong base, ulong size); static int cpqarray_eisa_detect(void); @@ -126,6 +146,9 @@ static cmdlist_t * cmd_alloc(ctlr_info_t *h, int get_from_pool); static void cmd_free(ctlr_info_t *h, cmdlist_t *c, int got_from_pool); +static void free_hba(int i); +static int alloc_cpqarray_hba(void); + static int sendcmd( __u8 cmd, int ctlr, @@ -154,6 +177,9 @@ static int revalidate_logvol(kdev_t dev, int maxusage); static int revalidate_allvol(kdev_t dev); +static int deregister_disk(int ctlr, int logvol); +static int register_new_disk(int cltr,int logvol); + #ifdef CONFIG_PROC_FS static void ida_procinit(int i); static int ida_proc_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data); @@ -172,18 +198,15 @@ drv = &hba[ctlr]->drv[i]; if (!drv->nr_blks) continue; - ida[(ctlr<nr_blks; + hba[ctlr]->hd[i<sizes[i<nr_blks; for(j=0; j<16; j++) { - ida_blocksizes[(ctlr<blk_size; + hba[ctlr]->blocksizes[(i<hardsizes[(i<blk_size; } - ida_gendisk[ctlr].nr_real++; } + hba[ctlr]->gendisk.nr_real = hba[ctlr]->highest_lun +1; } @@ -235,6 +258,7 @@ " I/O Port: 0x%04x\n" " IRQ: %d\n" " Logical drives: %d\n" + " Highest Logical ID: %d\n" " Physical drives: %d\n\n" " Current Q depth: %d\n" " Max Q depth since init: %d\n\n", @@ -243,8 +267,8 @@ (unsigned long)h->board_id, h->firm_rev[0], h->firm_rev[1], h->firm_rev[2], h->firm_rev[3], (unsigned long)h->ctlr_sig, (unsigned long)h->vaddr, - (unsigned int) h->ioaddr, (unsigned int)h->intr, - h->log_drives, h->phys_drives, + (unsigned int) h->io_mem_addr, (unsigned int)h->intr, + h->log_drives, h->highest_lun, h->phys_drives, h->Qdepth, h->maxQsinceinit); pos += size; len += size; @@ -252,11 +276,14 @@ size = sprintf(buffer+len, "Logical Drive Info:\n"); pos += size; len += size; - for(i=0; ilog_drives; i++) { + for(i=0; i<=h->highest_lun; i++) { drv = &h->drv[i]; - size = sprintf(buffer+len, "ida/c%dd%d: blksz=%d nr_blks=%d\n", + if(drv->blk_size != 0) + { + size = sprintf(buffer+len, "ida/c%dd%d: blksz=%d nr_blks=%d\n", ctlr, i, drv->blk_size, drv->nr_blks); - pos += size; len += size; + pos += size; len += size; + } } #ifdef CPQ_PROC_PRINT_QUEUES @@ -296,55 +323,112 @@ } #endif /* CONFIG_PROC_FS */ -#ifdef MODULE MODULE_PARM(eisa, "1-8i"); EXPORT_NO_SYMBOLS; /* This is a bit of a hack... */ -int __init init_module(void) +int __init init_cpqarray_module(void) { if (cpqarray_init() == 0) /* all the block dev numbers already used */ - return -EIO; /* or no controllers were found */ + return -ENODEV; /* or no controllers were found */ return 0; } -void cleanup_module(void) +static void release_io_mem(ctlr_info_t *c) +{ + /* if IO mem was not protected do nothing */ + if( c->io_mem_addr == 0) + return; + release_region(c->io_mem_addr, c->io_mem_length); + c->io_mem_addr = 0; + c->io_mem_length = 0; +} + +static void __devexit cpqarray_remove_one (struct pci_dev *pdev) { int i; + ctlr_info_t *tmp_ptr; char buff[4]; - for(i=0; idriver_data == NULL) + { + printk( KERN_ERR "cpqarray: Unable to remove device \n"); + return; + } + tmp_ptr = (ctlr_info_t *) pdev->driver_data; + i = tmp_ptr->ctlr; + if (hba[i] == NULL) + { + printk(KERN_ERR "cpqarray: device appears to " + "already be removed \n"); + return; + } - /* sendcmd will turn off interrupt, and send the flush... - * To write all data in the battery backed cache to disks - * no data returned, but don't want to send NULL to sendcmd */ - if( sendcmd(FLUSH_CACHE, i, buff, 4, 0, 0, 0)) - { - printk(KERN_WARNING "Unable to flush cache on " - "controller %d\n", i); - } - free_irq(hba[i]->intr, hba[i]); - iounmap(hba[i]->vaddr); - unregister_blkdev(MAJOR_NR+i, hba[i]->devname); - del_timer(&hba[i]->timer); - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i)); - remove_proc_entry(hba[i]->devname, proc_array); - pci_free_consistent(hba[i]->pci_dev, + /* sendcmd will turn off interrupt, and send the flush... + * To write all data in the battery backed cache to disks */ + memset(buff, 0 , 4); + if( sendcmd(FLUSH_CACHE, i, buff, 4, 0, 0, 0)) + { + printk(KERN_WARNING "Unable to flush cache on controller %d\n", + i); + } + free_irq(hba[i]->intr, hba[i]); + pdev->driver_data = NULL; + /* remove it from the disk list */ + del_gendisk(&(hba[i]->gendisk)); + + iounmap(hba[i]->vaddr); + unregister_blkdev(MAJOR_NR+i, hba[i]->devname); + del_timer(&hba[i]->timer); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i)); + remove_proc_entry(hba[i]->devname, proc_array); + pci_free_consistent(hba[i]->pci_dev, NR_CMDS * sizeof(cmdlist_t), (hba[i]->cmd_pool), hba[i]->cmd_pool_dhandle); - kfree(hba[i]->cmd_pool_bits); + kfree(hba[i]->cmd_pool_bits); + release_io_mem(hba[i]); + free_hba(i); +} + +/* removing an instance that was not removed automatically.. + * must be an eisa card. + */ +static void __devexit cpqarray_remove_one_eisa (int i) +{ + char buff[4]; - del_gendisk(&ida_gendisk[i]); + if (hba[i] == NULL) + { + printk(KERN_ERR "cpqarray: device appears to " + "already be removed \n"); + return; } - remove_proc_entry("cpqarray", proc_root_driver); - kfree(ida); - kfree(ida_sizes); - kfree(ida_hardsizes); - kfree(ida_blocksizes); -} -#endif /* MODULE */ + /* sendcmd will turn off interrupt, and send the flush... + * To write all data in the battery backed cache to disks + * no data returned, but don't want to send NULL to sendcmd */ + if( sendcmd(FLUSH_CACHE, i, buff, 4, 0, 0, 0)) + { + printk(KERN_WARNING "Unable to flush cache on controller %d\n", + i); + } + free_irq(hba[i]->intr, hba[i]); + /* remove it from the disk list */ + del_gendisk(&(hba[i]->gendisk)); + + iounmap(hba[i]->vaddr); + unregister_blkdev(MAJOR_NR+i, hba[i]->devname); + del_timer(&hba[i]->timer); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i)); + remove_proc_entry(hba[i]->devname, proc_array); + pci_free_consistent(hba[i]->pci_dev, + NR_CMDS * sizeof(cmdlist_t), (hba[i]->cmd_pool), + hba[i]->cmd_pool_dhandle); + kfree(hba[i]->cmd_pool_bits); + release_io_mem(hba[i]); + free_hba(i); +} static inline int cpq_new_segment(request_queue_t *q, struct request *rq, int max_segments) { @@ -386,246 +470,186 @@ return 1; } -/* - * This is it. Find all the controllers and register them. I really hate - * stealing all these major device numbers. - * returns the number of block devices registered. - */ -int __init cpqarray_init(void) +static int __init cpqarray_init_one( struct pci_dev *pdev, + const struct pci_device_id *ent) { request_queue_t *q; - int i,j; - int num_cntlrs_reg = 0; + int i,j; - /* detect controllers */ - cpqarray_pci_detect(); - cpqarray_eisa_detect(); - - if (nr_ctlr == 0) - return(num_cntlrs_reg); - printk(DRIVER_NAME "\n"); - printk("Found %d controller(s)\n", nr_ctlr); - - /* allocate space for disk structs */ - ida = kmalloc(sizeof(struct hd_struct)*nr_ctlr*NWD*16, GFP_KERNEL); - if(ida==NULL) - { - printk( KERN_ERR "cpqarray: out of memory"); - return(num_cntlrs_reg); - } - - ida_sizes = kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL); - if(ida_sizes==NULL) - { - kfree(ida); - printk( KERN_ERR "cpqarray: out of memory"); - return(num_cntlrs_reg); - } - - ida_blocksizes = kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL); - if(ida_blocksizes==NULL) - { - kfree(ida); - kfree(ida_sizes); - printk( KERN_ERR "cpqarray: out of memory"); - return(num_cntlrs_reg); - } + printk(KERN_DEBUG "cpqarray: Device 0x%x has been found at" + " bus %d dev %d func %d\n", + pdev->device, pdev->bus->number, PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn)); + i = alloc_cpqarray_hba(); + if( i < 0 ) + return (-1); + memset(hba[i], 0, sizeof(ctlr_info_t)); + sprintf(hba[i]->devname, "ida%d", i); + hba[i]->ctlr = i; + /* Initialize the pdev driver private data */ + pdev->driver_data = hba[i]; - ida_hardsizes = kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL); - if(ida_hardsizes==NULL) + if (cpqarray_pci_init(hba[i], pdev) != 0) { - kfree(ida); - kfree(ida_sizes); - kfree(ida_blocksizes); - printk( KERN_ERR "cpqarray: out of memory"); - return(num_cntlrs_reg); + release_io_mem(hba[i]); + free_hba(i); + return (-1); } - - memset(ida, 0, sizeof(struct hd_struct)*nr_ctlr*NWD*16); - memset(ida_sizes, 0, sizeof(int)*nr_ctlr*NWD*16); - memset(ida_blocksizes, 0, sizeof(int)*nr_ctlr*NWD*16); - memset(ida_hardsizes, 0, sizeof(int)*nr_ctlr*NWD*16); - memset(ida_gendisk, 0, sizeof(struct gendisk)*MAX_CTLR); - - /* + + /* * register block devices * Find disks and fill in structs * Get an interrupt, set the Q depth and get into /proc */ - for(i=0; i< nr_ctlr; i++) { - /* If this successful it should insure that we are the only */ - /* instance of the driver */ - if (register_blkdev(MAJOR_NR+i, hba[i]->devname, &ida_fops)) { - printk(KERN_ERR "cpqarray: Unable to get major number %d for ida\n", + + /* If this successful it should insure that we are the only */ + /* instance of the driver */ + if (register_blkdev(MAJOR_NR+i, hba[i]->devname, &ida_fops)) + { + printk(KERN_ERR "cpqarray: Unable to get major number %d for ida\n", MAJOR_NR+i); - continue; - } + release_io_mem(hba[i]); + free_hba(i); + return (-1); + } - hba[i]->access.set_intr_mask(hba[i], 0); - if (request_irq(hba[i]->intr, do_ida_intr, - SA_INTERRUPT|SA_SHIRQ, hba[i]->devname, hba[i])) { + hba[i]->access.set_intr_mask(hba[i], 0); + if (request_irq(hba[i]->intr, do_ida_intr, + SA_INTERRUPT|SA_SHIRQ, hba[i]->devname, hba[i])) + { - printk(KERN_ERR "cpqarray: Unable to get irq %d for %s\n", + printk(KERN_ERR "cpqarray: Unable to get irq %d for %s\n", hba[i]->intr, hba[i]->devname); - unregister_blkdev(MAJOR_NR+i, hba[i]->devname); - continue; - } - num_cntlrs_reg++; - hba[i]->cmd_pool = (cmdlist_t *)pci_alloc_consistent( - hba[i]->pci_dev, NR_CMDS * sizeof(cmdlist_t), + unregister_blkdev(MAJOR_NR+i, hba[i]->devname); + release_io_mem(hba[i]); + free_hba(i); + return (-1); + } + hba[i]->cmd_pool = (cmdlist_t *)pci_alloc_consistent( + hba[i]->pci_dev, NR_CMDS * sizeof(cmdlist_t), &(hba[i]->cmd_pool_dhandle)); - hba[i]->cmd_pool_bits = (__u32*)kmalloc( + hba[i]->cmd_pool_bits = (__u32*)kmalloc( ((NR_CMDS+31)/32)*sizeof(__u32), GFP_KERNEL); if(hba[i]->cmd_pool_bits == NULL || hba[i]->cmd_pool == NULL) - { - nr_ctlr = i; - if(hba[i]->cmd_pool_bits) - kfree(hba[i]->cmd_pool_bits); - if(hba[i]->cmd_pool) - pci_free_consistent(hba[i]->pci_dev, - NR_CMDS * sizeof(cmdlist_t), - hba[i]->cmd_pool, - hba[i]->cmd_pool_dhandle); - free_irq(hba[i]->intr, hba[i]); - unregister_blkdev(MAJOR_NR+i, hba[i]->devname); - num_cntlrs_reg--; - printk( KERN_ERR "cpqarray: out of memory"); - - /* If num_cntlrs_reg == 0, no controllers worked. - * init_module will fail, so clean up global - * memory that clean_module would do. - */ - - if (num_cntlrs_reg == 0) - { - kfree(ida); - kfree(ida_sizes); - kfree(ida_hardsizes); - kfree(ida_blocksizes); - } - return(num_cntlrs_reg); - - } - memset(hba[i]->cmd_pool, 0, NR_CMDS * sizeof(cmdlist_t)); - memset(hba[i]->cmd_pool_bits, 0, ((NR_CMDS+31)/32)*sizeof(__u32)); - printk(KERN_INFO "cpqarray: Finding drives on %s", - hba[i]->devname); - getgeometry(i); - start_fwbk(i); - - hba[i]->access.set_intr_mask(hba[i], FIFO_NOT_EMPTY); - - ida_procinit(i); - - q = BLK_DEFAULT_QUEUE(MAJOR_NR + i); - q->queuedata = hba[i]; - blk_init_queue(q, do_ida_request); - blk_queue_headactive(q, 0); - blksize_size[MAJOR_NR+i] = ida_blocksizes + (i*256); - hardsect_size[MAJOR_NR+i] = ida_hardsizes + (i*256); - read_ahead[MAJOR_NR+i] = READ_AHEAD; - - q->back_merge_fn = cpq_back_merge_fn; - q->front_merge_fn = cpq_front_merge_fn; - q->merge_requests_fn = cpq_merge_requests_fn; - - ida_gendisk[i].major = MAJOR_NR + i; - ida_gendisk[i].major_name = "ida"; - ida_gendisk[i].minor_shift = NWD_SHIFT; - ida_gendisk[i].max_p = 16; - ida_gendisk[i].part = ida + (i*256); - ida_gendisk[i].sizes = ida_sizes + (i*256); - ida_gendisk[i].nr_real = 0; + { + if(hba[i]->cmd_pool_bits) + kfree(hba[i]->cmd_pool_bits); + if(hba[i]->cmd_pool) + pci_free_consistent(hba[i]->pci_dev, + NR_CMDS * sizeof(cmdlist_t), + hba[i]->cmd_pool, hba[i]->cmd_pool_dhandle); + free_irq(hba[i]->intr, hba[i]); + unregister_blkdev(MAJOR_NR+i, hba[i]->devname); + printk( KERN_ERR "cpqarray: out of memory"); + release_io_mem(hba[i]); + free_hba(i); + return (-1); + } + memset(hba[i]->cmd_pool, 0, NR_CMDS * sizeof(cmdlist_t)); + memset(hba[i]->cmd_pool_bits, 0, ((NR_CMDS+31)/32)*sizeof(__u32)); + printk(KERN_INFO "cpqarray: Finding drives on %s", hba[i]->devname); + getgeometry(i); + start_fwbk(i); + + hba[i]->access.set_intr_mask(hba[i], FIFO_NOT_EMPTY); + + ida_procinit(i); + + q = BLK_DEFAULT_QUEUE(MAJOR_NR + i); + q->queuedata = hba[i]; + blk_init_queue(q, do_ida_request); + blk_queue_headactive(q, 0); + blksize_size[MAJOR_NR+i] = hba[i]->blocksizes; + hardsect_size[MAJOR_NR+i] = hba[i]->hardsizes; + read_ahead[MAJOR_NR+i] = READ_AHEAD; + + q->back_merge_fn = cpq_back_merge_fn; + q->front_merge_fn = cpq_front_merge_fn; + q->merge_requests_fn = cpq_merge_requests_fn; + + hba[i]->gendisk.major = MAJOR_NR + i; + hba[i]->gendisk.major_name = "ida"; + hba[i]->gendisk.minor_shift = NWD_SHIFT; + hba[i]->gendisk.max_p = 16; + hba[i]->gendisk.part = hba[i]->hd; + hba[i]->gendisk.sizes = hba[i]->sizes; + hba[i]->gendisk.nr_real = hba[i]->highest_lun+1; /* Get on the disk list */ - add_gendisk(&ida_gendisk[i]); + add_gendisk(&(hba[i]->gendisk)); - init_timer(&hba[i]->timer); - hba[i]->timer.expires = jiffies + IDA_TIMER; - hba[i]->timer.data = (unsigned long)hba[i]; - hba[i]->timer.function = ida_timer; - add_timer(&hba[i]->timer); - - ida_geninit(i); - for(j=0; jdrv[j].nr_blks); + init_timer(&hba[i]->timer); + hba[i]->timer.expires = jiffies + IDA_TIMER; + hba[i]->timer.data = (unsigned long)hba[i]; + hba[i]->timer.function = ida_timer; + add_timer(&hba[i]->timer); + + ida_geninit(i); + for(j=0; jgendisk), MKDEV(MAJOR_NR+i,j<<4), + 16, &ida_fops, hba[i]->drv[j].nr_blks); + + return(i); +} +static struct pci_driver cpqarray_pci_driver = { + name: "cpqarray", + probe: cpqarray_init_one, + remove: cpqarray_remove_one, + id_table: cpqarray_pci_device_id, +}; +/* + * This is it. Find all the controllers and register them. I really hate + * stealing all these major device numbers. + * returns the number of block devices registered. + */ +int __init cpqarray_init(void) +{ + int num_cntlrs_reg = 0; + int i; + + /* detect controllers */ + printk(DRIVER_NAME "\n"); + pci_register_driver(&cpqarray_pci_driver); + cpqarray_eisa_detect(); + + for(i=0; i< MAX_CTLR; i++) + { + if (hba[i] == NULL) + num_cntlrs_reg++; } - /* done ! */ return(num_cntlrs_reg); } - -/* - * Find the controller and initialize it - * Cannot use the class code to search, because older array controllers use - * 0x018000 and new ones use 0x010400. So I might as well search for each - * each device IDs, being there are only going to be three of them. - */ -static int cpqarray_pci_detect(void) -{ - struct pci_dev *pdev; - -#define IDA_BOARD_TYPES 3 - static int ida_vendor_id[IDA_BOARD_TYPES] = { PCI_VENDOR_ID_DEC, - PCI_VENDOR_ID_NCR, PCI_VENDOR_ID_COMPAQ }; - static int ida_device_id[IDA_BOARD_TYPES] = { PCI_DEVICE_ID_COMPAQ_42XX, PCI_DEVICE_ID_NCR_53C1510, PCI_DEVICE_ID_COMPAQ_SMART2P }; - int brdtype; - - /* search for all PCI board types that could be for this driver */ - for(brdtype=0; brdtypebus->number, PCI_SLOT(pdev->devfn), - PCI_FUNC(pdev->devfn)); - if (nr_ctlr == 8) { - printk(KERN_WARNING "cpqarray: This driver" - " supports a maximum of 8 controllers.\n"); - break; - } - -/* if it is a PCI_DEVICE_ID_NCR_53C1510, make sure it's the Compaq version of the chip */ - - if (ida_device_id[brdtype] == PCI_DEVICE_ID_NCR_53C1510) { - unsigned short subvendor=pdev->subsystem_vendor; - if(subvendor != PCI_VENDOR_ID_COMPAQ) - { - printk(KERN_DEBUG - "cpqarray: not a Compaq integrated array controller\n"); - continue; - } - } - - hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); if(hba[nr_ctlr]==NULL) + if (hba[i] == NULL) + { + hba[i] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); + if(hba[i]==NULL) { printk(KERN_ERR "cpqarray: out of memory.\n"); - continue; + return (-1); } - memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); - if (cpqarray_pci_init(hba[nr_ctlr], pdev) != 0) - { - kfree(hba[nr_ctlr]); - continue; - } - sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr); - hba[nr_ctlr]->ctlr = nr_ctlr; - nr_ctlr++; - - pdev = pci_find_device(ida_vendor_id[brdtype], - ida_device_id[brdtype], pdev); + return (i); } } + printk(KERN_WARNING "cpqarray: This driver supports a maximum" + " of 8 controllers.\n"); + return(-1); +} - return nr_ctlr; +static void free_hba(int i) +{ + kfree(hba[i]); + hba[i]=NULL; } /* @@ -642,6 +666,14 @@ int i; + pci_read_config_word(pdev, PCI_COMMAND, &command); + /* check to see if controller has been disabled */ + if(!(command & 0x02)) + { + printk(KERN_WARNING "cpqarray: controller appears to be disabled\n"); + return(-1); + } + c->pci_dev = pdev; vendor_id = pdev->vendor; device_id = pdev->device; @@ -661,7 +693,6 @@ return -1; } - pci_read_config_word(pdev, PCI_COMMAND, &command); pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size); pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer); @@ -682,11 +713,28 @@ ); c->intr = irq; - c->ioaddr = addr[0]; - + for(i=0; i<6; i++) + { + if (pci_resource_flags(pdev, i) & PCI_BASE_ADDRESS_SPACE_IO) + { /* IO space */ + c->io_mem_addr = addr[i]; + c->io_mem_length = pci_resource_end(pdev, i) + - pci_resource_start(pdev, i) +1; + // printk("IO Value found addr[%d] %lx %lx\n", + // i, c->io_mem_addr, c->io_mem_length); + if(!request_region( c->io_mem_addr, c->io_mem_length, + "cpqarray")) + { + printk( KERN_WARNING "cpqarray I/O memory range already in use addr %lx length = %ld\n", c->io_mem_addr, c->io_mem_length); + c->io_mem_addr = 0; + c->io_mem_length = 0; + } + break; + } + } c->paddr = 0; for(i=0; i<6; i++) - if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { + if (!(pci_resource_flags(pdev, i) & PCI_BASE_ADDRESS_SPACE_IO)) { c->paddr = pci_resource_start (pdev, i); break; } @@ -766,11 +814,11 @@ int i=0, j; __u32 board_id; int intr; - + int ctlr; + int num_ctlr = 0; while(i<8 && eisa[i]) { - if (nr_ctlr == 8) { - printk(KERN_WARNING "cpqarray: This driver supports" - " a maximum of 8 controllers.\n"); + ctlr = alloc_cpqarray_hba(); + if (ctlr == -1 ) { break; } board_id = inl(eisa[i]+0xC80); @@ -781,17 +829,21 @@ if (j == NR_PRODUCTS) { printk(KERN_WARNING "cpqarray: Sorry, I don't know how" " to access the SMART Array controller %08lx\n", (unsigned long)board_id); + free_hba(ctlr); continue; } - hba[nr_ctlr] = (ctlr_info_t *) kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); - if(hba[nr_ctlr]==NULL) + memset(hba[ctlr], 0, sizeof(ctlr_info_t)); + hba[ctlr]->io_mem_addr = eisa[i]; + hba[ctlr]->io_mem_length = 0x7FF; + if(!request_region( hba[ctlr]->io_mem_addr, + hba[ctlr]->io_mem_length, + "cpqarray")) { - printk(KERN_ERR "cpqarray: out of memory.\n"); - continue; + printk( KERN_WARNING "cpqarray: I/0 range already in use addr = %lx length=%ld\n", + hba[ctlr]->io_mem_addr, hba[ctlr]->io_mem_length); + free_hba(ctlr); + continue; } - memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); - hba[nr_ctlr]->ioaddr = eisa[i]; - /* * Read the config register to find our interrupt */ @@ -801,13 +853,13 @@ else if (intr & 4) intr = 14; else if (intr & 8) intr = 15; - hba[nr_ctlr]->intr = intr; - sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr); - hba[nr_ctlr]->product_name = products[j].product_name; - hba[nr_ctlr]->access = *(products[j].access); - hba[nr_ctlr]->ctlr = nr_ctlr; - hba[nr_ctlr]->board_id = board_id; - hba[nr_ctlr]->pci_dev = NULL; /* not PCI */ + hba[ctlr]->intr = intr; + sprintf(hba[ctlr]->devname, "ida%d", ctlr); + hba[ctlr]->product_name = products[j].product_name; + hba[ctlr]->access = *(products[j].access); + hba[ctlr]->ctlr = ctlr; + hba[ctlr]->board_id = board_id; + hba[ctlr]->pci_dev = NULL; /* not PCI */ DBGINFO( printk("i = %d, j = %d\n", i, j); @@ -816,11 +868,11 @@ printk("board_id = %x\n", board_id); ); - nr_ctlr++; + num_ctlr++; i++; } - return nr_ctlr; + return num_ctlr; } @@ -836,8 +888,7 @@ if (ctlr > MAX_CTLR || hba[ctlr] == NULL) return -ENXIO; - if (!suser() && ida_sizes[(ctlr << CTLR_SHIFT) + - MINOR(inode->i_rdev)] == 0) + if (!suser() && hba[ctlr]->sizes[ MINOR(inode->i_rdev)] == 0) return -ENXIO; /* @@ -847,7 +898,7 @@ * for "raw controller". */ if (suser() - && ida_sizes[(ctlr << CTLR_SHIFT) + MINOR(inode->i_rdev)] == 0 + && hba[ctlr]->sizes[ MINOR(inode->i_rdev)] == 0 && MINOR(inode->i_rdev) != 0) return -ENXIO; @@ -932,7 +983,7 @@ if (creq->nr_segments > SG_MAX) BUG(); - if (h->ctlr != MAJOR(creq->rq_dev)-MAJOR_NR || h->ctlr > nr_ctlr) + if (h->ctlr != MAJOR(creq->rq_dev)-MAJOR_NR ) { printk(KERN_WARNING "doreq cmd for %d, %x at %p\n", h->ctlr, creq->rq_dev, creq); @@ -955,9 +1006,11 @@ c->hdr.size = sizeof(rblk_t) >> 2; c->size += sizeof(rblk_t); - c->req.hdr.blk = ida[(h->ctlr<rq_dev)].start_sect + creq->sector; + c->req.hdr.blk = hba[h->ctlr]->hd[MINOR(creq->rq_dev)].start_sect + + creq->sector; c->bh = bh; -DBGPX( +DBGPX + ( if (bh == NULL) panic("bh == NULL?"); @@ -1193,33 +1246,56 @@ int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR; int dsk = MINOR(inode->i_rdev) >> NWD_SHIFT; int error; - int diskinfo[4]; - struct hd_geometry *geo = (struct hd_geometry *)arg; ida_ioctl_t *io = (ida_ioctl_t*)arg; ida_ioctl_t my_io; switch(cmd) { case HDIO_GETGEO: - if (hba[ctlr]->drv[dsk].cylinders) { - diskinfo[0] = hba[ctlr]->drv[dsk].heads; - diskinfo[1] = hba[ctlr]->drv[dsk].sectors; - diskinfo[2] = hba[ctlr]->drv[dsk].cylinders; - } else { - diskinfo[0] = 0xff; - diskinfo[1] = 0x3f; - diskinfo[2] = hba[ctlr]->drv[dsk].nr_blks / (0xff*0x3f); - } - put_user(diskinfo[0], &geo->heads); - put_user(diskinfo[1], &geo->sectors); - put_user(diskinfo[2], &geo->cylinders); - put_user(ida[(ctlr<i_rdev)].start_sect, &geo->start); - return 0; + { + struct hd_geometry driver_geo; + if (hba[ctlr]->drv[dsk].cylinders) { + driver_geo.heads = hba[ctlr]->drv[dsk].heads; + driver_geo.sectors = hba[ctlr]->drv[dsk].sectors; + driver_geo.cylinders = hba[ctlr]->drv[dsk].cylinders; + } else { + driver_geo.heads = 0xff; + driver_geo.sectors = 0x3f; + driver_geo.cylinders = hba[ctlr]->drv[dsk].nr_blks / (0xff*0x3f); + } + driver_geo.start= + hba[ctlr]->hd[MINOR(inode->i_rdev)].start_sect; + if (copy_to_user((void *) arg, &driver_geo, + sizeof( struct hd_geometry))) + return -EFAULT; + return(0); + } + case HDIO_GETGEO_BIG: + { + struct hd_big_geometry driver_geo; + if (hba[ctlr]->drv[dsk].cylinders) { + driver_geo.heads = hba[ctlr]->drv[dsk].heads; + driver_geo.sectors = hba[ctlr]->drv[dsk].sectors; + driver_geo.cylinders = hba[ctlr]->drv[dsk].cylinders; + } else { + driver_geo.heads = 0xff; + driver_geo.sectors = 0x3f; + driver_geo.cylinders = hba[ctlr]->drv[dsk].nr_blks / (0xff*0x3f); + } + driver_geo.start= + hba[ctlr]->hd[MINOR(inode->i_rdev)].start_sect; + if (copy_to_user((void *) arg, &driver_geo, + sizeof( struct hd_big_geometry))) + return -EFAULT; + return(0); + } case IDAGETDRVINFO: return copy_to_user(&io->c.drv,&hba[ctlr]->drv[dsk],sizeof(drv_info_t)); case BLKGETSIZE: - return put_user(ida[(ctlr<i_rdev)].nr_sects, (long*)arg); + if (!arg) return -EINVAL; + return put_user(hba[ctlr]->hd[MINOR(inode->i_rdev)].nr_sects, + (long*)arg); case BLKGETSIZE64: - return put_user((u64)(ida[(ctlr<i_rdev)].nr_sects) << 9, (u64*)arg); + return put_user((u64)(hba[ctlr]->hd[MINOR(inode->i_rdev)].nr_sects) << 9, (u64*)arg); case BLKRRPART: return revalidate_logvol(inode->i_rdev, 1); case IDAPASSTHRU: @@ -1254,6 +1330,13 @@ return -EFAULT; return(0); } + case IDADEREGDISK: + return( deregister_disk(ctlr,dsk)); + case IDAREGNEWDISK: + { + int logvol = arg; + return(register_new_disk(ctlr, logvol)); + } case BLKFLSBUF: case BLKBSZSET: @@ -1595,12 +1678,12 @@ * Set the partition and block size structures for all volumes * on this controller to zero. We will reread all of this data */ - memset(ida+(ctlr*256), 0, sizeof(struct hd_struct)*NWD*16); - memset(ida_sizes+(ctlr*256), 0, sizeof(int)*NWD*16); - memset(ida_blocksizes+(ctlr*256), 0, sizeof(int)*NWD*16); - memset(ida_hardsizes+(ctlr*256), 0, sizeof(int)*NWD*16); - memset(hba[ctlr]->drv, 0, sizeof(drv_info_t)*NWD); - ida_gendisk[ctlr].nr_real = 0; + memset(hba[ctlr]->hd, 0, sizeof(struct hd_struct)*NWD*16); + memset(hba[ctlr]->sizes, 0, sizeof(int)*NWD*16); + memset(hba[ctlr]->blocksizes, 0, sizeof(int)*NWD*16); + memset(hba[ctlr]->hardsizes, 0, sizeof(int)*NWD*16); + memset(hba[ctlr]->drv, 0, sizeof(drv_info_t)*NWD); + hba[ctlr]->gendisk.nr_real = 0; /* * Tell the array controller not to give us any interrupts while @@ -1613,13 +1696,257 @@ ida_geninit(ctlr); for(i=0; isizes[i<usage_count--; return 0; } +static int deregister_disk(int ctlr, int logvol) +{ + unsigned long flags; + struct gendisk *gdev = &(hba[ctlr]->gendisk); + ctlr_info_t *h = hba[ctlr]; + int start, max_p, i; + + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + spin_lock_irqsave(&io_request_lock, flags); + /* make sure logical volume is NOT is use */ + if( h->drv[logvol].usage_count > 1) + { + spin_unlock_irqrestore(&io_request_lock, flags); + return -EBUSY; + } + h->drv[logvol].usage_count++; + spin_unlock_irqrestore(&io_request_lock, flags); + + /* invalidate the devices and deregister the disk */ + max_p = gdev->max_p; + start = logvol << gdev->minor_shift; + for (i=max_p-1; i>=0; i--) + { + int minor = start+i; + // printk("invalidating( %d %d)\n", ctlr, minor); + invalidate_device(MKDEV(MAJOR_NR+ctlr, minor), 1); + /* so open will now fail */ + hba[ctlr]->sizes[minor] = 0; + } + /* check to see if it was the last disk */ + if (logvol == h->highest_lun) + { + /* if so, find the new hightest lun */ + int i, newhighest =-1; + for(i=0; ihighest_lun; i++) + { + /* if the disk has size > 0, it is available */ + if (hba[ctlr]->sizes[i << gdev->minor_shift] != 0) + newhighest = i; + } + h->highest_lun = newhighest; + + } + --h->log_drives; + gdev->nr_real = h->highest_lun+1; + /* zero out the disk size info */ + h->drv[logvol].nr_blks = 0; + h->drv[logvol].blk_size = 0; + return(0); + +} + +static int sendcmd_withirq( + __u8 cmd, + int ctlr, + void *buff, + size_t size, + unsigned int blk, + unsigned int blkcnt, + unsigned int log_unit ) +{ + cmdlist_t *c; + unsigned long flags; + ctlr_info_t *info_p = hba[ctlr]; + + c = cmd_alloc(info_p, 0); + if(!c) + return IO_ERROR; + c->type = CMD_IOCTL_PEND; + c->ctlr = ctlr; + c->hdr.unit = log_unit; + c->hdr.prio = 0; + c->hdr.size = sizeof(rblk_t) >> 2; + c->size += sizeof(rblk_t); + + /* The request information. */ + c->req.hdr.next = 0; + c->req.hdr.rcode = 0; + c->req.bp = 0; + c->req.hdr.sg_cnt = 1; + c->req.hdr.reserved = 0; + + if (size == 0) + c->req.sg[0].size = 512; + else + c->req.sg[0].size = size; + + c->req.hdr.blk = blk; + c->req.hdr.blk_cnt = blkcnt; + c->req.hdr.cmd = (unsigned char) cmd; + c->req.sg[0].addr = (__u32) pci_map_single(info_p->pci_dev, + buff, c->req.sg[0].size, PCI_DMA_BIDIRECTIONAL); + + /* Put the request on the tail of the request queue */ + spin_lock_irqsave(&io_request_lock, flags); + addQ(&info_p->reqQ, c); + info_p->Qdepth++; + start_io(info_p); + spin_unlock_irqrestore(&io_request_lock, flags); + + /* Wait for completion */ + while(c->type != CMD_IOCTL_DONE) + schedule(); + + if (c->req.hdr.rcode & RCODE_FATAL) { + printk(KERN_WARNING "Fatal error on ida/c%dd%d\n", + c->ctlr, c->hdr.unit); + cmd_free(info_p, c, 0); + return(IO_ERROR); + } + if (c->req.hdr.rcode & RCODE_INVREQ) { + printk(KERN_WARNING "Invalid request on ida/c%dd%d = (cmd=%x sect=%d cnt=%d sg=%d ret=%x)\n", + c->ctlr, c->hdr.unit, c->req.hdr.cmd, + c->req.hdr.blk, c->req.hdr.blk_cnt, + c->req.hdr.sg_cnt, c->req.hdr.rcode); + cmd_free(info_p, c, 0); + return(IO_ERROR); + } + cmd_free(info_p, c, 0); + return(IO_OK); +} + +static int register_new_disk(int ctlr, int logvol) +{ + struct gendisk *gdev = &(hba[ctlr]->gendisk); + ctlr_info_t *info_p = hba[ctlr]; + int ret_code, size; + sense_log_drv_stat_t *id_lstatus_buf; + id_log_drv_t *id_ldrive; + drv_info_t *drv; + int max_p; + int start; + int i; + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + if( (logvol < 0) || (logvol >= 16)) + return -EINVAL; + /* disk is already registered */ + if(hba[ctlr]->sizes[logvol << gdev->minor_shift] != 0 ) + return -EINVAL; + + id_ldrive = (id_log_drv_t *)kmalloc(sizeof(id_log_drv_t), GFP_KERNEL); + if(id_ldrive == NULL) + { + printk( KERN_ERR "cpqarray: out of memory.\n"); + return -1; + } + id_lstatus_buf = (sense_log_drv_stat_t *)kmalloc(sizeof(sense_log_drv_stat_t), GFP_KERNEL); + if(id_lstatus_buf == NULL) + { + kfree(id_ldrive); + printk( KERN_ERR "cpqarray: out of memory.\n"); + return -1; + } + + size = sizeof(sense_log_drv_stat_t); + + /* + Send "Identify logical drive status" cmd + */ + ret_code = sendcmd_withirq(SENSE_LOG_DRV_STAT, + ctlr, id_lstatus_buf, size, 0, 0, logvol); + if (ret_code == IO_ERROR) + { + /* + If can't get logical drive status, set + the logical drive map to 0, so the + idastubopen will fail for all logical drives + on the controller. + */ + /* Free all the buffers and return */ + + kfree(id_lstatus_buf); + kfree(id_ldrive); + return -1; + } + /* + Make sure the logical drive is configured + */ + if (id_lstatus_buf->status == LOG_NOT_CONF) + { + printk(KERN_WARNING "cpqarray: c%dd%d array not configured\n", + ctlr, logvol); + kfree(id_lstatus_buf); + kfree(id_ldrive); + return -1; + } + ret_code = sendcmd_withirq(ID_LOG_DRV, ctlr, id_ldrive, + sizeof(id_log_drv_t), 0, 0, logvol); + /* + If error, the bit for this + logical drive won't be set and + idastubopen will return error. + */ + if (ret_code == IO_ERROR) + { + printk(KERN_WARNING "cpqarray: c%dd%d unable to ID logical volume\n", + ctlr,logvol); + kfree(id_lstatus_buf); + kfree(id_ldrive); + return -1; + } + drv = &info_p->drv[logvol]; + drv->blk_size = id_ldrive->blk_size; + drv->nr_blks = id_ldrive->nr_blks; + drv->cylinders = id_ldrive->drv.cyl; + drv->heads = id_ldrive->drv.heads; + drv->sectors = id_ldrive->drv.sect_per_track; + info_p->log_drv_map |= (1 << logvol); + if (info_p->highest_lun < logvol) + info_p->highest_lun = logvol; + + printk(KERN_INFO "cpqarray ida/c%dd%d: blksz=%d nr_blks=%d\n", + ctlr, logvol, drv->blk_size, drv->nr_blks); + + hba[ctlr]->drv[logvol].usage_count = 0; + + max_p = gdev->max_p; + start = logvol<< gdev->minor_shift; + + for(i=max_p-1; i>=0; i--) + { + int minor = start+i; + invalidate_device(MKDEV(MAJOR_NR + ctlr, minor), 1); + gdev->part[minor].start_sect = 0; + gdev->part[minor].nr_sects = 0; + + /* reset the blocksize so we can read the partition table */ + blksize_size[MAJOR_NR+ctlr][minor] = 1024; + } + ++hba[ctlr]->log_drives; + gdev->nr_real = info_p->highest_lun + 1; + /* setup partitions per disk */ + grok_partitions(gdev, logvol, 16, drv->nr_blks); + + kfree(id_lstatus_buf); + kfree(id_ldrive); + return (0); +} + /* Borrowed and adapted from sd.c */ static int revalidate_logvol(kdev_t dev, int maxusage) { @@ -1632,7 +1959,7 @@ target = DEVICE_NR(dev); ctlr = MAJOR(dev) - MAJOR_NR; - gdev = &ida_gendisk[ctlr]; + gdev = &(hba[ctlr]->gendisk); spin_lock_irqsave(&io_request_lock, flags); if (hba[ctlr]->drv[target].usage_count > maxusage) { @@ -1893,6 +2220,8 @@ return; } + if(log_unit > info_p->highest_lun) + info_p->highest_lun = log_unit; info_p->phys_drives = sense_config_buf->ctlr_phys_drv; info_p->drv_assign_map @@ -1912,3 +2241,26 @@ return; } + +static void __exit cleanup_cpqarray_module(void) +{ + int i; + + pci_unregister_driver(&cpqarray_pci_driver); + /* double check that all controller entrys have been removed */ + for (i=0; i< MAX_CTLR; i++) + { + if (hba[i] != NULL) + { + printk(KERN_WARNING "cpqarray: had to remove" + " controller %d\n", i); + cpqarray_remove_one_eisa(i); + } + } + remove_proc_entry("cpqarray", proc_root_driver); +} + + +module_init(init_cpqarray_module); +module_exit(cleanup_cpqarray_module); + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/cpqarray.h linux.ac/drivers/block/cpqarray.h --- linux.vanilla/drivers/block/cpqarray.h Tue May 22 18:23:16 2001 +++ linux.ac/drivers/block/cpqarray.h Fri Oct 12 12:43:36 2001 @@ -87,9 +87,11 @@ __u32 mp_failed_drv_map; char firm_rev[4]; + struct pci_dev *pdev; int ctlr_sig; int log_drives; + int highest_lun; int phys_drives; struct pci_dev *pci_dev; /* NULL if EISA */ @@ -98,7 +100,8 @@ void *vaddr; unsigned long paddr; - unsigned long ioaddr; + unsigned long io_mem_addr; + unsigned long io_mem_length; int intr; int usage_count; drv_info_t drv[NWD]; @@ -120,6 +123,13 @@ unsigned int nr_frees; struct timer_list timer; unsigned int misc_tflags; + // Disk structures we need to pass back + struct gendisk gendisk; + // Index by Minor Numbers + struct hd_struct hd[256]; + int sizes[256]; + int blocksizes[256]; + int hardsizes[256]; }; #endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/elevator.c linux.ac/drivers/block/elevator.c --- linux.vanilla/drivers/block/elevator.c Fri Jul 20 04:59:41 2001 +++ linux.ac/drivers/block/elevator.c Wed Oct 10 01:47:40 2001 @@ -220,3 +220,11 @@ *elevator = type; elevator->queue_ID = queue_ID++; } + +EXPORT_SYMBOL(elevator_init); +EXPORT_SYMBOL(elevator_linus_merge); +EXPORT_SYMBOL(elevator_linus_merge_cleanup); +EXPORT_SYMBOL(elevator_linus_merge_req); +EXPORT_SYMBOL(elevator_noop_merge); +EXPORT_SYMBOL(elevator_noop_merge_cleanup); +EXPORT_SYMBOL(elevator_noop_merge_req); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/genhd.c linux.ac/drivers/block/genhd.c --- linux.vanilla/drivers/block/genhd.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/block/genhd.c Thu Oct 11 09:20:16 2001 @@ -32,10 +32,8 @@ * XXX: you should _never_ access this directly. * the only reason this is exported is source compatiblity. */ -/*static*/ struct gendisk *gendisk_head; - -EXPORT_SYMBOL(gendisk_head); +static struct gendisk *gendisk_head; /** * add_gendisk - add partitioning information to kernel list @@ -164,15 +162,10 @@ #ifdef CONFIG_BLK_DEV_DAC960 extern void DAC960_Initialize(void); #endif -#ifdef CONFIG_FUSION_BOOT -extern int fusion_init(void); -#endif extern int net_dev_init(void); extern void console_map_init(void); -extern int soc_probe(void); extern int atmdev_init(void); extern int i2o_init(void); -extern int cpqarray_init(void); int __init device_init(void) { @@ -184,16 +177,6 @@ #endif #ifdef CONFIG_BLK_DEV_DAC960 DAC960_Initialize(); -#endif -#ifdef CONFIG_FUSION_BOOT - fusion_init(); -#endif -#ifdef CONFIG_FC4_SOC - /* This has to be done before scsi_dev_init */ - soc_probe(); -#endif -#ifdef CONFIG_BLK_CPQ_DA - cpqarray_init(); #endif #ifdef CONFIG_NET net_dev_init(); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/ida_ioctl.h linux.ac/drivers/block/ida_ioctl.h --- linux.vanilla/drivers/block/ida_ioctl.h Wed Jul 25 22:12:01 2001 +++ linux.ac/drivers/block/ida_ioctl.h Fri Oct 12 12:43:36 2001 @@ -31,6 +31,8 @@ #define IDAREVALIDATEVOLS 0x30303131 #define IDADRIVERVERSION 0x31313232 #define IDAGETPCIINFO 0x32323333 +#define IDADEREGDISK 0x33333434 +#define IDAREGNEWDISK 0x34343535 typedef struct _ida_pci_info_struct { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/ll_rw_blk.c linux.ac/drivers/block/ll_rw_blk.c --- linux.vanilla/drivers/block/ll_rw_blk.c Fri Sep 21 05:02:01 2001 +++ linux.ac/drivers/block/ll_rw_blk.c Wed Oct 10 01:47:40 2001 @@ -118,10 +118,17 @@ int * max_sectors[MAX_BLKDEV]; /* - * How many reqeusts do we allocate per queue, - * and how many do we "batch" on freeing them? + * queued sectors for all devices, used to make sure we don't fill all + * of memory with locked buffers */ -static int queue_nr_requests, batch_requests; +atomic_t queued_sectors; + +/* + * high and low watermark for above + */ +static int high_queued_sectors, low_queued_sectors; +static int batch_requests, queue_nr_requests; +static DECLARE_WAIT_QUEUE_HEAD(blk_buffers_wait); static inline int get_max_sectors(kdev_t dev) { @@ -568,6 +575,13 @@ */ if (q) { /* + * we've released enough buffers to start I/O again + */ + if (waitqueue_active(&blk_buffers_wait) + && atomic_read(&queued_sectors) < low_queued_sectors) + wake_up(&blk_buffers_wait); + + /* * Add to pending free list and batch wakeups */ list_add(&req->table, &q->pending_freelist[rw]); @@ -996,6 +1010,16 @@ for (i = 0; i < nr; i++) { struct buffer_head *bh = bhs[i]; + /* + * don't lock any more buffers if we are above the high + * water mark. instead start I/O on the queued stuff. + */ + if (atomic_read(&queued_sectors) >= high_queued_sectors) { + run_task_queue(&tq_disk); + wait_event(blk_buffers_wait, + atomic_read(&queued_sectors) < low_queued_sectors); + } + /* Only one thread can actually submit the I/O. */ if (test_and_set_bit(BH_Lock, &bh->b_state)) continue; @@ -1122,21 +1146,42 @@ memset(max_readahead, 0, sizeof(max_readahead)); memset(max_sectors, 0, sizeof(max_sectors)); + atomic_set(&queued_sectors, 0); total_ram = nr_free_pages() << (PAGE_SHIFT - 10); /* - * Free request slots per queue. - * (Half for reads, half for writes) + * Try to keep 128MB max hysteris. If not possible, + * use half of RAM + */ + high_queued_sectors = (total_ram * 2) / 3; + low_queued_sectors = high_queued_sectors / 3; + if (high_queued_sectors - low_queued_sectors > MB(128)) + low_queued_sectors = high_queued_sectors - MB(128); + + + /* + * make it sectors (512b) */ - queue_nr_requests = 64; - if (total_ram > MB(32)) - queue_nr_requests = 128; + high_queued_sectors <<= 1; + low_queued_sectors <<= 1; /* - * Batch frees according to queue length + * Scale free request slots per queue too */ - batch_requests = queue_nr_requests >> 3; - printk("block: %d slots per queue, batch=%d\n", queue_nr_requests, batch_requests); + total_ram = (total_ram + MB(32) - 1) & ~(MB(32) - 1); + if ((queue_nr_requests = total_ram >> 9) > QUEUE_NR_REQUESTS) + queue_nr_requests = QUEUE_NR_REQUESTS; + + /* + * adjust batch frees according to queue length, with upper limit + */ + if ((batch_requests = queue_nr_requests >> 3) > 32) + batch_requests = 32; + + printk("block: queued sectors max/low %dkB/%dkB, %d slots per queue\n", + high_queued_sectors / 2, + low_queued_sectors / 2, + queue_nr_requests); #ifdef CONFIG_AMIGA_Z2RAM z2_init(); @@ -1256,3 +1301,4 @@ EXPORT_SYMBOL(generic_make_request); EXPORT_SYMBOL(blkdev_release_request); EXPORT_SYMBOL(generic_unplug_device); +EXPORT_SYMBOL(queued_sectors); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/loop.c linux.ac/drivers/block/loop.c --- linux.vanilla/drivers/block/loop.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/block/loop.c Thu Oct 11 22:07:41 2001 @@ -180,6 +180,8 @@ unsigned size, offset; int len; + down(&(file->f_dentry->d_inode->i_sem)); + index = pos >> PAGE_CACHE_SHIFT; offset = pos & (PAGE_CACHE_SIZE - 1); len = bh->b_size; @@ -197,8 +199,11 @@ goto unlock; kaddr = page_address(page); flush_dcache_page(page); - if (lo_do_transfer(lo, WRITE, kaddr + offset, data, size, IV)) + if (lo_do_transfer(lo, WRITE, kaddr + offset, data, size, IV)) { + if (aops->abort_write) + aops->abort_write(file, page); goto write_fail; + } if (aops->commit_write(file, page, offset, offset+size)) goto unlock; data += size; @@ -210,6 +215,7 @@ deactivate_page(page); page_cache_release(page); } + up(&(file->f_dentry->d_inode->i_sem)); return 0; write_fail: @@ -221,6 +227,7 @@ deactivate_page(page); page_cache_release(page); fail: + up(&(file->f_dentry->d_inode->i_sem)); return -1; } @@ -719,7 +726,7 @@ return err; } -static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) +static int loop_clr_fd(struct loop_device *lo, kdev_t dev) { struct file *filp = lo->lo_backing_file; int gfp = lo->old_gfp_mask; @@ -752,7 +759,7 @@ memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE); memset(lo->lo_name, 0, LO_NAME_SIZE); loop_sizes[lo->lo_number] = 0; - invalidate_bdev(bdev, 0); + invalidate_buffers(dev); filp->f_dentry->d_inode->i_mapping->gfp_mask = gfp; lo->lo_state = Lo_unbound; fput(filp); @@ -852,7 +859,7 @@ err = loop_set_fd(lo, file, inode->i_rdev, arg); break; case LOOP_CLR_FD: - err = loop_clr_fd(lo, inode->i_bdev); + err = loop_clr_fd(lo, inode->i_rdev); break; case LOOP_SET_STATUS: err = loop_set_status(lo, (struct loop_info *) arg); @@ -951,6 +958,7 @@ */ MODULE_PARM(max_loop, "i"); MODULE_PARM_DESC(max_loop, "Maximum number of loop devices (1-255)"); +MODULE_LICENSE("GPL"); int loop_register_transfer(struct loop_func_table *funcs) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/nbd.c linux.ac/drivers/block/nbd.c --- linux.vanilla/drivers/block/nbd.c Mon Sep 10 20:42:31 2001 +++ linux.ac/drivers/block/nbd.c Wed Oct 10 01:47:40 2001 @@ -542,4 +542,6 @@ module_exit(nbd_cleanup); MODULE_DESCRIPTION("Network Block Device"); +MODULE_LICENSE("GPL"); + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/aten.c linux.ac/drivers/block/paride/aten.c --- linux.vanilla/drivers/block/paride/aten.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/aten.c Wed Oct 10 01:47:40 2001 @@ -171,3 +171,4 @@ #endif /* end of aten.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/bpck.c linux.ac/drivers/block/paride/bpck.c --- linux.vanilla/drivers/block/paride/bpck.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/bpck.c Wed Oct 10 01:47:40 2001 @@ -482,3 +482,4 @@ #endif /* end of bpck.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/comm.c linux.ac/drivers/block/paride/comm.c --- linux.vanilla/drivers/block/paride/comm.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/comm.c Wed Oct 10 01:47:40 2001 @@ -227,3 +227,4 @@ #endif /* end of comm.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/dstr.c linux.ac/drivers/block/paride/dstr.c --- linux.vanilla/drivers/block/paride/dstr.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/dstr.c Wed Oct 10 01:47:40 2001 @@ -242,3 +242,4 @@ #endif /* end of dstr.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/epat.c linux.ac/drivers/block/paride/epat.c --- linux.vanilla/drivers/block/paride/epat.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/epat.c Wed Oct 10 01:47:40 2001 @@ -320,3 +320,4 @@ #endif /* end of epat.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/epia.c linux.ac/drivers/block/paride/epia.c --- linux.vanilla/drivers/block/paride/epia.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/epia.c Wed Oct 10 01:47:40 2001 @@ -325,3 +325,4 @@ /* end of epia.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/fit2.c linux.ac/drivers/block/paride/fit2.c --- linux.vanilla/drivers/block/paride/fit2.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/fit2.c Wed Oct 10 01:47:40 2001 @@ -160,3 +160,4 @@ #endif /* end of fit2.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/fit3.c linux.ac/drivers/block/paride/fit3.c --- linux.vanilla/drivers/block/paride/fit3.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/fit3.c Wed Oct 10 01:47:40 2001 @@ -220,3 +220,4 @@ #endif /* end of fit3.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/friq.c linux.ac/drivers/block/paride/friq.c --- linux.vanilla/drivers/block/paride/friq.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/friq.c Wed Oct 10 01:47:40 2001 @@ -281,3 +281,4 @@ #endif /* end of friq.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/frpw.c linux.ac/drivers/block/paride/frpw.c --- linux.vanilla/drivers/block/paride/frpw.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/frpw.c Wed Oct 10 01:47:40 2001 @@ -322,3 +322,4 @@ #endif /* end of frpw.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/kbic.c linux.ac/drivers/block/paride/kbic.c --- linux.vanilla/drivers/block/paride/kbic.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/kbic.c Wed Oct 10 01:47:40 2001 @@ -310,3 +310,4 @@ #endif /* end of kbic.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/ktti.c linux.ac/drivers/block/paride/ktti.c --- linux.vanilla/drivers/block/paride/ktti.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/ktti.c Wed Oct 10 01:47:40 2001 @@ -137,3 +137,4 @@ #endif /* end of ktti.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/on20.c linux.ac/drivers/block/paride/on20.c --- linux.vanilla/drivers/block/paride/on20.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/on20.c Wed Oct 10 01:47:40 2001 @@ -162,3 +162,4 @@ #endif /* end of on20.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/on26.c linux.ac/drivers/block/paride/on26.c --- linux.vanilla/drivers/block/paride/on26.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/on26.c Wed Oct 10 01:47:40 2001 @@ -328,3 +328,4 @@ /* end of on26.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/paride.c linux.ac/drivers/block/paride/paride.c --- linux.vanilla/drivers/block/paride/paride.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/block/paride/paride.c Thu Oct 11 14:29:16 2001 @@ -547,3 +547,4 @@ #endif /* end of paride.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/pcd.c linux.ac/drivers/block/paride/pcd.c --- linux.vanilla/drivers/block/paride/pcd.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/block/paride/pcd.c Wed Oct 10 01:47:40 2001 @@ -948,3 +948,4 @@ /* end of pcd.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/pd.c linux.ac/drivers/block/paride/pd.c --- linux.vanilla/drivers/block/paride/pd.c Mon Sep 10 20:42:31 2001 +++ linux.ac/drivers/block/paride/pd.c Wed Oct 10 01:47:40 2001 @@ -586,8 +586,7 @@ } void cleanup_module(void) - -{ struct gendisk **gdp; +{ int unit; devfs_unregister_blkdev(MAJOR_NR,name); @@ -1069,3 +1068,4 @@ /* end of pd.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/pf.c linux.ac/drivers/block/paride/pf.c --- linux.vanilla/drivers/block/paride/pf.c Mon Sep 10 20:42:31 2001 +++ linux.ac/drivers/block/paride/pf.c Wed Oct 10 01:47:40 2001 @@ -1111,3 +1111,4 @@ /* end of pf.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/pg.c linux.ac/drivers/block/paride/pg.c --- linux.vanilla/drivers/block/paride/pg.c Fri Feb 9 19:30:22 2001 +++ linux.ac/drivers/block/paride/pg.c Wed Oct 10 01:47:40 2001 @@ -694,3 +694,4 @@ /* end of pg.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/ppc6lnx.c linux.ac/drivers/block/paride/ppc6lnx.c --- linux.vanilla/drivers/block/paride/ppc6lnx.c Fri Sep 7 17:28:38 2001 +++ linux.ac/drivers/block/paride/ppc6lnx.c Wed Oct 10 01:47:40 2001 @@ -724,3 +724,4 @@ //*************************************************************************** +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/paride/pt.c linux.ac/drivers/block/paride/pt.c --- linux.vanilla/drivers/block/paride/pt.c Thu Jun 28 01:10:55 2001 +++ linux.ac/drivers/block/paride/pt.c Wed Oct 10 01:47:40 2001 @@ -964,3 +964,4 @@ /* end of pt.c */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/rd.c linux.ac/drivers/block/rd.c --- linux.vanilla/drivers/block/rd.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/block/rd.c Thu Oct 11 14:29:22 2001 @@ -187,134 +187,6 @@ #endif /* - * Copyright (C) 2000 Linus Torvalds. - * 2000 Transmeta Corp. - * aops copied from ramfs. - */ -static int ramdisk_readpage(struct file *file, struct page * page) -{ - if (!Page_Uptodate(page)) { - memset(kmap(page), 0, PAGE_CACHE_SIZE); - kunmap(page); - flush_dcache_page(page); - SetPageUptodate(page); - } - UnlockPage(page); - return 0; -} - -/* - * Writing: just make sure the page gets marked dirty, so that - * the page stealer won't grab it. - */ -static int ramdisk_writepage(struct page *page) -{ - SetPageDirty(page); - UnlockPage(page); - return 0; -} - -static int ramdisk_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) -{ - if (!Page_Uptodate(page)) { - void *addr = page_address(page); - memset(addr, 0, PAGE_CACHE_SIZE); - flush_dcache_page(page); - SetPageUptodate(page); - } - SetPageDirty(page); - return 0; -} - -static int ramdisk_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) -{ - return 0; -} - -static struct address_space_operations ramdisk_aops = { - readpage: ramdisk_readpage, - writepage: ramdisk_writepage, - prepare_write: ramdisk_prepare_write, - commit_write: ramdisk_commit_write, -}; - -static int rd_blkdev_pagecache_IO(int rw, struct buffer_head * sbh, int minor) -{ - struct address_space * mapping; - unsigned long index; - int offset, size, err; - - err = -EIO; - err = 0; - mapping = rd_bdev[minor]->bd_inode->i_mapping; - - index = sbh->b_rsector >> (PAGE_CACHE_SHIFT - 9); - offset = (sbh->b_rsector << 9) & ~PAGE_CACHE_MASK; - size = sbh->b_size; - - do { - int count; - struct page ** hash; - struct page * page; - char * src, * dst; - int unlock = 0; - - count = PAGE_CACHE_SIZE - offset; - if (count > size) - count = size; - size -= count; - - hash = page_hash(mapping, index); - page = __find_get_page(mapping, index, hash); - if (!page) { - page = grab_cache_page(mapping, index); - err = -ENOMEM; - if (!page) - goto out; - err = 0; - - if (!Page_Uptodate(page)) { - memset(kmap(page), 0, PAGE_CACHE_SIZE); - kunmap(page); - SetPageUptodate(page); - } - - unlock = 1; - } - - index++; - - if (rw == READ) { - src = kmap(page); - src += offset; - dst = bh_kmap(sbh); - } else { - dst = kmap(page); - dst += offset; - src = bh_kmap(sbh); - } - offset = 0; - - memcpy(dst, src, count); - - kunmap(page); - bh_kunmap(sbh); - - if (rw == READ) { - flush_dcache_page(page); - } else { - SetPageDirty(page); - } - if (unlock) - UnlockPage(page); - __free_page(page); - } while (size); - - out: - return err; -} - -/* * Basically, my strategy here is to set up a buffer-head which can't be * deleted, and make that my Ramdisk. If the request is outside of the * allocated size, we must get rid of it... @@ -326,7 +198,10 @@ { unsigned int minor; unsigned long offset, len; + struct buffer_head *rbh; + char *bdata; + minor = MINOR(sbh->b_rdev); if (minor >= NUM_RAMDISKS) @@ -346,8 +221,20 @@ goto fail; } - if (rd_blkdev_pagecache_IO(rw, sbh, minor)) - goto fail; + rbh = getblk(sbh->b_rdev, sbh->b_rsector/(sbh->b_size>>9), sbh->b_size); + /* I think that it is safe to assume that rbh is not in HighMem, though + * sbh might be - NeilBrown + */ + bdata = bh_kmap(sbh); + if (rw == READ) { + if (sbh != rbh) + memcpy(bdata, rbh->b_data, rbh->b_size); + } else + if (sbh != rbh) + memcpy(rbh->b_data, bdata, rbh->b_size); + bh_kunmap(sbh); + mark_buffer_protected(rbh); + brelse(rbh); sbh->b_end_io(sbh,1); return 0; @@ -358,11 +245,10 @@ static int rd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int error = -EINVAL; unsigned int minor; if (!inode || !inode->i_rdev) - goto out; + return -EINVAL; minor = MINOR(inode->i_rdev); @@ -373,29 +259,29 @@ /* special: we want to release the ramdisk memory, it's not like with the other blockdevices where this ioctl only flushes away the buffer cache. */ - error = -EBUSY; - down(&inode->i_bdev->bd_sem); - if (inode->i_bdev->bd_openers <= 2) { - truncate_inode_pages(inode->i_mapping, 0); - error = 0; - } - up(&inode->i_bdev->bd_sem); + if ((atomic_read(&rd_bdev[minor]->bd_openers) > 2)) + return -EBUSY; + destroy_buffers(inode->i_rdev); + rd_blocksizes[minor] = 0; break; + case BLKGETSIZE: /* Return device size */ - if (!arg) - break; - error = put_user(rd_kbsize[minor] << 1, (long *) arg); - break; + if (!arg) return -EINVAL; + return put_user(rd_kbsize[minor] << 1, (long *) arg); + case BLKGETSIZE64: - error = put_user((u64)rd_kbsize[minor]<<10, (u64*)arg); - break; + return put_user((u64)rd_kbsize[minor] << 10, (u64*)arg); + case BLKROSET: case BLKROGET: case BLKSSZGET: - error = blk_ioctl(inode->i_rdev, cmd, arg); + return blk_ioctl(inode->i_rdev, cmd, arg); + + default: + return -EINVAL; }; -out: - return error; + + return 0; } @@ -421,11 +307,11 @@ lock_kernel(); if (!--initrd_users) { + blkdev_put(inode->i_bdev, BDEV_FILE); free_initrd_mem(initrd_start, initrd_end); initrd_start = 0; } unlock_kernel(); - blkdev_put(inode->i_bdev, BDEV_FILE); return 0; } @@ -459,8 +345,7 @@ */ if (rd_bdev[unit] == NULL) { rd_bdev[unit] = bdget(kdev_t_to_nr(inode->i_rdev)); - rd_bdev[unit]->bd_openers++; - rd_bdev[unit]->bd_inode->i_mapping->a_ops = &ramdisk_aops; + atomic_inc(&rd_bdev[unit]->bd_openers); } MOD_INC_USE_COUNT; @@ -474,7 +359,7 @@ return 0; } -static struct block_device_operations rd_bd_op = { +static struct block_device_operations fd_fops = { open: rd_open, release: rd_release, ioctl: rd_ioctl, @@ -515,7 +400,7 @@ rd_blocksize = BLOCK_SIZE; } - if (register_blkdev(MAJOR_NR, "ramdisk", &rd_bd_op)) { + if (register_blkdev(MAJOR_NR, "ramdisk", &fd_fops)) { printk("RAMDISK: Could not get major %d", MAJOR_NR); return -EIO; } @@ -533,14 +418,14 @@ devfs_register_series (devfs_handle, "%u", NUM_RAMDISKS, DEVFS_FL_DEFAULT, MAJOR_NR, 0, S_IFBLK | S_IRUSR | S_IWUSR, - &rd_bd_op, NULL); + &fd_fops, NULL); for (i = 0; i < NUM_RAMDISKS; i++) - register_disk(NULL, MKDEV(MAJOR_NR,i), 1, &rd_bd_op, rd_size<<1); + register_disk(NULL, MKDEV(MAJOR_NR,i), 1, &fd_fops, rd_size<<1); #ifdef CONFIG_BLK_DEV_INITRD /* We ought to separate initrd operations here */ - register_disk(NULL, MKDEV(MAJOR_NR,INITRD_MINOR), 1, &rd_bd_op, rd_size<<1); + register_disk(NULL, MKDEV(MAJOR_NR,INITRD_MINOR), 1, &fd_fops, rd_size<<1); #endif hardsect_size[MAJOR_NR] = rd_hardsec; /* Size of the RAM disk blocks */ @@ -712,10 +597,8 @@ outfile.f_op = &def_blk_fops; init_special_inode(out_inode, S_IFBLK | S_IRUSR | S_IWUSR, kdev_t_to_nr(ram_device)); - if (blkdev_open(inode, &infile) != 0) { - iput(out_inode); + if (blkdev_open(inode, &infile) != 0) goto free_inode; - } if (blkdev_open(out_inode, &outfile) != 0) goto free_inodes; @@ -778,15 +661,14 @@ if (i && (i % devblocks == 0)) { printk("done disk #%d.\n", i/devblocks); rotate = 0; - if (infile.f_op->release(inode, &infile) != 0) { - printk("Error closing the disk.\n"); - goto noclose_input; - } + invalidate_buffers(device); + if (infile.f_op->release) + infile.f_op->release(inode, &infile); printk("Please insert disk #%d and press ENTER\n", i/devblocks+1); wait_for_keypress(); if (blkdev_open(inode, &infile) != 0) { printk("Error opening disk.\n"); - goto noclose_input; + goto done; } infile.f_pos = 0; printk("Loading disk #%d... ", i/devblocks+1); @@ -804,20 +686,18 @@ kfree(buf); successful_load: + invalidate_buffers(device); ROOT_DEV = MKDEV(MAJOR_NR, unit); if (ROOT_DEVICE_NAME != NULL) strcpy (ROOT_DEVICE_NAME, "rd/0"); done: - infile.f_op->release(inode, &infile); -noclose_input: - blkdev_close(out_inode, &outfile); - iput(inode); - iput(out_inode); + if (infile.f_op->release) + infile.f_op->release(inode, &infile); set_fs(fs); return; free_inodes: /* free inodes on error */ iput(out_inode); - infile.f_op->release(inode, &infile); + blkdev_put(inode->i_bdev, BDEV_FILE); free_inode: iput(inode); } @@ -975,10 +855,15 @@ static void __init flush_window(void) { ulg c = crc; /* temporary variable */ - unsigned n; + unsigned n, written; uch *in, ch; - crd_outfp->f_op->write(crd_outfp, window, outcnt, &crd_outfp->f_pos); + written = crd_outfp->f_op->write(crd_outfp, window, outcnt, &crd_outfp->f_pos); + if (written != outcnt && exit_code == 0) { + printk(KERN_ERR "RAMDISK: incomplete write (ramdisk too small?) " + "(%d != %d)\n", written, outcnt); + exit_code = 1; + } in = window; for (n = 0; n < outcnt; n++) { ch = *in++; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/smart1,2.h linux.ac/drivers/block/smart1,2.h --- linux.vanilla/drivers/block/smart1,2.h Wed May 2 00:05:00 2001 +++ linux.ac/drivers/block/smart1,2.h Thu Oct 11 09:20:16 2001 @@ -156,27 +156,27 @@ */ static void smart2e_submit_command(ctlr_info_t *h, cmdlist_t *c) { - outl(c->busaddr, h->ioaddr + COMMAND_FIFO); + outl(c->busaddr, h->io_mem_addr+ COMMAND_FIFO); } static void smart2e_intr_mask(ctlr_info_t *h, unsigned long val) { - outl(val, h->ioaddr + INTR_MASK); + outl(val, h->io_mem_addr+ INTR_MASK); } static unsigned long smart2e_fifo_full(ctlr_info_t *h) { - return inl(h->ioaddr + COMMAND_FIFO); + return inl(h->io_mem_addr+ COMMAND_FIFO); } static unsigned long smart2e_completed(ctlr_info_t *h) { - return inl(h->ioaddr + COMMAND_COMPLETE_FIFO); + return inl(h->io_mem_addr+ COMMAND_COMPLETE_FIFO); } static unsigned long smart2e_intr_pending(ctlr_info_t *h) { - return inl(h->ioaddr + INTR_PENDING); + return inl(h->io_mem_addr+ INTR_PENDING); } static struct access_method smart2e_access = { @@ -212,30 +212,30 @@ */ c->hdr.size = 0; - outb(CHANNEL_CLEAR, h->ioaddr + SMART1_SYSTEM_DOORBELL); + outb(CHANNEL_CLEAR, h->io_mem_addr + SMART1_SYSTEM_DOORBELL); - outl(c->busaddr, h->ioaddr + SMART1_LISTADDR); - outw(c->size, h->ioaddr + SMART1_LISTLEN); + outl(c->busaddr, h->io_mem_addr + SMART1_LISTADDR); + outw(c->size, h->io_mem_addr + SMART1_LISTLEN); - outb(CHANNEL_BUSY, h->ioaddr + SMART1_LOCAL_DOORBELL); + outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_LOCAL_DOORBELL); } static void smart1_intr_mask(ctlr_info_t *h, unsigned long val) { if (val == 1) { - outb(0xFD, h->ioaddr + SMART1_SYSTEM_DOORBELL); - outb(CHANNEL_BUSY, h->ioaddr + SMART1_LOCAL_DOORBELL); - outb(0x01, h->ioaddr + SMART1_INTR_MASK); - outb(0x01, h->ioaddr + SMART1_SYSTEM_MASK); + outb(0xFD, h->io_mem_addr + SMART1_SYSTEM_DOORBELL); + outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_LOCAL_DOORBELL); + outb(0x01, h->io_mem_addr + SMART1_INTR_MASK); + outb(0x01, h->io_mem_addr + SMART1_SYSTEM_MASK); } else { - outb(0, h->ioaddr + 0xC8E); + outb(0, h->io_mem_addr + 0xC8E); } } static unsigned long smart1_fifo_full(ctlr_info_t *h) { unsigned char chan; - chan = inb(h->ioaddr + SMART1_SYSTEM_DOORBELL) & CHANNEL_CLEAR; + chan = inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_CLEAR; return chan; } @@ -244,13 +244,13 @@ unsigned char status; unsigned long cmd; - if (inb(h->ioaddr + SMART1_SYSTEM_DOORBELL) & CHANNEL_BUSY) { - outb(CHANNEL_BUSY, h->ioaddr + SMART1_SYSTEM_DOORBELL); + if (inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_BUSY) { + outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_SYSTEM_DOORBELL); - cmd = inl(h->ioaddr + SMART1_COMPLETE_ADDR); - status = inb(h->ioaddr + SMART1_LISTSTATUS); + cmd = inl(h->io_mem_addr + SMART1_COMPLETE_ADDR); + status = inb(h->io_mem_addr + SMART1_LISTSTATUS); - outb(CHANNEL_CLEAR, h->ioaddr + SMART1_LOCAL_DOORBELL); + outb(CHANNEL_CLEAR, h->io_mem_addr + SMART1_LOCAL_DOORBELL); if (cmd) ((cmdlist_t*)bus_to_virt(cmd))->req.hdr.rcode = status; } else { @@ -262,7 +262,7 @@ static unsigned long smart1_intr_pending(ctlr_info_t *h) { unsigned char chan; - chan = inb(h->ioaddr + SMART1_SYSTEM_DOORBELL) & CHANNEL_BUSY; + chan = inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_BUSY; return chan; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/cdrom/cdrom.c linux.ac/drivers/cdrom/cdrom.c --- linux.vanilla/drivers/cdrom/cdrom.c Thu Aug 16 17:30:45 2001 +++ linux.ac/drivers/cdrom/cdrom.c Wed Oct 10 01:47:40 2001 @@ -279,12 +279,17 @@ static int lockdoor = 1; /* will we ever get to use this... sigh. */ static int check_media_type; +static unsigned long *cdrom_numbers; +static DECLARE_MUTEX(cdrom_sem); + MODULE_PARM(debug, "i"); MODULE_PARM(autoclose, "i"); MODULE_PARM(autoeject, "i"); MODULE_PARM(lockdoor, "i"); MODULE_PARM(check_media_type, "i"); +MODULE_LICENSE("GPL"); + #if (ERRLOGMASK!=CD_NOTHING) #define cdinfo(type, fmt, args...) \ if ((ERRLOGMASK & type) || debug==1 ) \ @@ -323,15 +328,14 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, unsigned long arg); -int cdrom_get_last_written(kdev_t dev, long *last_written); -int cdrom_get_next_writable(kdev_t dev, long *next_writable); +int cdrom_get_last_written(kdev_t dev, unsigned int *last_written); +int cdrom_get_next_writable(kdev_t dev, unsigned int *next_writable); #ifdef CONFIG_SYSCTL static void cdrom_sysctl_register(void); #endif /* CONFIG_SYSCTL */ static struct cdrom_device_info *topCdromPtr; static devfs_handle_t devfs_handle; -static struct unique_numspace cdrom_numspace = UNIQUE_NUMBERSPACE_INITIALISER; struct block_device_operations cdrom_fops = { @@ -341,13 +345,45 @@ check_media_change: cdrom_media_changed, }; +/* + * get or clear a new cdrom number, run under cdrom_sem + */ +static int cdrom_get_entry(void) +{ + int i, nr, foo; + + nr = 0; + foo = -1; + for (i = 0; i < CDROM_MAX_CDROMS / (sizeof(unsigned long) * 8); i++) { + if (cdrom_numbers[i] == ~0UL) { + nr += sizeof(unsigned long) * 8; + continue; + } + foo = ffz(cdrom_numbers[i]); + set_bit(foo, &cdrom_numbers[i]); + nr += foo; + break; + } + + return foo == -1 ? foo : nr; +} + +static void cdrom_clear_entry(struct cdrom_device_info *cdi) +{ + int bit_nr = cdi->nr & ~(sizeof(unsigned long) * 8); + int cd_index = cdi->nr / (sizeof(unsigned long) * 8); + + clear_bit(bit_nr, &cdrom_numbers[cd_index]); +} + + /* This macro makes sure we don't have to check on cdrom_device_ops * existence in the run-time routines below. Change_capability is a * hack to have the capability flags defined const, while we can still * change it here without gcc complaining at every line. */ #define ENSURE(call, bits) if (cdo->call == NULL) *change_capability &= ~(bits) - +static int cdrom_init(void); int register_cdrom(struct cdrom_device_info *cdi) { static char banner_printed; @@ -363,11 +399,9 @@ if (cdo->open == NULL || cdo->release == NULL) return -2; if ( !banner_printed ) { - printk(KERN_INFO "Uniform CD-ROM driver " REVISION "\n"); banner_printed = 1; -#ifdef CONFIG_SYSCTL - cdrom_sysctl_register(); -#endif /* CONFIG_SYSCTL */ + printk(KERN_INFO "Uniform CD-ROM driver " REVISION "\n"); + cdrom_init(); } ENSURE(drive_status, CDC_DRIVE_STATUS ); ENSURE(media_changed, CDC_MEDIA_CHANGED); @@ -395,8 +429,17 @@ if (!devfs_handle) devfs_handle = devfs_mk_dir (NULL, "cdroms", NULL); - cdi->number = devfs_alloc_unique_number (&cdrom_numspace); - sprintf (vname, "cdrom%d", cdi->number); + + /* + * get new cdrom number + */ + down(&cdrom_sem); + cdi->nr = cdrom_get_entry(); + up(&cdrom_sem); + if (cdi->nr == -1) + return -ENOMEM; + + sprintf(vname, "cdrom%u", cdi->nr); if (cdi->de) { int pos; devfs_handle_t slave; @@ -419,9 +462,13 @@ S_IFBLK | S_IRUGO | S_IWUGO, &cdrom_fops, NULL); } - cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name); + + down(&cdrom_sem); cdi->next = topCdromPtr; topCdromPtr = cdi; + up(&cdrom_sem); + + cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name); return 0; } #undef ENSURE @@ -436,6 +483,7 @@ if (major < 0 || major >= MAX_BLKDEV) return -1; + down(&cdrom_sem); prev = NULL; cdi = topCdromPtr; while (cdi != NULL && cdi->dev != unreg->dev) { @@ -443,15 +491,20 @@ cdi = cdi->next; } - if (cdi == NULL) + if (cdi == NULL) { + up(&cdrom_sem); return -2; + } + + cdrom_clear_entry(cdi); + if (prev) prev->next = cdi->next; else topCdromPtr = cdi->next; + up(&cdrom_sem); cdi->ops->n_minors--; - devfs_unregister (cdi->de); - devfs_dealloc_unique_number (&cdrom_numspace, cdi->number); + devfs_unregister(cdi->de); cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" unregistered\n", cdi->name); return 0; } @@ -460,10 +513,14 @@ { struct cdrom_device_info *cdi; + down(&cdrom_sem); + cdi = topCdromPtr; while (cdi != NULL && cdi->dev != dev) cdi = cdi->next; + up(&cdrom_sem); + return cdi; } @@ -1142,14 +1199,23 @@ memset(&rpc_state, 0, sizeof(rpc_state_t)); cgc.buffer = (char *) &rpc_state; - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; - - ai->lrpcs.type = rpc_state.type_code; - ai->lrpcs.vra = rpc_state.vra; - ai->lrpcs.ucca = rpc_state.ucca; - ai->lrpcs.region_mask = rpc_state.region_mask; - ai->lrpcs.rpc_scheme = rpc_state.rpc_scheme; + /* + * we already checked dvd capability, so if it fails + * assume it's a rpc phase-1 drive and report that back. + * this is necessary at least on some toshiba drives, + * that don't support REPORT_KEY with key format 001000b + */ + cgc.quiet = 1; + if ((ret = cdo->generic_packet(cdi, &cgc))) { + ai->lrpcs.type = 0; + ai->lrpcs.rpc_scheme = 0; + } else { + ai->lrpcs.type = rpc_state.type_code; + ai->lrpcs.vra = rpc_state.vra; + ai->lrpcs.ucca = rpc_state.ucca; + ai->lrpcs.region_mask = rpc_state.region_mask; + ai->lrpcs.rpc_scheme = rpc_state.rpc_scheme; + } break; /* Set region settings */ @@ -1899,8 +1965,10 @@ if (cgc->data_direction == CGC_DATA_UNKNOWN) return -EINVAL; + if (cgc->data_direction == CGC_DATA_NONE && cgc->buflen) + return -EINVAL; - if (cgc->buflen < 0 || cgc->buflen >= 131072) + if (cgc->buflen >= 131072) return -EINVAL; if ((ubuf = cgc->buffer)) { @@ -1911,24 +1979,24 @@ usense = cgc->sense; cgc->sense = &sense; + ret = -EFAULT; if (usense && !access_ok(VERIFY_WRITE, usense, sizeof(*usense))) - return -EFAULT; + goto out; if (cgc->data_direction == CGC_DATA_READ) { if (!access_ok(VERIFY_READ, ubuf, cgc->buflen)) - return -EFAULT; - } else if (cgc->data_direction == CGC_DATA_WRITE) { - if (copy_from_user(cgc->buffer, ubuf, cgc->buflen)) { - kfree(cgc->buffer); - return -EFAULT; - } - } + goto out; + } else if (cgc->data_direction == CGC_DATA_WRITE) + if (copy_from_user(cgc->buffer, ubuf, cgc->buflen)) + goto out; ret = cdi->ops->generic_packet(cdi, cgc); __copy_to_user(usense, cgc->sense, sizeof(*usense)); if (!ret && cgc->data_direction == CGC_DATA_READ) __copy_to_user(ubuf, cgc->buffer, cgc->buflen); - kfree(cgc->buffer); +out: + if (cgc->buffer) + kfree(cgc->buffer); return ret; } @@ -1938,7 +2006,7 @@ struct cdrom_device_ops *cdo = cdi->ops; struct cdrom_generic_command cgc; kdev_t dev = cdi->dev; - char buffer[32]; + unsigned char buffer[32]; int ret = 0; memset(&cgc, 0, sizeof(cgc)); @@ -2008,7 +2076,6 @@ else return -EINVAL; - /* FIXME: we need upper bound checking, too!! */ if (lba < 0 || ra.nframes <= 0) return -EINVAL; @@ -2016,6 +2083,9 @@ * start with will ra.nframes size, back down if alloc fails */ nr = ra.nframes; + if(nr > 64) + nr = 64; + do { cgc.buffer = kmalloc(CD_FRAMESIZE_RAW * nr, GFP_KERNEL); if (cgc.buffer) @@ -2096,7 +2166,7 @@ case CDROMVOLCTRL: case CDROMVOLREAD: { struct cdrom_volctrl volctrl; - char mask[32]; + unsigned char mask[32]; unsigned short offset; cdinfo(CD_DO_IOCTL, "entering CDROMVOLUME\n"); @@ -2207,19 +2277,19 @@ return cdrom_do_cmd(cdi, &cgc); } case CDROM_NEXT_WRITABLE: { - long next = 0; + unsigned int next = 0; cdinfo(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n"); if ((ret = cdrom_get_next_writable(dev, &next))) return ret; - IOCTL_OUT(arg, long, next); + IOCTL_OUT(arg, unsigned int, next); return 0; } case CDROM_LAST_WRITTEN: { - long last = 0; + unsigned int last = 0; cdinfo(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n"); if ((ret = cdrom_get_last_written(dev, &last))) return ret; - IOCTL_OUT(arg, long, last); + IOCTL_OUT(arg, unsigned int, last); return 0; } } /* switch */ @@ -2289,7 +2359,7 @@ /* return the last written block on the CD-R media. this is for the udf file system. */ -int cdrom_get_last_written(kdev_t dev, long *last_written) +int cdrom_get_last_written(kdev_t dev, unsigned int *last_written) { struct cdrom_device_info *cdi = cdrom_find_device(dev); struct cdrom_tocentry toc; @@ -2341,7 +2411,7 @@ } /* return the next writable block. also for udf file system. */ -int cdrom_get_next_writable(kdev_t dev, long *next_writable) +int cdrom_get_next_writable(kdev_t dev, unsigned int *next_writable) { struct cdrom_device_info *cdi = cdrom_find_device(dev); disc_information di; @@ -2425,6 +2495,8 @@ } pos = sprintf(info, "CD-ROM information, " VERSION "\n"); + + down(&cdrom_sem); pos += sprintf(info+pos, "\ndrive name:\t"); for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) @@ -2494,6 +2566,8 @@ for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_DVD_RAM) != 0); + up(&cdrom_sem); + strcpy(info+pos,"\n\n"); return proc_dostring(ctl, write, filp, buffer, lenp); @@ -2639,8 +2713,12 @@ #endif /* CONFIG_SYSCTL */ -static int __init cdrom_init(void) +static int cdrom_init(void) { + int n_entries = CDROM_MAX_CDROMS / (sizeof(unsigned long) * 8); + + cdrom_numbers = kmalloc(n_entries * sizeof(unsigned long), GFP_KERNEL); + #ifdef CONFIG_SYSCTL cdrom_sysctl_register(); #endif @@ -2651,11 +2729,11 @@ static void __exit cdrom_exit(void) { printk(KERN_INFO "Uniform CD-ROM driver unloaded\n"); + kfree(cdrom_numbers); #ifdef CONFIG_SYSCTL cdrom_sysctl_unregister(); #endif devfs_unregister(devfs_handle); } -module_init(cdrom_init); module_exit(cdrom_exit); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/Config.in linux.ac/drivers/char/Config.in --- linux.vanilla/drivers/char/Config.in Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/char/Config.in Thu Oct 11 21:08:53 2001 @@ -155,6 +155,7 @@ tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG tristate ' Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT tristate ' Advantech SBC Watchdog Timer' CONFIG_ADVANTECH_WDT + tristate ' IB700 SBC Watchdog Timer' CONFIG_IB700_WDT tristate ' SBC-60XX Watchdog Timer' CONFIG_60XX_WDT tristate ' W83877F (EMACS) Watchdog Timer' CONFIG_W83877F_WDT tristate ' Mixcom Watchdog' CONFIG_MIXCOMWD @@ -191,7 +192,7 @@ tristate 'Double Talk PC internal speech card support' CONFIG_DTLK tristate 'Siemens R3964 line discipline' CONFIG_R3964 tristate 'Applicom intelligent fieldbus card support' CONFIG_APPLICOM -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then +if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_X86" = "y" ]; then dep_tristate 'Sony Vaio Programmable I/O Control Device support' CONFIG_SONYPI $CONFIG_PCI fi @@ -205,18 +206,36 @@ dep_tristate '/dev/agpgart (AGP Support)' CONFIG_AGP $CONFIG_DRM_AGP if [ "$CONFIG_AGP" != "n" ]; then - bool ' Intel 440LX/BX/GX and I815/I830M/I840/I850 support' CONFIG_AGP_INTEL - bool ' Intel I810/I815/I830M (on-board) support' CONFIG_AGP_I810 - bool ' VIA chipset support' CONFIG_AGP_VIA + bool ' ALI chipset support' CONFIG_AGP_ALI bool ' AMD Irongate, 761, and 762 support' CONFIG_AGP_AMD bool ' Generic SiS support' CONFIG_AGP_SIS - bool ' ALI chipset support' CONFIG_AGP_ALI + bool ' Intel 440LX/BX/GX and I815/I830/I840/I850/I860 support' CONFIG_AGP_INTEL + bool ' Intel I810/I815/I830M (on-board) support' CONFIG_AGP_I810 bool ' Serverworks LE/HE support' CONFIG_AGP_SWORKS + bool ' VIA chipset support' CONFIG_AGP_VIA fi -source drivers/char/drm/Config.in +bool 'Direct Rendering Manager (XFree86 DRI support)' CONFIG_DRM + +if [ "$CONFIG_DRM" = "y" ]; then + bool ' Build drivers for new (XFree 4.1) DRM' CONFIG_DRM_NEW + if [ "$CONFIG_DRM_NEW" = "y" ]; then + comment 'DRM 4.1 drivers' + source drivers/char/drm/Config.in + else + comment 'DRM 4.0 drivers' + define_bool CONFIG_DRM_OLD y + source drivers/char/drm-4.0/Config.in + fi +fi if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then source drivers/char/pcmcia/Config.in fi +if [ "$CONFIG_MIPS_ITE8172" = "y" ]; then + tristate ' ITE GPIO' CONFIG_ITE_GPIO +fi + +tristate 'ACP Modem (Mwave) support' CONFIG_MWAVE + endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/Makefile linux.ac/drivers/char/Makefile --- linux.vanilla/drivers/char/Makefile Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/char/Makefile Thu Oct 11 14:29:38 2001 @@ -25,7 +25,7 @@ misc.o pty.o random.o selection.o serial.o \ sonypi.o tty_io.o tty_ioctl.o generic_serial.o -mod-subdirs := joystick ftape drm pcmcia +mod-subdirs := joystick ftape drm pcmcia drm-4.0 list-multi := @@ -72,6 +72,12 @@ endif endif +ifeq ($(ARCH),um) + KEYMAP = + KEYBD = + CONSOLE = +endif + ifeq ($(ARCH),sh) KEYMAP = KEYBD = @@ -194,11 +200,13 @@ obj-$(CONFIG_TOSHIBA) += toshiba.o obj-$(CONFIG_DS1620) += ds1620.o obj-$(CONFIG_INTEL_RNG) += i810_rng.o +obj-$(CONFIG_ITE_GPIO) += ite_gpio.o obj-$(CONFIG_QIC02_TAPE) += tpqic02.o subdir-$(CONFIG_FTAPE) += ftape -subdir-$(CONFIG_DRM) += drm +subdir-$(CONFIG_DRM_NEW) += drm +subdir-$(CONFIG_DRM_OLD) += drm-4.0 subdir-$(CONFIG_PCMCIA) += pcmcia subdir-$(CONFIG_AGP) += agp @@ -220,6 +228,7 @@ obj-$(CONFIG_PCWATCHDOG) += pcwd.o obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o +obj-$(CONFIG_IB700_WDT) += ib700wdt.o obj-$(CONFIG_MIXCOMWD) += mixcomwd.o obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o obj-$(CONFIG_WDT) += wdt.o @@ -230,6 +239,10 @@ obj-$(CONFIG_MACHZ_WDT) += machzwd.o obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o +subdir-$(CONFIG_MWAVE) += mwave +ifeq ($(CONFIG_MWAVE),y) + obj-y += mwave/mwave.o +endif include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/adbmouse.c linux.ac/drivers/char/adbmouse.c --- linux.vanilla/drivers/char/adbmouse.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/char/adbmouse.c Wed Oct 10 01:47:40 2001 @@ -206,4 +206,4 @@ module_init(adb_mouse_init); module_exit(adb_mouse_cleanup); -MODULE_LICENSE("GPL"): +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/agp/agp.h linux.ac/drivers/char/agp/agp.h --- linux.vanilla/drivers/char/agp/agp.h Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/char/agp/agp.h Thu Oct 11 21:11:07 2001 @@ -170,12 +170,21 @@ #ifndef PCI_DEVICE_ID_INTEL_810_0 #define PCI_DEVICE_ID_INTEL_810_0 0x7120 #endif +#ifndef PCI_DEVICE_ID_INTEL_830_M_0 +#define PCI_DEVICE_ID_INTEL_830_M_0 0x3575 +#endif +#ifndef PCI_DEVICE_ID_INTEL_830_M_1 +#define PCI_DEVICE_ID_INTEL_830_M_1 0x3577 +#endif #ifndef PCI_DEVICE_ID_INTEL_840_0 #define PCI_DEVICE_ID_INTEL_840_0 0x1a21 #endif #ifndef PCI_DEVICE_ID_INTEL_850_0 #define PCI_DEVICE_ID_INTEL_850_0 0x2530 #endif +#ifndef PCI_DEVICE_ID_INTEL_860_0 +#define PCI_DEVICE_ID_INTEL_860_0 0x2532 +#endif #ifndef PCI_DEVICE_ID_INTEL_810_DC100_0 #define PCI_DEVICE_ID_INTEL_810_DC100_0 0x7122 #endif @@ -200,12 +209,6 @@ #ifndef PCI_DEVICE_ID_INTEL_815_1 #define PCI_DEVICE_ID_INTEL_815_1 0x1132 #endif -#ifndef PCI_DEVICE_ID_INTEL_830_M_0 -#define PCI_DEVICE_ID_INTEL_830_M_0 0x3575 -#endif -#ifndef PCI_DEVICE_ID_INTEL_830_M_1 -#define PCI_DEVICE_ID_INTEL_830_M_1 0x3577 -#endif #ifndef PCI_DEVICE_ID_INTEL_82443GX_1 #define PCI_DEVICE_ID_INTEL_82443GX_1 0x71a1 #endif @@ -251,6 +254,22 @@ #define INTEL_NBXCFG 0x50 #define INTEL_ERRSTS 0x91 +/* intel i830 registers */ +#define I830_GMCH_CTRL 0x52 +#define I830_GMCH_ENABLED 0x4 +#define I830_GMCH_MEM_MASK 0x1 +#define I830_GMCH_MEM_64M 0x1 +#define I830_GMCH_MEM_128M 0 +#define I830_GMCH_GMS_MASK 0x70 +#define I830_GMCH_GMS_DISABLED 0x00 +#define I830_GMCH_GMS_LOCAL 0x10 +#define I830_GMCH_GMS_STOLEN_512 0x20 +#define I830_GMCH_GMS_STOLEN_1024 0x30 +#define I830_GMCH_GMS_STOLEN_8192 0x40 +#define I830_RDRAM_CHANNEL_TYPE 0x03010 +#define I830_RDRAM_ND(x) (((x) & 0x20) >> 5) +#define I830_RDRAM_DDT(x) (((x) & 0x18) >> 3) + /* intel i840 registers */ #define INTEL_I840_MCHCFG 0x50 #define INTEL_I840_ERRSTS 0xc8 @@ -259,6 +278,10 @@ #define INTEL_I850_MCHCFG 0x50 #define INTEL_I850_ERRSTS 0xc8 +/* intel i860 registers */ +#define INTEL_I860_MCHCFG 0x50 +#define INTEL_I860_ERRSTS 0xc8 + /* intel i810 registers */ #define I810_GMADDR 0x10 #define I810_MMADDR 0x14 @@ -276,22 +299,6 @@ #define I810_DRAM_CTL 0x3000 #define I810_DRAM_ROW_0 0x00000001 #define I810_DRAM_ROW_0_SDRAM 0x00000001 - -/* intel i830 registers */ -#define I830_GMCH_CTRL 0x52 -#define I830_GMCH_ENABLED 0x4 -#define I830_GMCH_MEM_MASK 0x1 -#define I830_GMCH_MEM_64M 0x1 -#define I830_GMCH_MEM_128M 0 -#define I830_GMCH_GMS_MASK 0x70 -#define I830_GMCH_GMS_DISABLED 0x00 -#define I830_GMCH_GMS_LOCAL 0x10 -#define I830_GMCH_GMS_STOLEN_512 0x20 -#define I830_GMCH_GMS_STOLEN_1024 0x30 -#define I830_GMCH_GMS_STOLEN_8192 0x40 -#define I830_RDRAM_CHANNEL_TYPE 0x03010 -#define I830_RDRAM_ND(x) (((x) & 0x20) >> 5) -#define I830_RDRAM_DDT(x) (((x) & 0x18) >> 3) /* VIA register */ #define VIA_APBASE 0x10 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/agp/agpgart_be.c linux.ac/drivers/char/agp/agpgart_be.c --- linux.vanilla/drivers/char/agp/agpgart_be.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/char/agp/agpgart_be.c Thu Oct 11 23:13:38 2001 @@ -387,7 +387,7 @@ /* * Driver routines - start * Currently this module supports the following chipsets: - * i810, i815, 440lx, 440bx, 440gx, i840, i850, via vp3, via mvp3, + * i810, i815, 440lx, 440bx, 440gx, i830, i840, i850, i860, via vp3, via mvp3, * via kx133, via kt133, amd irongate, amd 761, amd 762, ALi M1541, * and generic support for the SiS chipsets. */ @@ -1398,6 +1398,10 @@ } #endif /* CONFIG_AGP_I810 */ + + #ifdef CONFIG_AGP_INTEL + +#endif /* CONFIG_AGP_I810 */ #ifdef CONFIG_AGP_INTEL @@ -1536,6 +1540,38 @@ return 0; } +static int intel_860_configure(void) +{ + u32 temp; + u16 temp2; + aper_size_info_16 *current_size; + + current_size = A_SIZE_16(agp_bridge.current_size); + + /* aperture size */ + pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, + (char)current_size->size_value); + + /* address to map to */ + pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* attbase - aperture base */ + pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, + agp_bridge.gatt_bus_addr); + + /* agpctrl */ + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); + + /* mcgcfg */ + pci_read_config_word(agp_bridge.dev, INTEL_I860_MCHCFG, &temp2); + pci_write_config_word(agp_bridge.dev, INTEL_I860_MCHCFG, + temp2 | (1 << 9)); + /* clear any possible AGP-related error conditions */ + pci_write_config_word(agp_bridge.dev, INTEL_I860_ERRSTS, 0xf700); + return 0; +} + static unsigned long intel_mask_memory(unsigned long addr, int type) { /* Memory type is ignored */ @@ -1664,6 +1700,39 @@ (void) pdev; /* unused */ } +static int __init intel_860_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = intel_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) intel_generic_sizes; + agp_bridge.size_type = U16_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = intel_860_configure; + agp_bridge.fetch_size = intel_fetch_size; + agp_bridge.cleanup = intel_cleanup; + agp_bridge.tlb_flush = intel_tlbflush; + agp_bridge.mask_memory = intel_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; + + (void) pdev; /* unused */ +} + #endif /* CONFIG_AGP_INTEL */ #ifdef CONFIG_AGP_VIA @@ -3215,6 +3284,12 @@ "AMD", "Irongate", amd_irongate_setup }, + { PCI_DEVICE_ID_AMD_762_0, + PCI_VENDOR_ID_AMD, + AMD_IRONGATE, + "AMD", + "AMD 760MP", + amd_irongate_setup }, { PCI_DEVICE_ID_AMD_761_0, PCI_VENDOR_ID_AMD, AMD_761, @@ -3278,6 +3353,12 @@ "Intel", "i850", intel_850_setup }, + { PCI_DEVICE_ID_INTEL_860_0, + PCI_VENDOR_ID_INTEL, + INTEL_I860, + "Intel", + "i860", + intel_860_setup }, { 0, PCI_VENDOR_ID_INTEL, INTEL_GENERIC, @@ -3530,13 +3611,13 @@ PCI_DEVICE_ID_INTEL_815_1, NULL); if (i810_dev == NULL) { - printk(KERN_ERR PFX "Detected an " + printk(KERN_ERR PFX "agpgart: Detected an " "Intel i815, but could not find the" " secondary device. Assuming a " "non-integrated video card.\n"); break; } - printk(KERN_INFO PFX "Detected an Intel i815 " + printk(KERN_INFO PFX "agpgart: Detected an Intel i815 " "Chipset.\n"); agp_bridge.type = INTEL_I810; return intel_i810_setup(i810_dev); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/console.c linux.ac/drivers/char/console.c --- linux.vanilla/drivers/char/console.c Tue Sep 18 06:52:35 2001 +++ linux.ac/drivers/char/console.c Wed Oct 10 01:47:41 2001 @@ -106,6 +106,8 @@ #include #include +#include + #include "console_macros.h" @@ -154,6 +156,7 @@ static void unblank_screen_t(unsigned long dummy); static void console_callback(void *ignored); + static int printable; /* Is console ready for printing? */ int do_poke_blanked_console; @@ -399,20 +402,28 @@ else { u16 *q = p; int cnt = count; + u16 a; if (!can_do_color) { - while (cnt--) *q++ ^= 0x0800; + while (cnt--) { + a = scr_readw(q); + a ^= 0x0800; + scr_writew(a, q); + q++; + } } else if (hi_font_mask == 0x100) { while (cnt--) { - u16 a = *q; + a = scr_readw(q); a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) | (((a) & 0x0e00) << 4); - *q++ = a; + scr_writew(a, q); + q++; } } else { while (cnt--) { - u16 a = *q; + a = scr_readw(q); a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4); - *q++ = a; + scr_writew(a, q); + q++; } } } @@ -677,6 +688,8 @@ screenbuf = (unsigned short *) q; kmalloced = 1; vc_init(currcons, video_num_lines, video_num_columns, 1); + speakup_allocate(currcons); /* speakup needs more too. */ + if (!pm_con) { pm_con = pm_register(PM_SYS_DEV, @@ -903,6 +916,7 @@ pos += video_size_row; } need_wrap = 0; + speakup_con_write(currcons, "\n",1); } static void ri(int currcons) @@ -929,8 +943,9 @@ { if (x) { pos -= 2; - x--; need_wrap = 0; + x--; + speakup_bs(currcons); } } @@ -1424,7 +1439,10 @@ tab_stop[1] = tab_stop[2] = tab_stop[3] = - tab_stop[4] = 0x01010101; + tab_stop[4] = + tab_stop[5] = + tab_stop[6] = + tab_stop[7] = 0x01010101; bell_pitch = DEFAULT_BELL_PITCH; bell_duration = DEFAULT_BELL_DURATION; @@ -1671,7 +1689,12 @@ if (par[0]) par[0]--; gotoxay(currcons,x,par[0]); return; - case 'H': case 'f': + case 'f': + if (par[0]) par[0]--; + if (par[1]) par[1]--; + gotoxay(currcons,par[1],par[0]); + return; + case 'H': if (par[0]) par[0]--; if (par[1]) par[1]--; gotoxay(currcons,par[1],par[0]); @@ -1703,7 +1726,10 @@ tab_stop[1] = tab_stop[2] = tab_stop[3] = - tab_stop[4] = 0; + tab_stop[4] = + tab_stop[5] = + tab_stop[6] = + tab_stop[7] = 0; } return; case 'm': @@ -1839,7 +1865,7 @@ if (in_interrupt()) return count; - + currcons = vt->vc_num; if (!vc_cons_allocated(currcons)) { /* could this happen? */ @@ -1873,8 +1899,15 @@ * and therefore no access to userspace (and therefore sleeping) * will be needed. The con_buf_sem serializes all tty based * console rendering and vcs write/read operations. We hold - * the console spinlock during the entire write. + * the console semaphore during the entire write. + * + * If we are called during an IRQ that was naughty and we ignore + * it. Arguably we could remember con_buf used so far and schedule + * the I/O next write.... */ + + if(in_interrupt()) + return 0; acquire_console_sem(); @@ -1948,6 +1981,7 @@ if (vc_state == ESnormal && ok) { /* Now try to find out how to display it */ tc = conv_uni_to_pc(vc_cons[currcons].d, tc); + if ( tc == -4 ) { /* If we got -4 (not found) then see if we have defined a replacement character (U+FFFD) */ @@ -1973,6 +2007,7 @@ } if (decim) insert_char(currcons, 1); + speakup_con_write(currcons, (char *) &tc,1); scr_writew(himask ? ((attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) : (attr << 8) + tc, @@ -2013,6 +2048,7 @@ up(&con_buf_sem); } + speakup_con_update(currcons); return n; #undef FLUSH @@ -2038,6 +2074,7 @@ /* we only changed when the console had already been allocated - a new console is not created in an interrupt routine */ + speakup_con_update(want_console); } want_console = -1; } @@ -2109,6 +2146,7 @@ /* Contrived structure to try to emulate original need_wrap behaviour * Problems caused when we have need_wrap set on '\n' character */ + speakup_con_write(currcons, b, count); while (count--) { c = *b++; if (c == 10 || c == 13 || c == 8 || need_wrap) { @@ -2153,6 +2191,7 @@ } } set_cursor(currcons); + speakup_con_update(currcons); if (!oops_in_progress) poke_blanked_console(); @@ -2493,6 +2532,8 @@ master_display_fg = vc_cons[currcons].d; set_origin(currcons); save_screen(currcons); + speakup_init(currcons); + gotoxy(currcons,x,y); csi_J(currcons, 0); update_screen(fg_console); @@ -2722,7 +2763,6 @@ currcons = fg_console; if (vcmode != KD_TEXT) return; /* but leave console_blanked != 0 */ - console_timer.function = blank_screen; if (blankinterval) { mod_timer(&console_timer, jiffies + blankinterval); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/Config.in linux.ac/drivers/char/drm/Config.in --- linux.vanilla/drivers/char/drm/Config.in Wed Aug 8 17:42:10 2001 +++ linux.ac/drivers/char/drm/Config.in Wed Oct 10 01:47:41 2001 @@ -5,12 +5,9 @@ # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. # -bool 'Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)' CONFIG_DRM -if [ "$CONFIG_DRM" != "n" ]; then - tristate ' 3dfx Banshee/Voodoo3+' CONFIG_DRM_TDFX - tristate ' 3dlabs GMX 2000' CONFIG_DRM_GAMMA - tristate ' ATI Rage 128' CONFIG_DRM_R128 - dep_tristate ' ATI Radeon' CONFIG_DRM_RADEON $CONFIG_AGP - dep_tristate ' Intel I810' CONFIG_DRM_I810 $CONFIG_AGP - dep_tristate ' Matrox g200/g400' CONFIG_DRM_MGA $CONFIG_AGP -fi +tristate ' 3dfx Banshee/Voodoo3+' CONFIG_DRM_TDFX +tristate ' 3dlabs GMX 2000' CONFIG_DRM_GAMMA +tristate ' ATI Rage 128' CONFIG_DRM_R128 +dep_tristate ' ATI Radeon' CONFIG_DRM_RADEON $CONFIG_AGP +dep_tristate ' Intel I810' CONFIG_DRM_I810 $CONFIG_AGP +dep_tristate ' Matrox G200/G400/G450' CONFIG_DRM_MGA $CONFIG_AGP diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/ati_pcigart.h linux.ac/drivers/char/drm/ati_pcigart.h --- linux.vanilla/drivers/char/drm/ati_pcigart.h Sun Sep 23 18:31:41 2001 +++ linux.ac/drivers/char/drm/ati_pcigart.h Fri Oct 12 12:45:49 2001 @@ -57,7 +57,7 @@ page = virt_to_page( address ); - for ( i = 0 ; i <= ATI_PCIGART_TABLE_PAGES ; i++, page++ ) { + for ( i = 0 ; i < ATI_PCIGART_TABLE_PAGES ; i++, page++ ) { atomic_inc( &page->count ); SetPageReserved( page ); } @@ -74,7 +74,7 @@ page = virt_to_page( address ); - for ( i = 0 ; i <= ATI_PCIGART_TABLE_PAGES ; i++, page++ ) { + for ( i = 0 ; i < ATI_PCIGART_TABLE_PAGES ; i++, page++ ) { atomic_dec( &page->count ); ClearPageReserved( page ); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/drm_proc.h linux.ac/drivers/char/drm/drm_proc.h --- linux.vanilla/drivers/char/drm/drm_proc.h Sun Sep 23 18:31:41 2001 +++ linux.ac/drivers/char/drm/drm_proc.h Fri Oct 12 12:45:42 2001 @@ -186,7 +186,7 @@ DRM_PROC_PRINT("slot offset size type flags " "address mtrr\n\n"); i = 0; - list_for_each(list, &dev->maplist->head) { + if (dev->maplist != NULL) list_for_each(list, &dev->maplist->head) { r_list = (drm_map_list_t *)list; map = r_list->map; if(!map) continue; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/radeon_state.c linux.ac/drivers/char/drm/radeon_state.c --- linux.vanilla/drivers/char/drm/radeon_state.c Wed Aug 8 17:42:15 2001 +++ linux.ac/drivers/char/drm/radeon_state.c Wed Oct 10 01:47:41 2001 @@ -991,6 +991,7 @@ /* The compiler won't optimize away a division by a variable, * even if the only legal values are powers of two. Thus, we'll * use a shift instead. + * (It probably would if you used unsigned.. -ac) */ switch ( tex->format ) { case RADEON_TXFORMAT_ARGB8888: @@ -1061,6 +1062,8 @@ } else if ( size < 4 ) { size = 4; } + +#warning "FIX tex_width checks" dwords = size / 4; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/Config.in linux.ac/drivers/char/drm-4.0/Config.in --- linux.vanilla/drivers/char/drm-4.0/Config.in Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/Config.in Wed Oct 10 01:47:41 2001 @@ -0,0 +1,13 @@ +# +# drm device configuration +# +# This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.x. +# + +tristate ' 3dfx Banshee/Voodoo3+' CONFIG_DRM40_TDFX +tristate ' 3dlabs GMX 2000' CONFIG_DRM40_GAMMA +dep_tristate ' ATI Rage 128' CONFIG_DRM40_R128 $CONFIG_AGP +dep_tristate ' ATI Radeon' CONFIG_DRM40_RADEON $CONFIG_AGP +dep_tristate ' Intel I810' CONFIG_DRM40_I810 $CONFIG_AGP +dep_tristate ' Matrox G200/G400/G450' CONFIG_DRM40_MGA $CONFIG_AGP diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/Makefile linux.ac/drivers/char/drm-4.0/Makefile --- linux.vanilla/drivers/char/drm-4.0/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/Makefile Wed Oct 10 01:47:41 2001 @@ -0,0 +1,104 @@ +# +# Makefile for the drm device driver. This driver provides support for +# the Direct Rendering Infrastructure (DRI) in XFree86 4.x. +# + +O_TARGET := drm.o + +export-objs := gamma_drv.o tdfx_drv.o r128_drv.o ffb_drv.o mga_drv.o \ + i810_drv.o + +# lib-objs are included in every module so that radical changes to the +# architecture of the DRM support library can be made at a later time. +# +# The downside is that each module is larger, and a system that uses +# more than one module (i.e., a dual-head system) will use more memory +# (but a system that uses exactly one module will use the same amount of +# memory). +# +# The upside is that if the DRM support library ever becomes insufficient +# for new families of cards, a new library can be implemented for those new +# cards without impacting the drivers for the old cards. This is significant, +# because testing architectural changes to old cards may be impossible, and +# may delay the implementation of a better architecture. We've traded slight +# memory waste (in the dual-head case) for greatly improved long-term +# maintainability. +# +# NOTE: lib-objs will be eliminated in future versions, thereby +# eliminating the need to compile the .o files into every module, but +# for now we still need them. +# + +lib-objs := init.o memory.o proc.o auth.o context.o drawable.o bufs.o +lib-objs += lists.o lock.o ioctl.o fops.o vm.o dma.o ctxbitmap.o + +ifeq ($(CONFIG_AGP),y) + lib-objs += agpsupport.o +else + ifeq ($(CONFIG_AGP),m) + lib-objs += agpsupport.o + endif +endif + +list-multi := gamma.o tdfx.o r128.o ffb.o mga.o i810.o +gamma-objs := gamma_drv.o gamma_dma.o +tdfx-objs := tdfx_drv.o tdfx_context.o +r128-objs := r128_drv.o r128_cce.o r128_context.o r128_bufs.o r128_state.o +ffb-objs := ffb_drv.o ffb_context.o +mga-objs := mga_drv.o mga_dma.o mga_context.o mga_bufs.o mga_state.o +i810-objs := i810_drv.o i810_dma.o i810_context.o i810_bufs.o +radeon-objs := radeon_drv.o radeon_cp.o radeon_context.o radeon_bufs.o radeon_state.o + +obj-$(CONFIG_DRM40_GAMMA) += gamma.o +obj-$(CONFIG_DRM40_TDFX) += tdfx.o +obj-$(CONFIG_DRM40_R128) += r128.o +obj-$(CONFIG_DRM40_RADEON)+= radeon.o +obj-$(CONFIG_DRM40_FFB) += ffb.o +obj-$(CONFIG_DRM40_MGA) += mga.o +obj-$(CONFIG_DRM40_I810) += i810.o + + +# When linking into the kernel, link the library just once. +# If making modules, we include the library into each module + +lib-objs-mod := $(patsubst %.o,%-mod.o,$(lib-objs)) + +ifdef MAKING_MODULES + lib = drmlib-mod.a +else + obj-y += drmlib.a +endif + +include $(TOPDIR)/Rules.make + +$(patsubst %.o,%.c,$(lib-objs-mod)): + @ln -sf $(subst -mod,,$@) $@ + +drmlib-mod.a: $(lib-objs-mod) + rm -f $@ + $(AR) $(EXTRA_ARFLAGS) rcs $@ $(lib-objs-mod) + +drmlib.a: $(lib-objs) + rm -f $@ + $(AR) $(EXTRA_ARFLAGS) rcs $@ $(lib-objs) + +gamma.o: $(gamma-objs) $(lib) + $(LD) -r -o $@ $(gamma-objs) $(lib) + +tdfx.o: $(tdfx-objs) $(lib) + $(LD) -r -o $@ $(tdfx-objs) $(lib) + +mga.o: $(mga-objs) $(lib) + $(LD) -r -o $@ $(mga-objs) $(lib) + +i810.o: $(i810-objs) $(lib) + $(LD) -r -o $@ $(i810-objs) $(lib) + +r128.o: $(r128-objs) $(lib) + $(LD) -r -o $@ $(r128-objs) $(lib) + +radeon.o: $(radeon-objs) $(lib) + $(LD) -r -o $@ $(radeon-objs) $(lib) + +ffb.o: $(ffb-objs) $(lib) + $(LD) -r -o $@ $(ffb-objs) $(lib) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/README.drm linux.ac/drivers/char/drm-4.0/README.drm --- linux.vanilla/drivers/char/drm-4.0/README.drm Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/README.drm Wed Oct 10 01:47:41 2001 @@ -0,0 +1,46 @@ +************************************************************ +* For the very latest on DRI development, please see: * +* http://dri.sourceforge.net/ * +************************************************************ + +The Direct Rendering Manager (drm) is a device-independent kernel-level +device driver that provides support for the XFree86 Direct Rendering +Infrastructure (DRI). + +The DRM supports the Direct Rendering Infrastructure (DRI) in four major +ways: + + 1. The DRM provides synchronized access to the graphics hardware via + the use of an optimized two-tiered lock. + + 2. The DRM enforces the DRI security policy for access to the graphics + hardware by only allowing authenticated X11 clients access to + restricted regions of memory. + + 3. The DRM provides a generic DMA engine, complete with multiple + queues and the ability to detect the need for an OpenGL context + switch. + + 4. The DRM is extensible via the use of small device-specific modules + that rely extensively on the API exported by the DRM module. + + +Documentation on the DRI is available from: + http://precisioninsight.com/piinsights.html + +For specific information about kernel-level support, see: + + The Direct Rendering Manager, Kernel Support for the Direct Rendering + Infrastructure + http://precisioninsight.com/dr/drm.html + + Hardware Locking for the Direct Rendering Infrastructure + http://precisioninsight.com/dr/locking.html + + A Security Analysis of the Direct Rendering Infrastructure + http://precisioninsight.com/dr/security.html + +************************************************************ +* For the very latest on DRI development, please see: * +* http://dri.sourceforge.net/ * +************************************************************ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/agpsupport.c linux.ac/drivers/char/drm-4.0/agpsupport.c --- linux.vanilla/drivers/char/drm-4.0/agpsupport.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/agpsupport.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,339 @@ +/* agpsupport.c -- DRM support for AGP/GART backend -*- linux-c -*- + * Created: Mon Dec 13 09:56:45 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include +#if LINUX_VERSION_CODE < 0x020400 +#include "agpsupport-pre24.h" +#else +#define DRM_AGP_GET (drm_agp_t *)inter_module_get("drm_agp") +#define DRM_AGP_PUT inter_module_put("drm_agp") +#endif + +static const drm_agp_t *drm_agp = NULL; + +int drm_agp_info(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + agp_kern_info *kern; + drm_agp_info_t info; + + if (!dev->agp->acquired || !drm_agp->copy_info) return -EINVAL; + + kern = &dev->agp->agp_info; + info.agp_version_major = kern->version.major; + info.agp_version_minor = kern->version.minor; + info.mode = kern->mode; + info.aperture_base = kern->aper_base; + info.aperture_size = kern->aper_size * 1024 * 1024; + info.memory_allowed = kern->max_memory << PAGE_SHIFT; + info.memory_used = kern->current_memory << PAGE_SHIFT; + info.id_vendor = kern->device->vendor; + info.id_device = kern->device->device; + + if (copy_to_user((drm_agp_info_t *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +int drm_agp_acquire(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode; + + if (dev->agp->acquired || !drm_agp->acquire) return -EINVAL; + if ((retcode = drm_agp->acquire())) return retcode; + dev->agp->acquired = 1; + return 0; +} + +int drm_agp_release(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + if (!dev->agp->acquired || !drm_agp->release) return -EINVAL; + drm_agp->release(); + dev->agp->acquired = 0; + return 0; + +} + +void _drm_agp_release(void) +{ + if (drm_agp->release) drm_agp->release(); +} + +int drm_agp_enable(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_mode_t mode; + + if (!dev->agp->acquired || !drm_agp->enable) return -EINVAL; + + if (copy_from_user(&mode, (drm_agp_mode_t *)arg, sizeof(mode))) + return -EFAULT; + + dev->agp->mode = mode.mode; + drm_agp->enable(mode.mode); + dev->agp->base = dev->agp->agp_info.aper_base; + dev->agp->enabled = 1; + return 0; +} + +int drm_agp_alloc(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_buffer_t request; + drm_agp_mem_t *entry; + agp_memory *memory; + unsigned long pages; + u32 type; + if (!dev->agp->acquired) return -EINVAL; + if (copy_from_user(&request, (drm_agp_buffer_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = drm_alloc(sizeof(*entry), DRM_MEM_AGPLISTS))) + return -ENOMEM; + + memset(entry, 0, sizeof(*entry)); + + pages = (request.size + PAGE_SIZE - 1) / PAGE_SIZE; + type = (u32) request.type; + + if (!(memory = drm_alloc_agp(pages, type))) { + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return -ENOMEM; + } + + entry->handle = (unsigned long)memory->memory; + entry->memory = memory; + entry->bound = 0; + entry->pages = pages; + entry->prev = NULL; + entry->next = dev->agp->memory; + if (dev->agp->memory) dev->agp->memory->prev = entry; + dev->agp->memory = entry; + + request.handle = entry->handle; + request.physical = memory->physical; + + if (copy_to_user((drm_agp_buffer_t *)arg, &request, sizeof(request))) { + dev->agp->memory = entry->next; + dev->agp->memory->prev = NULL; + drm_free_agp(memory, pages); + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return -EFAULT; + } + return 0; +} + +static drm_agp_mem_t *drm_agp_lookup_entry(drm_device_t *dev, + unsigned long handle) +{ + drm_agp_mem_t *entry; + + for (entry = dev->agp->memory; entry; entry = entry->next) { + if (entry->handle == handle) return entry; + } + return NULL; +} + +int drm_agp_unbind(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_binding_t request; + drm_agp_mem_t *entry; + + if (!dev->agp->acquired) return -EINVAL; + if (copy_from_user(&request, (drm_agp_binding_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = drm_agp_lookup_entry(dev, request.handle))) + return -EINVAL; + if (!entry->bound) return -EINVAL; + return drm_unbind_agp(entry->memory); +} + +int drm_agp_bind(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_binding_t request; + drm_agp_mem_t *entry; + int retcode; + int page; + + if (!dev->agp->acquired || !drm_agp->bind_memory) return -EINVAL; + if (copy_from_user(&request, (drm_agp_binding_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = drm_agp_lookup_entry(dev, request.handle))) + return -EINVAL; + if (entry->bound) return -EINVAL; + page = (request.offset + PAGE_SIZE - 1) / PAGE_SIZE; + if ((retcode = drm_bind_agp(entry->memory, page))) return retcode; + entry->bound = dev->agp->base + (page << PAGE_SHIFT); + DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n", + dev->agp->base, entry->bound); + return 0; +} + +int drm_agp_free(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_buffer_t request; + drm_agp_mem_t *entry; + + if (!dev->agp->acquired) return -EINVAL; + if (copy_from_user(&request, (drm_agp_buffer_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = drm_agp_lookup_entry(dev, request.handle))) + return -EINVAL; + if (entry->bound) drm_unbind_agp(entry->memory); + + if (entry->prev) entry->prev->next = entry->next; + else dev->agp->memory = entry->next; + if (entry->next) entry->next->prev = entry->prev; + drm_free_agp(entry->memory, entry->pages); + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return 0; +} + +drm_agp_head_t *drm_agp_init(void) +{ + drm_agp_head_t *head = NULL; + + drm_agp = DRM_AGP_GET; + if (drm_agp) { + if (!(head = drm_alloc(sizeof(*head), DRM_MEM_AGPLISTS))) + return NULL; + memset((void *)head, 0, sizeof(*head)); + drm_agp->copy_info(&head->agp_info); + if (head->agp_info.chipset == NOT_SUPPORTED) { + drm_free(head, sizeof(*head), DRM_MEM_AGPLISTS); + return NULL; + } + head->memory = NULL; + switch (head->agp_info.chipset) { + case INTEL_GENERIC: head->chipset = "Intel"; break; + case INTEL_LX: head->chipset = "Intel 440LX"; break; + case INTEL_BX: head->chipset = "Intel 440BX"; break; + case INTEL_GX: head->chipset = "Intel 440GX"; break; + case INTEL_I810: head->chipset = "Intel i810"; break; + +#if LINUX_VERSION_CODE >= 0x020400 + case INTEL_I840: head->chipset = "Intel i840"; break; +#endif + + case VIA_GENERIC: head->chipset = "VIA"; break; + case VIA_VP3: head->chipset = "VIA VP3"; break; + case VIA_MVP3: head->chipset = "VIA MVP3"; break; + +#if LINUX_VERSION_CODE >= 0x020400 + case VIA_MVP4: head->chipset = "VIA MVP4"; break; + case VIA_APOLLO_KX133: head->chipset = "VIA Apollo KX133"; + break; + case VIA_APOLLO_KT133: head->chipset = "VIA Apollo KT133"; + break; +#endif + + case VIA_APOLLO_PRO: head->chipset = "VIA Apollo Pro"; + break; + case SIS_GENERIC: head->chipset = "SiS"; break; + case AMD_GENERIC: head->chipset = "AMD"; break; + case AMD_IRONGATE: head->chipset = "AMD Irongate"; break; + case ALI_GENERIC: head->chipset = "ALi"; break; + case ALI_M1541: head->chipset = "ALi M1541"; break; + case ALI_M1621: head->chipset = "ALi M1621"; break; + case ALI_M1631: head->chipset = "ALi M1631"; break; + case ALI_M1632: head->chipset = "ALi M1632"; break; + case ALI_M1641: head->chipset = "ALi M1641"; break; + case ALI_M1647: head->chipset = "ALi M1647"; break; + case ALI_M1651: head->chipset = "ALi M1651"; break; + case SVWRKS_GENERIC: head->chipset = "Serverworks Generic"; + break; + case SVWRKS_HE: head->chipset = "Serverworks HE"; break; + case SVWRKS_LE: head->chipset = "Serverworks LE"; break; + + default: head->chipset = "Unknown"; break; + } + DRM_INFO("AGP %d.%d on %s @ 0x%08lx %ZuMB\n", + head->agp_info.version.major, + head->agp_info.version.minor, + head->chipset, + head->agp_info.aper_base, + head->agp_info.aper_size); + } + return head; +} + +void drm_agp_uninit(void) +{ + DRM_AGP_PUT; + drm_agp = NULL; +} + +agp_memory *drm_agp_allocate_memory(size_t pages, u32 type) +{ + if (!drm_agp->allocate_memory) return NULL; + return drm_agp->allocate_memory(pages, type); +} + +int drm_agp_free_memory(agp_memory *handle) +{ + if (!handle || !drm_agp->free_memory) return 0; + drm_agp->free_memory(handle); + return 1; +} + +int drm_agp_bind_memory(agp_memory *handle, off_t start) +{ + if (!handle || !drm_agp->bind_memory) return -EINVAL; + return drm_agp->bind_memory(handle, start); +} + +int drm_agp_unbind_memory(agp_memory *handle) +{ + if (!handle || !drm_agp->unbind_memory) return -EINVAL; + return drm_agp->unbind_memory(handle); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/auth.c linux.ac/drivers/char/drm-4.0/auth.c --- linux.vanilla/drivers/char/drm-4.0/auth.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/auth.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,162 @@ +/* auth.c -- IOCTLs for authentication -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +static int drm_hash_magic(drm_magic_t magic) +{ + return magic & (DRM_HASH_SIZE-1); +} + +static drm_file_t *drm_find_file(drm_device_t *dev, drm_magic_t magic) +{ + drm_file_t *retval = NULL; + drm_magic_entry_t *pt; + int hash = drm_hash_magic(magic); + + down(&dev->struct_sem); + for (pt = dev->magiclist[hash].head; pt; pt = pt->next) { + if (pt->magic == magic) { + retval = pt->priv; + break; + } + } + up(&dev->struct_sem); + return retval; +} + +int drm_add_magic(drm_device_t *dev, drm_file_t *priv, drm_magic_t magic) +{ + int hash; + drm_magic_entry_t *entry; + + DRM_DEBUG("%d\n", magic); + + hash = drm_hash_magic(magic); + entry = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC); + if (!entry) return -ENOMEM; + entry->magic = magic; + entry->priv = priv; + entry->next = NULL; + + down(&dev->struct_sem); + if (dev->magiclist[hash].tail) { + dev->magiclist[hash].tail->next = entry; + dev->magiclist[hash].tail = entry; + } else { + dev->magiclist[hash].head = entry; + dev->magiclist[hash].tail = entry; + } + up(&dev->struct_sem); + + return 0; +} + +int drm_remove_magic(drm_device_t *dev, drm_magic_t magic) +{ + drm_magic_entry_t *prev = NULL; + drm_magic_entry_t *pt; + int hash; + + DRM_DEBUG("%d\n", magic); + hash = drm_hash_magic(magic); + + down(&dev->struct_sem); + for (pt = dev->magiclist[hash].head; pt; prev = pt, pt = pt->next) { + if (pt->magic == magic) { + if (dev->magiclist[hash].head == pt) { + dev->magiclist[hash].head = pt->next; + } + if (dev->magiclist[hash].tail == pt) { + dev->magiclist[hash].tail = prev; + } + if (prev) { + prev->next = pt->next; + } + up(&dev->struct_sem); + return 0; + } + } + up(&dev->struct_sem); + + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + + return -EINVAL; +} + +int drm_getmagic(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + static drm_magic_t sequence = 0; + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_auth_t auth; + + /* Find unique magic */ + if (priv->magic) { + auth.magic = priv->magic; + } else { + do { + spin_lock(&lock); + if (!sequence) ++sequence; /* reserve 0 */ + auth.magic = sequence++; + spin_unlock(&lock); + } while (drm_find_file(dev, auth.magic)); + priv->magic = auth.magic; + drm_add_magic(dev, priv, auth.magic); + } + + DRM_DEBUG("%u\n", auth.magic); + if (copy_to_user((drm_auth_t *)arg, &auth, sizeof(auth))) + return -EFAULT; + return 0; +} + +int drm_authmagic(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_auth_t auth; + drm_file_t *file; + + if (copy_from_user(&auth, (drm_auth_t *)arg, sizeof(auth))) + return -EFAULT; + DRM_DEBUG("%u\n", auth.magic); + if ((file = drm_find_file(dev, auth.magic))) { + file->authenticated = 1; + drm_remove_magic(dev, auth.magic); + return 0; + } + return -EINVAL; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/bufs.c linux.ac/drivers/char/drm-4.0/bufs.c --- linux.vanilla/drivers/char/drm-4.0/bufs.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/bufs.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,543 @@ +/* bufs.c -- IOCTLs to manage buffers -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include +#include "drmP.h" +#include "linux/un.h" + + /* Compute order. Can be made faster. */ +int drm_order(unsigned long size) +{ + int order; + unsigned long tmp; + + for (order = 0, tmp = size; tmp >>= 1; ++order); + if (size & ~(1 << order)) ++order; + return order; +} + +int drm_addmap(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map; + + if (!(filp->f_mode & 3)) return -EACCES; /* Require read/write */ + + map = drm_alloc(sizeof(*map), DRM_MEM_MAPS); + if (!map) return -ENOMEM; + if (copy_from_user(map, (drm_map_t *)arg, sizeof(*map))) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EFAULT; + } + + DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n", + map->offset, map->size, map->type); + if ((map->offset & (~PAGE_MASK)) || (map->size & (~PAGE_MASK))) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } + map->mtrr = -1; + map->handle = 0; + + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifndef __sparc__ + if (map->offset + map->size < map->offset + || map->offset < virt_to_phys(high_memory)) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } +#endif +#ifdef CONFIG_MTRR + if (map->type == _DRM_FRAME_BUFFER + || (map->flags & _DRM_WRITE_COMBINING)) { + map->mtrr = mtrr_add(map->offset, map->size, + MTRR_TYPE_WRCOMB, 1); + } +#endif + map->handle = drm_ioremap(map->offset, map->size); + break; + + + case _DRM_SHM: + map->handle = (void *)drm_alloc_pages(drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + DRM_DEBUG("%ld %d %p\n", map->size, drm_order(map->size), + map->handle); + if (!map->handle) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -ENOMEM; + } + map->offset = (unsigned long)map->handle; + if (map->flags & _DRM_CONTAINS_LOCK) { + dev->lock.hw_lock = map->handle; /* Pointer to lock */ + } + break; +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + case _DRM_AGP: + map->offset = map->offset + dev->agp->base; + break; +#endif + default: + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } + + down(&dev->struct_sem); + if (dev->maplist) { + ++dev->map_count; + dev->maplist = drm_realloc(dev->maplist, + (dev->map_count-1) + * sizeof(*dev->maplist), + dev->map_count + * sizeof(*dev->maplist), + DRM_MEM_MAPS); + } else { + dev->map_count = 1; + dev->maplist = drm_alloc(dev->map_count*sizeof(*dev->maplist), + DRM_MEM_MAPS); + } + dev->maplist[dev->map_count-1] = map; + up(&dev->struct_sem); + + if (copy_to_user((drm_map_t *)arg, map, sizeof(*map))) + return -EFAULT; + if (map->type != _DRM_SHM) { + if (copy_to_user(&((drm_map_t *)arg)->handle, + &map->offset, + sizeof(map->offset))) + return -EFAULT; + } + return 0; +} + +int drm_addbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int count; + int order; + int size; + int total; + int page_order; + drm_buf_entry_t *entry; + unsigned long page; + drm_buf_t *buf; + int alignment; + unsigned long offset; + int i; + int byte_count; + int page_count; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, + (drm_buf_desc_t *)arg, + sizeof(request))) + return -EFAULT; + + count = request.count; + order = drm_order(request.size); + size = 1 << order; + + DRM_DEBUG("count = %d, size = %d (%d), order = %d, queue_count = %d\n", + request.count, request.size, size, order, dev->queue_count); + + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + if (dev->queue_count) return -EBUSY; /* Not while in use */ + + alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + spin_lock(&dev->count_lock); + if (dev->buf_use) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + atomic_inc(&dev->buf_alloc); + spin_unlock(&dev->count_lock); + + down(&dev->struct_sem); + entry = &dma->bufs[order]; + if (entry->buf_count) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; /* May only call once for each order */ + } + + if(count < 0 || count > 4096) + { + up(&dev->struct_sem); + return -EINVAL; + } + + entry->buflist = drm_alloc(count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + if (!entry->buflist) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->buflist, 0, count * sizeof(*entry->buflist)); + + entry->seglist = drm_alloc(count * sizeof(*entry->seglist), + DRM_MEM_SEGS); + if (!entry->seglist) { + drm_free(entry->buflist, + count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->seglist, 0, count * sizeof(*entry->seglist)); + + dma->pagelist = drm_realloc(dma->pagelist, + dma->page_count * sizeof(*dma->pagelist), + (dma->page_count + (count << page_order)) + * sizeof(*dma->pagelist), + DRM_MEM_PAGES); + DRM_DEBUG("pagelist: %d entries\n", + dma->page_count + (count << page_order)); + + + entry->buf_size = size; + entry->page_order = page_order; + byte_count = 0; + page_count = 0; + while (entry->buf_count < count) { + if (!(page = drm_alloc_pages(page_order, DRM_MEM_DMA))) break; + entry->seglist[entry->seg_count++] = page; + for (i = 0; i < (1 << page_order); i++) { + DRM_DEBUG("page %d @ 0x%08lx\n", + dma->page_count + page_count, + page + PAGE_SIZE * i); + dma->pagelist[dma->page_count + page_count++] + = page + PAGE_SIZE * i; + } + for (offset = 0; + offset + size <= total && entry->buf_count < count; + offset += alignment, ++entry->buf_count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + buf->offset = (dma->byte_count + byte_count + offset); + buf->address = (void *)(page + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->pid = 0; +#if DRM_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + DRM_DEBUG("buffer %d @ %p\n", + entry->buf_count, buf->address); + } + byte_count += PAGE_SIZE << page_order; + } + + dma->buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS); + for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++) + dma->buflist[i] = &entry->buflist[i - dma->buf_count]; + + dma->buf_count += entry->buf_count; + dma->seg_count += entry->seg_count; + dma->page_count += entry->seg_count << page_order; + dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); + + drm_freelist_create(&entry->freelist, entry->buf_count); + for (i = 0; i < entry->buf_count; i++) { + drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]); + } + + up(&dev->struct_sem); + + request.count = entry->buf_count; + request.size = size; + + if (copy_to_user((drm_buf_desc_t *)arg, + &request, + sizeof(request))) + return -EFAULT; + + atomic_dec(&dev->buf_alloc); + return 0; +} + +int drm_infobufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_info_t request; + int i; + int count; + + if (!dma) return -EINVAL; + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + if (copy_from_user(&request, + (drm_buf_info_t *)arg, + sizeof(request))) + return -EFAULT; + + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) ++count; + } + + DRM_DEBUG("count = %d\n", count); + + if (request.count >= count) { + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) { + if (copy_to_user(&request.list[count].count, + &dma->bufs[i].buf_count, + sizeof(dma->bufs[0] + .buf_count)) || + copy_to_user(&request.list[count].size, + &dma->bufs[i].buf_size, + sizeof(dma->bufs[0].buf_size)) || + copy_to_user(&request.list[count].low_mark, + &dma->bufs[i] + .freelist.low_mark, + sizeof(dma->bufs[0] + .freelist.low_mark)) || + copy_to_user(&request.list[count] + .high_mark, + &dma->bufs[i] + .freelist.high_mark, + sizeof(dma->bufs[0] + .freelist.high_mark))) + return -EFAULT; + + DRM_DEBUG("%d %d %d %d %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].buf_size, + dma->bufs[i].freelist.low_mark, + dma->bufs[i].freelist.high_mark); + ++count; + } + } + } + request.count = count; + + if (copy_to_user((drm_buf_info_t *)arg, + &request, + sizeof(request))) + return -EFAULT; + + return 0; +} + +int drm_markbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int order; + drm_buf_entry_t *entry; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, + (drm_buf_desc_t *)arg, + sizeof(request))) + return -EFAULT; + + DRM_DEBUG("%d, %d, %d\n", + request.size, request.low_mark, request.high_mark); + order = drm_order(request.size); + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + entry = &dma->bufs[order]; + + if (request.low_mark < 0 || request.low_mark > entry->buf_count) + return -EINVAL; + if (request.high_mark < 0 || request.high_mark > entry->buf_count) + return -EINVAL; + + entry->freelist.low_mark = request.low_mark; + entry->freelist.high_mark = request.high_mark; + + return 0; +} + +int drm_freebufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_free_t request; + int i; + int idx; + drm_buf_t *buf; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, + (drm_buf_free_t *)arg, + sizeof(request))) + return -EFAULT; + + DRM_DEBUG("%d\n", request.count); + for (i = 0; i < request.count; i++) { + if (copy_from_user(&idx, + &request.list[i], + sizeof(idx))) + return -EFAULT; + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("Index %d (of %d max)\n", + idx, dma->buf_count - 1); + return -EINVAL; + } + buf = dma->buflist[idx]; + if (buf->pid != current->pid) { + DRM_ERROR("Process %d freeing buffer owned by %d\n", + current->pid, buf->pid); + return -EINVAL; + } + drm_free_buffer(dev, buf); + } + + return 0; +} + +int drm_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + const int zero = 0; + unsigned long virtual; + unsigned long address; + drm_buf_map_t request; + int i; + + if (!dma) return -EINVAL; + + DRM_DEBUG("\n"); + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + if (copy_from_user(&request, + (drm_buf_map_t *)arg, + sizeof(request))) + return -EFAULT; + + if (request.count >= dma->buf_count) { + down_write(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, dma->byte_count, + PROT_READ|PROT_WRITE, MAP_SHARED, 0); + up_write(¤t->mm->mmap_sem); + if (virtual > -1024UL) { + /* Real error */ + retcode = (signed long)virtual; + goto done; + } + request.virtual = (void *)virtual; + + for (i = 0; i < dma->buf_count; i++) { + if (copy_to_user(&request.list[i].idx, + &dma->buflist[i]->idx, + sizeof(request.list[0].idx))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].total, + &dma->buflist[i]->total, + sizeof(request.list[0].total))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].used, + &zero, + sizeof(zero))) { + retcode = -EFAULT; + goto done; + } + address = virtual + dma->buflist[i]->offset; + if (copy_to_user(&request.list[i].address, + &address, + sizeof(address))) { + retcode = -EFAULT; + goto done; + } + } + } +done: + request.count = dma->buf_count; + DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode); + + if (copy_to_user((drm_buf_map_t *)arg, + &request, + sizeof(request))) + return -EFAULT; + + return retcode; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/context.c linux.ac/drivers/char/drm-4.0/context.c --- linux.vanilla/drivers/char/drm-4.0/context.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/context.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,321 @@ +/* context.c -- IOCTLs for contexts and DMA queues -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +static int drm_init_queue(drm_device_t *dev, drm_queue_t *q, drm_ctx_t *ctx) +{ + DRM_DEBUG("\n"); + + if (atomic_read(&q->use_count) != 1 + || atomic_read(&q->finalization) + || atomic_read(&q->block_count)) { + DRM_ERROR("New queue is already in use: u%d f%d b%d\n", + atomic_read(&q->use_count), + atomic_read(&q->finalization), + atomic_read(&q->block_count)); + } + + atomic_set(&q->finalization, 0); + atomic_set(&q->block_count, 0); + atomic_set(&q->block_read, 0); + atomic_set(&q->block_write, 0); + atomic_set(&q->total_queued, 0); + atomic_set(&q->total_flushed, 0); + atomic_set(&q->total_locks, 0); + + init_waitqueue_head(&q->write_queue); + init_waitqueue_head(&q->read_queue); + init_waitqueue_head(&q->flush_queue); + + q->flags = ctx->flags; + + drm_waitlist_create(&q->waitlist, dev->dma->buf_count); + + return 0; +} + + +/* drm_alloc_queue: +PRE: 1) dev->queuelist[0..dev->queue_count] is allocated and will not + disappear (so all deallocation must be done after IOCTLs are off) + 2) dev->queue_count < dev->queue_slots + 3) dev->queuelist[i].use_count == 0 and + dev->queuelist[i].finalization == 0 if i not in use +POST: 1) dev->queuelist[i].use_count == 1 + 2) dev->queue_count < dev->queue_slots */ + +static int drm_alloc_queue(drm_device_t *dev) +{ + int i; + drm_queue_t *queue; + int oldslots; + int newslots; + /* Check for a free queue */ + for (i = 0; i < dev->queue_count; i++) { + atomic_inc(&dev->queuelist[i]->use_count); + if (atomic_read(&dev->queuelist[i]->use_count) == 1 + && !atomic_read(&dev->queuelist[i]->finalization)) { + DRM_DEBUG("%d (free)\n", i); + return i; + } + atomic_dec(&dev->queuelist[i]->use_count); + } + /* Allocate a new queue */ + + queue = drm_alloc(sizeof(*queue), DRM_MEM_QUEUES); + if(queue == NULL) + return -ENOMEM; + + memset(queue, 0, sizeof(*queue)); + down(&dev->struct_sem); + atomic_set(&queue->use_count, 1); + + ++dev->queue_count; + if (dev->queue_count >= dev->queue_slots) { + oldslots = dev->queue_slots * sizeof(*dev->queuelist); + if (!dev->queue_slots) dev->queue_slots = 1; + dev->queue_slots *= 2; + newslots = dev->queue_slots * sizeof(*dev->queuelist); + + dev->queuelist = drm_realloc(dev->queuelist, + oldslots, + newslots, + DRM_MEM_QUEUES); + if (!dev->queuelist) { + up(&dev->struct_sem); + DRM_DEBUG("out of memory\n"); + return -ENOMEM; + } + } + dev->queuelist[dev->queue_count-1] = queue; + + up(&dev->struct_sem); + DRM_DEBUG("%d (new)\n", dev->queue_count - 1); + return dev->queue_count - 1; +} + +int drm_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res))) + return -EFAULT; + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + if (copy_to_user(&res.contexts[i], + &i, + sizeof(i))) + return -EFAULT; + } + } + res.count = DRM_RESERVED_CONTEXTS; + if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res))) + return -EFAULT; + return 0; +} + + +int drm_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + if ((ctx.handle = drm_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) { + /* Init kernel's context and get a new one. */ + drm_init_queue(dev, dev->queuelist[ctx.handle], &ctx); + ctx.handle = drm_alloc_queue(dev); + } + drm_init_queue(dev, dev->queuelist[ctx.handle], &ctx); + DRM_DEBUG("%d\n", ctx.handle); + if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx))) + return -EFAULT; + return 0; +} + +int drm_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + drm_queue_t *q; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle < 0 || ctx.handle >= dev->queue_count) return -EINVAL; + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + return -EINVAL; + } + + if (DRM_BUFCOUNT(&q->waitlist)) { + atomic_dec(&q->use_count); + return -EBUSY; + } + + q->flags = ctx.flags; + + atomic_dec(&q->use_count); + return 0; +} + +int drm_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + drm_queue_t *q; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle >= dev->queue_count) return -EINVAL; + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + return -EINVAL; + } + + ctx.flags = q->flags; + atomic_dec(&q->use_count); + + if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx))) + return -EFAULT; + + return 0; +} + +int drm_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + return drm_context_switch(dev, dev->last_context, ctx.handle); +} + +int drm_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + drm_context_switch_complete(dev, ctx.handle); + + return 0; +} + +int drm_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + drm_queue_t *q; + drm_buf_t *buf; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle >= dev->queue_count) return -EINVAL; + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + return -EINVAL; + } + + atomic_inc(&q->finalization); /* Mark queue in finalization state */ + atomic_sub(2, &q->use_count); /* Mark queue as unused (pending + finalization) */ + + while (test_and_set_bit(0, &dev->interrupt_flag)) { + schedule(); + if (signal_pending(current)) { + clear_bit(0, &dev->interrupt_flag); + return -EINTR; + } + } + /* Remove queued buffers */ + while ((buf = drm_waitlist_get(&q->waitlist))) { + drm_free_buffer(dev, buf); + } + clear_bit(0, &dev->interrupt_flag); + + /* Wakeup blocked processes */ + wake_up_interruptible(&q->read_queue); + wake_up_interruptible(&q->write_queue); + wake_up_interruptible(&q->flush_queue); + + /* Finalization over. Queue is made + available when both use_count and + finalization become 0, which won't + happen until all the waiting processes + stop waiting. */ + atomic_dec(&q->finalization); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/ctxbitmap.c linux.ac/drivers/char/drm-4.0/ctxbitmap.c --- linux.vanilla/drivers/char/drm-4.0/ctxbitmap.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/ctxbitmap.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,85 @@ +/* ctxbitmap.c -- Context bitmap management -*- linux-c -*- + * Created: Thu Jan 6 03:56:42 2000 by jhartmann@precisioninsight.com + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Jeff Hartmann + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +void drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle) +{ + if (ctx_handle < 0) goto failed; + + if (ctx_handle < DRM_MAX_CTXBITMAP) { + clear_bit(ctx_handle, dev->ctx_bitmap); + return; + } +failed: + DRM_ERROR("Attempt to free invalid context handle: %d\n", + ctx_handle); + return; +} + +int drm_ctxbitmap_next(drm_device_t *dev) +{ + int bit; + + bit = find_first_zero_bit(dev->ctx_bitmap, DRM_MAX_CTXBITMAP); + if (bit < DRM_MAX_CTXBITMAP) { + set_bit(bit, dev->ctx_bitmap); + DRM_DEBUG("drm_ctxbitmap_next bit : %d\n", bit); + return bit; + } + return -1; +} + +int drm_ctxbitmap_init(drm_device_t *dev) +{ + int i; + int temp; + + dev->ctx_bitmap = (unsigned long *) drm_alloc(PAGE_SIZE, + DRM_MEM_CTXBITMAP); + if(dev->ctx_bitmap == NULL) { + return -ENOMEM; + } + memset((void *) dev->ctx_bitmap, 0, PAGE_SIZE); + for(i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + temp = drm_ctxbitmap_next(dev); + DRM_DEBUG("drm_ctxbitmap_init : %d\n", temp); + } + + return 0; +} + +void drm_ctxbitmap_cleanup(drm_device_t *dev) +{ + drm_free((void *)dev->ctx_bitmap, PAGE_SIZE, + DRM_MEM_CTXBITMAP); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/dma.c linux.ac/drivers/char/drm-4.0/dma.c --- linux.vanilla/drivers/char/drm-4.0/dma.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/dma.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,546 @@ +/* dma.c -- DMA IOCTL and function support -*- linux-c -*- + * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +#include /* For task queue support */ + +void drm_dma_setup(drm_device_t *dev) +{ + int i; + + if (!(dev->dma = drm_alloc(sizeof(*dev->dma), DRM_MEM_DRIVER))) { + printk(KERN_ERR "drm_dma_setup: can't drm_alloc dev->dma"); + return; + } + memset(dev->dma, 0, sizeof(*dev->dma)); + for (i = 0; i <= DRM_MAX_ORDER; i++) + memset(&dev->dma->bufs[i], 0, sizeof(dev->dma->bufs[0])); +} + +void drm_dma_takedown(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + int i, j; + + if (!dma) return; + + /* Clear dma buffers */ + for (i = 0; i <= DRM_MAX_ORDER; i++) { + if (dma->bufs[i].seg_count) { + DRM_DEBUG("order %d: buf_count = %d," + " seg_count = %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].seg_count); + for (j = 0; j < dma->bufs[i].seg_count; j++) { + drm_free_pages(dma->bufs[i].seglist[j], + dma->bufs[i].page_order, + DRM_MEM_DMA); + } + drm_free(dma->bufs[i].seglist, + dma->bufs[i].seg_count + * sizeof(*dma->bufs[0].seglist), + DRM_MEM_SEGS); + } + if(dma->bufs[i].buf_count) { + for(j = 0; j < dma->bufs[i].buf_count; j++) { + if(dma->bufs[i].buflist[j].dev_private) { + drm_free(dma->bufs[i].buflist[j].dev_private, + dma->bufs[i].buflist[j].dev_priv_size, + DRM_MEM_BUFS); + } + } + drm_free(dma->bufs[i].buflist, + dma->bufs[i].buf_count * + sizeof(*dma->bufs[0].buflist), + DRM_MEM_BUFS); + drm_freelist_destroy(&dma->bufs[i].freelist); + } + } + + if (dma->buflist) { + drm_free(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + DRM_MEM_BUFS); + } + + if (dma->pagelist) { + drm_free(dma->pagelist, + dma->page_count * sizeof(*dma->pagelist), + DRM_MEM_PAGES); + } + drm_free(dev->dma, sizeof(*dev->dma), DRM_MEM_DRIVER); + dev->dma = NULL; +} + +#if DRM_DMA_HISTOGRAM +/* This is slow, but is useful for debugging. */ +int drm_histogram_slot(unsigned long count) +{ + int value = DRM_DMA_HISTOGRAM_INITIAL; + int slot; + + for (slot = 0; + slot < DRM_DMA_HISTOGRAM_SLOTS; + ++slot, value = DRM_DMA_HISTOGRAM_NEXT(value)) { + if (count < value) return slot; + } + return DRM_DMA_HISTOGRAM_SLOTS - 1; +} + +void drm_histogram_compute(drm_device_t *dev, drm_buf_t *buf) +{ + cycles_t queued_to_dispatched; + cycles_t dispatched_to_completed; + cycles_t completed_to_freed; + int q2d, d2c, c2f, q2c, q2f; + + if (buf->time_queued) { + queued_to_dispatched = (buf->time_dispatched + - buf->time_queued); + dispatched_to_completed = (buf->time_completed + - buf->time_dispatched); + completed_to_freed = (buf->time_freed + - buf->time_completed); + + q2d = drm_histogram_slot(queued_to_dispatched); + d2c = drm_histogram_slot(dispatched_to_completed); + c2f = drm_histogram_slot(completed_to_freed); + + q2c = drm_histogram_slot(queued_to_dispatched + + dispatched_to_completed); + q2f = drm_histogram_slot(queued_to_dispatched + + dispatched_to_completed + + completed_to_freed); + + atomic_inc(&dev->histo.total); + atomic_inc(&dev->histo.queued_to_dispatched[q2d]); + atomic_inc(&dev->histo.dispatched_to_completed[d2c]); + atomic_inc(&dev->histo.completed_to_freed[c2f]); + + atomic_inc(&dev->histo.queued_to_completed[q2c]); + atomic_inc(&dev->histo.queued_to_freed[q2f]); + + } + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +} +#endif + +void drm_free_buffer(drm_device_t *dev, drm_buf_t *buf) +{ + drm_device_dma_t *dma = dev->dma; + + if (!buf) return; + + buf->waiting = 0; + buf->pending = 0; + buf->pid = 0; + buf->used = 0; +#if DRM_DMA_HISTOGRAM + buf->time_completed = get_cycles(); +#endif + if (waitqueue_active(&buf->dma_wait)) { + wake_up_interruptible(&buf->dma_wait); + } else { + /* If processes are waiting, the last one + to wake will put the buffer on the free + list. If no processes are waiting, we + put the buffer on the freelist here. */ + drm_freelist_put(dev, &dma->bufs[buf->order].freelist, buf); + } +} + +void drm_reclaim_buffers(drm_device_t *dev, pid_t pid) +{ + drm_device_dma_t *dma = dev->dma; + int i; + + if (!dma) return; + for (i = 0; i < dma->buf_count; i++) { + if (dma->buflist[i]->pid == pid) { + switch (dma->buflist[i]->list) { + case DRM_LIST_NONE: + drm_free_buffer(dev, dma->buflist[i]); + break; + case DRM_LIST_WAIT: + dma->buflist[i]->list = DRM_LIST_RECLAIM; + break; + default: + /* Buffer already on hardware. */ + break; + } + } + } +} + +int drm_context_switch(drm_device_t *dev, int old, int new) +{ + char buf[64]; + drm_queue_t *q; + + atomic_inc(&dev->total_ctx); + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new >= dev->queue_count) { + clear_bit(0, &dev->context_flag); + return -EINVAL; + } + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + q = dev->queuelist[new]; + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + atomic_dec(&q->use_count); + clear_bit(0, &dev->context_flag); + return -EINVAL; + } + + if (drm_flags & DRM_FLAG_NOCTX) { + drm_context_switch_complete(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + drm_write_string(dev, buf); + } + + atomic_dec(&q->use_count); + + return 0; +} + +int drm_context_switch_complete(drm_device_t *dev, int new) +{ + drm_device_dma_t *dma = dev->dma; + + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + if (!dma || !(dma->next_buffer && dma->next_buffer->while_locked)) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("Cannot free lock\n"); + } + } + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + wake_up_interruptible(&dev->context_wait); + + return 0; +} + +void drm_clear_next_buffer(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + + dma->next_buffer = NULL; + if (dma->next_queue && !DRM_BUFCOUNT(&dma->next_queue->waitlist)) { + wake_up_interruptible(&dma->next_queue->flush_queue); + } + dma->next_queue = NULL; +} + + +int drm_select_queue(drm_device_t *dev, void (*wrapper)(unsigned long)) +{ + int i; + int candidate = -1; + int j = jiffies; + + if (!dev) { + DRM_ERROR("No device\n"); + return -1; + } + if (!dev->queuelist || !dev->queuelist[DRM_KERNEL_CONTEXT]) { + /* This only happens between the time the + interrupt is initialized and the time + the queues are initialized. */ + return -1; + } + + /* Doing "while locked" DMA? */ + if (DRM_WAITCOUNT(dev, DRM_KERNEL_CONTEXT)) { + return DRM_KERNEL_CONTEXT; + } + + /* If there are buffers on the last_context + queue, and we have not been executing + this context very long, continue to + execute this context. */ + if (dev->last_switch <= j + && dev->last_switch + DRM_TIME_SLICE > j + && DRM_WAITCOUNT(dev, dev->last_context)) { + return dev->last_context; + } + + /* Otherwise, find a candidate */ + for (i = dev->last_checked + 1; i < dev->queue_count; i++) { + if (DRM_WAITCOUNT(dev, i)) { + candidate = dev->last_checked = i; + break; + } + } + + if (candidate < 0) { + for (i = 0; i < dev->queue_count; i++) { + if (DRM_WAITCOUNT(dev, i)) { + candidate = dev->last_checked = i; + break; + } + } + } + + if (wrapper + && candidate >= 0 + && candidate != dev->last_context + && dev->last_switch <= j + && dev->last_switch + DRM_TIME_SLICE > j) { + if (dev->timer.expires != dev->last_switch + DRM_TIME_SLICE) { + del_timer(&dev->timer); + dev->timer.function = wrapper; + dev->timer.data = (unsigned long)dev; + dev->timer.expires = dev->last_switch+DRM_TIME_SLICE; + add_timer(&dev->timer); + } + return -1; + } + + return candidate; +} + + +int drm_dma_enqueue(drm_device_t *dev, drm_dma_t *d) +{ + int i; + drm_queue_t *q; + drm_buf_t *buf; + int idx; + int while_locked = 0; + drm_device_dma_t *dma = dev->dma; + DECLARE_WAITQUEUE(entry, current); + + DRM_DEBUG("%d\n", d->send_count); + + if (d->flags & _DRM_DMA_WHILE_LOCKED) { + int context = dev->lock.hw_lock->lock; + + if (!_DRM_LOCK_IS_HELD(context)) { + DRM_ERROR("No lock held during \"while locked\"" + " request\n"); + return -EINVAL; + } + if (d->context != _DRM_LOCKING_CONTEXT(context) + && _DRM_LOCKING_CONTEXT(context) != DRM_KERNEL_CONTEXT) { + DRM_ERROR("Lock held by %d while %d makes" + " \"while locked\" request\n", + _DRM_LOCKING_CONTEXT(context), + d->context); + return -EINVAL; + } + q = dev->queuelist[DRM_KERNEL_CONTEXT]; + while_locked = 1; + } else { + q = dev->queuelist[d->context]; + } + + + atomic_inc(&q->use_count); + if (atomic_read(&q->block_write)) { + add_wait_queue(&q->write_queue, &entry); + atomic_inc(&q->block_count); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!atomic_read(&q->block_write)) break; + schedule(); + if (signal_pending(current)) { + atomic_dec(&q->use_count); + remove_wait_queue(&q->write_queue, &entry); + return -EINTR; + } + } + atomic_dec(&q->block_count); + current->state = TASK_RUNNING; + remove_wait_queue(&q->write_queue, &entry); + } + + for (i = 0; i < d->send_count; i++) { + idx = d->send_indices[i]; + if (idx < 0 || idx >= dma->buf_count) { + atomic_dec(&q->use_count); + DRM_ERROR("Index %d (of %d max)\n", + d->send_indices[i], dma->buf_count - 1); + return -EINVAL; + } + buf = dma->buflist[ idx ]; + if (buf->pid != current->pid) { + atomic_dec(&q->use_count); + DRM_ERROR("Process %d using buffer owned by %d\n", + current->pid, buf->pid); + return -EINVAL; + } + if (buf->list != DRM_LIST_NONE) { + atomic_dec(&q->use_count); + DRM_ERROR("Process %d using buffer %d on list %d\n", + current->pid, buf->idx, buf->list); + } + buf->used = d->send_sizes[i]; + buf->while_locked = while_locked; + buf->context = d->context; + if (!buf->used) { + DRM_ERROR("Queueing 0 length buffer\n"); + } + if (buf->pending) { + atomic_dec(&q->use_count); + DRM_ERROR("Queueing pending buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + return -EINVAL; + } + if (buf->waiting) { + atomic_dec(&q->use_count); + DRM_ERROR("Queueing waiting buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + return -EINVAL; + } + buf->waiting = 1; + if (atomic_read(&q->use_count) == 1 + || atomic_read(&q->finalization)) { + drm_free_buffer(dev, buf); + } else { + drm_waitlist_put(&q->waitlist, buf); + atomic_inc(&q->total_queued); + } + } + atomic_dec(&q->use_count); + + return 0; +} + +static int drm_dma_get_buffers_of_order(drm_device_t *dev, drm_dma_t *d, + int order) +{ + int i; + drm_buf_t *buf; + drm_device_dma_t *dma = dev->dma; + + for (i = d->granted_count; i < d->request_count; i++) { + buf = drm_freelist_get(&dma->bufs[order].freelist, + d->flags & _DRM_DMA_WAIT); + if (!buf) break; + if (buf->pending || buf->waiting) { + DRM_ERROR("Free buffer %d in use by %d (w%d, p%d)\n", + buf->idx, + buf->pid, + buf->waiting, + buf->pending); + } + buf->pid = current->pid; + if (copy_to_user(&d->request_indices[i], + &buf->idx, + sizeof(buf->idx))) + return -EFAULT; + + if (copy_to_user(&d->request_sizes[i], + &buf->total, + sizeof(buf->total))) + return -EFAULT; + + ++d->granted_count; + } + return 0; +} + + +int drm_dma_get_buffers(drm_device_t *dev, drm_dma_t *dma) +{ + int order; + int retcode = 0; + int tmp_order; + + order = drm_order(dma->request_size); + + dma->granted_count = 0; + retcode = drm_dma_get_buffers_of_order(dev, dma, order); + + if (dma->granted_count < dma->request_count + && (dma->flags & _DRM_DMA_SMALLER_OK)) { + for (tmp_order = order - 1; + !retcode + && dma->granted_count < dma->request_count + && tmp_order >= DRM_MIN_ORDER; + --tmp_order) { + + retcode = drm_dma_get_buffers_of_order(dev, dma, + tmp_order); + } + } + + if (dma->granted_count < dma->request_count + && (dma->flags & _DRM_DMA_LARGER_OK)) { + for (tmp_order = order + 1; + !retcode + && dma->granted_count < dma->request_count + && tmp_order <= DRM_MAX_ORDER; + ++tmp_order) { + + retcode = drm_dma_get_buffers_of_order(dev, dma, + tmp_order); + } + } + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/drawable.c linux.ac/drivers/char/drm-4.0/drawable.c --- linux.vanilla/drivers/char/drm-4.0/drawable.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/drawable.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,51 @@ +/* drawable.c -- IOCTLs for drawables -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_adddraw(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_draw_t draw; + + draw.handle = 0; /* NOOP */ + DRM_DEBUG("%d\n", draw.handle); + if (copy_to_user((drm_draw_t *)arg, &draw, sizeof(draw))) + return -EFAULT; + return 0; +} + +int drm_rmdraw(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + return 0; /* NOOP */ +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/drm.h linux.ac/drivers/char/drm-4.0/drm.h --- linux.vanilla/drivers/char/drm-4.0/drm.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/drm.h Wed Oct 10 01:47:41 2001 @@ -0,0 +1,414 @@ +/* drm.h -- Header for Direct Rendering Manager -*- linux-c -*- + * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + * Acknowledgements: + * Dec 1999, Richard Henderson , move to generic cmpxchg. + * + */ + +#ifndef _DRM_H_ +#define _DRM_H_ + +#include +#if defined(__linux__) +#include /* For _IO* macros */ +#define DRM_IOCTL_NR(n) _IOC_NR(n) +#elif defined(__FreeBSD__) +#include +#define DRM_IOCTL_NR(n) ((n) & 0xff) +#endif + +#define DRM_PROC_DEVICES "/proc/devices" +#define DRM_PROC_MISC "/proc/misc" +#define DRM_PROC_DRM "/proc/drm" +#define DRM_DEV_DRM "/dev/drm" +#define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) +#define DRM_DEV_UID 0 +#define DRM_DEV_GID 0 + + +#define DRM_NAME "drm" /* Name in kernel, /dev, and /proc */ +#define DRM_MIN_ORDER 5 /* At least 2^5 bytes = 32 bytes */ +#define DRM_MAX_ORDER 22 /* Up to 2^22 bytes = 4MB */ +#define DRM_RAM_PERCENT 10 /* How much system ram can we lock? */ + +#define _DRM_LOCK_HELD 0x80000000 /* Hardware lock is held */ +#define _DRM_LOCK_CONT 0x40000000 /* Hardware lock is contended */ +#define _DRM_LOCK_IS_HELD(lock) ((lock) & _DRM_LOCK_HELD) +#define _DRM_LOCK_IS_CONT(lock) ((lock) & _DRM_LOCK_CONT) +#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT)) + +typedef unsigned long drm_handle_t; +typedef unsigned int drm_context_t; +typedef unsigned int drm_drawable_t; +typedef unsigned int drm_magic_t; + +/* Warning: If you change this structure, make sure you change + * XF86DRIClipRectRec in the server as well */ + +typedef struct drm_clip_rect { + unsigned short x1; + unsigned short y1; + unsigned short x2; + unsigned short y2; +} drm_clip_rect_t; + +/* Seperate include files for the i810/mga/r128 specific structures */ +#include "mga_drm.h" +#include "i810_drm.h" +#include "r128_drm.h" +#include "radeon_drm.h" +#ifdef CONFIG_DRM40_SIS +#include "sis_drm.h" +#endif + +typedef struct drm_version { + int version_major; /* Major version */ + int version_minor; /* Minor version */ + int version_patchlevel;/* Patch level */ + size_t name_len; /* Length of name buffer */ + char *name; /* Name of driver */ + size_t date_len; /* Length of date buffer */ + char *date; /* User-space buffer to hold date */ + size_t desc_len; /* Length of desc buffer */ + char *desc; /* User-space buffer to hold desc */ +} drm_version_t; + +typedef struct drm_unique { + size_t unique_len; /* Length of unique */ + char *unique; /* Unique name for driver instantiation */ +} drm_unique_t; + +typedef struct drm_list { + int count; /* Length of user-space structures */ + drm_version_t *version; +} drm_list_t; + +typedef struct drm_block { + int unused; +} drm_block_t; + +typedef struct drm_control { + enum { + DRM_ADD_COMMAND, + DRM_RM_COMMAND, + DRM_INST_HANDLER, + DRM_UNINST_HANDLER + } func; + int irq; +} drm_control_t; + +typedef enum drm_map_type { + _DRM_FRAME_BUFFER = 0, /* WC (no caching), no core dump */ + _DRM_REGISTERS = 1, /* no caching, no core dump */ + _DRM_SHM = 2, /* shared, cached */ + _DRM_AGP = 3 /* AGP/GART */ +} drm_map_type_t; + +typedef enum drm_map_flags { + _DRM_RESTRICTED = 0x01, /* Cannot be mapped to user-virtual */ + _DRM_READ_ONLY = 0x02, + _DRM_LOCKED = 0x04, /* shared, cached, locked */ + _DRM_KERNEL = 0x08, /* kernel requires access */ + _DRM_WRITE_COMBINING = 0x10, /* use write-combining if available */ + _DRM_CONTAINS_LOCK = 0x20 /* SHM page that contains lock */ +} drm_map_flags_t; + +typedef struct drm_map { + unsigned long offset; /* Requested physical address (0 for SAREA)*/ + unsigned long size; /* Requested physical size (bytes) */ + drm_map_type_t type; /* Type of memory to map */ + drm_map_flags_t flags; /* Flags */ + void *handle; /* User-space: "Handle" to pass to mmap */ + /* Kernel-space: kernel-virtual address */ + int mtrr; /* MTRR slot used */ + /* Private data */ +} drm_map_t; + +typedef enum drm_lock_flags { + _DRM_LOCK_READY = 0x01, /* Wait until hardware is ready for DMA */ + _DRM_LOCK_QUIESCENT = 0x02, /* Wait until hardware quiescent */ + _DRM_LOCK_FLUSH = 0x04, /* Flush this context's DMA queue first */ + _DRM_LOCK_FLUSH_ALL = 0x08, /* Flush all DMA queues first */ + /* These *HALT* flags aren't supported yet + -- they will be used to support the + full-screen DGA-like mode. */ + _DRM_HALT_ALL_QUEUES = 0x10, /* Halt all current and future queues */ + _DRM_HALT_CUR_QUEUES = 0x20 /* Halt all current queues */ +} drm_lock_flags_t; + +typedef struct drm_lock { + int context; + drm_lock_flags_t flags; +} drm_lock_t; + +typedef enum drm_dma_flags { /* These values *MUST* match xf86drm.h */ + /* Flags for DMA buffer dispatch */ + _DRM_DMA_BLOCK = 0x01, /* Block until buffer dispatched. + Note, the buffer may not yet have + been processed by the hardware -- + getting a hardware lock with the + hardware quiescent will ensure + that the buffer has been + processed. */ + _DRM_DMA_WHILE_LOCKED = 0x02, /* Dispatch while lock held */ + _DRM_DMA_PRIORITY = 0x04, /* High priority dispatch */ + + /* Flags for DMA buffer request */ + _DRM_DMA_WAIT = 0x10, /* Wait for free buffers */ + _DRM_DMA_SMALLER_OK = 0x20, /* Smaller-than-requested buffers ok */ + _DRM_DMA_LARGER_OK = 0x40 /* Larger-than-requested buffers ok */ +} drm_dma_flags_t; + +typedef struct drm_buf_desc { + int count; /* Number of buffers of this size */ + int size; /* Size in bytes */ + int low_mark; /* Low water mark */ + int high_mark; /* High water mark */ + enum { + _DRM_PAGE_ALIGN = 0x01, /* Align on page boundaries for DMA */ + _DRM_AGP_BUFFER = 0x02 /* Buffer is in agp space */ + } flags; + unsigned long agp_start; /* Start address of where the agp buffers + * are in the agp aperture */ +} drm_buf_desc_t; + +typedef struct drm_buf_info { + int count; /* Entries in list */ + drm_buf_desc_t *list; +} drm_buf_info_t; + +typedef struct drm_buf_free { + int count; + int *list; +} drm_buf_free_t; + +typedef struct drm_buf_pub { + int idx; /* Index into master buflist */ + int total; /* Buffer size */ + int used; /* Amount of buffer in use (for DMA) */ + void *address; /* Address of buffer */ +} drm_buf_pub_t; + +typedef struct drm_buf_map { + int count; /* Length of buflist */ + void *virtual; /* Mmaped area in user-virtual */ + drm_buf_pub_t *list; /* Buffer information */ +} drm_buf_map_t; + +typedef struct drm_dma { + /* Indices here refer to the offset into + buflist in drm_buf_get_t. */ + int context; /* Context handle */ + int send_count; /* Number of buffers to send */ + int *send_indices; /* List of handles to buffers */ + int *send_sizes; /* Lengths of data to send */ + drm_dma_flags_t flags; /* Flags */ + int request_count; /* Number of buffers requested */ + int request_size; /* Desired size for buffers */ + int *request_indices; /* Buffer information */ + int *request_sizes; + int granted_count; /* Number of buffers granted */ +} drm_dma_t; + +typedef enum { + _DRM_CONTEXT_PRESERVED = 0x01, + _DRM_CONTEXT_2DONLY = 0x02 +} drm_ctx_flags_t; + +typedef struct drm_ctx { + drm_context_t handle; + drm_ctx_flags_t flags; +} drm_ctx_t; + +typedef struct drm_ctx_res { + int count; + drm_ctx_t *contexts; +} drm_ctx_res_t; + +typedef struct drm_draw { + drm_drawable_t handle; +} drm_draw_t; + +typedef struct drm_auth { + drm_magic_t magic; +} drm_auth_t; + +typedef struct drm_irq_busid { + int irq; + int busnum; + int devnum; + int funcnum; +} drm_irq_busid_t; + +typedef struct drm_agp_mode { + unsigned long mode; +} drm_agp_mode_t; + + /* For drm_agp_alloc -- allocated a buffer */ +typedef struct drm_agp_buffer { + unsigned long size; /* In bytes -- will round to page boundary */ + unsigned long handle; /* Used for BIND/UNBIND ioctls */ + unsigned long type; /* Type of memory to allocate */ + unsigned long physical; /* Physical used by i810 */ +} drm_agp_buffer_t; + + /* For drm_agp_bind */ +typedef struct drm_agp_binding { + unsigned long handle; /* From drm_agp_buffer */ + unsigned long offset; /* In bytes -- will round to page boundary */ +} drm_agp_binding_t; + +typedef struct drm_agp_info { + int agp_version_major; + int agp_version_minor; + unsigned long mode; + unsigned long aperture_base; /* physical address */ + unsigned long aperture_size; /* bytes */ + unsigned long memory_allowed; /* bytes */ + unsigned long memory_used; + + /* PCI information */ + unsigned short id_vendor; + unsigned short id_device; +} drm_agp_info_t; + +#define DRM_IOCTL_BASE 'd' +#define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) +#define DRM_IOR(nr,size) _IOR(DRM_IOCTL_BASE,nr,size) +#define DRM_IOW(nr,size) _IOW(DRM_IOCTL_BASE,nr,size) +#define DRM_IOWR(nr,size) _IOWR(DRM_IOCTL_BASE,nr,size) + + +#define DRM_IOCTL_VERSION DRM_IOWR(0x00, drm_version_t) +#define DRM_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm_unique_t) +#define DRM_IOCTL_GET_MAGIC DRM_IOR( 0x02, drm_auth_t) +#define DRM_IOCTL_IRQ_BUSID DRM_IOWR(0x03, drm_irq_busid_t) + +#define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm_unique_t) +#define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, drm_auth_t) +#define DRM_IOCTL_BLOCK DRM_IOWR(0x12, drm_block_t) +#define DRM_IOCTL_UNBLOCK DRM_IOWR(0x13, drm_block_t) +#define DRM_IOCTL_CONTROL DRM_IOW( 0x14, drm_control_t) +#define DRM_IOCTL_ADD_MAP DRM_IOWR(0x15, drm_map_t) +#define DRM_IOCTL_ADD_BUFS DRM_IOWR(0x16, drm_buf_desc_t) +#define DRM_IOCTL_MARK_BUFS DRM_IOW( 0x17, drm_buf_desc_t) +#define DRM_IOCTL_INFO_BUFS DRM_IOWR(0x18, drm_buf_info_t) +#define DRM_IOCTL_MAP_BUFS DRM_IOWR(0x19, drm_buf_map_t) +#define DRM_IOCTL_FREE_BUFS DRM_IOW( 0x1a, drm_buf_free_t) + +#define DRM_IOCTL_ADD_CTX DRM_IOWR(0x20, drm_ctx_t) +#define DRM_IOCTL_RM_CTX DRM_IOWR(0x21, drm_ctx_t) +#define DRM_IOCTL_MOD_CTX DRM_IOW( 0x22, drm_ctx_t) +#define DRM_IOCTL_GET_CTX DRM_IOWR(0x23, drm_ctx_t) +#define DRM_IOCTL_SWITCH_CTX DRM_IOW( 0x24, drm_ctx_t) +#define DRM_IOCTL_NEW_CTX DRM_IOW( 0x25, drm_ctx_t) +#define DRM_IOCTL_RES_CTX DRM_IOWR(0x26, drm_ctx_res_t) +#define DRM_IOCTL_ADD_DRAW DRM_IOWR(0x27, drm_draw_t) +#define DRM_IOCTL_RM_DRAW DRM_IOWR(0x28, drm_draw_t) +#define DRM_IOCTL_DMA DRM_IOWR(0x29, drm_dma_t) +#define DRM_IOCTL_LOCK DRM_IOW( 0x2a, drm_lock_t) +#define DRM_IOCTL_UNLOCK DRM_IOW( 0x2b, drm_lock_t) +#define DRM_IOCTL_FINISH DRM_IOW( 0x2c, drm_lock_t) + +#define DRM_IOCTL_AGP_ACQUIRE DRM_IO( 0x30) +#define DRM_IOCTL_AGP_RELEASE DRM_IO( 0x31) +#define DRM_IOCTL_AGP_ENABLE DRM_IOW( 0x32, drm_agp_mode_t) +#define DRM_IOCTL_AGP_INFO DRM_IOR( 0x33, drm_agp_info_t) +#define DRM_IOCTL_AGP_ALLOC DRM_IOWR(0x34, drm_agp_buffer_t) +#define DRM_IOCTL_AGP_FREE DRM_IOW( 0x35, drm_agp_buffer_t) +#define DRM_IOCTL_AGP_BIND DRM_IOW( 0x36, drm_agp_binding_t) +#define DRM_IOCTL_AGP_UNBIND DRM_IOW( 0x37, drm_agp_binding_t) + +/* Mga specific ioctls */ +#define DRM_IOCTL_MGA_INIT DRM_IOW( 0x40, drm_mga_init_t) +#define DRM_IOCTL_MGA_SWAP DRM_IOW( 0x41, drm_mga_swap_t) +#define DRM_IOCTL_MGA_CLEAR DRM_IOW( 0x42, drm_mga_clear_t) +#define DRM_IOCTL_MGA_ILOAD DRM_IOW( 0x43, drm_mga_iload_t) +#define DRM_IOCTL_MGA_VERTEX DRM_IOW( 0x44, drm_mga_vertex_t) +#define DRM_IOCTL_MGA_FLUSH DRM_IOW( 0x45, drm_lock_t ) +#define DRM_IOCTL_MGA_INDICES DRM_IOW( 0x46, drm_mga_indices_t) +#define DRM_IOCTL_MGA_BLIT DRM_IOW( 0x47, drm_mga_blit_t) + +/* I810 specific ioctls */ +#define DRM_IOCTL_I810_INIT DRM_IOW( 0x40, drm_i810_init_t) +#define DRM_IOCTL_I810_VERTEX DRM_IOW( 0x41, drm_i810_vertex_t) +#define DRM_IOCTL_I810_CLEAR DRM_IOW( 0x42, drm_i810_clear_t) +#define DRM_IOCTL_I810_FLUSH DRM_IO( 0x43) +#define DRM_IOCTL_I810_GETAGE DRM_IO( 0x44) +#define DRM_IOCTL_I810_GETBUF DRM_IOWR(0x45, drm_i810_dma_t) +#define DRM_IOCTL_I810_SWAP DRM_IO( 0x46) +#define DRM_IOCTL_I810_COPY DRM_IOW( 0x47, drm_i810_copy_t) +#define DRM_IOCTL_I810_DOCOPY DRM_IO( 0x48) + +/* Rage 128 specific ioctls */ +#define DRM_IOCTL_R128_INIT DRM_IOW( 0x40, drm_r128_init_t) +#define DRM_IOCTL_R128_CCE_START DRM_IO( 0x41) +#define DRM_IOCTL_R128_CCE_STOP DRM_IOW( 0x42, drm_r128_cce_stop_t) +#define DRM_IOCTL_R128_CCE_RESET DRM_IO( 0x43) +#define DRM_IOCTL_R128_CCE_IDLE DRM_IO( 0x44) +#define DRM_IOCTL_R128_RESET DRM_IO( 0x46) +#define DRM_IOCTL_R128_SWAP DRM_IO( 0x47) +#define DRM_IOCTL_R128_CLEAR DRM_IOW( 0x48, drm_r128_clear_t) +#define DRM_IOCTL_R128_VERTEX DRM_IOW( 0x49, drm_r128_vertex_t) +#define DRM_IOCTL_R128_INDICES DRM_IOW( 0x4a, drm_r128_indices_t) +#define DRM_IOCTL_R128_BLIT DRM_IOW( 0x4b, drm_r128_blit_t) +#define DRM_IOCTL_R128_DEPTH DRM_IOW( 0x4c, drm_r128_depth_t) +#define DRM_IOCTL_R128_STIPPLE DRM_IOW( 0x4d, drm_r128_stipple_t) +#define DRM_IOCTL_R128_PACKET DRM_IOWR(0x4e, drm_r128_packet_t) + +/* Radeon specific ioctls */ +#define DRM_IOCTL_RADEON_CP_INIT DRM_IOW( 0x40, drm_radeon_init_t) +#define DRM_IOCTL_RADEON_CP_START DRM_IO( 0x41) +#define DRM_IOCTL_RADEON_CP_STOP DRM_IOW( 0x42, drm_radeon_cp_stop_t) +#define DRM_IOCTL_RADEON_CP_RESET DRM_IO( 0x43) +#define DRM_IOCTL_RADEON_CP_IDLE DRM_IO( 0x44) +#define DRM_IOCTL_RADEON_RESET DRM_IO( 0x45) +#define DRM_IOCTL_RADEON_FULLSCREEN DRM_IOW( 0x46, drm_radeon_fullscreen_t) +#define DRM_IOCTL_RADEON_SWAP DRM_IO( 0x47) +#define DRM_IOCTL_RADEON_CLEAR DRM_IOW( 0x48, drm_radeon_clear_t) +#define DRM_IOCTL_RADEON_VERTEX DRM_IOW( 0x49, drm_radeon_vertex_t) +#define DRM_IOCTL_RADEON_INDICES DRM_IOW( 0x4a, drm_radeon_indices_t) +#define DRM_IOCTL_RADEON_BLIT DRM_IOW( 0x4b, drm_radeon_blit_t) +#define DRM_IOCTL_RADEON_STIPPLE DRM_IOW( 0x4c, drm_radeon_stipple_t) +#define DRM_IOCTL_RADEON_INDIRECT DRM_IOWR(0x4d, drm_radeon_indirect_t) + +#ifdef CONFIG_DRM40_SIS +/* SiS specific ioctls */ +#define SIS_IOCTL_FB_ALLOC DRM_IOWR(0x44, drm_sis_mem_t) +#define SIS_IOCTL_FB_FREE DRM_IOW( 0x45, drm_sis_mem_t) +#define SIS_IOCTL_AGP_INIT DRM_IOWR(0x53, drm_sis_agp_t) +#define SIS_IOCTL_AGP_ALLOC DRM_IOWR(0x54, drm_sis_mem_t) +#define SIS_IOCTL_AGP_FREE DRM_IOW( 0x55, drm_sis_mem_t) +#define SIS_IOCTL_FLIP DRM_IOW( 0x48, drm_sis_flip_t) +#define SIS_IOCTL_FLIP_INIT DRM_IO( 0x49) +#define SIS_IOCTL_FLIP_FINAL DRM_IO( 0x50) +#endif + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/drmP.h linux.ac/drivers/char/drm-4.0/drmP.h --- linux.vanilla/drivers/char/drm-4.0/drmP.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/drmP.h Wed Oct 10 01:47:41 2001 @@ -0,0 +1,836 @@ +/* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*- + * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#ifndef _DRM_P_H_ +#define _DRM_P_H_ + +#ifdef __KERNEL__ +#ifdef __alpha__ +/* add include of current.h so that "current" is defined + * before static inline funcs in wait.h. Doing this so we + * can build the DRM (part of PI DRI). 4/21/2000 S + B */ +#include +#endif /* __alpha__ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For (un)lock_kernel */ +#include +#ifdef __alpha__ +#include /* For pte_wrprotect */ +#endif +#include +#include +#include +#ifdef CONFIG_MTRR +#include +#endif +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) +#include +#include +#endif +#if LINUX_VERSION_CODE >= 0x020100 /* KERNEL_VERSION(2,1,0) */ +#include +#include +#endif +#if LINUX_VERSION_CODE < 0x020400 +#include "compat-pre24.h" +#endif +#include "drm.h" + +#define DRM_DEBUG_CODE 2 /* Include debugging code (if > 1, then + also include looping detection. */ +#define DRM_DMA_HISTOGRAM 1 /* Make histogram of DMA latency. */ + +#define DRM_HASH_SIZE 16 /* Size of key hash table */ +#define DRM_KERNEL_CONTEXT 0 /* Change drm_resctx if changed */ +#define DRM_RESERVED_CONTEXTS 1 /* Change drm_resctx if changed */ +#define DRM_LOOPING_LIMIT 5000000 +#define DRM_BSZ 1024 /* Buffer size for /dev/drm? output */ +#define DRM_TIME_SLICE (HZ/20) /* Time slice for GLXContexts */ +#define DRM_LOCK_SLICE 1 /* Time slice for lock, in jiffies */ + +#define DRM_FLAG_DEBUG 0x01 +#define DRM_FLAG_NOCTX 0x02 + +#define DRM_MEM_DMA 0 +#define DRM_MEM_SAREA 1 +#define DRM_MEM_DRIVER 2 +#define DRM_MEM_MAGIC 3 +#define DRM_MEM_IOCTLS 4 +#define DRM_MEM_MAPS 5 +#define DRM_MEM_VMAS 6 +#define DRM_MEM_BUFS 7 +#define DRM_MEM_SEGS 8 +#define DRM_MEM_PAGES 9 +#define DRM_MEM_FILES 10 +#define DRM_MEM_QUEUES 11 +#define DRM_MEM_CMDS 12 +#define DRM_MEM_MAPPINGS 13 +#define DRM_MEM_BUFLISTS 14 +#define DRM_MEM_AGPLISTS 15 +#define DRM_MEM_TOTALAGP 16 +#define DRM_MEM_BOUNDAGP 17 +#define DRM_MEM_CTXBITMAP 18 + +#define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) + + /* Backward compatibility section */ + /* _PAGE_WT changed to _PAGE_PWT in 2.2.6 */ +#ifndef _PAGE_PWT +#define _PAGE_PWT _PAGE_WT +#endif + /* Wait queue declarations changed in 2.3.1 */ +#ifndef DECLARE_WAITQUEUE +#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = { c, NULL } +typedef struct wait_queue *wait_queue_head_t; +#define init_waitqueue_head(q) *q = NULL; +#endif + + /* _PAGE_4M changed to _PAGE_PSE in 2.3.23 */ +#ifndef _PAGE_PSE +#define _PAGE_PSE _PAGE_4M +#endif + + /* vm_offset changed to vm_pgoff in 2.3.25 */ +#if LINUX_VERSION_CODE < 0x020319 +#define VM_OFFSET(vma) ((vma)->vm_offset) +#else +#define VM_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT) +#endif + + /* *_nopage return values defined in 2.3.26 */ +#ifndef NOPAGE_SIGBUS +#define NOPAGE_SIGBUS 0 +#endif +#ifndef NOPAGE_OOM +#define NOPAGE_OOM 0 +#endif + + /* module_init/module_exit added in 2.3.13 */ +#ifndef module_init +#define module_init(x) int init_module(void) { return x(); } +#endif +#ifndef module_exit +#define module_exit(x) void cleanup_module(void) { x(); } +#endif + + /* Generic cmpxchg added in 2.3.x */ +#ifndef __HAVE_ARCH_CMPXCHG + /* Include this here so that driver can be + used with older kernels. */ +#if defined(__alpha__) +static __inline__ unsigned long +__cmpxchg_u32(volatile int *m, int old, int new) +{ + unsigned long prev, cmp; + + __asm__ __volatile__( + "1: ldl_l %0,%2\n" + " cmpeq %0,%3,%1\n" + " beq %1,2f\n" + " mov %4,%1\n" + " stl_c %1,%2\n" + " beq %1,3f\n" + "2: mb\n" + ".subsection 2\n" + "3: br 1b\n" + ".previous" + : "=&r"(prev), "=&r"(cmp), "=m"(*m) + : "r"((long) old), "r"(new), "m"(*m)); + + return prev; +} + +static __inline__ unsigned long +__cmpxchg_u64(volatile long *m, unsigned long old, unsigned long new) +{ + unsigned long prev, cmp; + + __asm__ __volatile__( + "1: ldq_l %0,%2\n" + " cmpeq %0,%3,%1\n" + " beq %1,2f\n" + " mov %4,%1\n" + " stq_c %1,%2\n" + " beq %1,3f\n" + "2: mb\n" + ".subsection 2\n" + "3: br 1b\n" + ".previous" + : "=&r"(prev), "=&r"(cmp), "=m"(*m) + : "r"((long) old), "r"(new), "m"(*m)); + + return prev; +} + +static __inline__ unsigned long +__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) +{ + switch (size) { + case 4: + return __cmpxchg_u32(ptr, old, new); + case 8: + return __cmpxchg_u64(ptr, old, new); + } + return old; +} +#define cmpxchg(ptr,o,n) \ + ({ \ + __typeof__(*(ptr)) _o_ = (o); \ + __typeof__(*(ptr)) _n_ = (n); \ + (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ + (unsigned long)_n_, sizeof(*(ptr))); \ + }) + +#elif __i386__ +static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, + unsigned long new, int size) +{ + unsigned long prev; + switch (size) { + case 1: + __asm__ __volatile__(LOCK_PREFIX "cmpxchgb %b1,%2" + : "=a"(prev) + : "q"(new), "m"(*__xg(ptr)), "0"(old) + : "memory"); + return prev; + case 2: + __asm__ __volatile__(LOCK_PREFIX "cmpxchgw %w1,%2" + : "=a"(prev) + : "q"(new), "m"(*__xg(ptr)), "0"(old) + : "memory"); + return prev; + case 4: + __asm__ __volatile__(LOCK_PREFIX "cmpxchgl %1,%2" + : "=a"(prev) + : "q"(new), "m"(*__xg(ptr)), "0"(old) + : "memory"); + return prev; + } + return old; +} + +#define cmpxchg(ptr,o,n) \ + ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o), \ + (unsigned long)(n),sizeof(*(ptr)))) +#endif /* i386 & alpha */ +#endif + + /* Macros to make printk easier */ +#define DRM_ERROR(fmt, arg...) \ + printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ "] *ERROR* " fmt , ##arg) +#define DRM_MEM_ERROR(area, fmt, arg...) \ + printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ ":%s] *ERROR* " fmt , \ + drm_mem_stats[area].name , ##arg) +#define DRM_INFO(fmt, arg...) printk(KERN_INFO "[" DRM_NAME "] " fmt , ##arg) + +#if DRM_DEBUG_CODE +#define DRM_DEBUG(fmt, arg...) \ + do { \ + if (drm_flags&DRM_FLAG_DEBUG) \ + printk(KERN_DEBUG \ + "[" DRM_NAME ":" __FUNCTION__ "] " fmt , \ + ##arg); \ + } while (0) +#else +#define DRM_DEBUG(fmt, arg...) do { } while (0) +#endif + +#define DRM_PROC_LIMIT (PAGE_SIZE-80) + +#define DRM_PROC_PRINT(fmt, arg...) \ + len += sprintf(&buf[len], fmt , ##arg); \ + if (len > DRM_PROC_LIMIT) return len; + +#define DRM_PROC_PRINT_RET(ret, fmt, arg...) \ + len += sprintf(&buf[len], fmt , ##arg); \ + if (len > DRM_PROC_LIMIT) { ret; return len; } + + /* Internal types and structures */ +#define DRM_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) +#define DRM_MIN(a,b) ((a)<(b)?(a):(b)) +#define DRM_MAX(a,b) ((a)>(b)?(a):(b)) + +#define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1)) +#define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x)) +#define DRM_WAITCOUNT(dev,idx) DRM_BUFCOUNT(&dev->queuelist[idx]->waitlist) + +typedef int drm_ioctl_t(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +typedef struct drm_ioctl_desc { + drm_ioctl_t *func; + int auth_needed; + int root_only; +} drm_ioctl_desc_t; + +typedef struct drm_devstate { + pid_t owner; /* X server pid holding x_lock */ + +} drm_devstate_t; + +typedef struct drm_magic_entry { + drm_magic_t magic; + struct drm_file *priv; + struct drm_magic_entry *next; +} drm_magic_entry_t; + +typedef struct drm_magic_head { + struct drm_magic_entry *head; + struct drm_magic_entry *tail; +} drm_magic_head_t; + +typedef struct drm_vma_entry { + struct vm_area_struct *vma; + struct drm_vma_entry *next; + pid_t pid; +} drm_vma_entry_t; + +typedef struct drm_buf { + int idx; /* Index into master buflist */ + int total; /* Buffer size */ + int order; /* log-base-2(total) */ + int used; /* Amount of buffer in use (for DMA) */ + unsigned long offset; /* Byte offset (used internally) */ + void *address; /* Address of buffer */ + unsigned long bus_address; /* Bus address of buffer */ + struct drm_buf *next; /* Kernel-only: used for free list */ + __volatile__ int waiting; /* On kernel DMA queue */ + __volatile__ int pending; /* On hardware DMA queue */ + wait_queue_head_t dma_wait; /* Processes waiting */ + pid_t pid; /* PID of holding process */ + int context; /* Kernel queue for this buffer */ + int while_locked;/* Dispatch this buffer while locked */ + enum { + DRM_LIST_NONE = 0, + DRM_LIST_FREE = 1, + DRM_LIST_WAIT = 2, + DRM_LIST_PEND = 3, + DRM_LIST_PRIO = 4, + DRM_LIST_RECLAIM = 5 + } list; /* Which list we're on */ + +#if DRM_DMA_HISTOGRAM + cycles_t time_queued; /* Queued to kernel DMA queue */ + cycles_t time_dispatched; /* Dispatched to hardware */ + cycles_t time_completed; /* Completed by hardware */ + cycles_t time_freed; /* Back on freelist */ +#endif + + int dev_priv_size; /* Size of buffer private stoarge */ + void *dev_private; /* Per-buffer private storage */ +} drm_buf_t; + +#if DRM_DMA_HISTOGRAM +#define DRM_DMA_HISTOGRAM_SLOTS 9 +#define DRM_DMA_HISTOGRAM_INITIAL 10 +#define DRM_DMA_HISTOGRAM_NEXT(current) ((current)*10) +typedef struct drm_histogram { + atomic_t total; + + atomic_t queued_to_dispatched[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t dispatched_to_completed[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t completed_to_freed[DRM_DMA_HISTOGRAM_SLOTS]; + + atomic_t queued_to_completed[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t queued_to_freed[DRM_DMA_HISTOGRAM_SLOTS]; + + atomic_t dma[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t schedule[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t ctx[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t lacq[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t lhld[DRM_DMA_HISTOGRAM_SLOTS]; +} drm_histogram_t; +#endif + + /* bufs is one longer than it has to be */ +typedef struct drm_waitlist { + int count; /* Number of possible buffers */ + drm_buf_t **bufs; /* List of pointers to buffers */ + drm_buf_t **rp; /* Read pointer */ + drm_buf_t **wp; /* Write pointer */ + drm_buf_t **end; /* End pointer */ + spinlock_t read_lock; + spinlock_t write_lock; +} drm_waitlist_t; + +typedef struct drm_freelist { + int initialized; /* Freelist in use */ + atomic_t count; /* Number of free buffers */ + drm_buf_t *next; /* End pointer */ + + wait_queue_head_t waiting; /* Processes waiting on free bufs */ + int low_mark; /* Low water mark */ + int high_mark; /* High water mark */ + atomic_t wfh; /* If waiting for high mark */ + spinlock_t lock; +} drm_freelist_t; + +typedef struct drm_buf_entry { + int buf_size; + int buf_count; + drm_buf_t *buflist; + int seg_count; + int page_order; + unsigned long *seglist; + + drm_freelist_t freelist; +} drm_buf_entry_t; + +typedef struct drm_hw_lock { + __volatile__ unsigned int lock; + char padding[60]; /* Pad to cache line */ +} drm_hw_lock_t; + +typedef struct drm_file { + int authenticated; + int minor; + pid_t pid; + uid_t uid; + drm_magic_t magic; + unsigned long ioctl_count; + struct drm_file *next; + struct drm_file *prev; + struct drm_device *dev; + int remove_auth_on_close; +} drm_file_t; + + +typedef struct drm_queue { + atomic_t use_count; /* Outstanding uses (+1) */ + atomic_t finalization; /* Finalization in progress */ + atomic_t block_count; /* Count of processes waiting */ + atomic_t block_read; /* Queue blocked for reads */ + wait_queue_head_t read_queue; /* Processes waiting on block_read */ + atomic_t block_write; /* Queue blocked for writes */ + wait_queue_head_t write_queue; /* Processes waiting on block_write */ + atomic_t total_queued; /* Total queued statistic */ + atomic_t total_flushed;/* Total flushes statistic */ + atomic_t total_locks; /* Total locks statistics */ + drm_ctx_flags_t flags; /* Context preserving and 2D-only */ + drm_waitlist_t waitlist; /* Pending buffers */ + wait_queue_head_t flush_queue; /* Processes waiting until flush */ +} drm_queue_t; + +typedef struct drm_lock_data { + drm_hw_lock_t *hw_lock; /* Hardware lock */ + pid_t pid; /* PID of lock holder (0=kernel) */ + wait_queue_head_t lock_queue; /* Queue of blocked processes */ + unsigned long lock_time; /* Time of last lock in jiffies */ +} drm_lock_data_t; + +typedef struct drm_device_dma { + /* Performance Counters */ + atomic_t total_prio; /* Total DRM_DMA_PRIORITY */ + atomic_t total_bytes; /* Total bytes DMA'd */ + atomic_t total_dmas; /* Total DMA buffers dispatched */ + + atomic_t total_missed_dma; /* Missed drm_do_dma */ + atomic_t total_missed_lock; /* Missed lock in drm_do_dma */ + atomic_t total_missed_free; /* Missed drm_free_this_buffer */ + atomic_t total_missed_sched;/* Missed drm_dma_schedule */ + + atomic_t total_tried; /* Tried next_buffer */ + atomic_t total_hit; /* Sent next_buffer */ + atomic_t total_lost; /* Lost interrupt */ + + drm_buf_entry_t bufs[DRM_MAX_ORDER+1]; + int buf_count; + drm_buf_t **buflist; /* Vector of pointers info bufs */ + int seg_count; + int page_count; + unsigned long *pagelist; + unsigned long byte_count; + enum { + _DRM_DMA_USE_AGP = 0x01 + } flags; + + /* DMA support */ + drm_buf_t *this_buffer; /* Buffer being sent */ + drm_buf_t *next_buffer; /* Selected buffer to send */ + drm_queue_t *next_queue; /* Queue from which buffer selected*/ + wait_queue_head_t waiting; /* Processes waiting on free bufs */ +} drm_device_dma_t; + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) +typedef struct drm_agp_mem { + unsigned long handle; + agp_memory *memory; + unsigned long bound; /* address */ + int pages; + struct drm_agp_mem *prev; + struct drm_agp_mem *next; +} drm_agp_mem_t; + +typedef struct drm_agp_head { + agp_kern_info agp_info; + const char *chipset; + drm_agp_mem_t *memory; + unsigned long mode; + int enabled; + int acquired; + unsigned long base; + int agp_mtrr; +} drm_agp_head_t; +#endif + +typedef struct drm_sigdata { + int context; + drm_hw_lock_t *lock; +} drm_sigdata_t; + +typedef struct drm_device { + const char *name; /* Simple driver name */ + char *unique; /* Unique identifier: e.g., busid */ + int unique_len; /* Length of unique field */ + dev_t device; /* Device number for mknod */ + char *devname; /* For /proc/interrupts */ + + int blocked; /* Blocked due to VC switch? */ + struct proc_dir_entry *root; /* Root for this device's entries */ + + /* Locks */ + spinlock_t count_lock; /* For inuse, open_count, buf_use */ + struct semaphore struct_sem; /* For others */ + + /* Usage Counters */ + int open_count; /* Outstanding files open */ + atomic_t ioctl_count; /* Outstanding IOCTLs pending */ + atomic_t vma_count; /* Outstanding vma areas open */ + int buf_use; /* Buffers in use -- cannot alloc */ + atomic_t buf_alloc; /* Buffer allocation in progress */ + + /* Performance Counters */ + atomic_t total_open; + atomic_t total_close; + atomic_t total_ioctl; + atomic_t total_irq; /* Total interruptions */ + atomic_t total_ctx; /* Total context switches */ + + atomic_t total_locks; + atomic_t total_unlocks; + atomic_t total_contends; + atomic_t total_sleeps; + + /* Authentication */ + drm_file_t *file_first; + drm_file_t *file_last; + drm_magic_head_t magiclist[DRM_HASH_SIZE]; + + /* Memory management */ + drm_map_t **maplist; /* Vector of pointers to regions */ + int map_count; /* Number of mappable regions */ + + drm_vma_entry_t *vmalist; /* List of vmas (for debugging) */ + drm_lock_data_t lock; /* Information on hardware lock */ + + /* DMA queues (contexts) */ + int queue_count; /* Number of active DMA queues */ + int queue_reserved; /* Number of reserved DMA queues */ + int queue_slots; /* Actual length of queuelist */ + drm_queue_t **queuelist; /* Vector of pointers to DMA queues */ + drm_device_dma_t *dma; /* Optional pointer for DMA support */ + + /* Context support */ + int irq; /* Interrupt used by board */ + __volatile__ long context_flag; /* Context swapping flag */ + __volatile__ long interrupt_flag; /* Interruption handler flag */ + __volatile__ long dma_flag; /* DMA dispatch flag */ + struct timer_list timer; /* Timer for delaying ctx switch */ + wait_queue_head_t context_wait; /* Processes waiting on ctx switch */ + int last_checked; /* Last context checked for DMA */ + int last_context; /* Last current context */ + unsigned long last_switch; /* jiffies at last context switch */ + struct tq_struct tq; + cycles_t ctx_start; + cycles_t lck_start; +#if DRM_DMA_HISTOGRAM + drm_histogram_t histo; +#endif + + /* Callback to X server for context switch + and for heavy-handed reset. */ + char buf[DRM_BSZ]; /* Output buffer */ + char *buf_rp; /* Read pointer */ + char *buf_wp; /* Write pointer */ + char *buf_end; /* End pointer */ + struct fasync_struct *buf_async;/* Processes waiting for SIGIO */ + wait_queue_head_t buf_readers; /* Processes waiting to read */ + wait_queue_head_t buf_writers; /* Processes waiting to ctx switch */ + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + drm_agp_head_t *agp; +#endif + unsigned long *ctx_bitmap; + void *dev_private; + drm_sigdata_t sigdata; /* For block_all_signals */ + sigset_t sigmask; +} drm_device_t; + + + /* Internal function definitions */ + + /* Misc. support (init.c) */ +extern int drm_flags; +extern void drm_parse_options(char *s); +extern int drm_cpu_valid(void); + + + /* Device support (fops.c) */ +extern int drm_open_helper(struct inode *inode, struct file *filp, + drm_device_t *dev); +extern int drm_flush(struct file *filp); +extern int drm_release(struct inode *inode, struct file *filp); +extern int drm_fasync(int fd, struct file *filp, int on); +extern ssize_t drm_read(struct file *filp, char *buf, size_t count, + loff_t *off); +extern int drm_write_string(drm_device_t *dev, const char *s); +extern unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait); + + /* Mapping support (vm.c) */ +#if LINUX_VERSION_CODE < 0x020317 +extern unsigned long drm_vm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +extern unsigned long drm_vm_shm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +extern unsigned long drm_vm_shm_nopage_lock(struct vm_area_struct *vma, + unsigned long address, + int write_access); +extern unsigned long drm_vm_dma_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +#else + /* Return type changed in 2.3.23 */ +extern struct page *drm_vm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +extern struct page *drm_vm_shm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +extern struct page *drm_vm_shm_nopage_lock(struct vm_area_struct *vma, + unsigned long address, + int write_access); +extern struct page *drm_vm_dma_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +#endif +extern void drm_vm_open(struct vm_area_struct *vma); +extern void drm_vm_close(struct vm_area_struct *vma); +extern int drm_mmap_dma(struct file *filp, + struct vm_area_struct *vma); +extern int drm_mmap(struct file *filp, struct vm_area_struct *vma); + + + /* Proc support (proc.c) */ +extern int drm_proc_init(drm_device_t *dev); +extern int drm_proc_cleanup(void); + + /* Memory management support (memory.c) */ +extern void drm_mem_init(void); +extern int drm_mem_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +extern void *drm_alloc(size_t size, int area); +extern void *drm_realloc(void *oldpt, size_t oldsize, size_t size, + int area); +extern char *drm_strdup(const char *s, int area); +extern void drm_strfree(const char *s, int area); +extern void drm_free(void *pt, size_t size, int area); +extern unsigned long drm_alloc_pages(int order, int area); +extern void drm_free_pages(unsigned long address, int order, + int area); +extern void *drm_ioremap(unsigned long offset, unsigned long size); +extern void drm_ioremapfree(void *pt, unsigned long size); + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) +extern agp_memory *drm_alloc_agp(int pages, u32 type); +extern int drm_free_agp(agp_memory *handle, int pages); +extern int drm_bind_agp(agp_memory *handle, unsigned int start); +extern int drm_unbind_agp(agp_memory *handle); +#endif + + + /* Buffer management support (bufs.c) */ +extern int drm_order(unsigned long size); +extern int drm_addmap(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_addbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_infobufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_markbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_freebufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_mapbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Buffer list management support (lists.c) */ +extern int drm_waitlist_create(drm_waitlist_t *bl, int count); +extern int drm_waitlist_destroy(drm_waitlist_t *bl); +extern int drm_waitlist_put(drm_waitlist_t *bl, drm_buf_t *buf); +extern drm_buf_t *drm_waitlist_get(drm_waitlist_t *bl); + +extern int drm_freelist_create(drm_freelist_t *bl, int count); +extern int drm_freelist_destroy(drm_freelist_t *bl); +extern int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, + drm_buf_t *buf); +extern drm_buf_t *drm_freelist_get(drm_freelist_t *bl, int block); + + /* DMA support (gen_dma.c) */ +extern void drm_dma_setup(drm_device_t *dev); +extern void drm_dma_takedown(drm_device_t *dev); +extern void drm_free_buffer(drm_device_t *dev, drm_buf_t *buf); +extern void drm_reclaim_buffers(drm_device_t *dev, pid_t pid); +extern int drm_context_switch(drm_device_t *dev, int old, int new); +extern int drm_context_switch_complete(drm_device_t *dev, int new); +extern void drm_clear_next_buffer(drm_device_t *dev); +extern int drm_select_queue(drm_device_t *dev, + void (*wrapper)(unsigned long)); +extern int drm_dma_enqueue(drm_device_t *dev, drm_dma_t *dma); +extern int drm_dma_get_buffers(drm_device_t *dev, drm_dma_t *dma); +#if DRM_DMA_HISTOGRAM +extern int drm_histogram_slot(unsigned long count); +extern void drm_histogram_compute(drm_device_t *dev, drm_buf_t *buf); +#endif + + + /* Misc. IOCTL support (ioctl.c) */ +extern int drm_irq_busid(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_getunique(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_setunique(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Context IOCTL support (context.c) */ +extern int drm_resctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_addctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_modctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_getctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_switchctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_newctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_rmctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Drawable IOCTL support (drawable.c) */ +extern int drm_adddraw(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_rmdraw(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Authentication IOCTL support (auth.c) */ +extern int drm_add_magic(drm_device_t *dev, drm_file_t *priv, + drm_magic_t magic); +extern int drm_remove_magic(drm_device_t *dev, drm_magic_t magic); +extern int drm_getmagic(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_authmagic(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Locking IOCTL support (lock.c) */ +extern int drm_block(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_unblock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_lock_take(__volatile__ unsigned int *lock, + unsigned int context); +extern int drm_lock_transfer(drm_device_t *dev, + __volatile__ unsigned int *lock, + unsigned int context); +extern int drm_lock_free(drm_device_t *dev, + __volatile__ unsigned int *lock, + unsigned int context); +extern int drm_finish(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_flush_unblock(drm_device_t *dev, int context, + drm_lock_flags_t flags); +extern int drm_flush_block_and_flush(drm_device_t *dev, int context, + drm_lock_flags_t flags); +extern int drm_notifier(void *priv); + + /* Context Bitmap support (ctxbitmap.c) */ +extern int drm_ctxbitmap_init(drm_device_t *dev); +extern void drm_ctxbitmap_cleanup(drm_device_t *dev); +extern int drm_ctxbitmap_next(drm_device_t *dev); +extern void drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle); + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + /* AGP/GART support (agpsupport.c) */ +extern drm_agp_head_t *drm_agp_init(void); +extern void drm_agp_uninit(void); +extern int drm_agp_acquire(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern void _drm_agp_release(void); +extern int drm_agp_release(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_agp_enable(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_agp_info(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_agp_alloc(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_agp_free(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_agp_unbind(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_agp_bind(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern agp_memory *drm_agp_allocate_memory(size_t pages, u32 type); +extern int drm_agp_free_memory(agp_memory *handle); +extern int drm_agp_bind_memory(agp_memory *handle, off_t start); +extern int drm_agp_unbind_memory(agp_memory *handle); +#endif +#endif +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/ffb_context.c linux.ac/drivers/char/drm-4.0/ffb_context.c --- linux.vanilla/drivers/char/drm-4.0/ffb_context.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/ffb_context.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,540 @@ +/* $Id: ffb_context.c,v 1.4 2000/08/29 07:01:55 davem Exp $ + * ffb_context.c: Creator/Creator3D DRI/DRM context switching. + * + * Copyright (C) 2000 David S. Miller (davem@redhat.com) + * + * Almost entirely stolen from tdfx_context.c, see there + * for authors. + */ + +#include +#include + +#include "drmP.h" + +#include "ffb_drv.h" + +static int ffb_alloc_queue(drm_device_t *dev, int is_2d_only) +{ + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + int i; + + for (i = 0; i < FFB_MAX_CTXS; i++) { + if (fpriv->hw_state[i] == NULL) + break; + } + if (i == FFB_MAX_CTXS) + return -1; + + fpriv->hw_state[i] = kmalloc(sizeof(struct ffb_hw_context), GFP_KERNEL); + if (fpriv->hw_state[i] == NULL) + return -1; + + fpriv->hw_state[i]->is_2d_only = is_2d_only; + + /* Plus one because 0 is the special DRM_KERNEL_CONTEXT. */ + return i + 1; +} + +static void ffb_save_context(ffb_dev_priv_t *fpriv, int idx) +{ + ffb_fbcPtr ffb = fpriv->regs; + struct ffb_hw_context *ctx; + int i; + + ctx = fpriv->hw_state[idx - 1]; + if (idx == 0 || ctx == NULL) + return; + + if (ctx->is_2d_only) { + /* 2D applications only care about certain pieces + * of state. + */ + ctx->drawop = upa_readl(&ffb->drawop); + ctx->ppc = upa_readl(&ffb->ppc); + ctx->wid = upa_readl(&ffb->wid); + ctx->fg = upa_readl(&ffb->fg); + ctx->bg = upa_readl(&ffb->bg); + ctx->xclip = upa_readl(&ffb->xclip); + ctx->fbc = upa_readl(&ffb->fbc); + ctx->rop = upa_readl(&ffb->rop); + ctx->cmp = upa_readl(&ffb->cmp); + ctx->matchab = upa_readl(&ffb->matchab); + ctx->magnab = upa_readl(&ffb->magnab); + ctx->pmask = upa_readl(&ffb->pmask); + ctx->xpmask = upa_readl(&ffb->xpmask); + ctx->lpat = upa_readl(&ffb->lpat); + ctx->fontxy = upa_readl(&ffb->fontxy); + ctx->fontw = upa_readl(&ffb->fontw); + ctx->fontinc = upa_readl(&ffb->fontinc); + + /* stencil/stencilctl only exists on FFB2+ and later + * due to the introduction of 3DRAM-III. + */ + if (fpriv->ffb_type == ffb2_vertical_plus || + fpriv->ffb_type == ffb2_horizontal_plus) { + ctx->stencil = upa_readl(&ffb->stencil); + ctx->stencilctl = upa_readl(&ffb->stencilctl); + } + + for (i = 0; i < 32; i++) + ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]); + ctx->ucsr = upa_readl(&ffb->ucsr); + return; + } + + /* Fetch drawop. */ + ctx->drawop = upa_readl(&ffb->drawop); + + /* If we were saving the vertex registers, this is where + * we would do it. We would save 32 32-bit words starting + * at ffb->suvtx. + */ + + /* Capture rendering attributes. */ + + ctx->ppc = upa_readl(&ffb->ppc); /* Pixel Processor Control */ + ctx->wid = upa_readl(&ffb->wid); /* Current WID */ + ctx->fg = upa_readl(&ffb->fg); /* Constant FG color */ + ctx->bg = upa_readl(&ffb->bg); /* Constant BG color */ + ctx->consty = upa_readl(&ffb->consty); /* Constant Y */ + ctx->constz = upa_readl(&ffb->constz); /* Constant Z */ + ctx->xclip = upa_readl(&ffb->xclip); /* X plane clip */ + ctx->dcss = upa_readl(&ffb->dcss); /* Depth Cue Scale Slope */ + ctx->vclipmin = upa_readl(&ffb->vclipmin); /* Primary XY clip, minimum */ + ctx->vclipmax = upa_readl(&ffb->vclipmax); /* Primary XY clip, maximum */ + ctx->vclipzmin = upa_readl(&ffb->vclipzmin); /* Primary Z clip, minimum */ + ctx->vclipzmax = upa_readl(&ffb->vclipzmax); /* Primary Z clip, maximum */ + ctx->dcsf = upa_readl(&ffb->dcsf); /* Depth Cue Scale Front Bound */ + ctx->dcsb = upa_readl(&ffb->dcsb); /* Depth Cue Scale Back Bound */ + ctx->dczf = upa_readl(&ffb->dczf); /* Depth Cue Scale Z Front */ + ctx->dczb = upa_readl(&ffb->dczb); /* Depth Cue Scale Z Back */ + ctx->blendc = upa_readl(&ffb->blendc); /* Alpha Blend Control */ + ctx->blendc1 = upa_readl(&ffb->blendc1); /* Alpha Blend Color 1 */ + ctx->blendc2 = upa_readl(&ffb->blendc2); /* Alpha Blend Color 2 */ + ctx->fbc = upa_readl(&ffb->fbc); /* Frame Buffer Control */ + ctx->rop = upa_readl(&ffb->rop); /* Raster Operation */ + ctx->cmp = upa_readl(&ffb->cmp); /* Compare Controls */ + ctx->matchab = upa_readl(&ffb->matchab); /* Buffer A/B Match Ops */ + ctx->matchc = upa_readl(&ffb->matchc); /* Buffer C Match Ops */ + ctx->magnab = upa_readl(&ffb->magnab); /* Buffer A/B Magnitude Ops */ + ctx->magnc = upa_readl(&ffb->magnc); /* Buffer C Magnitude Ops */ + ctx->pmask = upa_readl(&ffb->pmask); /* RGB Plane Mask */ + ctx->xpmask = upa_readl(&ffb->xpmask); /* X Plane Mask */ + ctx->ypmask = upa_readl(&ffb->ypmask); /* Y Plane Mask */ + ctx->zpmask = upa_readl(&ffb->zpmask); /* Z Plane Mask */ + + /* Auxiliary Clips. */ + ctx->auxclip0min = upa_readl(&ffb->auxclip[0].min); + ctx->auxclip0max = upa_readl(&ffb->auxclip[0].max); + ctx->auxclip1min = upa_readl(&ffb->auxclip[1].min); + ctx->auxclip1max = upa_readl(&ffb->auxclip[1].max); + ctx->auxclip2min = upa_readl(&ffb->auxclip[2].min); + ctx->auxclip2max = upa_readl(&ffb->auxclip[2].max); + ctx->auxclip3min = upa_readl(&ffb->auxclip[3].min); + ctx->auxclip3max = upa_readl(&ffb->auxclip[3].max); + + ctx->lpat = upa_readl(&ffb->lpat); /* Line Pattern */ + ctx->fontxy = upa_readl(&ffb->fontxy); /* XY Font Coordinate */ + ctx->fontw = upa_readl(&ffb->fontw); /* Font Width */ + ctx->fontinc = upa_readl(&ffb->fontinc); /* Font X/Y Increment */ + + /* These registers/features only exist on FFB2 and later chips. */ + if (fpriv->ffb_type >= ffb2_prototype) { + ctx->dcss1 = upa_readl(&ffb->dcss1); /* Depth Cue Scale Slope 1 */ + ctx->dcss2 = upa_readl(&ffb->dcss2); /* Depth Cue Scale Slope 2 */ + ctx->dcss2 = upa_readl(&ffb->dcss3); /* Depth Cue Scale Slope 3 */ + ctx->dcs2 = upa_readl(&ffb->dcs2); /* Depth Cue Scale 2 */ + ctx->dcs3 = upa_readl(&ffb->dcs3); /* Depth Cue Scale 3 */ + ctx->dcs4 = upa_readl(&ffb->dcs4); /* Depth Cue Scale 4 */ + ctx->dcd2 = upa_readl(&ffb->dcd2); /* Depth Cue Depth 2 */ + ctx->dcd3 = upa_readl(&ffb->dcd3); /* Depth Cue Depth 3 */ + ctx->dcd4 = upa_readl(&ffb->dcd4); /* Depth Cue Depth 4 */ + + /* And stencil/stencilctl only exists on FFB2+ and later + * due to the introduction of 3DRAM-III. + */ + if (fpriv->ffb_type == ffb2_vertical_plus || + fpriv->ffb_type == ffb2_horizontal_plus) { + ctx->stencil = upa_readl(&ffb->stencil); + ctx->stencilctl = upa_readl(&ffb->stencilctl); + } + } + + /* Save the 32x32 area pattern. */ + for (i = 0; i < 32; i++) + ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]); + + /* Finally, stash away the User Constol/Status Register. */ + ctx->ucsr = upa_readl(&ffb->ucsr); +} + +static void ffb_restore_context(ffb_dev_priv_t *fpriv, int old, int idx) +{ + ffb_fbcPtr ffb = fpriv->regs; + struct ffb_hw_context *ctx; + int i; + + ctx = fpriv->hw_state[idx - 1]; + if (idx == 0 || ctx == NULL) + return; + + if (ctx->is_2d_only) { + /* 2D applications only care about certain pieces + * of state. + */ + upa_writel(ctx->drawop, &ffb->drawop); + + /* If we were restoring the vertex registers, this is where + * we would do it. We would restore 32 32-bit words starting + * at ffb->suvtx. + */ + + upa_writel(ctx->ppc, &ffb->ppc); + upa_writel(ctx->wid, &ffb->wid); + upa_writel(ctx->fg, &ffb->fg); + upa_writel(ctx->bg, &ffb->bg); + upa_writel(ctx->xclip, &ffb->xclip); + upa_writel(ctx->fbc, &ffb->fbc); + upa_writel(ctx->rop, &ffb->rop); + upa_writel(ctx->cmp, &ffb->cmp); + upa_writel(ctx->matchab, &ffb->matchab); + upa_writel(ctx->magnab, &ffb->magnab); + upa_writel(ctx->pmask, &ffb->pmask); + upa_writel(ctx->xpmask, &ffb->xpmask); + upa_writel(ctx->lpat, &ffb->lpat); + upa_writel(ctx->fontxy, &ffb->fontxy); + upa_writel(ctx->fontw, &ffb->fontw); + upa_writel(ctx->fontinc, &ffb->fontinc); + + /* stencil/stencilctl only exists on FFB2+ and later + * due to the introduction of 3DRAM-III. + */ + if (fpriv->ffb_type == ffb2_vertical_plus || + fpriv->ffb_type == ffb2_horizontal_plus) { + upa_writel(ctx->stencil, &ffb->stencil); + upa_writel(ctx->stencilctl, &ffb->stencilctl); + upa_writel(0x80000000, &ffb->fbc); + upa_writel((ctx->stencilctl | 0x80000), + &ffb->rawstencilctl); + upa_writel(ctx->fbc, &ffb->fbc); + } + + for (i = 0; i < 32; i++) + upa_writel(ctx->area_pattern[i], &ffb->pattern[i]); + upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr); + return; + } + + /* Restore drawop. */ + upa_writel(ctx->drawop, &ffb->drawop); + + /* If we were restoring the vertex registers, this is where + * we would do it. We would restore 32 32-bit words starting + * at ffb->suvtx. + */ + + /* Restore rendering attributes. */ + + upa_writel(ctx->ppc, &ffb->ppc); /* Pixel Processor Control */ + upa_writel(ctx->wid, &ffb->wid); /* Current WID */ + upa_writel(ctx->fg, &ffb->fg); /* Constant FG color */ + upa_writel(ctx->bg, &ffb->bg); /* Constant BG color */ + upa_writel(ctx->consty, &ffb->consty); /* Constant Y */ + upa_writel(ctx->constz, &ffb->constz); /* Constant Z */ + upa_writel(ctx->xclip, &ffb->xclip); /* X plane clip */ + upa_writel(ctx->dcss, &ffb->dcss); /* Depth Cue Scale Slope */ + upa_writel(ctx->vclipmin, &ffb->vclipmin); /* Primary XY clip, minimum */ + upa_writel(ctx->vclipmax, &ffb->vclipmax); /* Primary XY clip, maximum */ + upa_writel(ctx->vclipzmin, &ffb->vclipzmin); /* Primary Z clip, minimum */ + upa_writel(ctx->vclipzmax, &ffb->vclipzmax); /* Primary Z clip, maximum */ + upa_writel(ctx->dcsf, &ffb->dcsf); /* Depth Cue Scale Front Bound */ + upa_writel(ctx->dcsb, &ffb->dcsb); /* Depth Cue Scale Back Bound */ + upa_writel(ctx->dczf, &ffb->dczf); /* Depth Cue Scale Z Front */ + upa_writel(ctx->dczb, &ffb->dczb); /* Depth Cue Scale Z Back */ + upa_writel(ctx->blendc, &ffb->blendc); /* Alpha Blend Control */ + upa_writel(ctx->blendc1, &ffb->blendc1); /* Alpha Blend Color 1 */ + upa_writel(ctx->blendc2, &ffb->blendc2); /* Alpha Blend Color 2 */ + upa_writel(ctx->fbc, &ffb->fbc); /* Frame Buffer Control */ + upa_writel(ctx->rop, &ffb->rop); /* Raster Operation */ + upa_writel(ctx->cmp, &ffb->cmp); /* Compare Controls */ + upa_writel(ctx->matchab, &ffb->matchab); /* Buffer A/B Match Ops */ + upa_writel(ctx->matchc, &ffb->matchc); /* Buffer C Match Ops */ + upa_writel(ctx->magnab, &ffb->magnab); /* Buffer A/B Magnitude Ops */ + upa_writel(ctx->magnc, &ffb->magnc); /* Buffer C Magnitude Ops */ + upa_writel(ctx->pmask, &ffb->pmask); /* RGB Plane Mask */ + upa_writel(ctx->xpmask, &ffb->xpmask); /* X Plane Mask */ + upa_writel(ctx->ypmask, &ffb->ypmask); /* Y Plane Mask */ + upa_writel(ctx->zpmask, &ffb->zpmask); /* Z Plane Mask */ + + /* Auxiliary Clips. */ + upa_writel(ctx->auxclip0min, &ffb->auxclip[0].min); + upa_writel(ctx->auxclip0max, &ffb->auxclip[0].max); + upa_writel(ctx->auxclip1min, &ffb->auxclip[1].min); + upa_writel(ctx->auxclip1max, &ffb->auxclip[1].max); + upa_writel(ctx->auxclip2min, &ffb->auxclip[2].min); + upa_writel(ctx->auxclip2max, &ffb->auxclip[2].max); + upa_writel(ctx->auxclip3min, &ffb->auxclip[3].min); + upa_writel(ctx->auxclip3max, &ffb->auxclip[3].max); + + upa_writel(ctx->lpat, &ffb->lpat); /* Line Pattern */ + upa_writel(ctx->fontxy, &ffb->fontxy); /* XY Font Coordinate */ + upa_writel(ctx->fontw, &ffb->fontw); /* Font Width */ + upa_writel(ctx->fontinc, &ffb->fontinc); /* Font X/Y Increment */ + + /* These registers/features only exist on FFB2 and later chips. */ + if (fpriv->ffb_type >= ffb2_prototype) { + upa_writel(ctx->dcss1, &ffb->dcss1); /* Depth Cue Scale Slope 1 */ + upa_writel(ctx->dcss2, &ffb->dcss2); /* Depth Cue Scale Slope 2 */ + upa_writel(ctx->dcss3, &ffb->dcss2); /* Depth Cue Scale Slope 3 */ + upa_writel(ctx->dcs2, &ffb->dcs2); /* Depth Cue Scale 2 */ + upa_writel(ctx->dcs3, &ffb->dcs3); /* Depth Cue Scale 3 */ + upa_writel(ctx->dcs4, &ffb->dcs4); /* Depth Cue Scale 4 */ + upa_writel(ctx->dcd2, &ffb->dcd2); /* Depth Cue Depth 2 */ + upa_writel(ctx->dcd3, &ffb->dcd3); /* Depth Cue Depth 3 */ + upa_writel(ctx->dcd4, &ffb->dcd4); /* Depth Cue Depth 4 */ + + /* And stencil/stencilctl only exists on FFB2+ and later + * due to the introduction of 3DRAM-III. + */ + if (fpriv->ffb_type == ffb2_vertical_plus || + fpriv->ffb_type == ffb2_horizontal_plus) { + /* Unfortunately, there is a hardware bug on + * the FFB2+ chips which prevents a normal write + * to the stencil control register from working + * as it should. + * + * The state controlled by the FFB stencilctl register + * really gets transferred to the per-buffer instances + * of the stencilctl register in the 3DRAM chips. + * + * The bug is that FFB does not update buffer C correctly, + * so we have to do it by hand for them. + */ + + /* This will update buffers A and B. */ + upa_writel(ctx->stencil, &ffb->stencil); + upa_writel(ctx->stencilctl, &ffb->stencilctl); + + /* Force FFB to use buffer C 3dram regs. */ + upa_writel(0x80000000, &ffb->fbc); + upa_writel((ctx->stencilctl | 0x80000), + &ffb->rawstencilctl); + + /* Now restore the correct FBC controls. */ + upa_writel(ctx->fbc, &ffb->fbc); + } + } + + /* Restore the 32x32 area pattern. */ + for (i = 0; i < 32; i++) + upa_writel(ctx->area_pattern[i], &ffb->pattern[i]); + + /* Finally, stash away the User Constol/Status Register. + * The only state we really preserve here is the picking + * control. + */ + upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr); +} + +#define FFB_UCSR_FB_BUSY 0x01000000 +#define FFB_UCSR_RP_BUSY 0x02000000 +#define FFB_UCSR_ALL_BUSY (FFB_UCSR_RP_BUSY|FFB_UCSR_FB_BUSY) + +static void FFBWait(ffb_fbcPtr ffb) +{ + int limit = 100000; + + do { + u32 regval = upa_readl(&ffb->ucsr); + + if ((regval & FFB_UCSR_ALL_BUSY) == 0) + break; + } while (--limit); +} + +int ffb_context_switch(drm_device_t *dev, int old, int new) +{ + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + + atomic_inc(&dev->total_ctx); + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new == dev->last_context || + dev->last_context == 0) { + dev->last_context = new; + return 0; + } + + FFBWait(fpriv->regs); + ffb_save_context(fpriv, old); + ffb_restore_context(fpriv, old, new); + FFBWait(fpriv->regs); + + dev->last_context = new; + + return 0; +} + +int ffb_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res))) + return -EFAULT; + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + if (copy_to_user(&res.contexts[i], + &i, + sizeof(i))) + return -EFAULT; + } + } + res.count = DRM_RESERVED_CONTEXTS; + if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res))) + return -EFAULT; + return 0; +} + + +int ffb_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + int idx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + idx = ffb_alloc_queue(dev, (ctx.flags & _DRM_CONTEXT_2DONLY)); + if (idx < 0) + return -ENFILE; + + DRM_DEBUG("%d\n", ctx.handle); + ctx.handle = idx; + if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx))) + return -EFAULT; + return 0; +} + +int ffb_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + struct ffb_hw_context *hwctx; + drm_ctx_t ctx; + int idx; + + if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx))) + return -EFAULT; + + idx = ctx.handle; + if (idx <= 0 || idx >= FFB_MAX_CTXS) + return -EINVAL; + + hwctx = fpriv->hw_state[idx - 1]; + if (hwctx == NULL) + return -EINVAL; + + if ((ctx.flags & _DRM_CONTEXT_2DONLY) == 0) + hwctx->is_2d_only = 0; + else + hwctx->is_2d_only = 1; + + return 0; +} + +int ffb_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + struct ffb_hw_context *hwctx; + drm_ctx_t ctx; + int idx; + + if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx))) + return -EFAULT; + + idx = ctx.handle; + if (idx <= 0 || idx >= FFB_MAX_CTXS) + return -EINVAL; + + hwctx = fpriv->hw_state[idx - 1]; + if (hwctx == NULL) + return -EINVAL; + + if (hwctx->is_2d_only != 0) + ctx.flags = _DRM_CONTEXT_2DONLY; + else + ctx.flags = 0; + + if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx))) + return -EFAULT; + + return 0; +} + +int ffb_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + return ffb_context_switch(dev, dev->last_context, ctx.handle); +} + +int ffb_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + + return 0; +} + +int ffb_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + int idx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + + idx = ctx.handle - 1; + if (idx < 0 || idx >= FFB_MAX_CTXS) + return -EINVAL; + + if (fpriv->hw_state[idx] != NULL) { + kfree(fpriv->hw_state[idx]); + fpriv->hw_state[idx] = NULL; + } + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/ffb_drv.c linux.ac/drivers/char/drm-4.0/ffb_drv.c --- linux.vanilla/drivers/char/drm-4.0/ffb_drv.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/ffb_drv.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,952 @@ +/* $Id: ffb_drv.c,v 1.14 2001/05/24 12:01:47 davem Exp $ + * ffb_drv.c: Creator/Creator3D direct rendering driver. + * + * Copyright (C) 2000 David S. Miller (davem@redhat.com) + */ + +#include "drmP.h" + +#include +#include +#include +#include +#include + +#include "ffb_drv.h" + +#define FFB_NAME "ffb" +#define FFB_DESC "Creator/Creator3D" +#define FFB_DATE "20000517" +#define FFB_MAJOR 0 +#define FFB_MINOR 0 +#define FFB_PATCHLEVEL 1 + +/* Forward declarations. */ +int ffb_init(void); +void ffb_cleanup(void); +static int ffb_version(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int ffb_open(struct inode *inode, struct file *filp); +static int ffb_release(struct inode *inode, struct file *filp); +static int ffb_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int ffb_lock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int ffb_unlock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int ffb_mmap(struct file *filp, struct vm_area_struct *vma); +static unsigned long ffb_get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); + +/* From ffb_context.c */ +extern int ffb_resctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_addctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_modctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_getctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_switchctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_newctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_rmctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_context_switch(drm_device_t *, int, int); + +static struct file_operations ffb_fops = { + owner: THIS_MODULE, + open: ffb_open, + flush: drm_flush, + release: ffb_release, + ioctl: ffb_ioctl, + mmap: ffb_mmap, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, + get_unmapped_area: ffb_get_unmapped_area, +}; + +/* This is just a template, we make a new copy for each FFB + * we discover at init time so that each one gets a unique + * misc device minor number. + */ +static struct miscdevice ffb_misc = { + minor: MISC_DYNAMIC_MINOR, + name: FFB_NAME, + fops: &ffb_fops, +}; + +static drm_ioctl_desc_t ffb_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { ffb_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, /* XXX */ + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + + /* The implementation is currently a nop just like on tdfx. + * Later we can do something more clever. -DaveM + */ + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { ffb_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { ffb_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { ffb_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { ffb_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { ffb_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { ffb_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { ffb_resctx, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { ffb_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { ffb_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, +}; +#define FFB_IOCTL_COUNT DRM_ARRAY_SIZE(ffb_ioctls) + +#ifdef MODULE +static char *ffb = NULL; +#endif + +MODULE_AUTHOR("David S. Miller (davem@redhat.com)"); +MODULE_DESCRIPTION("Sun Creator/Creator3D DRI"); +MODULE_LICENSE("GPL"); + +static int ffb_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } + + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: + drm_ioremapfree(map->handle, map->size); + break; + + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + + default: + break; + }; + + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +drm_device_t **ffb_dev_table; +static int ffb_dev_table_size; + +static void get_ffb_type(ffb_dev_priv_t *ffb_priv, int instance) +{ + volatile unsigned char *strap_bits; + unsigned char val; + + strap_bits = (volatile unsigned char *) + (ffb_priv->card_phys_base + 0x00200000UL); + + /* Don't ask, you have to read the value twice for whatever + * reason to get correct contents. + */ + val = upa_readb(strap_bits); + val = upa_readb(strap_bits); + switch (val & 0x78) { + case (0x0 << 5) | (0x0 << 3): + ffb_priv->ffb_type = ffb1_prototype; + printk("ffb%d: Detected FFB1 pre-FCS prototype\n", instance); + break; + case (0x0 << 5) | (0x1 << 3): + ffb_priv->ffb_type = ffb1_standard; + printk("ffb%d: Detected FFB1\n", instance); + break; + case (0x0 << 5) | (0x3 << 3): + ffb_priv->ffb_type = ffb1_speedsort; + printk("ffb%d: Detected FFB1-SpeedSort\n", instance); + break; + case (0x1 << 5) | (0x0 << 3): + ffb_priv->ffb_type = ffb2_prototype; + printk("ffb%d: Detected FFB2/vertical pre-FCS prototype\n", instance); + break; + case (0x1 << 5) | (0x1 << 3): + ffb_priv->ffb_type = ffb2_vertical; + printk("ffb%d: Detected FFB2/vertical\n", instance); + break; + case (0x1 << 5) | (0x2 << 3): + ffb_priv->ffb_type = ffb2_vertical_plus; + printk("ffb%d: Detected FFB2+/vertical\n", instance); + break; + case (0x2 << 5) | (0x0 << 3): + ffb_priv->ffb_type = ffb2_horizontal; + printk("ffb%d: Detected FFB2/horizontal\n", instance); + break; + case (0x2 << 5) | (0x2 << 3): + ffb_priv->ffb_type = ffb2_horizontal; + printk("ffb%d: Detected FFB2+/horizontal\n", instance); + break; + default: + ffb_priv->ffb_type = ffb2_vertical; + printk("ffb%d: Unknown boardID[%08x], assuming FFB2\n", instance, val); + break; + }; +} + +static void __init ffb_apply_upa_parent_ranges(int parent, struct linux_prom64_registers *regs) +{ + struct linux_prom64_ranges ranges[PROMREG_MAX]; + char name[128]; + int len, i; + + prom_getproperty(parent, "name", name, sizeof(name)); + if (strcmp(name, "upa") != 0) + return; + + len = prom_getproperty(parent, "ranges", (void *) ranges, sizeof(ranges)); + if (len <= 0) + return; + + len /= sizeof(struct linux_prom64_ranges); + for (i = 0; i < len; i++) { + struct linux_prom64_ranges *rng = &ranges[i]; + u64 phys_addr = regs->phys_addr; + + if (phys_addr >= rng->ot_child_base && + phys_addr < (rng->ot_child_base + rng->or_size)) { + regs->phys_addr -= rng->ot_child_base; + regs->phys_addr += rng->ot_parent_base; + return; + } + } + + return; +} + +static int __init ffb_init_one(int prom_node, int parent_node, int instance) +{ + struct linux_prom64_registers regs[2*PROMREG_MAX]; + drm_device_t *dev; + ffb_dev_priv_t *ffb_priv; + int ret, i; + + dev = kmalloc(sizeof(drm_device_t) + sizeof(ffb_dev_priv_t), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + memset(dev, 0, sizeof(*dev)); + spin_lock_init(&dev->count_lock); + sema_init(&dev->struct_sem, 1); + + ffb_priv = (ffb_dev_priv_t *) (dev + 1); + ffb_priv->prom_node = prom_node; + if (prom_getproperty(ffb_priv->prom_node, "reg", + (void *)regs, sizeof(regs)) <= 0) { + kfree(dev); + return -EINVAL; + } + ffb_apply_upa_parent_ranges(parent_node, ®s[0]); + ffb_priv->card_phys_base = regs[0].phys_addr; + ffb_priv->regs = (ffb_fbcPtr) + (regs[0].phys_addr + 0x00600000UL); + get_ffb_type(ffb_priv, instance); + for (i = 0; i < FFB_MAX_CTXS; i++) + ffb_priv->hw_state[i] = NULL; + + ffb_dev_table[instance] = dev; + +#ifdef MODULE + drm_parse_options(ffb); +#endif + + memcpy(&ffb_priv->miscdev, &ffb_misc, sizeof(ffb_misc)); + ret = misc_register(&ffb_priv->miscdev); + if (ret) { + ffb_dev_table[instance] = NULL; + kfree(dev); + return ret; + } + + dev->device = MKDEV(MISC_MAJOR, ffb_priv->miscdev.minor); + dev->name = FFB_NAME; + + drm_mem_init(); + drm_proc_init(dev); + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d at %016lx\n", + FFB_NAME, + FFB_MAJOR, + FFB_MINOR, + FFB_PATCHLEVEL, + FFB_DATE, + ffb_priv->miscdev.minor, + ffb_priv->card_phys_base); + + return 0; +} + +static int __init ffb_count_siblings(int root) +{ + int node, child, count = 0; + + child = prom_getchild(root); + for (node = prom_searchsiblings(child, "SUNW,ffb"); node; + node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb")) + count++; + + return count; +} + +static int __init ffb_init_dev_table(void) +{ + int root, total; + + total = ffb_count_siblings(prom_root_node); + root = prom_getchild(prom_root_node); + for (root = prom_searchsiblings(root, "upa"); root; + root = prom_searchsiblings(prom_getsibling(root), "upa")) + total += ffb_count_siblings(root); + + if (!total) + return -ENODEV; + + ffb_dev_table = kmalloc(sizeof(drm_device_t *) * total, GFP_KERNEL); + if (!ffb_dev_table) + return -ENOMEM; + + ffb_dev_table_size = total; + + return 0; +} + +static int __init ffb_scan_siblings(int root, int instance) +{ + int node, child; + + child = prom_getchild(root); + for (node = prom_searchsiblings(child, "SUNW,ffb"); node; + node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb")) { + ffb_init_one(node, root, instance); + instance++; + } + + return instance; +} + +int __init ffb_init(void) +{ + int root, instance, ret; + + ret = ffb_init_dev_table(); + if (ret) + return ret; + + instance = ffb_scan_siblings(prom_root_node, 0); + + root = prom_getchild(prom_root_node); + for (root = prom_searchsiblings(root, "upa"); root; + root = prom_searchsiblings(prom_getsibling(root), "upa")) + instance = ffb_scan_siblings(root, instance); + + return 0; +} + +void __exit ffb_cleanup(void) +{ + int instance; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + for (instance = 0; instance < ffb_dev_table_size; instance++) { + drm_device_t *dev = ffb_dev_table[instance]; + ffb_dev_priv_t *ffb_priv; + + if (!dev) + continue; + + ffb_priv = (ffb_dev_priv_t *) (dev + 1); + if (misc_deregister(&ffb_priv->miscdev)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + ffb_takedown(dev); + kfree(dev); + ffb_dev_table[instance] = NULL; + } + kfree(ffb_dev_table); + ffb_dev_table = NULL; + ffb_dev_table_size = 0; +} + +static int ffb_version(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + drm_version_t version; + int len, ret; + + ret = copy_from_user(&version, (drm_version_t *)arg, sizeof(version)); + if (ret) + return -EFAULT; + + version.version_major = FFB_MAJOR; + version.version_minor = FFB_MINOR; + version.version_patchlevel = FFB_PATCHLEVEL; + + len = strlen(FFB_NAME); + if (len > version.name_len) + len = version.name_len; + version.name_len = len; + if (len && version.name) { + ret = copy_to_user(version.name, FFB_NAME, len); + if (ret) + return -EFAULT; + } + + len = strlen(FFB_DATE); + if (len > version.date_len) + len = version.date_len; + version.date_len = len; + if (len && version.date) { + ret = copy_to_user(version.date, FFB_DATE, len); + if (ret) + return -EFAULT; + } + + len = strlen(FFB_DESC); + if (len > version.desc_len) + len = version.desc_len; + version.desc_len = len; + if (len && version.desc) { + ret = copy_to_user(version.desc, FFB_DESC, len); + if (ret) + return -EFAULT; + } + + ret = copy_to_user((drm_version_t *) arg, &version, sizeof(version)); + if (ret) + ret = -EFAULT; + + return ret; +} + +static int ffb_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); + + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + return 0; +} + +static int ffb_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev; + int minor, i; + int ret = 0; + + minor = MINOR(inode->i_rdev); + for (i = 0; i < ffb_dev_table_size; i++) { + ffb_dev_priv_t *ffb_priv; + + ffb_priv = (ffb_dev_priv_t *) (ffb_dev_table[i] + 1); + + if (ffb_priv->miscdev.minor == minor) + break; + } + + if (i >= ffb_dev_table_size) + return -EINVAL; + + dev = ffb_dev_table[i]; + if (!dev) + return -EINVAL; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + ret = drm_open_helper(inode, filp, dev); + if (!ret) { + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return ffb_setup(dev); + } + spin_unlock(&dev->count_lock); + } + + return ret; +} + +static int ffb_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + int ret = 0; + + lock_kernel(); + dev = priv->dev; + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (dev->lock.hw_lock != NULL + && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) + && dev->lock.pid == current->pid) { + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + int context = _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock); + int idx; + + /* We have to free up the rogue hw context state + * holding error or else we will leak it. + */ + idx = context - 1; + if (fpriv->hw_state[idx] != NULL) { + kfree(fpriv->hw_state[idx]); + fpriv->hw_state[idx] = NULL; + } + } + + ret = drm_release(inode, filp); + + if (!ret) { + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + unlock_kernel(); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + ret = ffb_takedown(dev); + unlock_kernel(); + return ret; + } + spin_unlock(&dev->count_lock); + } + + unlock_kernel(); + return ret; +} + +static int ffb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + int ret; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, priv->authenticated); + + if (nr >= FFB_IOCTL_COUNT) { + ret = -EINVAL; + } else { + ioctl = &ffb_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function\n"); + ret = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + ret = -EACCES; + } else { + ret = (func)(inode, filp, cmd, arg); + } + } + + atomic_dec(&dev->ioctl_count); + + return ret; +} + +static int ffb_lock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; + + ret = copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)); + if (ret) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, current->pid, dev->lock.hw_lock->lock, + lock.flags); + + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); + current->state = TASK_INTERRUPTIBLE; + current->policy |= SCHED_YIELD; + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + + if (!ret) { + sigemptyset(&dev->sigmask); + sigaddset(&dev->sigmask, SIGSTOP); + sigaddset(&dev->sigmask, SIGTSTP); + sigaddset(&dev->sigmask, SIGTTIN); + sigaddset(&dev->sigmask, SIGTTOU); + dev->sigdata.context = lock.context; + dev->sigdata.lock = dev->lock.hw_lock; + block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask); + + if (dev->last_context != lock.context) + ffb_context_switch(dev, dev->last_context, lock.context); + } + + DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); + + return ret; +} + +int ffb_unlock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + unsigned int old, new, prev, ctx; + int ret; + + ret = copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)); + if (ret) + return -EFAULT; + + if ((ctx = lock.context) == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d frees lock (%d holds)\n", + lock.context, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + + /* We no longer really hold it, but if we are the next + * agent to request it then we should just be able to + * take it immediately and not eat the ioctl. + */ + dev->lock.pid = 0; + { + __volatile__ unsigned int *plock = &dev->lock.hw_lock->lock; + + do { + old = *plock; + new = ctx; + prev = cmpxchg(plock, old, new); + } while (prev != old); + } + + wake_up_interruptible(&dev->lock.lock_queue); + + unblock_all_signals(); + return 0; +} + +extern struct vm_operations_struct drm_vm_ops; +extern struct vm_operations_struct drm_vm_shm_ops; +extern struct vm_operations_struct drm_vm_shm_lock_ops; + +static int ffb_mmap(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map = NULL; + ffb_dev_priv_t *ffb_priv; + int i, minor; + + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + + minor = MINOR(filp->f_dentry->d_inode->i_rdev); + ffb_priv = NULL; + for (i = 0; i < ffb_dev_table_size; i++) { + ffb_priv = (ffb_dev_priv_t *) (ffb_dev_table[i] + 1); + if (ffb_priv->miscdev.minor == minor) + break; + } + if (i >= ffb_dev_table_size) + return -EINVAL; + + /* We don't support/need dma mappings, so... */ + if (!VM_OFFSET(vma)) + return -EINVAL; + + for (i = 0; i < dev->map_count; i++) { + unsigned long off; + + map = dev->maplist[i]; + + /* Ok, a little hack to make 32-bit apps work. */ + off = (map->offset & 0xffffffff); + if (off == VM_OFFSET(vma)) + break; + } + + if (i >= dev->map_count) + return -EINVAL; + + if (!map || + ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) + return -EPERM; + + if (map->size != (vma->vm_end - vma->vm_start)) + return -EINVAL; + + /* Set read-only attribute before mappings are created + * so it works for fb/reg maps too. + */ + if (map->flags & _DRM_READ_ONLY) + vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect( + __pte(pgprot_val(vma->vm_page_prot))))); + + switch (map->type) { + case _DRM_FRAME_BUFFER: + /* FALLTHROUGH */ + + case _DRM_REGISTERS: + /* In order to handle 32-bit drm apps/xserver we + * play a trick. The mappings only really specify + * the 32-bit offset from the cards 64-bit base + * address, and we just add in the base here. + */ + vma->vm_flags |= VM_IO; + if (io_remap_page_range(vma->vm_start, + ffb_priv->card_phys_base + VM_OFFSET(vma), + vma->vm_end - vma->vm_start, + vma->vm_page_prot, 0)) + return -EAGAIN; + + vma->vm_ops = &drm_vm_ops; + break; + case _DRM_SHM: + if (map->flags & _DRM_CONTAINS_LOCK) + vma->vm_ops = &drm_vm_shm_lock_ops; + else { + vma->vm_ops = &drm_vm_shm_ops; + vma->vm_private_data = (void *) map; + } + + /* Don't let this area swap. Change when + * DRM_KERNEL advisory is supported. + */ + vma->vm_flags |= VM_LOCKED; + break; + default: + return -EINVAL; /* This should never happen. */ + }; + + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + + vma->vm_file = filp; /* Needed for drm_vm_open() */ + drm_vm_open(vma); + return 0; +} + +static drm_map_t *ffb_find_map(struct file *filp, unsigned long off) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + drm_map_t *map; + int i; + + if (!priv || (dev = priv->dev) == NULL) + return NULL; + + for (i = 0; i < dev->map_count; i++) { + unsigned long uoff; + + map = dev->maplist[i]; + + /* Ok, a little hack to make 32-bit apps work. */ + uoff = (map->offset & 0xffffffff); + if (uoff == off) + return map; + } + return NULL; +} + +static unsigned long ffb_get_unmapped_area(struct file *filp, unsigned long hint, unsigned long len, unsigned long pgoff, unsigned long flags) +{ + drm_map_t *map = ffb_find_map(filp, pgoff << PAGE_SHIFT); + unsigned long addr = -ENOMEM; + + if (!map) + return get_unmapped_area(NULL, hint, len, pgoff, flags); + + if (map->type == _DRM_FRAME_BUFFER || + map->type == _DRM_REGISTERS) { +#ifdef HAVE_ARCH_FB_UNMAPPED_AREA + addr = get_fb_unmapped_area(filp, hint, len, pgoff, flags); +#else + addr = get_unmapped_area(NULL, hint, len, pgoff, flags); +#endif + } else if (map->type == _DRM_SHM && SHMLBA > PAGE_SIZE) { + unsigned long slack = SHMLBA - PAGE_SIZE; + + addr = get_unmapped_area(NULL, hint, len + slack, pgoff, flags); + if (!(addr & ~PAGE_MASK)) { + unsigned long kvirt = (unsigned long) map->handle; + + if ((kvirt & (SHMLBA - 1)) != (addr & (SHMLBA - 1))) { + unsigned long koff, aoff; + + koff = kvirt & (SHMLBA - 1); + aoff = addr & (SHMLBA - 1); + if (koff < aoff) + koff += SHMLBA; + + addr += (koff - aoff); + } + } + } else { + addr = get_unmapped_area(NULL, hint, len, pgoff, flags); + } + + return addr; +} + +module_init(ffb_init); +module_exit(ffb_cleanup); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/ffb_drv.h linux.ac/drivers/char/drm-4.0/ffb_drv.h --- linux.vanilla/drivers/char/drm-4.0/ffb_drv.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/ffb_drv.h Wed Oct 10 01:47:41 2001 @@ -0,0 +1,276 @@ +/* $Id: ffb_drv.h,v 1.1 2000/06/01 04:24:39 davem Exp $ + * ffb_drv.h: Creator/Creator3D direct rendering driver. + * + * Copyright (C) 2000 David S. Miller (davem@redhat.com) + */ + +/* Auxilliary clips. */ +typedef struct { + volatile unsigned int min; + volatile unsigned int max; +} ffb_auxclip, *ffb_auxclipPtr; + +/* FFB register set. */ +typedef struct _ffb_fbc { + /* Next vertex registers, on the right we list which drawops + * use said register and the logical name the register has in + * that context. + */ /* DESCRIPTION DRAWOP(NAME) */ +/*0x00*/unsigned int pad1[3]; /* Reserved */ +/*0x0c*/volatile unsigned int alpha; /* ALPHA Transparency */ +/*0x10*/volatile unsigned int red; /* RED */ +/*0x14*/volatile unsigned int green; /* GREEN */ +/*0x18*/volatile unsigned int blue; /* BLUE */ +/*0x1c*/volatile unsigned int z; /* DEPTH */ +/*0x20*/volatile unsigned int y; /* Y triangle(DOYF) */ + /* aadot(DYF) */ + /* ddline(DYF) */ + /* aaline(DYF) */ +/*0x24*/volatile unsigned int x; /* X triangle(DOXF) */ + /* aadot(DXF) */ + /* ddline(DXF) */ + /* aaline(DXF) */ +/*0x28*/unsigned int pad2[2]; /* Reserved */ +/*0x30*/volatile unsigned int ryf; /* Y (alias to DOYF) ddline(RYF) */ + /* aaline(RYF) */ + /* triangle(RYF) */ +/*0x34*/volatile unsigned int rxf; /* X ddline(RXF) */ + /* aaline(RXF) */ + /* triangle(RXF) */ +/*0x38*/unsigned int pad3[2]; /* Reserved */ +/*0x40*/volatile unsigned int dmyf; /* Y (alias to DOYF) triangle(DMYF) */ +/*0x44*/volatile unsigned int dmxf; /* X triangle(DMXF) */ +/*0x48*/unsigned int pad4[2]; /* Reserved */ +/*0x50*/volatile unsigned int ebyi; /* Y (alias to RYI) polygon(EBYI) */ +/*0x54*/volatile unsigned int ebxi; /* X polygon(EBXI) */ +/*0x58*/unsigned int pad5[2]; /* Reserved */ +/*0x60*/volatile unsigned int by; /* Y brline(RYI) */ + /* fastfill(OP) */ + /* polygon(YI) */ + /* rectangle(YI) */ + /* bcopy(SRCY) */ + /* vscroll(SRCY) */ +/*0x64*/volatile unsigned int bx; /* X brline(RXI) */ + /* polygon(XI) */ + /* rectangle(XI) */ + /* bcopy(SRCX) */ + /* vscroll(SRCX) */ + /* fastfill(GO) */ +/*0x68*/volatile unsigned int dy; /* destination Y fastfill(DSTY) */ + /* bcopy(DSRY) */ + /* vscroll(DSRY) */ +/*0x6c*/volatile unsigned int dx; /* destination X fastfill(DSTX) */ + /* bcopy(DSTX) */ + /* vscroll(DSTX) */ +/*0x70*/volatile unsigned int bh; /* Y (alias to RYI) brline(DYI) */ + /* dot(DYI) */ + /* polygon(ETYI) */ + /* Height fastfill(H) */ + /* bcopy(H) */ + /* vscroll(H) */ + /* Y count fastfill(NY) */ +/*0x74*/volatile unsigned int bw; /* X dot(DXI) */ + /* brline(DXI) */ + /* polygon(ETXI) */ + /* fastfill(W) */ + /* bcopy(W) */ + /* vscroll(W) */ + /* fastfill(NX) */ +/*0x78*/unsigned int pad6[2]; /* Reserved */ +/*0x80*/unsigned int pad7[32]; /* Reserved */ + + /* Setup Unit's vertex state register */ +/*100*/ volatile unsigned int suvtx; +/*104*/ unsigned int pad8[63]; /* Reserved */ + + /* Frame Buffer Control Registers */ +/*200*/ volatile unsigned int ppc; /* Pixel Processor Control */ +/*204*/ volatile unsigned int wid; /* Current WID */ +/*208*/ volatile unsigned int fg; /* FG data */ +/*20c*/ volatile unsigned int bg; /* BG data */ +/*210*/ volatile unsigned int consty; /* Constant Y */ +/*214*/ volatile unsigned int constz; /* Constant Z */ +/*218*/ volatile unsigned int xclip; /* X Clip */ +/*21c*/ volatile unsigned int dcss; /* Depth Cue Scale Slope */ +/*220*/ volatile unsigned int vclipmin; /* Viewclip XY Min Bounds */ +/*224*/ volatile unsigned int vclipmax; /* Viewclip XY Max Bounds */ +/*228*/ volatile unsigned int vclipzmin; /* Viewclip Z Min Bounds */ +/*22c*/ volatile unsigned int vclipzmax; /* Viewclip Z Max Bounds */ +/*230*/ volatile unsigned int dcsf; /* Depth Cue Scale Front Bound */ +/*234*/ volatile unsigned int dcsb; /* Depth Cue Scale Back Bound */ +/*238*/ volatile unsigned int dczf; /* Depth Cue Z Front */ +/*23c*/ volatile unsigned int dczb; /* Depth Cue Z Back */ +/*240*/ unsigned int pad9; /* Reserved */ +/*244*/ volatile unsigned int blendc; /* Alpha Blend Control */ +/*248*/ volatile unsigned int blendc1; /* Alpha Blend Color 1 */ +/*24c*/ volatile unsigned int blendc2; /* Alpha Blend Color 2 */ +/*250*/ volatile unsigned int fbramitc; /* FB RAM Interleave Test Control */ +/*254*/ volatile unsigned int fbc; /* Frame Buffer Control */ +/*258*/ volatile unsigned int rop; /* Raster OPeration */ +/*25c*/ volatile unsigned int cmp; /* Frame Buffer Compare */ +/*260*/ volatile unsigned int matchab; /* Buffer AB Match Mask */ +/*264*/ volatile unsigned int matchc; /* Buffer C(YZ) Match Mask */ +/*268*/ volatile unsigned int magnab; /* Buffer AB Magnitude Mask */ +/*26c*/ volatile unsigned int magnc; /* Buffer C(YZ) Magnitude Mask */ +/*270*/ volatile unsigned int fbcfg0; /* Frame Buffer Config 0 */ +/*274*/ volatile unsigned int fbcfg1; /* Frame Buffer Config 1 */ +/*278*/ volatile unsigned int fbcfg2; /* Frame Buffer Config 2 */ +/*27c*/ volatile unsigned int fbcfg3; /* Frame Buffer Config 3 */ +/*280*/ volatile unsigned int ppcfg; /* Pixel Processor Config */ +/*284*/ volatile unsigned int pick; /* Picking Control */ +/*288*/ volatile unsigned int fillmode; /* FillMode */ +/*28c*/ volatile unsigned int fbramwac; /* FB RAM Write Address Control */ +/*290*/ volatile unsigned int pmask; /* RGB PlaneMask */ +/*294*/ volatile unsigned int xpmask; /* X PlaneMask */ +/*298*/ volatile unsigned int ypmask; /* Y PlaneMask */ +/*29c*/ volatile unsigned int zpmask; /* Z PlaneMask */ +/*2a0*/ ffb_auxclip auxclip[4]; /* Auxilliary Viewport Clip */ + + /* New 3dRAM III support regs */ +/*2c0*/ volatile unsigned int rawblend2; +/*2c4*/ volatile unsigned int rawpreblend; +/*2c8*/ volatile unsigned int rawstencil; +/*2cc*/ volatile unsigned int rawstencilctl; +/*2d0*/ volatile unsigned int threedram1; +/*2d4*/ volatile unsigned int threedram2; +/*2d8*/ volatile unsigned int passin; +/*2dc*/ volatile unsigned int rawclrdepth; +/*2e0*/ volatile unsigned int rawpmask; +/*2e4*/ volatile unsigned int rawcsrc; +/*2e8*/ volatile unsigned int rawmatch; +/*2ec*/ volatile unsigned int rawmagn; +/*2f0*/ volatile unsigned int rawropblend; +/*2f4*/ volatile unsigned int rawcmp; +/*2f8*/ volatile unsigned int rawwac; +/*2fc*/ volatile unsigned int fbramid; + +/*300*/ volatile unsigned int drawop; /* Draw OPeration */ +/*304*/ unsigned int pad10[2]; /* Reserved */ +/*30c*/ volatile unsigned int lpat; /* Line Pattern control */ +/*310*/ unsigned int pad11; /* Reserved */ +/*314*/ volatile unsigned int fontxy; /* XY Font coordinate */ +/*318*/ volatile unsigned int fontw; /* Font Width */ +/*31c*/ volatile unsigned int fontinc; /* Font Increment */ +/*320*/ volatile unsigned int font; /* Font bits */ +/*324*/ unsigned int pad12[3]; /* Reserved */ +/*330*/ volatile unsigned int blend2; +/*334*/ volatile unsigned int preblend; +/*338*/ volatile unsigned int stencil; +/*33c*/ volatile unsigned int stencilctl; + +/*340*/ unsigned int pad13[4]; /* Reserved */ +/*350*/ volatile unsigned int dcss1; /* Depth Cue Scale Slope 1 */ +/*354*/ volatile unsigned int dcss2; /* Depth Cue Scale Slope 2 */ +/*358*/ volatile unsigned int dcss3; /* Depth Cue Scale Slope 3 */ +/*35c*/ volatile unsigned int widpmask; +/*360*/ volatile unsigned int dcs2; +/*364*/ volatile unsigned int dcs3; +/*368*/ volatile unsigned int dcs4; +/*36c*/ unsigned int pad14; /* Reserved */ +/*370*/ volatile unsigned int dcd2; +/*374*/ volatile unsigned int dcd3; +/*378*/ volatile unsigned int dcd4; +/*37c*/ unsigned int pad15; /* Reserved */ +/*380*/ volatile unsigned int pattern[32]; /* area Pattern */ +/*400*/ unsigned int pad16[8]; /* Reserved */ +/*420*/ volatile unsigned int reset; /* chip RESET */ +/*424*/ unsigned int pad17[247]; /* Reserved */ +/*800*/ volatile unsigned int devid; /* Device ID */ +/*804*/ unsigned int pad18[63]; /* Reserved */ +/*900*/ volatile unsigned int ucsr; /* User Control & Status Register */ +/*904*/ unsigned int pad19[31]; /* Reserved */ +/*980*/ volatile unsigned int mer; /* Mode Enable Register */ +/*984*/ unsigned int pad20[1439]; /* Reserved */ +} ffb_fbc, *ffb_fbcPtr; + +struct ffb_hw_context { + int is_2d_only; + + unsigned int ppc; + unsigned int wid; + unsigned int fg; + unsigned int bg; + unsigned int consty; + unsigned int constz; + unsigned int xclip; + unsigned int dcss; + unsigned int vclipmin; + unsigned int vclipmax; + unsigned int vclipzmin; + unsigned int vclipzmax; + unsigned int dcsf; + unsigned int dcsb; + unsigned int dczf; + unsigned int dczb; + unsigned int blendc; + unsigned int blendc1; + unsigned int blendc2; + unsigned int fbc; + unsigned int rop; + unsigned int cmp; + unsigned int matchab; + unsigned int matchc; + unsigned int magnab; + unsigned int magnc; + unsigned int pmask; + unsigned int xpmask; + unsigned int ypmask; + unsigned int zpmask; + unsigned int auxclip0min; + unsigned int auxclip0max; + unsigned int auxclip1min; + unsigned int auxclip1max; + unsigned int auxclip2min; + unsigned int auxclip2max; + unsigned int auxclip3min; + unsigned int auxclip3max; + unsigned int drawop; + unsigned int lpat; + unsigned int fontxy; + unsigned int fontw; + unsigned int fontinc; + unsigned int area_pattern[32]; + unsigned int ucsr; + unsigned int stencil; + unsigned int stencilctl; + unsigned int dcss1; + unsigned int dcss2; + unsigned int dcss3; + unsigned int dcs2; + unsigned int dcs3; + unsigned int dcs4; + unsigned int dcd2; + unsigned int dcd3; + unsigned int dcd4; + unsigned int mer; +}; + +#define FFB_MAX_CTXS 32 + +enum ffb_chip_type { + ffb1_prototype = 0, /* Early pre-FCS FFB */ + ffb1_standard, /* First FCS FFB, 100Mhz UPA, 66MHz gclk */ + ffb1_speedsort, /* Second FCS FFB, 100Mhz UPA, 75MHz gclk */ + ffb2_prototype, /* Early pre-FCS vertical FFB2 */ + ffb2_vertical, /* First FCS FFB2/vertical, 100Mhz UPA, 100MHZ gclk, + 75(SingleBuffer)/83(DoubleBuffer) MHz fclk */ + ffb2_vertical_plus, /* Second FCS FFB2/vertical, same timings */ + ffb2_horizontal, /* First FCS FFB2/horizontal, same timings as FFB2/vert */ + ffb2_horizontal_plus, /* Second FCS FFB2/horizontal, same timings */ + afb_m3, /* FCS Elite3D, 3 float chips */ + afb_m6 /* FCS Elite3D, 6 float chips */ +}; + +typedef struct ffb_dev_priv { + /* Misc software state. */ + int prom_node; + enum ffb_chip_type ffb_type; + u64 card_phys_base; + struct miscdevice miscdev; + + /* Controller registers. */ + ffb_fbcPtr regs; + + /* Context table. */ + struct ffb_hw_context *hw_state[FFB_MAX_CTXS]; +} ffb_dev_priv_t; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/fops.c linux.ac/drivers/char/drm-4.0/fops.c --- linux.vanilla/drivers/char/drm-4.0/fops.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/fops.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,253 @@ +/* fops.c -- File operations for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * Daryll Strauss + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include + +/* drm_open is called whenever a process opens /dev/drm. */ + +int drm_open_helper(struct inode *inode, struct file *filp, drm_device_t *dev) +{ + kdev_t minor = MINOR(inode->i_rdev); + drm_file_t *priv; + + if (filp->f_flags & O_EXCL) return -EBUSY; /* No exclusive opens */ + if (!drm_cpu_valid()) return -EINVAL; + + DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor); + + priv = drm_alloc(sizeof(*priv), DRM_MEM_FILES); + if(priv == NULL) + return -ENOMEM; + memset(priv, 0, sizeof(*priv)); + + filp->private_data = priv; + priv->uid = current->euid; + priv->pid = current->pid; + priv->minor = minor; + priv->dev = dev; + priv->ioctl_count = 0; + priv->authenticated = capable(CAP_SYS_ADMIN); + + down(&dev->struct_sem); + if (!dev->file_last) { + priv->next = NULL; + priv->prev = NULL; + dev->file_first = priv; + dev->file_last = priv; + } else { + priv->next = NULL; + priv->prev = dev->file_last; + dev->file_last->next = priv; + dev->file_last = priv; + } + up(&dev->struct_sem); + + return 0; +} + +int drm_flush(struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", + current->pid, dev->device, dev->open_count); + return 0; +} + +/* drm_release is called whenever a process closes /dev/drm*. Linux calls + this only if any mappings have been closed. */ + +int drm_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", + current->pid, dev->device, dev->open_count); + + if (dev->lock.hw_lock + && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) + && dev->lock.pid == current->pid) { + DRM_ERROR("Process %d dead, freeing lock for context %d\n", + current->pid, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + drm_lock_free(dev, + &dev->lock.hw_lock->lock, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + + /* FIXME: may require heavy-handed reset of + hardware at this point, possibly + processed via a callback to the X + server. */ + } + drm_reclaim_buffers(dev, priv->pid); + + drm_fasync(-1, filp, 0); + + down(&dev->struct_sem); + if (priv->prev) priv->prev->next = priv->next; + else dev->file_first = priv->next; + if (priv->next) priv->next->prev = priv->prev; + else dev->file_last = priv->prev; + up(&dev->struct_sem); + + drm_free(priv, sizeof(*priv), DRM_MEM_FILES); + + return 0; +} + +int drm_fasync(int fd, struct file *filp, int on) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode; + + DRM_DEBUG("fd = %d, device = 0x%x\n", fd, dev->device); + retcode = fasync_helper(fd, filp, on, &dev->buf_async); + if (retcode < 0) return retcode; + return 0; +} + + +/* The drm_read and drm_write_string code (especially that which manages + the circular buffer), is based on Alessandro Rubini's LINUX DEVICE + DRIVERS (Cambridge: O'Reilly, 1998), pages 111-113. */ + +ssize_t drm_read(struct file *filp, char *buf, size_t count, loff_t *off) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int left; + int avail; + int send; + int cur; + + DRM_DEBUG("%p, %p\n", dev->buf_rp, dev->buf_wp); + + while (dev->buf_rp == dev->buf_wp) { + DRM_DEBUG(" sleeping\n"); + if (filp->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + interruptible_sleep_on(&dev->buf_readers); + if (signal_pending(current)) { + DRM_DEBUG(" interrupted\n"); + return -ERESTARTSYS; + } + DRM_DEBUG(" awake\n"); + } + + left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + avail = DRM_BSZ - left; + send = DRM_MIN(avail, count); + + while (send) { + if (dev->buf_wp > dev->buf_rp) { + cur = DRM_MIN(send, dev->buf_wp - dev->buf_rp); + } else { + cur = DRM_MIN(send, dev->buf_end - dev->buf_rp); + } + if (copy_to_user(buf, dev->buf_rp, cur)) + return -EFAULT; + dev->buf_rp += cur; + if (dev->buf_rp == dev->buf_end) dev->buf_rp = dev->buf; + send -= cur; + } + + wake_up_interruptible(&dev->buf_writers); + return DRM_MIN(avail, count);; +} + +int drm_write_string(drm_device_t *dev, const char *s) +{ + int left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + int send = strlen(s); + int count; + + DRM_DEBUG("%d left, %d to send (%p, %p)\n", + left, send, dev->buf_rp, dev->buf_wp); + + if (left == 1 || dev->buf_wp != dev->buf_rp) { + DRM_ERROR("Buffer not empty (%d left, wp = %p, rp = %p)\n", + left, + dev->buf_wp, + dev->buf_rp); + } + + while (send) { + if (dev->buf_wp >= dev->buf_rp) { + count = DRM_MIN(send, dev->buf_end - dev->buf_wp); + if (count == left) --count; /* Leave a hole */ + } else { + count = DRM_MIN(send, dev->buf_rp - dev->buf_wp - 1); + } + strncpy(dev->buf_wp, s, count); + dev->buf_wp += count; + if (dev->buf_wp == dev->buf_end) dev->buf_wp = dev->buf; + send -= count; + } + +#if LINUX_VERSION_CODE < 0x020315 && !defined(KILLFASYNCHASTHREEPARAMETERS) + /* The extra parameter to kill_fasync was added in 2.3.21, and is + _not_ present in _stock_ 2.2.14 and 2.2.15. However, some + distributions patch 2.2.x kernels to add this parameter. The + Makefile.linux attempts to detect this addition and defines + KILLFASYNCHASTHREEPARAMETERS if three parameters are found. */ + if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO); +#else + + /* Parameter added in 2.3.21. */ +#if LINUX_VERSION_CODE < 0x020400 + if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO, POLL_IN); +#else + /* Type of first parameter changed in + Linux 2.4.0-test2... */ + if (dev->buf_async) kill_fasync(&dev->buf_async, SIGIO, POLL_IN); +#endif +#endif + DRM_DEBUG("waking\n"); + wake_up_interruptible(&dev->buf_readers); + return 0; +} + +unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + poll_wait(filp, &dev->buf_readers, wait); + if (dev->buf_wp != dev->buf_rp) return POLLIN | POLLRDNORM; + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/gamma_dma.c linux.ac/drivers/char/drm-4.0/gamma_dma.c --- linux.vanilla/drivers/char/drm-4.0/gamma_dma.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/gamma_dma.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,836 @@ +/* gamma_dma.c -- DMA support for GMX 2000 -*- linux-c -*- + * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "gamma_drv.h" + +#include /* For task queue support */ + + +/* WARNING!!! MAGIC NUMBER!!! The number of regions already added to the + kernel must be specified here. Currently, the number is 2. This must + match the order the X server uses for instantiating register regions , + or must be passed in a new ioctl. */ +#define GAMMA_REG(reg) \ + (2 \ + + ((reg < 0x1000) \ + ? 0 \ + : ((reg < 0x10000) ? 1 : ((reg < 0x11000) ? 2 : 3)))) + +#define GAMMA_OFF(reg) \ + ((reg < 0x1000) \ + ? reg \ + : ((reg < 0x10000) \ + ? (reg - 0x1000) \ + : ((reg < 0x11000) \ + ? (reg - 0x10000) \ + : (reg - 0x11000)))) + +#define GAMMA_BASE(reg) ((unsigned long)dev->maplist[GAMMA_REG(reg)]->handle) +#define GAMMA_ADDR(reg) (GAMMA_BASE(reg) + GAMMA_OFF(reg)) +#define GAMMA_DEREF(reg) *(__volatile__ int *)GAMMA_ADDR(reg) +#define GAMMA_READ(reg) GAMMA_DEREF(reg) +#define GAMMA_WRITE(reg,val) do { GAMMA_DEREF(reg) = val; } while (0) + +#define GAMMA_BROADCASTMASK 0x9378 +#define GAMMA_COMMANDINTENABLE 0x0c48 +#define GAMMA_DMAADDRESS 0x0028 +#define GAMMA_DMACOUNT 0x0030 +#define GAMMA_FILTERMODE 0x8c00 +#define GAMMA_GCOMMANDINTFLAGS 0x0c50 +#define GAMMA_GCOMMANDMODE 0x0c40 +#define GAMMA_GCOMMANDSTATUS 0x0c60 +#define GAMMA_GDELAYTIMER 0x0c38 +#define GAMMA_GDMACONTROL 0x0060 +#define GAMMA_GINTENABLE 0x0808 +#define GAMMA_GINTFLAGS 0x0810 +#define GAMMA_INFIFOSPACE 0x0018 +#define GAMMA_OUTFIFOWORDS 0x0020 +#define GAMMA_OUTPUTFIFO 0x2000 +#define GAMMA_SYNC 0x8c40 +#define GAMMA_SYNC_TAG 0x0188 + +static inline void gamma_dma_dispatch(drm_device_t *dev, unsigned long address, + unsigned long length) +{ + GAMMA_WRITE(GAMMA_DMAADDRESS, virt_to_phys((void *)address)); + while (GAMMA_READ(GAMMA_GCOMMANDSTATUS) != 4) + ; + GAMMA_WRITE(GAMMA_DMACOUNT, length / 4); +} + +static inline void gamma_dma_quiescent_single(drm_device_t *dev) +{ + while (GAMMA_READ(GAMMA_DMACOUNT)) + ; + while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3) + ; + + GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10); + GAMMA_WRITE(GAMMA_SYNC, 0); + + do { + while (!GAMMA_READ(GAMMA_OUTFIFOWORDS)) + ; + } while (GAMMA_READ(GAMMA_OUTPUTFIFO) != GAMMA_SYNC_TAG); +} + +static inline void gamma_dma_quiescent_dual(drm_device_t *dev) +{ + while (GAMMA_READ(GAMMA_DMACOUNT)) + ; + while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3) + ; + + GAMMA_WRITE(GAMMA_BROADCASTMASK, 3); + + GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10); + GAMMA_WRITE(GAMMA_SYNC, 0); + + /* Read from first MX */ + do { + while (!GAMMA_READ(GAMMA_OUTFIFOWORDS)) + ; + } while (GAMMA_READ(GAMMA_OUTPUTFIFO) != GAMMA_SYNC_TAG); + + /* Read from second MX */ + do { + while (!GAMMA_READ(GAMMA_OUTFIFOWORDS + 0x10000)) + ; + } while (GAMMA_READ(GAMMA_OUTPUTFIFO + 0x10000) != GAMMA_SYNC_TAG); +} + +static inline void gamma_dma_ready(drm_device_t *dev) +{ + while (GAMMA_READ(GAMMA_DMACOUNT)) + ; +} + +static inline int gamma_dma_is_ready(drm_device_t *dev) +{ + return !GAMMA_READ(GAMMA_DMACOUNT); +} + +static void gamma_dma_service(int irq, void *device, struct pt_regs *regs) +{ + drm_device_t *dev = (drm_device_t *)device; + drm_device_dma_t *dma = dev->dma; + + atomic_inc(&dev->total_irq); + GAMMA_WRITE(GAMMA_GDELAYTIMER, 0xc350/2); /* 0x05S */ + GAMMA_WRITE(GAMMA_GCOMMANDINTFLAGS, 8); + GAMMA_WRITE(GAMMA_GINTFLAGS, 0x2001); + if (gamma_dma_is_ready(dev)) { + /* Free previous buffer */ + if (test_and_set_bit(0, &dev->dma_flag)) { + atomic_inc(&dma->total_missed_free); + return; + } + if (dma->this_buffer) { + drm_free_buffer(dev, dma->this_buffer); + dma->this_buffer = NULL; + } + clear_bit(0, &dev->dma_flag); + + /* Dispatch new buffer */ + queue_task(&dev->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } +} + +/* Only called by gamma_dma_schedule. */ +static int gamma_do_dma(drm_device_t *dev, int locked) +{ + unsigned long address; + unsigned long length; + drm_buf_t *buf; + int retcode = 0; + drm_device_dma_t *dma = dev->dma; +#if DRM_DMA_HISTOGRAM + cycles_t dma_start, dma_stop; +#endif + + if (test_and_set_bit(0, &dev->dma_flag)) { + atomic_inc(&dma->total_missed_dma); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dma_start = get_cycles(); +#endif + + if (!dma->next_buffer) { + DRM_ERROR("No next_buffer\n"); + clear_bit(0, &dev->dma_flag); + return -EINVAL; + } + + buf = dma->next_buffer; + address = (unsigned long)buf->address; + length = buf->used; + + DRM_DEBUG("context %d, buffer %d (%ld bytes)\n", + buf->context, buf->idx, length); + + if (buf->list == DRM_LIST_RECLAIM) { + drm_clear_next_buffer(dev); + drm_free_buffer(dev, buf); + clear_bit(0, &dev->dma_flag); + return -EINVAL; + } + + if (!length) { + DRM_ERROR("0 length buffer\n"); + drm_clear_next_buffer(dev); + drm_free_buffer(dev, buf); + clear_bit(0, &dev->dma_flag); + return 0; + } + + if (!gamma_dma_is_ready(dev)) { + clear_bit(0, &dev->dma_flag); + return -EBUSY; + } + + if (buf->while_locked) { + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Dispatching buffer %d from pid %d" + " \"while locked\", but no lock held\n", + buf->idx, buf->pid); + } + } else { + if (!locked && !drm_lock_take(&dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + atomic_inc(&dma->total_missed_lock); + clear_bit(0, &dev->dma_flag); + return -EBUSY; + } + } + + if (dev->last_context != buf->context + && !(dev->queuelist[buf->context]->flags + & _DRM_CONTEXT_PRESERVED)) { + /* PRE: dev->last_context != buf->context */ + if (drm_context_switch(dev, dev->last_context, buf->context)) { + drm_clear_next_buffer(dev); + drm_free_buffer(dev, buf); + } + retcode = -EBUSY; + goto cleanup; + + /* POST: we will wait for the context + switch and will dispatch on a later call + when dev->last_context == buf->context. + NOTE WE HOLD THE LOCK THROUGHOUT THIS + TIME! */ + } + + drm_clear_next_buffer(dev); + buf->pending = 1; + buf->waiting = 0; + buf->list = DRM_LIST_PEND; +#if DRM_DMA_HISTOGRAM + buf->time_dispatched = get_cycles(); +#endif + + gamma_dma_dispatch(dev, address, length); + drm_free_buffer(dev, dma->this_buffer); + dma->this_buffer = buf; + + atomic_add(length, &dma->total_bytes); + atomic_inc(&dma->total_dmas); + + if (!buf->while_locked && !dev->context_flag && !locked) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } +cleanup: + + clear_bit(0, &dev->dma_flag); + +#if DRM_DMA_HISTOGRAM + dma_stop = get_cycles(); + atomic_inc(&dev->histo.dma[drm_histogram_slot(dma_stop - dma_start)]); +#endif + + return retcode; +} + +static void gamma_dma_schedule_timer_wrapper(unsigned long dev) +{ + gamma_dma_schedule((drm_device_t *)dev, 0); +} + +static void gamma_dma_schedule_tq_wrapper(void *dev) +{ + gamma_dma_schedule(dev, 0); +} + +int gamma_dma_schedule(drm_device_t *dev, int locked) +{ + int next; + drm_queue_t *q; + drm_buf_t *buf; + int retcode = 0; + int processed = 0; + int missed; + int expire = 20; + drm_device_dma_t *dma = dev->dma; +#if DRM_DMA_HISTOGRAM + cycles_t schedule_start; +#endif + + if (test_and_set_bit(0, &dev->interrupt_flag)) { + /* Not reentrant */ + atomic_inc(&dma->total_missed_sched); + return -EBUSY; + } + missed = atomic_read(&dma->total_missed_sched); + +#if DRM_DMA_HISTOGRAM + schedule_start = get_cycles(); +#endif + +again: + if (dev->context_flag) { + clear_bit(0, &dev->interrupt_flag); + return -EBUSY; + } + if (dma->next_buffer) { + /* Unsent buffer that was previously + selected, but that couldn't be sent + because the lock could not be obtained + or the DMA engine wasn't ready. Try + again. */ + atomic_inc(&dma->total_tried); + if (!(retcode = gamma_do_dma(dev, locked))) { + atomic_inc(&dma->total_hit); + ++processed; + } + } else { + do { + next = drm_select_queue(dev, + gamma_dma_schedule_timer_wrapper); + if (next >= 0) { + q = dev->queuelist[next]; + buf = drm_waitlist_get(&q->waitlist); + dma->next_buffer = buf; + dma->next_queue = q; + if (buf && buf->list == DRM_LIST_RECLAIM) { + drm_clear_next_buffer(dev); + drm_free_buffer(dev, buf); + } + } + } while (next >= 0 && !dma->next_buffer); + if (dma->next_buffer) { + if (!(retcode = gamma_do_dma(dev, locked))) { + ++processed; + } + } + } + + if (--expire) { + if (missed != atomic_read(&dma->total_missed_sched)) { + atomic_inc(&dma->total_lost); + if (gamma_dma_is_ready(dev)) goto again; + } + if (processed && gamma_dma_is_ready(dev)) { + atomic_inc(&dma->total_lost); + processed = 0; + goto again; + } + } + + clear_bit(0, &dev->interrupt_flag); + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.schedule[drm_histogram_slot(get_cycles() + - schedule_start)]); +#endif + return retcode; +} + +static int gamma_dma_priority(drm_device_t *dev, drm_dma_t *d) +{ + unsigned long address; + unsigned long length; + int must_free = 0; + int retcode = 0; + int i; + int idx; + drm_buf_t *buf; + drm_buf_t *last_buf = NULL; + drm_device_dma_t *dma = dev->dma; + DECLARE_WAITQUEUE(entry, current); + + /* Turn off interrupt handling */ + while (test_and_set_bit(0, &dev->interrupt_flag)) { + schedule(); + if (signal_pending(current)) return -EINTR; + } + if (!(d->flags & _DRM_DMA_WHILE_LOCKED)) { + while (!drm_lock_take(&dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + schedule(); + if (signal_pending(current)) { + clear_bit(0, &dev->interrupt_flag); + return -EINTR; + } + } + ++must_free; + } + atomic_inc(&dma->total_prio); + + for (i = 0; i < d->send_count; i++) { + idx = d->send_indices[i]; + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("Index %d (of %d max)\n", + d->send_indices[i], dma->buf_count - 1); + continue; + } + buf = dma->buflist[ idx ]; + if (buf->pid != current->pid) { + DRM_ERROR("Process %d using buffer owned by %d\n", + current->pid, buf->pid); + retcode = -EINVAL; + goto cleanup; + } + if (buf->list != DRM_LIST_NONE) { + DRM_ERROR("Process %d using %d's buffer on list %d\n", + current->pid, buf->pid, buf->list); + retcode = -EINVAL; + goto cleanup; + } + /* This isn't a race condition on + buf->list, since our concern is the + buffer reclaim during the time the + process closes the /dev/drm? handle, so + it can't also be doing DMA. */ + buf->list = DRM_LIST_PRIO; + buf->used = d->send_sizes[i]; + buf->context = d->context; + buf->while_locked = d->flags & _DRM_DMA_WHILE_LOCKED; + address = (unsigned long)buf->address; + length = buf->used; + if (!length) { + DRM_ERROR("0 length buffer\n"); + } + if (buf->pending) { + DRM_ERROR("Sending pending buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + retcode = -EINVAL; + goto cleanup; + } + if (buf->waiting) { + DRM_ERROR("Sending waiting buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + retcode = -EINVAL; + goto cleanup; + } + buf->pending = 1; + + if (dev->last_context != buf->context + && !(dev->queuelist[buf->context]->flags + & _DRM_CONTEXT_PRESERVED)) { + add_wait_queue(&dev->context_wait, &entry); + current->state = TASK_INTERRUPTIBLE; + /* PRE: dev->last_context != buf->context */ + drm_context_switch(dev, dev->last_context, + buf->context); + /* POST: we will wait for the context + switch and will dispatch on a later call + when dev->last_context == buf->context. + NOTE WE HOLD THE LOCK THROUGHOUT THIS + TIME! */ + schedule(); + current->state = TASK_RUNNING; + remove_wait_queue(&dev->context_wait, &entry); + if (signal_pending(current)) { + retcode = -EINTR; + goto cleanup; + } + if (dev->last_context != buf->context) { + DRM_ERROR("Context mismatch: %d %d\n", + dev->last_context, + buf->context); + } + } + +#if DRM_DMA_HISTOGRAM + buf->time_queued = get_cycles(); + buf->time_dispatched = buf->time_queued; +#endif + gamma_dma_dispatch(dev, address, length); + atomic_add(length, &dma->total_bytes); + atomic_inc(&dma->total_dmas); + + if (last_buf) { + drm_free_buffer(dev, last_buf); + } + last_buf = buf; + } + + +cleanup: + if (last_buf) { + gamma_dma_ready(dev); + drm_free_buffer(dev, last_buf); + } + + if (must_free && !dev->context_flag) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } + clear_bit(0, &dev->interrupt_flag); + return retcode; +} + +static int gamma_dma_send_buffers(drm_device_t *dev, drm_dma_t *d) +{ + DECLARE_WAITQUEUE(entry, current); + drm_buf_t *last_buf = NULL; + int retcode = 0; + drm_device_dma_t *dma = dev->dma; + + if (d->flags & _DRM_DMA_BLOCK) { + last_buf = dma->buflist[d->send_indices[d->send_count-1]]; + add_wait_queue(&last_buf->dma_wait, &entry); + } + + if ((retcode = drm_dma_enqueue(dev, d))) { + if (d->flags & _DRM_DMA_BLOCK) + remove_wait_queue(&last_buf->dma_wait, &entry); + return retcode; + } + + gamma_dma_schedule(dev, 0); + + if (d->flags & _DRM_DMA_BLOCK) { + DRM_DEBUG("%d waiting\n", current->pid); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!last_buf->waiting && !last_buf->pending) + break; /* finished */ + schedule(); + if (signal_pending(current)) { + retcode = -EINTR; /* Can't restart */ + break; + } + } + current->state = TASK_RUNNING; + DRM_DEBUG("%d running\n", current->pid); + remove_wait_queue(&last_buf->dma_wait, &entry); + if (!retcode + || (last_buf->list==DRM_LIST_PEND && !last_buf->pending)) { + if (!waitqueue_active(&last_buf->dma_wait)) { + drm_free_buffer(dev, last_buf); + } + } + if (retcode) { + DRM_ERROR("ctx%d w%d p%d c%d i%d l%d %d/%d\n", + d->context, + last_buf->waiting, + last_buf->pending, + DRM_WAITCOUNT(dev, d->context), + last_buf->idx, + last_buf->list, + last_buf->pid, + current->pid); + } + } + return retcode; +} + +int gamma_dma(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + drm_dma_t d; + + if (copy_from_user(&d, (drm_dma_t *)arg, sizeof(d))) + return -EFAULT; + DRM_DEBUG("%d %d: %d send, %d req\n", + current->pid, d.context, d.send_count, d.request_count); + + if (d.context == DRM_KERNEL_CONTEXT || d.context >= dev->queue_slots) { + DRM_ERROR("Process %d using context %d\n", + current->pid, d.context); + return -EINVAL; + } + if (d.send_count < 0 || d.send_count > dma->buf_count) { + DRM_ERROR("Process %d trying to send %d buffers (of %d max)\n", + current->pid, d.send_count, dma->buf_count); + return -EINVAL; + } + if (d.request_count < 0 || d.request_count > dma->buf_count) { + DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n", + current->pid, d.request_count, dma->buf_count); + return -EINVAL; + } + + if (d.send_count) { + if (d.flags & _DRM_DMA_PRIORITY) + retcode = gamma_dma_priority(dev, &d); + else + retcode = gamma_dma_send_buffers(dev, &d); + } + + d.granted_count = 0; + + if (!retcode && d.request_count) { + retcode = drm_dma_get_buffers(dev, &d); + } + + DRM_DEBUG("%d returning, granted = %d\n", + current->pid, d.granted_count); + if (copy_to_user((drm_dma_t *)arg, &d, sizeof(d))) + return -EFAULT; + + return retcode; +} + +int gamma_irq_install(drm_device_t *dev, int irq) +{ + int retcode; + + if (!irq) return -EINVAL; + + down(&dev->struct_sem); + if (dev->irq) { + up(&dev->struct_sem); + return -EBUSY; + } + dev->irq = irq; + up(&dev->struct_sem); + + DRM_DEBUG("%d\n", irq); + + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + + dev->dma->next_buffer = NULL; + dev->dma->next_queue = NULL; + dev->dma->this_buffer = NULL; + + INIT_LIST_HEAD(&dev->tq.list); + dev->tq.sync = 0; + dev->tq.routine = gamma_dma_schedule_tq_wrapper; + dev->tq.data = dev; + + + /* Before installing handler */ + GAMMA_WRITE(GAMMA_GCOMMANDMODE, 0); + GAMMA_WRITE(GAMMA_GDMACONTROL, 0); + + /* Install handler */ + if ((retcode = request_irq(dev->irq, + gamma_dma_service, + 0, + dev->devname, + dev))) { + down(&dev->struct_sem); + dev->irq = 0; + up(&dev->struct_sem); + return retcode; + } + + /* After installing handler */ + GAMMA_WRITE(GAMMA_GINTENABLE, 0x2001); + GAMMA_WRITE(GAMMA_COMMANDINTENABLE, 0x0008); + GAMMA_WRITE(GAMMA_GDELAYTIMER, 0x39090); + + return 0; +} + +int gamma_irq_uninstall(drm_device_t *dev) +{ + int irq; + + down(&dev->struct_sem); + irq = dev->irq; + dev->irq = 0; + up(&dev->struct_sem); + + if (!irq) return -EINVAL; + + DRM_DEBUG("%d\n", irq); + + GAMMA_WRITE(GAMMA_GDELAYTIMER, 0); + GAMMA_WRITE(GAMMA_COMMANDINTENABLE, 0); + GAMMA_WRITE(GAMMA_GINTENABLE, 0); + free_irq(irq, dev); + + return 0; +} + + +int gamma_control(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_control_t ctl; + int retcode; + + if (copy_from_user(&ctl, (drm_control_t *)arg, sizeof(ctl))) + return -EFAULT; + + switch (ctl.func) { + case DRM_INST_HANDLER: + if ((retcode = gamma_irq_install(dev, ctl.irq))) + return retcode; + break; + case DRM_UNINST_HANDLER: + if ((retcode = gamma_irq_uninstall(dev))) + return retcode; + break; + default: + return -EINVAL; + } + return 0; +} + +int gamma_lock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; + drm_queue_t *q; +#if DRM_DMA_HISTOGRAM + cycles_t start; + + dev->lck_start = start = get_cycles(); +#endif + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, current->pid, dev->lock.hw_lock->lock, + lock.flags); + + if (lock.context < 0 || lock.context >= dev->queue_count) + return -EINVAL; + q = dev->queuelist[lock.context]; + + ret = drm_flush_block_and_flush(dev, lock.context, lock.flags); + + if (!ret) { + if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) + != lock.context) { + long j = jiffies - dev->lock.lock_time; + + if (j > 0 && j <= DRM_LOCK_SLICE) { + /* Can't take lock if we just had it and + there is contention. */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(j); + } + } + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + atomic_inc(&q->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + } + + drm_flush_unblock(dev, lock.context, lock.flags); /* cleanup phase */ + + if (!ret) { + sigemptyset(&dev->sigmask); + sigaddset(&dev->sigmask, SIGSTOP); + sigaddset(&dev->sigmask, SIGTSTP); + sigaddset(&dev->sigmask, SIGTTIN); + sigaddset(&dev->sigmask, SIGTTOU); + dev->sigdata.context = lock.context; + dev->sigdata.lock = dev->lock.hw_lock; + block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask); + + if (lock.flags & _DRM_LOCK_READY) + gamma_dma_ready(dev); + if (lock.flags & _DRM_LOCK_QUIESCENT) { + if (gamma_found() == 1) { + gamma_dma_quiescent_single(dev); + } else { + gamma_dma_quiescent_dual(dev); + } + } + } + DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]); +#endif + + return ret; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/gamma_drv.c linux.ac/drivers/char/drm-4.0/gamma_drv.c --- linux.vanilla/drivers/char/drm-4.0/gamma_drv.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/gamma_drv.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,572 @@ +/* gamma.c -- 3dlabs GMX 2000 driver -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#include +#include "drmP.h" +#include "gamma_drv.h" + +#ifndef PCI_DEVICE_ID_3DLABS_GAMMA +#define PCI_DEVICE_ID_3DLABS_GAMMA 0x0008 +#endif +#ifndef PCI_DEVICE_ID_3DLABS_MX +#define PCI_DEVICE_ID_3DLABS_MX 0x0006 +#endif + +#define GAMMA_NAME "gamma" +#define GAMMA_DESC "3dlabs GMX 2000" +#define GAMMA_DATE "20000910" +#define GAMMA_MAJOR 1 +#define GAMMA_MINOR 0 +#define GAMMA_PATCHLEVEL 0 + +static drm_device_t gamma_device; + +static struct file_operations gamma_fops = { +#if LINUX_VERSION_CODE >= 0x020400 + /* This started being used during 2.4.0-test */ + owner: THIS_MODULE, +#endif + open: gamma_open, + flush: drm_flush, + release: gamma_release, + ioctl: gamma_ioctl, + mmap: drm_mmap, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +static struct miscdevice gamma_misc = { + minor: MISC_DYNAMIC_MINOR, + name: GAMMA_NAME, + fops: &gamma_fops, +}; + +static drm_ioctl_desc_t gamma_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { gamma_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)] = { gamma_control, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { drm_addbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { drm_markbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { drm_infobufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)] = { drm_mapbufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { drm_freebufs, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { drm_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { drm_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { drm_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { drm_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { drm_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { drm_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { drm_resctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { gamma_dma, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { gamma_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { gamma_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, +}; +#define GAMMA_IOCTL_COUNT DRM_ARRAY_SIZE(gamma_ioctls) + +#ifdef MODULE +static char *gamma = NULL; +#endif +static int devices = 0; + +MODULE_AUTHOR("VA Linux Systems, Inc."); +MODULE_DESCRIPTION("3dlabs GMX 2000"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_PARM(gamma, "s"); +MODULE_PARM(devices, "i"); +MODULE_PARM_DESC(devices, + "devices=x, where x is the number of MX chips on card\n"); +#ifndef MODULE +/* gamma_options is called by the kernel to parse command-line options + * passed via the boot-loader (e.g., LILO). It calls the insmod option + * routine, drm_parse_options. + */ + + +static int __init gamma_options(char *str) +{ + drm_parse_options(str); + return 1; +} + +__setup("gamma=", gamma_options); +#endif + +static int gamma_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + drm_dma_setup(dev); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); +#if DRM_DMA_HISTO + memset(&dev->histo, 0, sizeof(dev->histo)); +#endif + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + DRM_DEBUG("\n"); + + /* The kernel's context could be created here, but is now created + in drm_dma_enqueue. This is more resource-efficient for + hardware that does not do DMA, but may mean that + drm_select_queue fails between the time the interrupt is + initialized and the time the queues are initialized. */ + + return 0; +} + + +static int gamma_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + if (dev->irq) gamma_irq_uninstall(dev); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } + + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area and mtrr information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifdef CONFIG_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + drm_ioremapfree(map->handle, map->size); + break; + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + case _DRM_AGP: + /* Do nothing here, because this is all + handled in the AGP/GART driver. */ + break; + } + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + if (dev->queuelist) { + for (i = 0; i < dev->queue_count; i++) { + drm_waitlist_destroy(&dev->queuelist[i]->waitlist); + if (dev->queuelist[i]) { + drm_free(dev->queuelist[i], + sizeof(*dev->queuelist[0]), + DRM_MEM_QUEUES); + dev->queuelist[i] = NULL; + } + } + drm_free(dev->queuelist, + dev->queue_slots * sizeof(*dev->queuelist), + DRM_MEM_QUEUES); + dev->queuelist = NULL; + } + + drm_dma_takedown(dev); + + dev->queue_count = 0; + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +int gamma_found(void) +{ + return devices; +} + +int gamma_find_devices(void) +{ + struct pci_dev *d = NULL, *one = NULL, *two = NULL; + + d = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_GAMMA,d); + if (!d) return 0; + + one = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_MX,d); + if (!one) return 0; + + /* Make sure it's on the same card, if not - no MX's found */ + if (PCI_SLOT(d->devfn) != PCI_SLOT(one->devfn)) return 0; + + two = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_MX,one); + if (!two) return 1; + + /* Make sure it's on the same card, if not - only 1 MX found */ + if (PCI_SLOT(d->devfn) != PCI_SLOT(two->devfn)) return 1; + + /* Two MX's found - we don't currently support more than 2 */ + return 2; +} + +/* gamma_init is called via init_module at module load time, or via + * linux/init/main.c (this is not currently supported). */ + +static int __init gamma_init(void) +{ + int retcode; + drm_device_t *dev = &gamma_device; + + DRM_DEBUG("\n"); + + memset((void *)dev, 0, sizeof(*dev)); + dev->count_lock = SPIN_LOCK_UNLOCKED; + sema_init(&dev->struct_sem, 1); + +#ifdef MODULE + drm_parse_options(gamma); +#endif + devices = gamma_find_devices(); + if (devices == 0) return -1; + + if ((retcode = misc_register(&gamma_misc))) { + DRM_ERROR("Cannot register \"%s\"\n", GAMMA_NAME); + return retcode; + } + dev->device = MKDEV(MISC_MAJOR, gamma_misc.minor); + dev->name = GAMMA_NAME; + + drm_mem_init(); + drm_proc_init(dev); + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d with %d MX devices\n", + GAMMA_NAME, + GAMMA_MAJOR, + GAMMA_MINOR, + GAMMA_PATCHLEVEL, + GAMMA_DATE, + gamma_misc.minor, + devices); + + return 0; +} + +/* gamma_cleanup is called via cleanup_module at module unload time. */ + +static void __exit gamma_cleanup(void) +{ + drm_device_t *dev = &gamma_device; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + if (misc_deregister(&gamma_misc)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + gamma_takedown(dev); +} + +module_init(gamma_init); +module_exit(gamma_cleanup); + + +int gamma_version(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_version_t version; + int len; + + if (copy_from_user(&version, + (drm_version_t *)arg, + sizeof(version))) + return -EFAULT; + +#define DRM_COPY(name,value) \ + len = strlen(value); \ + if (len > name##_len) len = name##_len; \ + name##_len = strlen(value); \ + if (len && name) { \ + if (copy_to_user(name, value, len)) \ + return -EFAULT; \ + } + + version.version_major = GAMMA_MAJOR; + version.version_minor = GAMMA_MINOR; + version.version_patchlevel = GAMMA_PATCHLEVEL; + + DRM_COPY(version.name, GAMMA_NAME); + DRM_COPY(version.date, GAMMA_DATE); + DRM_COPY(version.desc, GAMMA_DESC); + + if (copy_to_user((drm_version_t *)arg, + &version, + sizeof(version))) + return -EFAULT; + return 0; +} + +int gamma_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev = &gamma_device; + int retcode = 0; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_open_helper(inode, filp, dev))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return gamma_setup(dev); + } + spin_unlock(&dev->count_lock); + } + return retcode; +} + +int gamma_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + int retcode = 0; + + lock_kernel(); + dev = priv->dev; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_release(inode, filp))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + unlock_kernel(); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return gamma_takedown(dev); + } + spin_unlock(&dev->count_lock); + } + unlock_kernel(); + return retcode; +} + +/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */ + +int gamma_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, priv->authenticated); + + if (nr >= GAMMA_IOCTL_COUNT) { + retcode = -EINVAL; + } else { + ioctl = &gamma_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function\n"); + retcode = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + retcode = -EACCES; + } else { + retcode = (func)(inode, filp, cmd, arg); + } + } + + atomic_dec(&dev->ioctl_count); + return retcode; +} + + +int gamma_unlock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d frees lock (%d holds)\n", + lock.context, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); + gamma_dma_schedule(dev, 1); + if (!dev->context_flag) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.lhld[drm_histogram_slot(get_cycles() + - dev->lck_start)]); +#endif + + unblock_all_signals(); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/gamma_drv.h linux.ac/drivers/char/drm-4.0/gamma_drv.h --- linux.vanilla/drivers/char/drm-4.0/gamma_drv.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/gamma_drv.h Wed Oct 10 01:47:41 2001 @@ -0,0 +1,58 @@ +/* gamma_drv.h -- Private header for 3dlabs GMX 2000 driver -*- linux-c -*- + * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#ifndef _GAMMA_DRV_H_ +#define _GAMMA_DRV_H_ + + /* gamma_drv.c */ +extern int gamma_version(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int gamma_open(struct inode *inode, struct file *filp); +extern int gamma_release(struct inode *inode, struct file *filp); +extern int gamma_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int gamma_lock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int gamma_unlock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* gamma_dma.c */ +extern int gamma_dma_schedule(drm_device_t *dev, int locked); +extern int gamma_dma(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int gamma_irq_install(drm_device_t *dev, int irq); +extern int gamma_irq_uninstall(drm_device_t *dev); +extern int gamma_control(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int gamma_find_devices(void); +extern int gamma_found(void); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/i810_bufs.c linux.ac/drivers/char/drm-4.0/i810_bufs.c --- linux.vanilla/drivers/char/drm-4.0/i810_bufs.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/i810_bufs.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,339 @@ +/* i810_bufs.c -- IOCTLs to manage buffers -*- linux-c -*- + * Created: Thu Jan 6 01:47:26 2000 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "i810_drv.h" +#include "linux/un.h" + +int i810_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + drm_buf_entry_t *entry; + drm_buf_t *buf; + unsigned long offset; + unsigned long agp_offset; + int count; + int order; + int size; + int alignment; + int page_order; + int total; + int byte_count; + int i; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, + (drm_buf_desc_t *)arg, + sizeof(request))) + return -EFAULT; + + count = request.count; + order = drm_order(request.size); + size = 1 << order; + agp_offset = request.agp_start; + alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) :size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + byte_count = 0; + + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + if (dev->queue_count) return -EBUSY; /* Not while in use */ + spin_lock(&dev->count_lock); + if (dev->buf_use) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + atomic_inc(&dev->buf_alloc); + spin_unlock(&dev->count_lock); + + down(&dev->struct_sem); + entry = &dma->bufs[order]; + if (entry->buf_count) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; /* May only call once for each order */ + } + + if(count < 0 || count > 4096) + { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -EINVAL; + } + + entry->buflist = drm_alloc(count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + if (!entry->buflist) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->buflist, 0, count * sizeof(*entry->buflist)); + + entry->buf_size = size; + entry->page_order = page_order; + offset = 0; + + while(entry->buf_count < count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + buf->offset = offset; + buf->bus_address = dev->agp->base + agp_offset + offset; + buf->address = (void *)(agp_offset + offset + dev->agp->base); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->pid = 0; + + buf->dev_private = drm_alloc(sizeof(drm_i810_buf_priv_t), + DRM_MEM_BUFS); + buf->dev_priv_size = sizeof(drm_i810_buf_priv_t); + memset(buf->dev_private, 0, sizeof(drm_i810_buf_priv_t)); + +#if DRM_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + offset = offset + alignment; + entry->buf_count++; + byte_count += PAGE_SIZE << page_order; + + DRM_DEBUG("buffer %d @ %p\n", + entry->buf_count, buf->address); + } + + dma->buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS); + for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++) + dma->buflist[i] = &entry->buflist[i - dma->buf_count]; + + dma->buf_count += entry->buf_count; + dma->byte_count += byte_count; + drm_freelist_create(&entry->freelist, entry->buf_count); + for (i = 0; i < entry->buf_count; i++) { + drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]); + } + + up(&dev->struct_sem); + + request.count = entry->buf_count; + request.size = size; + + if (copy_to_user((drm_buf_desc_t *)arg, + &request, + sizeof(request))) + return -EFAULT; + + atomic_dec(&dev->buf_alloc); + dma->flags = _DRM_DMA_USE_AGP; + return 0; +} + +int i810_addbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_buf_desc_t request; + + if (copy_from_user(&request, + (drm_buf_desc_t *)arg, + sizeof(request))) + return -EFAULT; + + if(request.flags & _DRM_AGP_BUFFER) + return i810_addbufs_agp(inode, filp, cmd, arg); + else + return -EINVAL; +} + +int i810_infobufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_info_t request; + int i; + int count; + + if (!dma) return -EINVAL; + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + if (copy_from_user(&request, + (drm_buf_info_t *)arg, + sizeof(request))) + return -EFAULT; + + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) ++count; + } + + DRM_DEBUG("count = %d\n", count); + + if (request.count >= count) { + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) { + if (copy_to_user(&request.list[count].count, + &dma->bufs[i].buf_count, + sizeof(dma->bufs[0] + .buf_count)) || + copy_to_user(&request.list[count].size, + &dma->bufs[i].buf_size, + sizeof(dma->bufs[0].buf_size)) || + copy_to_user(&request.list[count].low_mark, + &dma->bufs[i] + .freelist.low_mark, + sizeof(dma->bufs[0] + .freelist.low_mark)) || + copy_to_user(&request.list[count] + .high_mark, + &dma->bufs[i] + .freelist.high_mark, + sizeof(dma->bufs[0] + .freelist.high_mark))) + return -EFAULT; + + DRM_DEBUG("%d %d %d %d %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].buf_size, + dma->bufs[i].freelist.low_mark, + dma->bufs[i].freelist.high_mark); + ++count; + } + } + } + request.count = count; + + if (copy_to_user((drm_buf_info_t *)arg, + &request, + sizeof(request))) + return -EFAULT; + + return 0; +} + +int i810_markbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int order; + drm_buf_entry_t *entry; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, + (drm_buf_desc_t *)arg, + sizeof(request))) + return -EFAULT; + + DRM_DEBUG("%d, %d, %d\n", + request.size, request.low_mark, request.high_mark); + order = drm_order(request.size); + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + entry = &dma->bufs[order]; + + if (request.low_mark < 0 || request.low_mark > entry->buf_count) + return -EINVAL; + if (request.high_mark < 0 || request.high_mark > entry->buf_count) + return -EINVAL; + + entry->freelist.low_mark = request.low_mark; + entry->freelist.high_mark = request.high_mark; + + return 0; +} + +int i810_freebufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_free_t request; + int i; + int idx; + drm_buf_t *buf; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, + (drm_buf_free_t *)arg, + sizeof(request))) + return -EFAULT; + + DRM_DEBUG("%d\n", request.count); + for (i = 0; i < request.count; i++) { + if (copy_from_user(&idx, + &request.list[i], + sizeof(idx))) + return -EFAULT; + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("Index %d (of %d max)\n", + idx, dma->buf_count - 1); + return -EINVAL; + } + buf = dma->buflist[idx]; + if (buf->pid != current->pid) { + DRM_ERROR("Process %d freeing buffer owned by %d\n", + current->pid, buf->pid); + return -EINVAL; + } + drm_free_buffer(dev, buf); + } + + return 0; +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/i810_context.c linux.ac/drivers/char/drm-4.0/i810_context.c --- linux.vanilla/drivers/char/drm-4.0/i810_context.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/i810_context.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,212 @@ +/* i810_context.c -- IOCTLs for i810 contexts -*- linux-c -*- + * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "i810_drv.h" + +static int i810_alloc_queue(drm_device_t *dev) +{ + int temp = drm_ctxbitmap_next(dev); + DRM_DEBUG("i810_alloc_queue: %d\n", temp); + return temp; +} + +int i810_context_switch(drm_device_t *dev, int old, int new) +{ + char buf[64]; + + atomic_inc(&dev->total_ctx); + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + if (drm_flags & DRM_FLAG_NOCTX) { + i810_context_switch_complete(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + drm_write_string(dev, buf); + } + + return 0; +} + +int i810_context_switch_complete(drm_device_t *dev, int new) +{ + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + /* If a context switch is ever initiated + when the kernel holds the lock, release + that lock here. */ +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + wake_up(&dev->context_wait); + + return 0; +} + +int i810_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res))) + return -EFAULT; + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + if (copy_to_user(&res.contexts[i], + &i, + sizeof(i))) + return -EFAULT; + } + } + res.count = DRM_RESERVED_CONTEXTS; + if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res))) + return -EFAULT; + return 0; +} + +int i810_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + if ((ctx.handle = i810_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) { + /* Skip kernel's context and get a new one. */ + ctx.handle = i810_alloc_queue(dev); + } + if (ctx.handle == -1) { + DRM_DEBUG("Not enough free contexts.\n"); + /* Should this return -EBUSY instead? */ + return -ENOMEM; + } + DRM_DEBUG("%d\n", ctx.handle); + if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx))) + return -EFAULT; + return 0; +} + +int i810_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + /* This does nothing for the i810 */ + return 0; +} + +int i810_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx))) + return -EFAULT; + /* This is 0, because we don't hanlde any context flags */ + ctx.flags = 0; + if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx))) + return -EFAULT; + return 0; +} + +int i810_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + return i810_context_switch(dev, dev->last_context, ctx.handle); +} + +int i810_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + i810_context_switch_complete(dev, ctx.handle); + + return 0; +} + +int i810_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + if(ctx.handle != DRM_KERNEL_CONTEXT) { + drm_ctxbitmap_free(dev, ctx.handle); + } + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/i810_dma.c linux.ac/drivers/char/drm-4.0/i810_dma.c --- linux.vanilla/drivers/char/drm-4.0/i810_dma.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/i810_dma.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,1437 @@ +/* i810_dma.c -- DMA support for the i810 -*- linux-c -*- + * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * Keith Whitwell + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "i810_drv.h" +#include /* For task queue support */ + +/* in case we don't have a 2.3.99-pre6 kernel or later: */ +#ifndef VM_DONTCOPY +#define VM_DONTCOPY 0 +#endif + +#define I810_BUF_FREE 2 +#define I810_BUF_CLIENT 1 +#define I810_BUF_HARDWARE 0 + +#define I810_BUF_UNMAPPED 0 +#define I810_BUF_MAPPED 1 + +#define I810_REG(reg) 2 +#define I810_BASE(reg) ((unsigned long) \ + dev->maplist[I810_REG(reg)]->handle) +#define I810_ADDR(reg) (I810_BASE(reg) + reg) +#define I810_DEREF(reg) *(__volatile__ int *)I810_ADDR(reg) +#define I810_READ(reg) I810_DEREF(reg) +#define I810_WRITE(reg,val) do { I810_DEREF(reg) = val; } while (0) +#define I810_DEREF16(reg) *(__volatile__ u16 *)I810_ADDR(reg) +#define I810_READ16(reg) I810_DEREF16(reg) +#define I810_WRITE16(reg,val) do { I810_DEREF16(reg) = val; } while (0) + +#define RING_LOCALS unsigned int outring, ringmask; volatile char *virt; + +#define BEGIN_LP_RING(n) do { \ + if (I810_VERBOSE) \ + DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n", \ + n, __FUNCTION__); \ + if (dev_priv->ring.space < n*4) \ + i810_wait_ring(dev, n*4); \ + dev_priv->ring.space -= n*4; \ + outring = dev_priv->ring.tail; \ + ringmask = dev_priv->ring.tail_mask; \ + virt = dev_priv->ring.virtual_start; \ +} while (0) + +#define ADVANCE_LP_RING() do { \ + if (I810_VERBOSE) DRM_DEBUG("ADVANCE_LP_RING\n"); \ + dev_priv->ring.tail = outring; \ + I810_WRITE(LP_RING + RING_TAIL, outring); \ +} while(0) + +#define OUT_RING(n) do { \ + if (I810_VERBOSE) DRM_DEBUG(" OUT_RING %x\n", (int)(n)); \ + *(volatile unsigned int *)(virt + outring) = n; \ + outring += 4; \ + outring &= ringmask; \ +} while (0); + +static inline void i810_print_status_page(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + drm_i810_private_t *dev_priv = dev->dev_private; + u32 *temp = (u32 *)dev_priv->hw_status_page; + int i; + + DRM_DEBUG( "hw_status: Interrupt Status : %x\n", temp[0]); + DRM_DEBUG( "hw_status: LpRing Head ptr : %x\n", temp[1]); + DRM_DEBUG( "hw_status: IRing Head ptr : %x\n", temp[2]); + DRM_DEBUG( "hw_status: Reserved : %x\n", temp[3]); + DRM_DEBUG( "hw_status: Driver Counter : %d\n", temp[5]); + for(i = 6; i < dma->buf_count + 6; i++) { + DRM_DEBUG( "buffer status idx : %d used: %d\n", i - 6, temp[i]); + } +} + +static drm_buf_t *i810_freelist_get(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + int i; + int used; + + /* Linear search might not be the best solution */ + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + /* In use is already a pointer */ + used = cmpxchg(buf_priv->in_use, I810_BUF_FREE, + I810_BUF_CLIENT); + if(used == I810_BUF_FREE) { + return buf; + } + } + return NULL; +} + +/* This should only be called if the buffer is not sent to the hardware + * yet, the hardware updates in use for us once its on the ring buffer. + */ + +static int i810_freelist_put(drm_device_t *dev, drm_buf_t *buf) +{ + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + int used; + + /* In use is already a pointer */ + used = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, I810_BUF_FREE); + if(used != I810_BUF_CLIENT) { + DRM_ERROR("Freeing buffer thats not in use : %d\n", buf->idx); + return -EINVAL; + } + + return 0; +} + +static struct file_operations i810_buffer_fops = { + open: i810_open, + flush: drm_flush, + release: i810_release, + ioctl: i810_ioctl, + mmap: i810_mmap_buffers, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + drm_i810_private_t *dev_priv; + drm_buf_t *buf; + drm_i810_buf_priv_t *buf_priv; + + lock_kernel(); + dev = priv->dev; + dev_priv = dev->dev_private; + buf = dev_priv->mmap_buffer; + buf_priv = buf->dev_private; + + vma->vm_flags |= (VM_IO | VM_DONTCOPY); + vma->vm_file = filp; + + buf_priv->currently_mapped = I810_BUF_MAPPED; + unlock_kernel(); + + if (remap_page_range(vma->vm_start, + VM_OFFSET(vma), + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) return -EAGAIN; + return 0; +} + +static int i810_map_buffer(drm_buf_t *buf, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + drm_i810_private_t *dev_priv = dev->dev_private; + struct file_operations *old_fops; + int retcode = 0; + + if(buf_priv->currently_mapped == I810_BUF_MAPPED) return -EINVAL; + + if(VM_DONTCOPY != 0) { + down_write(¤t->mm->mmap_sem); + old_fops = filp->f_op; + filp->f_op = &i810_buffer_fops; + dev_priv->mmap_buffer = buf; + buf_priv->virtual = (void *)do_mmap(filp, 0, buf->total, + PROT_READ|PROT_WRITE, + MAP_SHARED, + buf->bus_address); + dev_priv->mmap_buffer = NULL; + filp->f_op = old_fops; + if ((unsigned long)buf_priv->virtual > -1024UL) { + /* Real error */ + DRM_DEBUG("mmap error\n"); + retcode = (signed int)buf_priv->virtual; + buf_priv->virtual = 0; + } + up_write(¤t->mm->mmap_sem); + } else { + buf_priv->virtual = buf_priv->kernel_virtual; + buf_priv->currently_mapped = I810_BUF_MAPPED; + } + return retcode; +} + +static int i810_unmap_buffer(drm_buf_t *buf) +{ + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + int retcode = 0; + + if(VM_DONTCOPY != 0) { + if(buf_priv->currently_mapped != I810_BUF_MAPPED) + return -EINVAL; + down_write(¤t->mm->mmap_sem); +#if LINUX_VERSION_CODE < 0x020399 + retcode = do_munmap((unsigned long)buf_priv->virtual, + (size_t) buf->total); +#else + retcode = do_munmap(current->mm, + (unsigned long)buf_priv->virtual, + (size_t) buf->total); +#endif + up_write(¤t->mm->mmap_sem); + } + buf_priv->currently_mapped = I810_BUF_UNMAPPED; + buf_priv->virtual = 0; + + return retcode; +} + +static int i810_dma_get_buffer(drm_device_t *dev, drm_i810_dma_t *d, + struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_buf_t *buf; + drm_i810_buf_priv_t *buf_priv; + int retcode = 0; + + buf = i810_freelist_get(dev); + if (!buf) { + retcode = -ENOMEM; + DRM_DEBUG("retcode=%d\n", retcode); + return retcode; + } + + retcode = i810_map_buffer(buf, filp); + if(retcode) { + i810_freelist_put(dev, buf); + DRM_DEBUG("mapbuf failed, retcode %d\n", retcode); + return retcode; + } + buf->pid = priv->pid; + buf_priv = buf->dev_private; + d->granted = 1; + d->request_idx = buf->idx; + d->request_size = buf->total; + d->virtual = buf_priv->virtual; + + return retcode; +} + +static unsigned long i810_alloc_page(drm_device_t *dev) +{ + unsigned long address; + + address = __get_free_page(GFP_KERNEL); + if(address == 0UL) + return 0; + + atomic_inc(&virt_to_page(address)->count); + set_bit(PG_locked, &virt_to_page(address)->flags); + + return address; +} + +static void i810_free_page(drm_device_t *dev, unsigned long page) +{ + if(page == 0UL) + return; + + atomic_dec(&virt_to_page(page)->count); + clear_bit(PG_locked, &virt_to_page(page)->flags); + wake_up(&virt_to_page(page)->wait); + free_page(page); + return; +} + +static int i810_dma_cleanup(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + + if(dev->dev_private) { + int i; + drm_i810_private_t *dev_priv = + (drm_i810_private_t *) dev->dev_private; + + if(dev_priv->ring.virtual_start) { + drm_ioremapfree((void *) dev_priv->ring.virtual_start, + dev_priv->ring.Size); + } + if(dev_priv->hw_status_page != 0UL) { + i810_free_page(dev, dev_priv->hw_status_page); + /* Need to rewrite hardware status page */ + I810_WRITE(0x02080, 0x1ffff000); + } + drm_free(dev->dev_private, sizeof(drm_i810_private_t), + DRM_MEM_DRIVER); + dev->dev_private = NULL; + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + drm_ioremapfree(buf_priv->kernel_virtual, buf->total); + } + } + return 0; +} + +static int i810_wait_ring(drm_device_t *dev, int n) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + drm_i810_ring_buffer_t *ring = &(dev_priv->ring); + int iters = 0; + unsigned long end; + unsigned int last_head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR; + + end = jiffies + (HZ*3); + while (ring->space < n) { + int i; + + ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR; + ring->space = ring->head - (ring->tail+8); + if (ring->space < 0) ring->space += ring->Size; + + if (ring->head != last_head) + end = jiffies + (HZ*3); + + iters++; + if((signed)(end - jiffies) <= 0) { + DRM_ERROR("space: %d wanted %d\n", ring->space, n); + DRM_ERROR("lockup\n"); + goto out_wait_ring; + } + + for (i = 0 ; i < 2000 ; i++) ; + } + +out_wait_ring: + return iters; +} + +static void i810_kernel_lost_context(drm_device_t *dev) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + drm_i810_ring_buffer_t *ring = &(dev_priv->ring); + + ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR; + ring->tail = I810_READ(LP_RING + RING_TAIL); + ring->space = ring->head - (ring->tail+8); + if (ring->space < 0) ring->space += ring->Size; +} + +static int i810_freelist_init(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + int my_idx = 24; + u32 *hw_status = (u32 *)(dev_priv->hw_status_page + my_idx); + int i; + + if(dma->buf_count > 1019) { + /* Not enough space in the status page for the freelist */ + return -EINVAL; + } + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + + buf_priv->in_use = hw_status++; + buf_priv->my_use_idx = my_idx; + my_idx += 4; + + *buf_priv->in_use = I810_BUF_FREE; + + buf_priv->kernel_virtual = drm_ioremap(buf->bus_address, + buf->total); + } + return 0; +} + +static int i810_dma_initialize(drm_device_t *dev, + drm_i810_private_t *dev_priv, + drm_i810_init_t *init) +{ + drm_map_t *sarea_map; + + dev->dev_private = (void *) dev_priv; + memset(dev_priv, 0, sizeof(drm_i810_private_t)); + + if (init->ring_map_idx >= dev->map_count || + init->buffer_map_idx >= dev->map_count) { + i810_dma_cleanup(dev); + DRM_ERROR("ring_map or buffer_map are invalid\n"); + return -EINVAL; + } + + dev_priv->ring_map_idx = init->ring_map_idx; + dev_priv->buffer_map_idx = init->buffer_map_idx; + sarea_map = dev->maplist[0]; + dev_priv->sarea_priv = (drm_i810_sarea_t *) + ((u8 *)sarea_map->handle + + init->sarea_priv_offset); + + atomic_set(&dev_priv->flush_done, 0); + init_waitqueue_head(&dev_priv->flush_queue); + + dev_priv->ring.Start = init->ring_start; + dev_priv->ring.End = init->ring_end; + dev_priv->ring.Size = init->ring_size; + + dev_priv->ring.virtual_start = drm_ioremap(dev->agp->base + + init->ring_start, + init->ring_size); + + dev_priv->ring.tail_mask = dev_priv->ring.Size - 1; + + if (dev_priv->ring.virtual_start == NULL) { + i810_dma_cleanup(dev); + DRM_ERROR("can not ioremap virtual address for" + " ring buffer\n"); + return -ENOMEM; + } + + dev_priv->w = init->w; + dev_priv->h = init->h; + dev_priv->pitch = init->pitch; + dev_priv->back_offset = init->back_offset; + dev_priv->depth_offset = init->depth_offset; + + dev_priv->front_di1 = init->front_offset | init->pitch_bits; + dev_priv->back_di1 = init->back_offset | init->pitch_bits; + dev_priv->zi1 = init->depth_offset | init->pitch_bits; + + + /* Program Hardware Status Page */ + dev_priv->hw_status_page = i810_alloc_page(dev); + memset((void *) dev_priv->hw_status_page, 0, PAGE_SIZE); + if(dev_priv->hw_status_page == 0UL) { + i810_dma_cleanup(dev); + DRM_ERROR("Can not allocate hardware status page\n"); + return -ENOMEM; + } + DRM_DEBUG("hw status page @ %lx\n", dev_priv->hw_status_page); + + I810_WRITE(0x02080, virt_to_bus((void *)dev_priv->hw_status_page)); + DRM_DEBUG("Enabled hardware status page\n"); + + /* Now we need to init our freelist */ + if(i810_freelist_init(dev) != 0) { + i810_dma_cleanup(dev); + DRM_ERROR("Not enough space in the status page for" + " the freelist\n"); + return -ENOMEM; + } + return 0; +} + +int i810_dma_init(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_private_t *dev_priv; + drm_i810_init_t init; + int retcode = 0; + + if (copy_from_user(&init, (drm_i810_init_t *)arg, sizeof(init))) + return -EFAULT; + + switch(init.func) { + case I810_INIT_DMA: + dev_priv = drm_alloc(sizeof(drm_i810_private_t), + DRM_MEM_DRIVER); + if(dev_priv == NULL) return -ENOMEM; + retcode = i810_dma_initialize(dev, dev_priv, &init); + break; + case I810_CLEANUP_DMA: + retcode = i810_dma_cleanup(dev); + break; + default: + retcode = -EINVAL; + break; + } + + return retcode; +} + + + +/* Most efficient way to verify state for the i810 is as it is + * emitted. Non-conformant state is silently dropped. + * + * Use 'volatile' & local var tmp to force the emitted values to be + * identical to the verified ones. + */ +static void i810EmitContextVerified( drm_device_t *dev, + volatile unsigned int *code ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + int i, j = 0; + unsigned int tmp; + RING_LOCALS; + + BEGIN_LP_RING( I810_CTX_SETUP_SIZE ); + + OUT_RING( GFX_OP_COLOR_FACTOR ); + OUT_RING( code[I810_CTXREG_CF1] ); + + OUT_RING( GFX_OP_STIPPLE ); + OUT_RING( code[I810_CTXREG_ST1] ); + + for ( i = 4 ; i < I810_CTX_SETUP_SIZE ; i++ ) { + tmp = code[i]; + + if ((tmp & (7<<29)) == (3<<29) && + (tmp & (0x1f<<24)) < (0x1d<<24)) + { + OUT_RING( tmp ); + j++; + } + } + + if (j & 1) + OUT_RING( 0 ); + + ADVANCE_LP_RING(); +} + +static void i810EmitTexVerified( drm_device_t *dev, + volatile unsigned int *code ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + int i, j = 0; + unsigned int tmp; + RING_LOCALS; + + BEGIN_LP_RING( I810_TEX_SETUP_SIZE ); + + OUT_RING( GFX_OP_MAP_INFO ); + OUT_RING( code[I810_TEXREG_MI1] ); + OUT_RING( code[I810_TEXREG_MI2] ); + OUT_RING( code[I810_TEXREG_MI3] ); + + for ( i = 4 ; i < I810_TEX_SETUP_SIZE ; i++ ) { + tmp = code[i]; + + if ((tmp & (7<<29)) == (3<<29) && + (tmp & (0x1f<<24)) < (0x1d<<24)) + { + OUT_RING( tmp ); + j++; + } + } + + if (j & 1) + OUT_RING( 0 ); + + ADVANCE_LP_RING(); +} + + +/* Need to do some additional checking when setting the dest buffer. + */ +static void i810EmitDestVerified( drm_device_t *dev, + volatile unsigned int *code ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + unsigned int tmp; + RING_LOCALS; + + BEGIN_LP_RING( I810_DEST_SETUP_SIZE + 2 ); + + tmp = code[I810_DESTREG_DI1]; + if (tmp == dev_priv->front_di1 || tmp == dev_priv->back_di1) { + OUT_RING( CMD_OP_DESTBUFFER_INFO ); + OUT_RING( tmp ); + } else + DRM_DEBUG("bad di1 %x (allow %x or %x)\n", + tmp, dev_priv->front_di1, dev_priv->back_di1); + + /* invarient: + */ + OUT_RING( CMD_OP_Z_BUFFER_INFO ); + OUT_RING( dev_priv->zi1 ); + + OUT_RING( GFX_OP_DESTBUFFER_VARS ); + OUT_RING( code[I810_DESTREG_DV1] ); + + OUT_RING( GFX_OP_DRAWRECT_INFO ); + OUT_RING( code[I810_DESTREG_DR1] ); + OUT_RING( code[I810_DESTREG_DR2] ); + OUT_RING( code[I810_DESTREG_DR3] ); + OUT_RING( code[I810_DESTREG_DR4] ); + OUT_RING( 0 ); + + ADVANCE_LP_RING(); +} + + + +static void i810EmitState( drm_device_t *dev ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int dirty = sarea_priv->dirty; + + if (dirty & I810_UPLOAD_BUFFERS) { + i810EmitDestVerified( dev, sarea_priv->BufferState ); + sarea_priv->dirty &= ~I810_UPLOAD_BUFFERS; + } + + if (dirty & I810_UPLOAD_CTX) { + i810EmitContextVerified( dev, sarea_priv->ContextState ); + sarea_priv->dirty &= ~I810_UPLOAD_CTX; + } + + if (dirty & I810_UPLOAD_TEX0) { + i810EmitTexVerified( dev, sarea_priv->TexState[0] ); + sarea_priv->dirty &= ~I810_UPLOAD_TEX0; + } + + if (dirty & I810_UPLOAD_TEX1) { + i810EmitTexVerified( dev, sarea_priv->TexState[1] ); + sarea_priv->dirty &= ~I810_UPLOAD_TEX1; + } +} + + + +/* need to verify + */ +static void i810_dma_dispatch_clear( drm_device_t *dev, int flags, + unsigned int clear_color, + unsigned int clear_zval ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; + int nbox = sarea_priv->nbox; + drm_clip_rect_t *pbox = sarea_priv->boxes; + int pitch = dev_priv->pitch; + int cpp = 2; + int i; + RING_LOCALS; + + i810_kernel_lost_context(dev); + + if (nbox > I810_NR_SAREA_CLIPRECTS) + nbox = I810_NR_SAREA_CLIPRECTS; + + for (i = 0 ; i < nbox ; i++, pbox++) { + unsigned int x = pbox->x1; + unsigned int y = pbox->y1; + unsigned int width = (pbox->x2 - x) * cpp; + unsigned int height = pbox->y2 - y; + unsigned int start = y * pitch + x * cpp; + + if (pbox->x1 > pbox->x2 || + pbox->y1 > pbox->y2 || + pbox->x2 > dev_priv->w || + pbox->y2 > dev_priv->h) + continue; + + if ( flags & I810_FRONT ) { + DRM_DEBUG("clear front\n"); + BEGIN_LP_RING( 6 ); + OUT_RING( BR00_BITBLT_CLIENT | + BR00_OP_COLOR_BLT | 0x3 ); + OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch ); + OUT_RING( (height << 16) | width ); + OUT_RING( start ); + OUT_RING( clear_color ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + } + + if ( flags & I810_BACK ) { + DRM_DEBUG("clear back\n"); + BEGIN_LP_RING( 6 ); + OUT_RING( BR00_BITBLT_CLIENT | + BR00_OP_COLOR_BLT | 0x3 ); + OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch ); + OUT_RING( (height << 16) | width ); + OUT_RING( dev_priv->back_offset + start ); + OUT_RING( clear_color ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + } + + if ( flags & I810_DEPTH ) { + DRM_DEBUG("clear depth\n"); + BEGIN_LP_RING( 6 ); + OUT_RING( BR00_BITBLT_CLIENT | + BR00_OP_COLOR_BLT | 0x3 ); + OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch ); + OUT_RING( (height << 16) | width ); + OUT_RING( dev_priv->depth_offset + start ); + OUT_RING( clear_zval ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + } + } +} + +static void i810_dma_dispatch_swap( drm_device_t *dev ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; + int nbox = sarea_priv->nbox; + drm_clip_rect_t *pbox = sarea_priv->boxes; + int pitch = dev_priv->pitch; + int cpp = 2; + int ofs = dev_priv->back_offset; + int i; + RING_LOCALS; + + DRM_DEBUG("swapbuffers\n"); + + i810_kernel_lost_context(dev); + + if (nbox > I810_NR_SAREA_CLIPRECTS) + nbox = I810_NR_SAREA_CLIPRECTS; + + for (i = 0 ; i < nbox; i++, pbox++) + { + unsigned int w = pbox->x2 - pbox->x1; + unsigned int h = pbox->y2 - pbox->y1; + unsigned int dst = pbox->x1*cpp + pbox->y1*pitch; + unsigned int start = ofs + dst; + + if (pbox->x1 > pbox->x2 || + pbox->y1 > pbox->y2 || + pbox->x2 > dev_priv->w || + pbox->y2 > dev_priv->h) + continue; + + DRM_DEBUG("dispatch swap %d,%d-%d,%d!\n", + pbox[i].x1, pbox[i].y1, + pbox[i].x2, pbox[i].y2); + + BEGIN_LP_RING( 6 ); + OUT_RING( BR00_BITBLT_CLIENT | BR00_OP_SRC_COPY_BLT | 0x4 ); + OUT_RING( pitch | (0xCC << 16)); + OUT_RING( (h << 16) | (w * cpp)); + OUT_RING( dst ); + OUT_RING( pitch ); + OUT_RING( start ); + ADVANCE_LP_RING(); + } +} + + +static void i810_dma_dispatch_vertex(drm_device_t *dev, + drm_buf_t *buf, + int discard, + int used) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_clip_rect_t *box = sarea_priv->boxes; + int nbox = sarea_priv->nbox; + unsigned long address = (unsigned long)buf->bus_address; + unsigned long start = address - dev->agp->base; + int i = 0, u; + RING_LOCALS; + + i810_kernel_lost_context(dev); + + if (nbox > I810_NR_SAREA_CLIPRECTS) + nbox = I810_NR_SAREA_CLIPRECTS; + + if (discard) { + u = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, + I810_BUF_HARDWARE); + if(u != I810_BUF_CLIENT) { + DRM_DEBUG("xxxx 2\n"); + } + } + + if (used > 4*1024) + used = 0; + + if (sarea_priv->dirty) + i810EmitState( dev ); + + DRM_DEBUG("dispatch vertex addr 0x%lx, used 0x%x nbox %d\n", + address, used, nbox); + + dev_priv->counter++; + DRM_DEBUG( "dispatch counter : %ld\n", dev_priv->counter); + DRM_DEBUG( "i810_dma_dispatch\n"); + DRM_DEBUG( "start : %lx\n", start); + DRM_DEBUG( "used : %d\n", used); + DRM_DEBUG( "start + used - 4 : %ld\n", start + used - 4); + + if (buf_priv->currently_mapped == I810_BUF_MAPPED) { + *(u32 *)buf_priv->virtual = (GFX_OP_PRIMITIVE | + sarea_priv->vertex_prim | + ((used/4)-2)); + + if (used & 4) { + *(u32 *)((u32)buf_priv->virtual + used) = 0; + used += 4; + } + + i810_unmap_buffer(buf); + } + + if (used) { + do { + if (i < nbox) { + BEGIN_LP_RING(4); + OUT_RING( GFX_OP_SCISSOR | SC_UPDATE_SCISSOR | + SC_ENABLE ); + OUT_RING( GFX_OP_SCISSOR_INFO ); + OUT_RING( box[i].x1 | (box[i].y1<<16) ); + OUT_RING( (box[i].x2-1) | ((box[i].y2-1)<<16) ); + ADVANCE_LP_RING(); + } + + BEGIN_LP_RING(4); + OUT_RING( CMD_OP_BATCH_BUFFER ); + OUT_RING( start | BB1_PROTECTED ); + OUT_RING( start + used - 4 ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + + } while (++i < nbox); + } + + BEGIN_LP_RING(10); + OUT_RING( CMD_STORE_DWORD_IDX ); + OUT_RING( 20 ); + OUT_RING( dev_priv->counter ); + OUT_RING( 0 ); + + if (discard) { + OUT_RING( CMD_STORE_DWORD_IDX ); + OUT_RING( buf_priv->my_use_idx ); + OUT_RING( I810_BUF_FREE ); + OUT_RING( 0 ); + } + + OUT_RING( CMD_REPORT_HEAD ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); +} + + +/* Interrupts are only for flushing */ +static void i810_dma_service(int irq, void *device, struct pt_regs *regs) +{ + drm_device_t *dev = (drm_device_t *)device; + u16 temp; + + atomic_inc(&dev->total_irq); + temp = I810_READ16(I810REG_INT_IDENTITY_R); + temp = temp & ~(0x6000); + if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R, + temp); /* Clear all interrupts */ + else + return; + + queue_task(&dev->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void i810_dma_task_queue(void *device) +{ + drm_device_t *dev = (drm_device_t *) device; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + + atomic_set(&dev_priv->flush_done, 1); + wake_up_interruptible(&dev_priv->flush_queue); +} + +int i810_irq_install(drm_device_t *dev, int irq) +{ + int retcode; + u16 temp; + + if (!irq) return -EINVAL; + + down(&dev->struct_sem); + if (dev->irq) { + up(&dev->struct_sem); + return -EBUSY; + } + dev->irq = irq; + up(&dev->struct_sem); + + DRM_DEBUG( "Interrupt Install : %d\n", irq); + DRM_DEBUG("%d\n", irq); + + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + + dev->dma->next_buffer = NULL; + dev->dma->next_queue = NULL; + dev->dma->this_buffer = NULL; + + INIT_LIST_HEAD(&dev->tq.list); + dev->tq.sync = 0; + dev->tq.routine = i810_dma_task_queue; + dev->tq.data = dev; + + /* Before installing handler */ + temp = I810_READ16(I810REG_HWSTAM); + temp = temp & 0x6000; + I810_WRITE16(I810REG_HWSTAM, temp); + + temp = I810_READ16(I810REG_INT_MASK_R); + temp = temp & 0x6000; + I810_WRITE16(I810REG_INT_MASK_R, temp); /* Unmask interrupts */ + temp = I810_READ16(I810REG_INT_ENABLE_R); + temp = temp & 0x6000; + I810_WRITE16(I810REG_INT_ENABLE_R, temp); /* Disable all interrupts */ + + /* Install handler */ + if ((retcode = request_irq(dev->irq, + i810_dma_service, + SA_SHIRQ, + dev->devname, + dev))) { + down(&dev->struct_sem); + dev->irq = 0; + up(&dev->struct_sem); + return retcode; + } + temp = I810_READ16(I810REG_INT_ENABLE_R); + temp = temp & 0x6000; + temp = temp | 0x0003; + I810_WRITE16(I810REG_INT_ENABLE_R, + temp); /* Enable bp & user interrupts */ + return 0; +} + +int i810_irq_uninstall(drm_device_t *dev) +{ + int irq; + u16 temp; + + +/* return 0; */ + + down(&dev->struct_sem); + irq = dev->irq; + dev->irq = 0; + up(&dev->struct_sem); + + if (!irq) return -EINVAL; + + DRM_DEBUG( "Interrupt UnInstall: %d\n", irq); + DRM_DEBUG("%d\n", irq); + + temp = I810_READ16(I810REG_INT_IDENTITY_R); + temp = temp & ~(0x6000); + if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R, + temp); /* Clear all interrupts */ + + temp = I810_READ16(I810REG_INT_ENABLE_R); + temp = temp & 0x6000; + I810_WRITE16(I810REG_INT_ENABLE_R, + temp); /* Disable all interrupts */ + + free_irq(irq, dev); + + return 0; +} + +int i810_control(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_control_t ctl; + int retcode; + + DRM_DEBUG( "i810_control\n"); + + if (copy_from_user(&ctl, (drm_control_t *)arg, sizeof(ctl))) + return -EFAULT; + + switch (ctl.func) { + case DRM_INST_HANDLER: + if ((retcode = i810_irq_install(dev, ctl.irq))) + return retcode; + break; + case DRM_UNINST_HANDLER: + if ((retcode = i810_irq_uninstall(dev))) + return retcode; + break; + default: + return -EINVAL; + } + return 0; +} + +static inline void i810_dma_emit_flush(drm_device_t *dev) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + i810_kernel_lost_context(dev); + + BEGIN_LP_RING(2); + OUT_RING( CMD_REPORT_HEAD ); + OUT_RING( GFX_OP_USER_INTERRUPT ); + ADVANCE_LP_RING(); + +/* i810_wait_ring( dev, dev_priv->ring.Size - 8 ); */ +/* atomic_set(&dev_priv->flush_done, 1); */ +/* wake_up_interruptible(&dev_priv->flush_queue); */ +} + +static inline void i810_dma_quiescent_emit(drm_device_t *dev) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + i810_kernel_lost_context(dev); + + BEGIN_LP_RING(4); + OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE ); + OUT_RING( CMD_REPORT_HEAD ); + OUT_RING( 0 ); + OUT_RING( GFX_OP_USER_INTERRUPT ); + ADVANCE_LP_RING(); + +/* i810_wait_ring( dev, dev_priv->ring.Size - 8 ); */ +/* atomic_set(&dev_priv->flush_done, 1); */ +/* wake_up_interruptible(&dev_priv->flush_queue); */ +} + +static void i810_dma_quiescent(drm_device_t *dev) +{ + DECLARE_WAITQUEUE(entry, current); + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + unsigned long end; + + if(dev_priv == NULL) { + return; + } + atomic_set(&dev_priv->flush_done, 0); + add_wait_queue(&dev_priv->flush_queue, &entry); + end = jiffies + (HZ*3); + + for (;;) { + current->state = TASK_INTERRUPTIBLE; + i810_dma_quiescent_emit(dev); + if (atomic_read(&dev_priv->flush_done) == 1) break; + if((signed)(end - jiffies) <= 0) { + DRM_ERROR("lockup\n"); + break; + } + schedule_timeout(HZ*3); + if (signal_pending(current)) { + break; + } + } + + current->state = TASK_RUNNING; + remove_wait_queue(&dev_priv->flush_queue, &entry); + + return; +} + +static int i810_flush_queue(drm_device_t *dev) +{ + DECLARE_WAITQUEUE(entry, current); + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + drm_device_dma_t *dma = dev->dma; + unsigned long end; + int i, ret = 0; + + if(dev_priv == NULL) { + return 0; + } + atomic_set(&dev_priv->flush_done, 0); + add_wait_queue(&dev_priv->flush_queue, &entry); + end = jiffies + (HZ*3); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + i810_dma_emit_flush(dev); + if (atomic_read(&dev_priv->flush_done) == 1) break; + if((signed)(end - jiffies) <= 0) { + DRM_ERROR("lockup\n"); + break; + } + schedule_timeout(HZ*3); + if (signal_pending(current)) { + ret = -EINTR; /* Can't restart */ + break; + } + } + + current->state = TASK_RUNNING; + remove_wait_queue(&dev_priv->flush_queue, &entry); + + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + + int used = cmpxchg(buf_priv->in_use, I810_BUF_HARDWARE, + I810_BUF_FREE); + + if (used == I810_BUF_HARDWARE) + DRM_DEBUG("reclaimed from HARDWARE\n"); + if (used == I810_BUF_CLIENT) + DRM_DEBUG("still on client HARDWARE\n"); + } + + return ret; +} + +/* Must be called with the lock held */ +void i810_reclaim_buffers(drm_device_t *dev, pid_t pid) +{ + drm_device_dma_t *dma = dev->dma; + int i; + + if (!dma) return; + if (!dev->dev_private) return; + if (!dma->buflist) return; + + i810_flush_queue(dev); + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + + if (buf->pid == pid && buf_priv) { + int used = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, + I810_BUF_FREE); + + if (used == I810_BUF_CLIENT) + DRM_DEBUG("reclaimed from client\n"); + if(buf_priv->currently_mapped == I810_BUF_MAPPED) + buf_priv->currently_mapped = I810_BUF_UNMAPPED; + } + } +} + +int i810_lock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, current->pid, dev->lock.hw_lock->lock, + lock.flags); + + if (lock.context < 0) { + return -EINVAL; + } + /* Only one queue: + */ + + if (!ret) { + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); + DRM_DEBUG("Calling lock schedule\n"); + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + } + + if (!ret) { + sigemptyset(&dev->sigmask); + sigaddset(&dev->sigmask, SIGSTOP); + sigaddset(&dev->sigmask, SIGTSTP); + sigaddset(&dev->sigmask, SIGTTIN); + sigaddset(&dev->sigmask, SIGTTOU); + dev->sigdata.context = lock.context; + dev->sigdata.lock = dev->lock.hw_lock; + block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask); + + if (lock.flags & _DRM_LOCK_QUIESCENT) { + DRM_DEBUG("_DRM_LOCK_QUIESCENT\n"); + DRM_DEBUG("fred\n"); + i810_dma_quiescent(dev); + } + } + DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); + return ret; +} + +int i810_flush_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("i810_flush_ioctl\n"); + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_flush_ioctl called without lock held\n"); + return -EINVAL; + } + + i810_flush_queue(dev); + return 0; +} + + +int i810_dma_vertex(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + u32 *hw_status = (u32 *)dev_priv->hw_status_page; + drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) + dev_priv->sarea_priv; + drm_i810_vertex_t vertex; + + if (copy_from_user(&vertex, (drm_i810_vertex_t *)arg, sizeof(vertex))) + return -EFAULT; + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_dma_vertex called without lock held\n"); + return -EINVAL; + } + + DRM_DEBUG("i810 dma vertex, idx %d used %d discard %d\n", + vertex.idx, vertex.used, vertex.discard); + + if(vertex.idx < 0 || vertex.idx > dma->buf_count) return -EINVAL; + + i810_dma_dispatch_vertex( dev, + dma->buflist[ vertex.idx ], + vertex.discard, vertex.used ); + + atomic_add(vertex.used, &dma->total_bytes); + atomic_inc(&dma->total_dmas); + sarea_priv->last_enqueue = dev_priv->counter-1; + sarea_priv->last_dispatch = (int) hw_status[5]; + + return 0; +} + + + +int i810_clear_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_clear_t clear; + + if (copy_from_user(&clear, (drm_i810_clear_t *)arg, sizeof(clear))) + return -EFAULT; + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_clear_bufs called without lock held\n"); + return -EINVAL; + } + + i810_dma_dispatch_clear( dev, clear.flags, + clear.clear_color, + clear.clear_depth ); + return 0; +} + +int i810_swap_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("i810_swap_bufs\n"); + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_swap_buf called without lock held\n"); + return -EINVAL; + } + + i810_dma_dispatch_swap( dev ); + return 0; +} + +int i810_getage(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + u32 *hw_status = (u32 *)dev_priv->hw_status_page; + drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) + dev_priv->sarea_priv; + + sarea_priv->last_dispatch = (int) hw_status[5]; + return 0; +} + +int i810_getbuf(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_i810_dma_t d; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + u32 *hw_status = (u32 *)dev_priv->hw_status_page; + drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) + dev_priv->sarea_priv; + + DRM_DEBUG("getbuf\n"); + if (copy_from_user(&d, (drm_i810_dma_t *)arg, sizeof(d))) + return -EFAULT; + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_dma called without lock held\n"); + return -EINVAL; + } + + d.granted = 0; + + retcode = i810_dma_get_buffer(dev, &d, filp); + + DRM_DEBUG("i810_dma: %d returning %d, granted = %d\n", + current->pid, retcode, d.granted); + + if (copy_to_user((drm_dma_t *)arg, &d, sizeof(d))) + return -EFAULT; + sarea_priv->last_dispatch = (int) hw_status[5]; + + return retcode; +} + +int i810_copybuf(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_copy_t d; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + u32 *hw_status = (u32 *)dev_priv->hw_status_page; + drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) + dev_priv->sarea_priv; + drm_buf_t *buf; + drm_i810_buf_priv_t *buf_priv; + drm_device_dma_t *dma = dev->dma; + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_dma called without lock held\n"); + return -EINVAL; + } + + if (copy_from_user(&d, (drm_i810_copy_t *)arg, sizeof(d))) + return -EFAULT; + + if(d.idx < 0 || d.idx > dma->buf_count) return -EINVAL; + buf = dma->buflist[ d.idx ]; + buf_priv = buf->dev_private; + if (buf_priv->currently_mapped != I810_BUF_MAPPED) return -EPERM; + + /* Stopping end users copying their data to the entire kernel + is good.. */ + if (d.used < 0 || d.used > buf->total) + return -EINVAL; + + if (copy_from_user(buf_priv->virtual, d.address, d.used)) + return -EFAULT; + + sarea_priv->last_dispatch = (int) hw_status[5]; + + return 0; +} + +int i810_docopy(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + if(VM_DONTCOPY == 0) return 1; + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/i810_drm.h linux.ac/drivers/char/drm-4.0/i810_drm.h --- linux.vanilla/drivers/char/drm-4.0/i810_drm.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/i810_drm.h Wed Oct 10 01:47:41 2001 @@ -0,0 +1,194 @@ +#ifndef _I810_DRM_H_ +#define _I810_DRM_H_ + +/* WARNING: These defines must be the same as what the Xserver uses. + * if you change them, you must change the defines in the Xserver. + */ + +#ifndef _I810_DEFINES_ +#define _I810_DEFINES_ + +#define I810_DMA_BUF_ORDER 12 +#define I810_DMA_BUF_SZ (1< + * Jeff Hartmann + * + */ + +#include +#include "drmP.h" +#include "i810_drv.h" + +#define I810_NAME "i810" +#define I810_DESC "Intel I810" +#define I810_DATE "20000928" +#define I810_MAJOR 1 +#define I810_MINOR 1 +#define I810_PATCHLEVEL 0 + +static drm_device_t i810_device; +drm_ctx_t i810_res_ctx; + +static struct file_operations i810_fops = { +#if LINUX_VERSION_CODE >= 0x020400 + /* This started being used during 2.4.0-test */ + owner: THIS_MODULE, +#endif + open: i810_open, + flush: drm_flush, + release: i810_release, + ioctl: i810_ioctl, + mmap: drm_mmap, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +static struct miscdevice i810_misc = { + minor: MISC_DYNAMIC_MINOR, + name: I810_NAME, + fops: &i810_fops, +}; + +static drm_ioctl_desc_t i810_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { i810_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)] = { i810_control, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { i810_addbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { i810_markbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { i810_infobufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { i810_freebufs, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { i810_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { i810_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { i810_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { i810_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { i810_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { i810_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { i810_resctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { i810_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { i810_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = { drm_agp_enable, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = { drm_agp_info, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = { drm_agp_alloc, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = { drm_agp_free, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { drm_agp_bind, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { drm_agp_unbind, 1, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_I810_INIT)] = { i810_dma_init, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_VERTEX)] = { i810_dma_vertex, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_CLEAR)] = { i810_clear_bufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_FLUSH)] = { i810_flush_ioctl,1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_GETAGE)] = { i810_getage, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_GETBUF)] = { i810_getbuf, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_SWAP)] = { i810_swap_bufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_COPY)] = { i810_copybuf, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_DOCOPY)] = { i810_docopy, 1, 0 }, +}; + +#define I810_IOCTL_COUNT DRM_ARRAY_SIZE(i810_ioctls) + +#ifdef MODULE +static char *i810 = NULL; +#endif + +MODULE_AUTHOR("VA Linux Systems, Inc."); +MODULE_DESCRIPTION("Intel I810"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_PARM(i810, "s"); + +#ifndef MODULE +/* i810_options is called by the kernel to parse command-line options + * passed via the boot-loader (e.g., LILO). It calls the insmod option + * routine, drm_parse_drm. + */ + +static int __init i810_options(char *str) +{ + drm_parse_options(str); + return 1; +} + +__setup("i810=", i810_options); +#endif + +static int i810_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + drm_dma_setup(dev); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); +#if DRM_DMA_HISTO + memset(&dev->histo, 0, sizeof(dev->histo)); +#endif + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + DRM_DEBUG("\n"); + + /* The kernel's context could be created here, but is now created + in drm_dma_enqueue. This is more resource-efficient for + hardware that does not do DMA, but may mean that + drm_select_queue fails between the time the interrupt is + initialized and the time the queues are initialized. */ + + return 0; +} + + +static int i810_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + if (dev->irq) i810_irq_uninstall(dev); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } + /* Clear AGP information */ + if (dev->agp) { + drm_agp_mem_t *entry; + drm_agp_mem_t *nexte; + + /* Remove AGP resources, but leave dev->agp + intact until r128_cleanup is called. */ + for (entry = dev->agp->memory; entry; entry = nexte) { + nexte = entry->next; + if (entry->bound) drm_unbind_agp(entry->memory); + drm_free_agp(entry->memory, entry->pages); + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + } + dev->agp->memory = NULL; + + if (dev->agp->acquired) _drm_agp_release(); + + dev->agp->acquired = 0; + dev->agp->enabled = 0; + } + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area and mtrr information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifdef CONFIG_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + drm_ioremapfree(map->handle, map->size); + break; + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + case _DRM_AGP: + break; + } + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + if (dev->queuelist) { + for (i = 0; i < dev->queue_count; i++) { + drm_waitlist_destroy(&dev->queuelist[i]->waitlist); + if (dev->queuelist[i]) { + drm_free(dev->queuelist[i], + sizeof(*dev->queuelist[0]), + DRM_MEM_QUEUES); + dev->queuelist[i] = NULL; + } + } + drm_free(dev->queuelist, + dev->queue_slots * sizeof(*dev->queuelist), + DRM_MEM_QUEUES); + dev->queuelist = NULL; + } + + drm_dma_takedown(dev); + + dev->queue_count = 0; + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +/* i810_init is called via init_module at module load time, or via + * linux/init/main.c (this is not currently supported). */ + +static int __init i810_init(void) +{ + int retcode; + drm_device_t *dev = &i810_device; + + DRM_DEBUG("\n"); + + memset((void *)dev, 0, sizeof(*dev)); + dev->count_lock = SPIN_LOCK_UNLOCKED; + sema_init(&dev->struct_sem, 1); + +#ifdef MODULE + drm_parse_options(i810); +#endif + DRM_DEBUG("doing misc_register\n"); + if ((retcode = misc_register(&i810_misc))) { + DRM_ERROR("Cannot register \"%s\"\n", I810_NAME); + return retcode; + } + dev->device = MKDEV(MISC_MAJOR, i810_misc.minor); + dev->name = I810_NAME; + + DRM_DEBUG("doing mem init\n"); + drm_mem_init(); + DRM_DEBUG("doing proc init\n"); + drm_proc_init(dev); + DRM_DEBUG("doing agp init\n"); + dev->agp = drm_agp_init(); + if(dev->agp == NULL) { + DRM_INFO("The i810 drm module requires the agpgart module" + " to function correctly\nPlease load the agpgart" + " module before you load the i810 module\n"); + drm_proc_cleanup(); + misc_deregister(&i810_misc); + i810_takedown(dev); + return -ENOMEM; + } + DRM_DEBUG("doing ctxbitmap init\n"); + if((retcode = drm_ctxbitmap_init(dev))) { + DRM_ERROR("Cannot allocate memory for context bitmap.\n"); + drm_proc_cleanup(); + misc_deregister(&i810_misc); + i810_takedown(dev); + return retcode; + } + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", + I810_NAME, + I810_MAJOR, + I810_MINOR, + I810_PATCHLEVEL, + I810_DATE, + i810_misc.minor); + + return 0; +} + +/* i810_cleanup is called via cleanup_module at module unload time. */ + +static void __exit i810_cleanup(void) +{ + drm_device_t *dev = &i810_device; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + if (misc_deregister(&i810_misc)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + drm_ctxbitmap_cleanup(dev); + i810_takedown(dev); + if (dev->agp) { + drm_agp_uninit(); + drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS); + dev->agp = NULL; + } +} + +module_init(i810_init); +module_exit(i810_cleanup); + + +int i810_version(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_version_t version; + int len; + + if (copy_from_user(&version, + (drm_version_t *)arg, + sizeof(version))) + return -EFAULT; + +#define DRM_COPY(name,value) \ + len = strlen(value); \ + if (len > name##_len) len = name##_len; \ + name##_len = strlen(value); \ + if (len && name) { \ + if (copy_to_user(name, value, len)) \ + return -EFAULT; \ + } + + version.version_major = I810_MAJOR; + version.version_minor = I810_MINOR; + version.version_patchlevel = I810_PATCHLEVEL; + + DRM_COPY(version.name, I810_NAME); + DRM_COPY(version.date, I810_DATE); + DRM_COPY(version.desc, I810_DESC); + + if (copy_to_user((drm_version_t *)arg, + &version, + sizeof(version))) + return -EFAULT; + return 0; +} + +int i810_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev = &i810_device; + int retcode = 0; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_open_helper(inode, filp, dev))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return i810_setup(dev); + } + spin_unlock(&dev->count_lock); + } + return retcode; +} + +int i810_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + int retcode = 0; + + lock_kernel(); + dev = priv->dev; + DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", + current->pid, dev->device, dev->open_count); + + if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) + && dev->lock.pid == current->pid) { + i810_reclaim_buffers(dev, priv->pid); + DRM_ERROR("Process %d dead, freeing lock for context %d\n", + current->pid, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + drm_lock_free(dev, + &dev->lock.hw_lock->lock, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + + /* FIXME: may require heavy-handed reset of + hardware at this point, possibly + processed via a callback to the X + server. */ + } else if (dev->lock.hw_lock) { + /* The lock is required to reclaim buffers */ + DECLARE_WAITQUEUE(entry, current); + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + retcode = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + dev->lock.pid = priv->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + /* Contention */ + atomic_inc(&dev->total_sleeps); + schedule(); + if (signal_pending(current)) { + retcode = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + if(!retcode) { + i810_reclaim_buffers(dev, priv->pid); + drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT); + } + } + drm_fasync(-1, filp, 0); + + down(&dev->struct_sem); + if (priv->prev) priv->prev->next = priv->next; + else dev->file_first = priv->next; + if (priv->next) priv->next->prev = priv->prev; + else dev->file_last = priv->prev; + up(&dev->struct_sem); + + drm_free(priv, sizeof(*priv), DRM_MEM_FILES); +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + unlock_kernel(); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return i810_takedown(dev); + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return retcode; +} + +/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */ + +int i810_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, priv->authenticated); + + if (nr >= I810_IOCTL_COUNT) { + retcode = -EINVAL; + } else { + ioctl = &i810_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function\n"); + retcode = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + retcode = -EACCES; + } else { + retcode = (func)(inode, filp, cmd, arg); + } + } + + atomic_dec(&dev->ioctl_count); + return retcode; +} + +int i810_unlock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d frees lock (%d holds)\n", + lock.context, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); + if (!dev->context_flag) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.lhld[drm_histogram_slot(get_cycles() + - dev->lck_start)]); +#endif + + unblock_all_signals(); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/i810_drv.h linux.ac/drivers/char/drm-4.0/i810_drv.h --- linux.vanilla/drivers/char/drm-4.0/i810_drv.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/i810_drv.h Wed Oct 10 01:47:41 2001 @@ -0,0 +1,225 @@ +/* i810_drv.h -- Private header for the Matrox g200/g400 driver -*- linux-c -*- + * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#ifndef _I810_DRV_H_ +#define _I810_DRV_H_ + +typedef struct drm_i810_buf_priv { + u32 *in_use; + int my_use_idx; + int currently_mapped; + void *virtual; + void *kernel_virtual; + int map_count; + struct vm_area_struct *vma; +} drm_i810_buf_priv_t; + +typedef struct _drm_i810_ring_buffer{ + int tail_mask; + unsigned long Start; + unsigned long End; + unsigned long Size; + u8 *virtual_start; + int head; + int tail; + int space; +} drm_i810_ring_buffer_t; + +typedef struct drm_i810_private { + int ring_map_idx; + int buffer_map_idx; + + drm_i810_ring_buffer_t ring; + drm_i810_sarea_t *sarea_priv; + + unsigned long hw_status_page; + unsigned long counter; + + atomic_t flush_done; + wait_queue_head_t flush_queue; /* Processes waiting until flush */ + drm_buf_t *mmap_buffer; + + + u32 front_di1, back_di1, zi1; + + int back_offset; + int depth_offset; + int w, h; + int pitch; +} drm_i810_private_t; + + /* i810_drv.c */ +extern int i810_version(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_open(struct inode *inode, struct file *filp); +extern int i810_release(struct inode *inode, struct file *filp); +extern int i810_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_unlock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* i810_dma.c */ +extern int i810_dma_schedule(drm_device_t *dev, int locked); +extern int i810_getbuf(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_irq_install(drm_device_t *dev, int irq); +extern int i810_irq_uninstall(drm_device_t *dev); +extern int i810_control(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_lock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_dma_init(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_flush_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern void i810_reclaim_buffers(drm_device_t *dev, pid_t pid); +extern int i810_getage(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg); +extern int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma); +extern int i810_copybuf(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_docopy(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* i810_bufs.c */ +extern int i810_addbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_infobufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_markbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_freebufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_addmap(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* i810_context.c */ +extern int i810_resctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_addctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_modctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_getctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_switchctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_newctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_rmctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int i810_context_switch(drm_device_t *dev, int old, int new); +extern int i810_context_switch_complete(drm_device_t *dev, int new); + +#define I810_VERBOSE 0 + + +int i810_dma_vertex(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +int i810_swap_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +int i810_clear_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +#define GFX_OP_USER_INTERRUPT ((0<<29)|(2<<23)) +#define GFX_OP_BREAKPOINT_INTERRUPT ((0<<29)|(1<<23)) +#define CMD_REPORT_HEAD (7<<23) +#define CMD_STORE_DWORD_IDX ((0x21<<23) | 0x1) +#define CMD_OP_BATCH_BUFFER ((0x0<<29)|(0x30<<23)|0x1) + +#define INST_PARSER_CLIENT 0x00000000 +#define INST_OP_FLUSH 0x02000000 +#define INST_FLUSH_MAP_CACHE 0x00000001 + + +#define BB1_START_ADDR_MASK (~0x7) +#define BB1_PROTECTED (1<<0) +#define BB1_UNPROTECTED (0<<0) +#define BB2_END_ADDR_MASK (~0x7) + +#define I810REG_HWSTAM 0x02098 +#define I810REG_INT_IDENTITY_R 0x020a4 +#define I810REG_INT_MASK_R 0x020a8 +#define I810REG_INT_ENABLE_R 0x020a0 + +#define LP_RING 0x2030 +#define HP_RING 0x2040 +#define RING_TAIL 0x00 +#define TAIL_ADDR 0x000FFFF8 +#define RING_HEAD 0x04 +#define HEAD_WRAP_COUNT 0xFFE00000 +#define HEAD_WRAP_ONE 0x00200000 +#define HEAD_ADDR 0x001FFFFC +#define RING_START 0x08 +#define START_ADDR 0x00FFFFF8 +#define RING_LEN 0x0C +#define RING_NR_PAGES 0x000FF000 +#define RING_REPORT_MASK 0x00000006 +#define RING_REPORT_64K 0x00000002 +#define RING_REPORT_128K 0x00000004 +#define RING_NO_REPORT 0x00000000 +#define RING_VALID_MASK 0x00000001 +#define RING_VALID 0x00000001 +#define RING_INVALID 0x00000000 + +#define GFX_OP_SCISSOR ((0x3<<29)|(0x1c<<24)|(0x10<<19)) +#define SC_UPDATE_SCISSOR (0x1<<1) +#define SC_ENABLE_MASK (0x1<<0) +#define SC_ENABLE (0x1<<0) + +#define GFX_OP_SCISSOR_INFO ((0x3<<29)|(0x1d<<24)|(0x81<<16)|(0x1)) +#define SCI_YMIN_MASK (0xffff<<16) +#define SCI_XMIN_MASK (0xffff<<0) +#define SCI_YMAX_MASK (0xffff<<16) +#define SCI_XMAX_MASK (0xffff<<0) + +#define GFX_OP_COLOR_FACTOR ((0x3<<29)|(0x1d<<24)|(0x1<<16)|0x0) +#define GFX_OP_STIPPLE ((0x3<<29)|(0x1d<<24)|(0x83<<16)) +#define GFX_OP_MAP_INFO ((0x3<<29)|(0x1d<<24)|0x2) +#define GFX_OP_DESTBUFFER_VARS ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0) +#define GFX_OP_DRAWRECT_INFO ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3)) +#define GFX_OP_PRIMITIVE ((0x3<<29)|(0x1f<<24)) + +#define CMD_OP_Z_BUFFER_INFO ((0x0<<29)|(0x16<<23)) +#define CMD_OP_DESTBUFFER_INFO ((0x0<<29)|(0x15<<23)) + +#define BR00_BITBLT_CLIENT 0x40000000 +#define BR00_OP_COLOR_BLT 0x10000000 +#define BR00_OP_SRC_COPY_BLT 0x10C00000 +#define BR13_SOLID_PATTERN 0x80000000 + + + +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/init.c linux.ac/drivers/char/drm-4.0/init.c --- linux.vanilla/drivers/char/drm-4.0/init.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/init.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,113 @@ +/* init.c -- Setup/Cleanup for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_flags = 0; + +/* drm_parse_option parses a single option. See description for + drm_parse_options for details. */ + +static void drm_parse_option(char *s) +{ + char *c, *r; + + DRM_DEBUG("\"%s\"\n", s); + if (!s || !*s) return; + for (c = s; *c && *c != ':'; c++); /* find : or \0 */ + if (*c) r = c + 1; else r = NULL; /* remember remainder */ + *c = '\0'; /* terminate */ + if (!strcmp(s, "noctx")) { + drm_flags |= DRM_FLAG_NOCTX; + DRM_INFO("Server-mediated context switching OFF\n"); + return; + } + if (!strcmp(s, "debug")) { + drm_flags |= DRM_FLAG_DEBUG; + DRM_INFO("Debug messages ON\n"); + return; + } + DRM_ERROR("\"%s\" is not a valid option\n", s); + return; +} + +/* drm_parse_options parse the insmod "drm=" options, or the command-line + * options passed to the kernel via LILO. The grammar of the format is as + * follows: + * + * drm ::= 'drm=' option_list + * option_list ::= option [ ';' option_list ] + * option ::= 'device:' major + * | 'debug' + * | 'noctx' + * major ::= INTEGER + * + * Note that 's' contains option_list without the 'drm=' part. + * + * device=major,minor specifies the device number used for /dev/drm + * if major == 0 then the misc device is used + * if major == 0 and minor == 0 then dynamic misc allocation is used + * debug=on specifies that debugging messages will be printk'd + * debug=trace specifies that each function call will be logged via printk + * debug=off turns off all debugging options + * + */ + +void drm_parse_options(char *s) +{ + char *h, *t, *n; + + DRM_DEBUG("\"%s\"\n", s ?: ""); + if (!s || !*s) return; + + for (h = t = n = s; h && *h; h = n) { + for (; *t && *t != ';'; t++); /* find ; or \0 */ + if (*t) n = t + 1; else n = NULL; /* remember next */ + *t = '\0'; /* terminate */ + drm_parse_option(h); /* parse */ + } +} + +/* drm_cpu_valid returns non-zero if the DRI will run on this CPU, and 0 + * otherwise. */ + +int drm_cpu_valid(void) +{ +#if defined(__i386__) + if (boot_cpu_data.x86 == 3) return 0; /* No cmpxchg on a 386 */ +#endif +#if defined(__sparc__) && !defined(__sparc_v9__) + if (1) + return 0; /* No cmpxchg before v9 sparc. */ +#endif + return 1; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/ioctl.c linux.ac/drivers/char/drm-4.0/ioctl.c --- linux.vanilla/drivers/char/drm-4.0/ioctl.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/ioctl.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,99 @@ +/* ioctl.c -- IOCTL processing for DRM -*- linux-c -*- + * Created: Fri Jan 8 09:01:26 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_irq_busid(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_irq_busid_t p; + struct pci_dev *dev; + + if (copy_from_user(&p, (drm_irq_busid_t *)arg, sizeof(p))) + return -EFAULT; + dev = pci_find_slot(p.busnum, PCI_DEVFN(p.devnum, p.funcnum)); + if (dev) p.irq = dev->irq; + else p.irq = 0; + DRM_DEBUG("%d:%d:%d => IRQ %d\n", + p.busnum, p.devnum, p.funcnum, p.irq); + if (copy_to_user((drm_irq_busid_t *)arg, &p, sizeof(p))) + return -EFAULT; + return 0; +} + +int drm_getunique(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_unique_t u; + + if (copy_from_user(&u, (drm_unique_t *)arg, sizeof(u))) + return -EFAULT; + if (u.unique_len >= dev->unique_len) { + if (copy_to_user(u.unique, dev->unique, dev->unique_len)) + return -EFAULT; + } + u.unique_len = dev->unique_len; + if (copy_to_user((drm_unique_t *)arg, &u, sizeof(u))) + return -EFAULT; + return 0; +} + +int drm_setunique(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_unique_t u; + + if (dev->unique_len || dev->unique) + return -EBUSY; + + if (copy_from_user(&u, (drm_unique_t *)arg, sizeof(u))) + return -EFAULT; + + if (!u.unique_len || u.unique_len > 1024) + return -EINVAL; + + dev->unique_len = u.unique_len; + dev->unique = drm_alloc(u.unique_len + 1, DRM_MEM_DRIVER); + if (copy_from_user(dev->unique, u.unique, dev->unique_len)) + return -EFAULT; + dev->unique[dev->unique_len] = '\0'; + + dev->devname = drm_alloc(strlen(dev->name) + strlen(dev->unique) + 2, + DRM_MEM_DRIVER); + sprintf(dev->devname, "%s@%s", dev->name, dev->unique); + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/lists.c linux.ac/drivers/char/drm-4.0/lists.c --- linux.vanilla/drivers/char/drm-4.0/lists.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/lists.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,218 @@ +/* lists.c -- Buffer list handling routines -*- linux-c -*- + * Created: Mon Apr 19 20:54:22 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_waitlist_create(drm_waitlist_t *bl, int count) +{ + if (bl->count) return -EINVAL; + + bl->count = count; + bl->bufs = drm_alloc((bl->count + 2) * sizeof(*bl->bufs), + DRM_MEM_BUFLISTS); + bl->rp = bl->bufs; + bl->wp = bl->bufs; + bl->end = &bl->bufs[bl->count+1]; + bl->write_lock = SPIN_LOCK_UNLOCKED; + bl->read_lock = SPIN_LOCK_UNLOCKED; + return 0; +} + +int drm_waitlist_destroy(drm_waitlist_t *bl) +{ + if (bl->rp != bl->wp) return -EINVAL; + if (bl->bufs) drm_free(bl->bufs, + (bl->count + 2) * sizeof(*bl->bufs), + DRM_MEM_BUFLISTS); + bl->count = 0; + bl->bufs = NULL; + bl->rp = NULL; + bl->wp = NULL; + bl->end = NULL; + return 0; +} + +int drm_waitlist_put(drm_waitlist_t *bl, drm_buf_t *buf) +{ + int left; + unsigned long flags; + + left = DRM_LEFTCOUNT(bl); + if (!left) { + DRM_ERROR("Overflow while adding buffer %d from pid %d\n", + buf->idx, buf->pid); + return -EINVAL; + } +#if DRM_DMA_HISTOGRAM + buf->time_queued = get_cycles(); +#endif + buf->list = DRM_LIST_WAIT; + + spin_lock_irqsave(&bl->write_lock, flags); + *bl->wp = buf; + if (++bl->wp >= bl->end) bl->wp = bl->bufs; + spin_unlock_irqrestore(&bl->write_lock, flags); + + return 0; +} + +drm_buf_t *drm_waitlist_get(drm_waitlist_t *bl) +{ + drm_buf_t *buf; + unsigned long flags; + + spin_lock_irqsave(&bl->read_lock, flags); + buf = *bl->rp; + if (bl->rp == bl->wp) { + spin_unlock_irqrestore(&bl->read_lock, flags); + return NULL; + } + if (++bl->rp >= bl->end) bl->rp = bl->bufs; + spin_unlock_irqrestore(&bl->read_lock, flags); + + return buf; +} + +int drm_freelist_create(drm_freelist_t *bl, int count) +{ + atomic_set(&bl->count, 0); + bl->next = NULL; + init_waitqueue_head(&bl->waiting); + bl->low_mark = 0; + bl->high_mark = 0; + atomic_set(&bl->wfh, 0); + bl->lock = SPIN_LOCK_UNLOCKED; + ++bl->initialized; + return 0; +} + +int drm_freelist_destroy(drm_freelist_t *bl) +{ + atomic_set(&bl->count, 0); + bl->next = NULL; + return 0; +} + +int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf) +{ + drm_device_dma_t *dma = dev->dma; + + if (!dma) { + DRM_ERROR("No DMA support\n"); + return 1; + } + + if (buf->waiting || buf->pending || buf->list == DRM_LIST_FREE) { + DRM_ERROR("Freed buffer %d: w%d, p%d, l%d\n", + buf->idx, buf->waiting, buf->pending, buf->list); + } + if (!bl) return 1; +#if DRM_DMA_HISTOGRAM + buf->time_freed = get_cycles(); + drm_histogram_compute(dev, buf); +#endif + buf->list = DRM_LIST_FREE; + + spin_lock(&bl->lock); + buf->next = bl->next; + bl->next = buf; + spin_unlock(&bl->lock); + + atomic_inc(&bl->count); + if (atomic_read(&bl->count) > dma->buf_count) { + DRM_ERROR("%d of %d buffers free after addition of %d\n", + atomic_read(&bl->count), dma->buf_count, buf->idx); + return 1; + } + /* Check for high water mark */ + if (atomic_read(&bl->wfh) && atomic_read(&bl->count)>=bl->high_mark) { + atomic_set(&bl->wfh, 0); + wake_up_interruptible(&bl->waiting); + } + return 0; +} + +static drm_buf_t *drm_freelist_try(drm_freelist_t *bl) +{ + drm_buf_t *buf; + + if (!bl) return NULL; + + /* Get buffer */ + spin_lock(&bl->lock); + if (!bl->next) { + spin_unlock(&bl->lock); + return NULL; + } + buf = bl->next; + bl->next = bl->next->next; + spin_unlock(&bl->lock); + + atomic_dec(&bl->count); + buf->next = NULL; + buf->list = DRM_LIST_NONE; + if (buf->waiting || buf->pending) { + DRM_ERROR("Free buffer %d: w%d, p%d, l%d\n", + buf->idx, buf->waiting, buf->pending, buf->list); + } + + return buf; +} + +drm_buf_t *drm_freelist_get(drm_freelist_t *bl, int block) +{ + drm_buf_t *buf = NULL; + DECLARE_WAITQUEUE(entry, current); + + if (!bl || !bl->initialized) return NULL; + + /* Check for low water mark */ + if (atomic_read(&bl->count) <= bl->low_mark) /* Became low */ + atomic_set(&bl->wfh, 1); + if (atomic_read(&bl->wfh)) { + if (block) { + add_wait_queue(&bl->waiting, &entry); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!atomic_read(&bl->wfh) + && (buf = drm_freelist_try(bl))) break; + schedule(); + if (signal_pending(current)) break; + } + current->state = TASK_RUNNING; + remove_wait_queue(&bl->waiting, &entry); + } + return buf; + } + + return drm_freelist_try(bl); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/lock.c linux.ac/drivers/char/drm-4.0/lock.c --- linux.vanilla/drivers/char/drm-4.0/lock.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/lock.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,252 @@ +/* lock.c -- IOCTLs for locking -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_block(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + DRM_DEBUG("\n"); + return 0; +} + +int drm_unblock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + DRM_DEBUG("\n"); + return 0; +} + +int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old, new, prev; + + do { + old = *lock; + if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT; + else new = context | _DRM_LOCK_HELD; + prev = cmpxchg(lock, old, new); + } while (prev != old); + if (_DRM_LOCKING_CONTEXT(old) == context) { + if (old & _DRM_LOCK_HELD) { + if (context != DRM_KERNEL_CONTEXT) { + DRM_ERROR("%d holds heavyweight lock\n", + context); + } + return 0; + } + } + if (new == (context | _DRM_LOCK_HELD)) { + /* Have lock */ + return 1; + } + return 0; +} + +/* This takes a lock forcibly and hands it to context. Should ONLY be used + inside *_unlock to give lock to kernel before calling *_dma_schedule. */ +int drm_lock_transfer(drm_device_t *dev, + __volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old, new, prev; + + dev->lock.pid = 0; + do { + old = *lock; + new = context | _DRM_LOCK_HELD; + prev = cmpxchg(lock, old, new); + } while (prev != old); + return 1; +} + +int drm_lock_free(drm_device_t *dev, + __volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old, new, prev; + pid_t pid = dev->lock.pid; + + dev->lock.pid = 0; + do { + old = *lock; + new = 0; + prev = cmpxchg(lock, old, new); + } while (prev != old); + if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) { + DRM_ERROR("%d freed heavyweight lock held by %d (pid %d)\n", + context, + _DRM_LOCKING_CONTEXT(old), + pid); + return 1; + } + wake_up_interruptible(&dev->lock.lock_queue); + return 0; +} + +static int drm_flush_queue(drm_device_t *dev, int context) +{ + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_queue_t *q = dev->queuelist[context]; + + DRM_DEBUG("\n"); + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) > 1) { + atomic_inc(&q->block_write); + add_wait_queue(&q->flush_queue, &entry); + atomic_inc(&q->block_count); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!DRM_BUFCOUNT(&q->waitlist)) break; + schedule(); + if (signal_pending(current)) { + ret = -EINTR; /* Can't restart */ + break; + } + } + atomic_dec(&q->block_count); + current->state = TASK_RUNNING; + remove_wait_queue(&q->flush_queue, &entry); + } + atomic_dec(&q->use_count); + atomic_inc(&q->total_flushed); + + /* NOTE: block_write is still incremented! + Use drm_flush_unlock_queue to decrement. */ + return ret; +} + +static int drm_flush_unblock_queue(drm_device_t *dev, int context) +{ + drm_queue_t *q = dev->queuelist[context]; + + DRM_DEBUG("\n"); + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) > 1) { + if (atomic_read(&q->block_write)) { + atomic_dec(&q->block_write); + wake_up_interruptible(&q->write_queue); + } + } + atomic_dec(&q->use_count); + return 0; +} + +int drm_flush_block_and_flush(drm_device_t *dev, int context, + drm_lock_flags_t flags) +{ + int ret = 0; + int i; + + DRM_DEBUG("\n"); + + if (flags & _DRM_LOCK_FLUSH) { + ret = drm_flush_queue(dev, DRM_KERNEL_CONTEXT); + if (!ret) ret = drm_flush_queue(dev, context); + } + if (flags & _DRM_LOCK_FLUSH_ALL) { + for (i = 0; !ret && i < dev->queue_count; i++) { + ret = drm_flush_queue(dev, i); + } + } + return ret; +} + +int drm_flush_unblock(drm_device_t *dev, int context, drm_lock_flags_t flags) +{ + int ret = 0; + int i; + + DRM_DEBUG("\n"); + + if (flags & _DRM_LOCK_FLUSH) { + ret = drm_flush_unblock_queue(dev, DRM_KERNEL_CONTEXT); + if (!ret) ret = drm_flush_unblock_queue(dev, context); + } + if (flags & _DRM_LOCK_FLUSH_ALL) { + for (i = 0; !ret && i < dev->queue_count; i++) { + ret = drm_flush_unblock_queue(dev, i); + } + } + + return ret; +} + +int drm_finish(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int ret = 0; + drm_lock_t lock; + + DRM_DEBUG("\n"); + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + ret = drm_flush_block_and_flush(dev, lock.context, lock.flags); + drm_flush_unblock(dev, lock.context, lock.flags); + return ret; +} + +/* If we get here, it means that the process has called DRM_IOCTL_LOCK + without calling DRM_IOCTL_UNLOCK. + + If the lock is not held, then let the signal proceed as usual. + + If the lock is held, then set the contended flag and keep the signal + blocked. + + + Return 1 if the signal should be delivered normally. + Return 0 if the signal should be blocked. */ + +int drm_notifier(void *priv) +{ + drm_sigdata_t *s = (drm_sigdata_t *)priv; + unsigned int old, new, prev; + + + /* Allow signal delivery if lock isn't held */ + if (!_DRM_LOCK_IS_HELD(s->lock->lock) + || _DRM_LOCKING_CONTEXT(s->lock->lock) != s->context) return 1; + + /* Otherwise, set flag to force call to + drmUnlock */ + do { + old = s->lock->lock; + new = old | _DRM_LOCK_CONT; + prev = cmpxchg(&s->lock->lock, old, new); + } while (prev != old); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/memory.c linux.ac/drivers/char/drm-4.0/memory.c --- linux.vanilla/drivers/char/drm-4.0/memory.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/memory.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,448 @@ +/* memory.c -- Memory management wrappers for DRM -*- linux-c -*- + * Created: Thu Feb 4 14:00:34 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include +#include "drmP.h" +#include + +typedef struct drm_mem_stats { + const char *name; + int succeed_count; + int free_count; + int fail_count; + unsigned long bytes_allocated; + unsigned long bytes_freed; +} drm_mem_stats_t; + +static spinlock_t drm_mem_lock = SPIN_LOCK_UNLOCKED; +static unsigned long drm_ram_available = 0; /* In pages */ +static unsigned long drm_ram_used = 0; +static drm_mem_stats_t drm_mem_stats[] = { + [DRM_MEM_DMA] = { "dmabufs" }, + [DRM_MEM_SAREA] = { "sareas" }, + [DRM_MEM_DRIVER] = { "driver" }, + [DRM_MEM_MAGIC] = { "magic" }, + [DRM_MEM_IOCTLS] = { "ioctltab" }, + [DRM_MEM_MAPS] = { "maplist" }, + [DRM_MEM_VMAS] = { "vmalist" }, + [DRM_MEM_BUFS] = { "buflist" }, + [DRM_MEM_SEGS] = { "seglist" }, + [DRM_MEM_PAGES] = { "pagelist" }, + [DRM_MEM_FILES] = { "files" }, + [DRM_MEM_QUEUES] = { "queues" }, + [DRM_MEM_CMDS] = { "commands" }, + [DRM_MEM_MAPPINGS] = { "mappings" }, + [DRM_MEM_BUFLISTS] = { "buflists" }, + [DRM_MEM_AGPLISTS] = { "agplist" }, + [DRM_MEM_TOTALAGP] = { "totalagp" }, + [DRM_MEM_BOUNDAGP] = { "boundagp" }, + [DRM_MEM_CTXBITMAP] = { "ctxbitmap"}, + { NULL, 0, } /* Last entry must be null */ +}; + +void drm_mem_init(void) +{ + drm_mem_stats_t *mem; + struct sysinfo si; + + for (mem = drm_mem_stats; mem->name; ++mem) { + mem->succeed_count = 0; + mem->free_count = 0; + mem->fail_count = 0; + mem->bytes_allocated = 0; + mem->bytes_freed = 0; + } + + si_meminfo(&si); +#if LINUX_VERSION_CODE < 0x020317 + /* Changed to page count in 2.3.23 */ + drm_ram_available = si.totalram >> PAGE_SHIFT; +#else + drm_ram_available = si.totalram; +#endif + drm_ram_used = 0; +} + +/* drm_mem_info is called whenever a process reads /dev/drm/mem. */ + +static int _drm_mem_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_mem_stats_t *pt; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT(" total counts " + " | outstanding \n"); + DRM_PROC_PRINT("type alloc freed fail bytes freed" + " | allocs bytes\n\n"); + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB |\n", + "system", 0, 0, 0, + drm_ram_available << (PAGE_SHIFT - 10)); + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB |\n", + "locked", 0, 0, 0, drm_ram_used >> 10); + DRM_PROC_PRINT("\n"); + for (pt = drm_mem_stats; pt->name; pt++) { + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu %10lu | %6d %10ld\n", + pt->name, + pt->succeed_count, + pt->free_count, + pt->fail_count, + pt->bytes_allocated, + pt->bytes_freed, + pt->succeed_count - pt->free_count, + (long)pt->bytes_allocated + - (long)pt->bytes_freed); + } + + return len; +} + +int drm_mem_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + int ret; + + spin_lock(&drm_mem_lock); + ret = _drm_mem_info(buf, start, offset, len, eof, data); + spin_unlock(&drm_mem_lock); + return ret; +} + +void *drm_alloc(size_t size, int area) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(area, "Allocating 0 bytes\n"); + return NULL; + } + + if (!(pt = kmalloc(size, GFP_KERNEL))) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].fail_count; + spin_unlock(&drm_mem_lock); + return NULL; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].succeed_count; + drm_mem_stats[area].bytes_allocated += size; + spin_unlock(&drm_mem_lock); + return pt; +} + +void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area) +{ + void *pt; + + if (!(pt = drm_alloc(size, area))) return NULL; + if (oldpt && oldsize) { + memcpy(pt, oldpt, oldsize); + drm_free(oldpt, oldsize, area); + } + return pt; +} + +char *drm_strdup(const char *s, int area) +{ + char *pt; + int length = s ? strlen(s) : 0; + + if (!(pt = drm_alloc(length+1, area))) return NULL; + strcpy(pt, s); + return pt; +} + +void drm_strfree(const char *s, int area) +{ + unsigned int size; + + if (!s) return; + + size = 1 + (s ? strlen(s) : 0); + drm_free((void *)s, size, area); +} + +void drm_free(void *pt, size_t size, int area) +{ + int alloc_count; + int free_count; + + if (!pt) DRM_MEM_ERROR(area, "Attempt to free NULL pointer\n"); + else kfree(pt); + spin_lock(&drm_mem_lock); + drm_mem_stats[area].bytes_freed += size; + free_count = ++drm_mem_stats[area].free_count; + alloc_count = drm_mem_stats[area].succeed_count; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(area, "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +unsigned long drm_alloc_pages(int order, int area) +{ + unsigned long address; + unsigned long bytes = PAGE_SIZE << order; + unsigned long addr; + unsigned int sz; + + spin_lock(&drm_mem_lock); + if ((drm_ram_used >> PAGE_SHIFT) + > (DRM_RAM_PERCENT * drm_ram_available) / 100) { + spin_unlock(&drm_mem_lock); + return 0; + } + spin_unlock(&drm_mem_lock); + + address = __get_free_pages(GFP_KERNEL, order); + if (!address) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].fail_count; + spin_unlock(&drm_mem_lock); + return 0; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].succeed_count; + drm_mem_stats[area].bytes_allocated += bytes; + drm_ram_used += bytes; + spin_unlock(&drm_mem_lock); + + + /* Zero outside the lock */ + memset((void *)address, 0, bytes); + + /* Reserve */ + for (addr = address, sz = bytes; + sz > 0; + addr += PAGE_SIZE, sz -= PAGE_SIZE) { +#if LINUX_VERSION_CODE >= 0x020400 + /* Argument type changed in 2.4.0-test6/pre8 */ + mem_map_reserve(virt_to_page(addr)); +#else + mem_map_reserve(MAP_NR(addr)); +#endif + } + + return address; +} + +void drm_free_pages(unsigned long address, int order, int area) +{ + unsigned long bytes = PAGE_SIZE << order; + int alloc_count; + int free_count; + unsigned long addr; + unsigned int sz; + + if (!address) { + DRM_MEM_ERROR(area, "Attempt to free address 0\n"); + } else { + /* Unreserve */ + for (addr = address, sz = bytes; + sz > 0; + addr += PAGE_SIZE, sz -= PAGE_SIZE) { +#if LINUX_VERSION_CODE >= 0x020400 + /* Argument type changed in 2.4.0-test6/pre8 */ + mem_map_unreserve(virt_to_page(addr)); +#else + mem_map_unreserve(MAP_NR(addr)); +#endif + } + free_pages(address, order); + } + + spin_lock(&drm_mem_lock); + free_count = ++drm_mem_stats[area].free_count; + alloc_count = drm_mem_stats[area].succeed_count; + drm_mem_stats[area].bytes_freed += bytes; + drm_ram_used -= bytes; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(area, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +void *drm_ioremap(unsigned long offset, unsigned long size) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Mapping 0 bytes at 0x%08lx\n", offset); + return NULL; + } + + if (!(pt = ioremap(offset, size))) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_MAPPINGS].fail_count; + spin_unlock(&drm_mem_lock); + return NULL; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_MAPPINGS].succeed_count; + drm_mem_stats[DRM_MEM_MAPPINGS].bytes_allocated += size; + spin_unlock(&drm_mem_lock); + return pt; +} + +void drm_ioremapfree(void *pt, unsigned long size) +{ + int alloc_count; + int free_count; + + if (!pt) + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Attempt to free NULL pointer\n"); + else + iounmap(pt); + + spin_lock(&drm_mem_lock); + drm_mem_stats[DRM_MEM_MAPPINGS].bytes_freed += size; + free_count = ++drm_mem_stats[DRM_MEM_MAPPINGS].free_count; + alloc_count = drm_mem_stats[DRM_MEM_MAPPINGS].succeed_count; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) +agp_memory *drm_alloc_agp(int pages, u32 type) +{ + agp_memory *handle; + + if (!pages) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, "Allocating 0 pages\n"); + return NULL; + } + + if ((handle = drm_agp_allocate_memory(pages, type))) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_TOTALAGP].succeed_count; + drm_mem_stats[DRM_MEM_TOTALAGP].bytes_allocated + += pages << PAGE_SHIFT; + spin_unlock(&drm_mem_lock); + return handle; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_TOTALAGP].fail_count; + spin_unlock(&drm_mem_lock); + return NULL; +} + +int drm_free_agp(agp_memory *handle, int pages) +{ + int alloc_count; + int free_count; + int retval = -EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, + "Attempt to free NULL AGP handle\n"); + return retval;; + } + + if (drm_agp_free_memory(handle)) { + spin_lock(&drm_mem_lock); + free_count = ++drm_mem_stats[DRM_MEM_TOTALAGP].free_count; + alloc_count = drm_mem_stats[DRM_MEM_TOTALAGP].succeed_count; + drm_mem_stats[DRM_MEM_TOTALAGP].bytes_freed + += pages << PAGE_SHIFT; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } + return 0; + } + return retval; +} + +int drm_bind_agp(agp_memory *handle, unsigned int start) +{ + int retcode = -EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Attempt to bind NULL AGP handle\n"); + return retcode; + } + + if (!(retcode = drm_agp_bind_memory(handle, start))) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_BOUNDAGP].succeed_count; + drm_mem_stats[DRM_MEM_BOUNDAGP].bytes_allocated + += handle->page_count << PAGE_SHIFT; + spin_unlock(&drm_mem_lock); + return retcode; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_BOUNDAGP].fail_count; + spin_unlock(&drm_mem_lock); + return retcode; +} + +int drm_unbind_agp(agp_memory *handle) +{ + int alloc_count; + int free_count; + int retcode = -EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Attempt to unbind NULL AGP handle\n"); + return retcode; + } + + if ((retcode = drm_agp_unbind_memory(handle))) return retcode; + spin_lock(&drm_mem_lock); + free_count = ++drm_mem_stats[DRM_MEM_BOUNDAGP].free_count; + alloc_count = drm_mem_stats[DRM_MEM_BOUNDAGP].succeed_count; + drm_mem_stats[DRM_MEM_BOUNDAGP].bytes_freed + += handle->page_count << PAGE_SHIFT; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } + return retcode; +} +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/mga_bufs.c linux.ac/drivers/char/drm-4.0/mga_bufs.c --- linux.vanilla/drivers/char/drm-4.0/mga_bufs.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/mga_bufs.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,629 @@ +/* mga_bufs.c -- IOCTLs to manage buffers -*- linux-c -*- + * Created: Thu Jan 6 01:47:26 2000 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "mga_drv.h" +#include "linux/un.h" + + +int mga_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + drm_buf_entry_t *entry; + drm_buf_t *buf; + unsigned long offset; + unsigned long agp_offset; + int count; + int order; + int size; + int alignment; + int page_order; + int total; + int byte_count; + int i; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, + (drm_buf_desc_t *)arg, + sizeof(request))) + return -EFAULT; + + count = request.count; + order = drm_order(request.size); + size = 1 << order; + agp_offset = request.agp_start; + alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + byte_count = 0; + + DRM_DEBUG("count: %d\n", count); + DRM_DEBUG("order: %d\n", order); + DRM_DEBUG("size: %d\n", size); + DRM_DEBUG("agp_offset: %ld\n", agp_offset); + DRM_DEBUG("alignment: %d\n", alignment); + DRM_DEBUG("page_order: %d\n", page_order); + DRM_DEBUG("total: %d\n", total); + DRM_DEBUG("byte_count: %d\n", byte_count); + + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + if (dev->queue_count) return -EBUSY; /* Not while in use */ + spin_lock(&dev->count_lock); + if (dev->buf_use) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + atomic_inc(&dev->buf_alloc); + spin_unlock(&dev->count_lock); + + down(&dev->struct_sem); + entry = &dma->bufs[order]; + if (entry->buf_count) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; /* May only call once for each order */ + } + + /* This isnt neccessarily a good limit, but we have to stop a dumb + 32 bit overflow problem below */ + + if ( count < 0 || count > 4096) + { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -EINVAL; + } + + entry->buflist = drm_alloc(count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + if (!entry->buflist) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->buflist, 0, count * sizeof(*entry->buflist)); + + entry->buf_size = size; + entry->page_order = page_order; + offset = 0; + + + while(entry->buf_count < count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + + buf->offset = offset; /* Hrm */ + buf->bus_address = dev->agp->base + agp_offset + offset; + buf->address = (void *)(agp_offset + offset + dev->agp->base); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->pid = 0; + + buf->dev_private = drm_alloc(sizeof(drm_mga_buf_priv_t), + DRM_MEM_BUFS); + buf->dev_priv_size = sizeof(drm_mga_buf_priv_t); + +#if DRM_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + offset = offset + alignment; + entry->buf_count++; + byte_count += PAGE_SIZE << page_order; + } + + dma->buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS); + for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++) + dma->buflist[i] = &entry->buflist[i - dma->buf_count]; + + dma->buf_count += entry->buf_count; + + DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); + + dma->byte_count += byte_count; + + DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); + + drm_freelist_create(&entry->freelist, entry->buf_count); + for (i = 0; i < entry->buf_count; i++) { + drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]); + } + + up(&dev->struct_sem); + + request.count = entry->buf_count; + request.size = size; + + if (copy_to_user((drm_buf_desc_t *)arg, + &request, + sizeof(request))) + return -EFAULT; + + atomic_dec(&dev->buf_alloc); + + DRM_DEBUG("count: %d\n", count); + DRM_DEBUG("order: %d\n", order); + DRM_DEBUG("size: %d\n", size); + DRM_DEBUG("agp_offset: %ld\n", agp_offset); + DRM_DEBUG("alignment: %d\n", alignment); + DRM_DEBUG("page_order: %d\n", page_order); + DRM_DEBUG("total: %d\n", total); + DRM_DEBUG("byte_count: %d\n", byte_count); + + dma->flags = _DRM_DMA_USE_AGP; + + DRM_DEBUG("dma->flags : %x\n", dma->flags); + + return 0; +} + +int mga_addbufs_pci(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int count; + int order; + int size; + int total; + int page_order; + drm_buf_entry_t *entry; + unsigned long page; + drm_buf_t *buf; + int alignment; + unsigned long offset; + int i; + int byte_count; + int page_count; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, + (drm_buf_desc_t *)arg, + sizeof(request))) + return -EFAULT; + + count = request.count; + order = drm_order(request.size); + size = 1 << order; + + DRM_DEBUG("count = %d, size = %d (%d), order = %d, queue_count = %d\n", + request.count, request.size, size, order, dev->queue_count); + + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + if (dev->queue_count) return -EBUSY; /* Not while in use */ + + alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + spin_lock(&dev->count_lock); + if (dev->buf_use) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + atomic_inc(&dev->buf_alloc); + spin_unlock(&dev->count_lock); + + down(&dev->struct_sem); + entry = &dma->bufs[order]; + if (entry->buf_count) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; /* May only call once for each order */ + } + + if(count < 0 || count > 4096) + { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -EINVAL; + } + + entry->buflist = drm_alloc(count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + if (!entry->buflist) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->buflist, 0, count * sizeof(*entry->buflist)); + + entry->seglist = drm_alloc(count * sizeof(*entry->seglist), + DRM_MEM_SEGS); + if (!entry->seglist) { + drm_free(entry->buflist, + count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->seglist, 0, count * sizeof(*entry->seglist)); + + dma->pagelist = drm_realloc(dma->pagelist, + dma->page_count * sizeof(*dma->pagelist), + (dma->page_count + (count << page_order)) + * sizeof(*dma->pagelist), + DRM_MEM_PAGES); + DRM_DEBUG("pagelist: %d entries\n", + dma->page_count + (count << page_order)); + + + entry->buf_size = size; + entry->page_order = page_order; + byte_count = 0; + page_count = 0; + while (entry->buf_count < count) { + if (!(page = drm_alloc_pages(page_order, DRM_MEM_DMA))) break; + entry->seglist[entry->seg_count++] = page; + for (i = 0; i < (1 << page_order); i++) { + DRM_DEBUG("page %d @ 0x%08lx\n", + dma->page_count + page_count, + page + PAGE_SIZE * i); + dma->pagelist[dma->page_count + page_count++] + = page + PAGE_SIZE * i; + } + for (offset = 0; + offset + size <= total && entry->buf_count < count; + offset += alignment, ++entry->buf_count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + buf->offset = (dma->byte_count + byte_count + offset); + buf->address = (void *)(page + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->pid = 0; +#if DRM_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + DRM_DEBUG("buffer %d @ %p\n", + entry->buf_count, buf->address); + } + byte_count += PAGE_SIZE << page_order; + } + + dma->buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS); + for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++) + dma->buflist[i] = &entry->buflist[i - dma->buf_count]; + + dma->buf_count += entry->buf_count; + dma->seg_count += entry->seg_count; + dma->page_count += entry->seg_count << page_order; + dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); + + drm_freelist_create(&entry->freelist, entry->buf_count); + for (i = 0; i < entry->buf_count; i++) { + drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]); + } + + up(&dev->struct_sem); + + request.count = entry->buf_count; + request.size = size; + + if (copy_to_user((drm_buf_desc_t *)arg, + &request, + sizeof(request))) + return -EFAULT; + + atomic_dec(&dev->buf_alloc); + return 0; +} + +int mga_addbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_buf_desc_t request; + + if (copy_from_user(&request, + (drm_buf_desc_t *)arg, + sizeof(request))) + return -EFAULT; + + if(request.flags & _DRM_AGP_BUFFER) + return mga_addbufs_agp(inode, filp, cmd, arg); + else + return mga_addbufs_pci(inode, filp, cmd, arg); +} + +int mga_infobufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_info_t request; + int i; + int count; + + if (!dma) return -EINVAL; + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + if (copy_from_user(&request, + (drm_buf_info_t *)arg, + sizeof(request))) + return -EFAULT; + + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) ++count; + } + + if (request.count >= count) { + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) { + if (copy_to_user(&request.list[count].count, + &dma->bufs[i].buf_count, + sizeof(dma->bufs[0] + .buf_count)) || + copy_to_user(&request.list[count].size, + &dma->bufs[i].buf_size, + sizeof(dma->bufs[0].buf_size)) || + copy_to_user(&request.list[count].low_mark, + &dma->bufs[i] + .freelist.low_mark, + sizeof(dma->bufs[0] + .freelist.low_mark)) || + copy_to_user(&request.list[count] + .high_mark, + &dma->bufs[i] + .freelist.high_mark, + sizeof(dma->bufs[0] + .freelist.high_mark))) + return -EFAULT; + ++count; + } + } + } + request.count = count; + + if (copy_to_user((drm_buf_info_t *)arg, + &request, + sizeof(request))) + return -EFAULT; + + return 0; +} + +int mga_markbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int order; + drm_buf_entry_t *entry; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, (drm_buf_desc_t *)arg, sizeof(request))) + return -EFAULT; + + order = drm_order(request.size); + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + entry = &dma->bufs[order]; + + if (request.low_mark < 0 || request.low_mark > entry->buf_count) + return -EINVAL; + if (request.high_mark < 0 || request.high_mark > entry->buf_count) + return -EINVAL; + + entry->freelist.low_mark = request.low_mark; + entry->freelist.high_mark = request.high_mark; + + return 0; +} + +int mga_freebufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_free_t request; + int i; + int idx; + drm_buf_t *buf; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, + (drm_buf_free_t *)arg, + sizeof(request))) + return -EFAULT; + + for (i = 0; i < request.count; i++) { + if (copy_from_user(&idx, + &request.list[i], + sizeof(idx))) + return -EFAULT; + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("Index %d (of %d max)\n", + idx, dma->buf_count - 1); + return -EINVAL; + } + buf = dma->buflist[idx]; + if (buf->pid != current->pid) { + DRM_ERROR("Process %d freeing buffer owned by %d\n", + current->pid, buf->pid); + return -EINVAL; + } + drm_free_buffer(dev, buf); + } + + return 0; +} + +int mga_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + const int zero = 0; + unsigned long virtual; + unsigned long address; + drm_buf_map_t request; + int i; + + if (!dma) return -EINVAL; + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + if (copy_from_user(&request, + (drm_buf_map_t *)arg, + sizeof(request))) + return -EFAULT; + + if (request.count >= dma->buf_count) { + if(dma->flags & _DRM_DMA_USE_AGP) { + drm_mga_private_t *dev_priv = dev->dev_private; + drm_map_t *map = NULL; + + map = dev->maplist[dev_priv->buffer_map_idx]; + if (!map) { + retcode = -EINVAL; + goto done; + } + + DRM_DEBUG("map->offset : %lx\n", map->offset); + DRM_DEBUG("map->size : %lx\n", map->size); + DRM_DEBUG("map->type : %d\n", map->type); + DRM_DEBUG("map->flags : %x\n", map->flags); + DRM_DEBUG("map->handle : %p\n", map->handle); + DRM_DEBUG("map->mtrr : %d\n", map->mtrr); + down_write(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, map->size, + PROT_READ|PROT_WRITE, + MAP_SHARED, + (unsigned long)map->offset); + up_write(¤t->mm->mmap_sem); + } else { + down_write(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, dma->byte_count, + PROT_READ|PROT_WRITE, MAP_SHARED, 0); + up_write(¤t->mm->mmap_sem); + } + if (virtual > -1024UL) { + /* Real error */ + DRM_DEBUG("mmap error\n"); + retcode = (signed long)virtual; + goto done; + } + request.virtual = (void *)virtual; + + for (i = 0; i < dma->buf_count; i++) { + if (copy_to_user(&request.list[i].idx, + &dma->buflist[i]->idx, + sizeof(request.list[0].idx))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].total, + &dma->buflist[i]->total, + sizeof(request.list[0].total))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].used, + &zero, + sizeof(zero))) { + retcode = -EFAULT; + goto done; + } + address = virtual + dma->buflist[i]->offset; + if (copy_to_user(&request.list[i].address, + &address, + sizeof(address))) { + retcode = -EFAULT; + goto done; + } + } + } + done: + request.count = dma->buf_count; + DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode); + + if (copy_to_user((drm_buf_map_t *)arg, + &request, + sizeof(request))) + return -EFAULT; + + DRM_DEBUG("retcode : %d\n", retcode); + + return retcode; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/mga_context.c linux.ac/drivers/char/drm-4.0/mga_context.c --- linux.vanilla/drivers/char/drm-4.0/mga_context.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/mga_context.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,209 @@ +/* mga_context.c -- IOCTLs for mga contexts -*- linux-c -*- + * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "mga_drv.h" + +static int mga_alloc_queue(drm_device_t *dev) +{ + return drm_ctxbitmap_next(dev); +} + +int mga_context_switch(drm_device_t *dev, int old, int new) +{ + char buf[64]; + + atomic_inc(&dev->total_ctx); + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + if (drm_flags & DRM_FLAG_NOCTX) { + mga_context_switch_complete(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + drm_write_string(dev, buf); + } + + return 0; +} + +int mga_context_switch_complete(drm_device_t *dev, int new) +{ + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + /* If a context switch is ever initiated + when the kernel holds the lock, release + that lock here. */ +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + wake_up(&dev->context_wait); + + return 0; +} + +int mga_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res))) + return -EFAULT; + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + if (copy_to_user(&res.contexts[i], + &i, + sizeof(i))) + return -EFAULT; + } + } + res.count = DRM_RESERVED_CONTEXTS; + if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res))) + return -EFAULT; + return 0; +} + +int mga_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + if ((ctx.handle = mga_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) { + /* Skip kernel's context and get a new one. */ + ctx.handle = mga_alloc_queue(dev); + } + if (ctx.handle == -1) { + return -ENOMEM; + } + DRM_DEBUG("%d\n", ctx.handle); + if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx))) + return -EFAULT; + return 0; +} + +int mga_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + /* This does nothing for the mga */ + return 0; +} + +int mga_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx))) + return -EFAULT; + /* This is 0, because we don't hanlde any context flags */ + ctx.flags = 0; + if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx))) + return -EFAULT; + return 0; +} + +int mga_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + return mga_context_switch(dev, dev->last_context, ctx.handle); +} + +int mga_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + mga_context_switch_complete(dev, ctx.handle); + + return 0; +} + +int mga_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + if(ctx.handle == DRM_KERNEL_CONTEXT+1) priv->remove_auth_on_close = 1; + + if(ctx.handle != DRM_KERNEL_CONTEXT) { + drm_ctxbitmap_free(dev, ctx.handle); + } + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/mga_dma.c linux.ac/drivers/char/drm-4.0/mga_dma.c --- linux.vanilla/drivers/char/drm-4.0/mga_dma.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/mga_dma.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,1051 @@ +/* mga_dma.c -- DMA support for mga g200/g400 -*- linux-c -*- + * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * Keith Whitwell + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "mga_drv.h" + +#include /* For task queue support */ + +#define MGA_REG(reg) 2 +#define MGA_BASE(reg) ((unsigned long) \ + ((drm_device_t *)dev)->maplist[MGA_REG(reg)]->handle) +#define MGA_ADDR(reg) (MGA_BASE(reg) + reg) +#define MGA_DEREF(reg) *(__volatile__ int *)MGA_ADDR(reg) +#define MGA_READ(reg) MGA_DEREF(reg) +#define MGA_WRITE(reg,val) do { MGA_DEREF(reg) = val; } while (0) + +#define PDEA_pagpxfer_enable 0x2 + +static int mga_flush_queue(drm_device_t *dev); + +static unsigned long mga_alloc_page(drm_device_t *dev) +{ + unsigned long address; + + address = __get_free_page(GFP_KERNEL); + if(address == 0UL) { + return 0; + } + atomic_inc(&virt_to_page(address)->count); + set_bit(PG_reserved, &virt_to_page(address)->flags); + + return address; +} + +static void mga_free_page(drm_device_t *dev, unsigned long page) +{ + if(!page) return; + atomic_dec(&virt_to_page(page)->count); + clear_bit(PG_reserved, &virt_to_page(page)->flags); + free_page(page); + return; +} + +static void mga_delay(void) +{ + return; +} + +/* These are two age tags that will never be sent to + * the hardware */ +#define MGA_BUF_USED 0xffffffff +#define MGA_BUF_FREE 0 + +static int mga_freelist_init(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_mga_buf_priv_t *buf_priv; + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + drm_mga_freelist_t *item; + int i; + + dev_priv->head = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER); + if(dev_priv->head == NULL) return -ENOMEM; + memset(dev_priv->head, 0, sizeof(drm_mga_freelist_t)); + dev_priv->head->age = MGA_BUF_USED; + + for (i = 0; i < dma->buf_count; i++) { + buf = dma->buflist[ i ]; + buf_priv = buf->dev_private; + item = drm_alloc(sizeof(drm_mga_freelist_t), + DRM_MEM_DRIVER); + if(item == NULL) return -ENOMEM; + memset(item, 0, sizeof(drm_mga_freelist_t)); + item->age = MGA_BUF_FREE; + item->prev = dev_priv->head; + item->next = dev_priv->head->next; + if(dev_priv->head->next != NULL) + dev_priv->head->next->prev = item; + if(item->next == NULL) dev_priv->tail = item; + item->buf = buf; + buf_priv->my_freelist = item; + buf_priv->discard = 0; + buf_priv->dispatched = 0; + dev_priv->head->next = item; + } + + return 0; +} + +static void mga_freelist_cleanup(drm_device_t *dev) +{ + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + drm_mga_freelist_t *item; + drm_mga_freelist_t *prev; + + item = dev_priv->head; + while(item) { + prev = item; + item = item->next; + drm_free(prev, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER); + } + + dev_priv->head = dev_priv->tail = NULL; +} + +/* Frees dispatch lock */ +static inline void mga_dma_quiescent(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned long end; + int i; + + DRM_DEBUG("dispatch_status = 0x%02lx\n", dev_priv->dispatch_status); + end = jiffies + (HZ*3); + while(1) { + if(!test_and_set_bit(MGA_IN_DISPATCH, + &dev_priv->dispatch_status)) { + break; + } + if((signed)(end - jiffies) <= 0) { + DRM_ERROR("irqs: %d wanted %d\n", + atomic_read(&dev->total_irq), + atomic_read(&dma->total_lost)); + DRM_ERROR("lockup: dispatch_status = 0x%02lx," + " jiffies = %lu, end = %lu\n", + dev_priv->dispatch_status, jiffies, end); + return; + } + for (i = 0 ; i < 2000 ; i++) mga_delay(); + } + end = jiffies + (HZ*3); + DRM_DEBUG("quiescent status : %x\n", MGA_READ(MGAREG_STATUS)); + while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) { + if((signed)(end - jiffies) <= 0) { + DRM_ERROR("irqs: %d wanted %d\n", + atomic_read(&dev->total_irq), + atomic_read(&dma->total_lost)); + DRM_ERROR("lockup\n"); + clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); + return; + } + for (i = 0 ; i < 2000 ; i++) mga_delay(); + } + sarea_priv->dirty |= MGA_DMA_FLUSH; + + clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); + DRM_DEBUG("exit, dispatch_status = 0x%02lx\n", + dev_priv->dispatch_status); +} + +static void mga_reset_freelist(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_mga_buf_priv_t *buf_priv; + int i; + + for (i = 0; i < dma->buf_count; i++) { + buf = dma->buflist[ i ]; + buf_priv = buf->dev_private; + buf_priv->my_freelist->age = MGA_BUF_FREE; + } +} + +/* Least recently used : + * These operations are not atomic b/c they are protected by the + * hardware lock */ + +drm_buf_t *mga_freelist_get(drm_device_t *dev) +{ + DECLARE_WAITQUEUE(entry, current); + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + drm_mga_freelist_t *prev; + drm_mga_freelist_t *next; + static int failed = 0; + int return_null = 0; + + if(failed >= 1000 && dev_priv->tail->age >= dev_priv->last_prim_age) { + DRM_DEBUG("Waiting on freelist," + " tail->age = %d, last_prim_age= %d\n", + dev_priv->tail->age, + dev_priv->last_prim_age); + add_wait_queue(&dev_priv->buf_queue, &entry); + set_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + mga_dma_schedule(dev, 0); + if(dev_priv->tail->age < dev_priv->last_prim_age) + break; + atomic_inc(&dev->total_sleeps); + schedule(); + if (signal_pending(current)) { + ++return_null; + break; + } + } + clear_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status); + current->state = TASK_RUNNING; + remove_wait_queue(&dev_priv->buf_queue, &entry); + if (return_null) return NULL; + } + + if(dev_priv->tail->age < dev_priv->last_prim_age) { + prev = dev_priv->tail->prev; + next = dev_priv->tail; + prev->next = NULL; + next->prev = next->next = NULL; + dev_priv->tail = prev; + next->age = MGA_BUF_USED; + failed = 0; + return next->buf; + } + + failed++; + return NULL; +} + +int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf) +{ + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + drm_mga_buf_priv_t *buf_priv = buf->dev_private; + drm_mga_freelist_t *prev; + drm_mga_freelist_t *head; + drm_mga_freelist_t *next; + + if(buf_priv->my_freelist->age == MGA_BUF_USED) { + /* Discarded buffer, put it on the tail */ + next = buf_priv->my_freelist; + next->age = MGA_BUF_FREE; + prev = dev_priv->tail; + prev->next = next; + next->prev = prev; + next->next = NULL; + dev_priv->tail = next; + } else { + /* Normally aged buffer, put it on the head + 1, + * as the real head is a sentinal element + */ + next = buf_priv->my_freelist; + head = dev_priv->head; + prev = head->next; + head->next = next; + prev->prev = next; + next->prev = head; + next->next = prev; + } + + return 0; +} + +static int mga_init_primary_bufs(drm_device_t *dev, drm_mga_init_t *init) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + drm_mga_prim_buf_t *prim_buffer; + int i, temp, size_of_buf; + int offset = init->reserved_map_agpstart; + + dev_priv->primary_size = ((init->primary_size + PAGE_SIZE - 1) / + PAGE_SIZE) * PAGE_SIZE; + size_of_buf = dev_priv->primary_size / MGA_NUM_PRIM_BUFS; + dev_priv->warp_ucode_size = init->warp_ucode_size; + dev_priv->prim_bufs = drm_alloc(sizeof(drm_mga_prim_buf_t *) * + (MGA_NUM_PRIM_BUFS + 1), + DRM_MEM_DRIVER); + if(dev_priv->prim_bufs == NULL) { + DRM_ERROR("Unable to allocate memory for prim_buf\n"); + return -ENOMEM; + } + memset(dev_priv->prim_bufs, + 0, sizeof(drm_mga_prim_buf_t *) * (MGA_NUM_PRIM_BUFS + 1)); + + temp = init->warp_ucode_size + dev_priv->primary_size; + temp = ((temp + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE; + + dev_priv->ioremap = drm_ioremap(dev->agp->base + offset, + temp); + if(dev_priv->ioremap == NULL) { + DRM_ERROR("Ioremap failed\n"); + return -ENOMEM; + } + init_waitqueue_head(&dev_priv->wait_queue); + + for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) { + prim_buffer = drm_alloc(sizeof(drm_mga_prim_buf_t), + DRM_MEM_DRIVER); + if(prim_buffer == NULL) return -ENOMEM; + memset(prim_buffer, 0, sizeof(drm_mga_prim_buf_t)); + prim_buffer->phys_head = offset + dev->agp->base; + prim_buffer->current_dma_ptr = + prim_buffer->head = + (u32 *) (dev_priv->ioremap + + offset - + init->reserved_map_agpstart); + prim_buffer->num_dwords = 0; + prim_buffer->max_dwords = size_of_buf / sizeof(u32); + prim_buffer->max_dwords -= 5; /* Leave room for the softrap */ + prim_buffer->sec_used = 0; + prim_buffer->idx = i; + prim_buffer->prim_age = i + 1; + offset = offset + size_of_buf; + dev_priv->prim_bufs[i] = prim_buffer; + } + dev_priv->current_prim_idx = 0; + dev_priv->next_prim = + dev_priv->last_prim = + dev_priv->current_prim = + dev_priv->prim_bufs[0]; + dev_priv->next_prim_age = 2; + dev_priv->last_prim_age = 1; + set_bit(MGA_BUF_IN_USE, &dev_priv->current_prim->buffer_status); + return 0; +} + +void mga_fire_primary(drm_device_t *dev, drm_mga_prim_buf_t *prim) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + int use_agp = PDEA_pagpxfer_enable; + unsigned long end; + int i; + int next_idx; + PRIMLOCALS; + + dev_priv->last_prim = prim; + + /* We never check for overflow, b/c there is always room */ + PRIMPTR(prim); + if(num_dwords <= 0) { + DRM_ERROR("num_dwords == 0 when dispatched\n"); + goto out_prim_wait; + } + PRIMOUTREG( MGAREG_DMAPAD, 0); + PRIMOUTREG( MGAREG_DMAPAD, 0); + PRIMOUTREG( MGAREG_DMAPAD, 0); + PRIMOUTREG( MGAREG_SOFTRAP, 0); + PRIMFINISH(prim); + + end = jiffies + (HZ*3); + if(sarea_priv->dirty & MGA_DMA_FLUSH) { + while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) { + if((signed)(end - jiffies) <= 0) { + DRM_ERROR("irqs: %d wanted %d\n", + atomic_read(&dev->total_irq), + atomic_read(&dma->total_lost)); + DRM_ERROR("lockup (flush)\n"); + goto out_prim_wait; + } + + for (i = 0 ; i < 4096 ; i++) mga_delay(); + } + sarea_priv->dirty &= ~(MGA_DMA_FLUSH); + } else { + while((MGA_READ(MGAREG_STATUS) & 0x00020001) != 0x00020000) { + if((signed)(end - jiffies) <= 0) { + DRM_ERROR("irqs: %d wanted %d\n", + atomic_read(&dev->total_irq), + atomic_read(&dma->total_lost)); + DRM_ERROR("lockup (wait)\n"); + goto out_prim_wait; + } + + for (i = 0 ; i < 4096 ; i++) mga_delay(); + } + } + + mga_flush_write_combine(); + atomic_inc(&dev_priv->pending_bufs); + MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL); + MGA_WRITE(MGAREG_PRIMEND, (phys_head + num_dwords * 4) | use_agp); + prim->num_dwords = 0; + sarea_priv->last_enqueue = prim->prim_age; + + next_idx = prim->idx + 1; + if(next_idx >= MGA_NUM_PRIM_BUFS) + next_idx = 0; + + dev_priv->next_prim = dev_priv->prim_bufs[next_idx]; + return; + + out_prim_wait: + prim->num_dwords = 0; + prim->sec_used = 0; + clear_bit(MGA_BUF_IN_USE, &prim->buffer_status); + wake_up_interruptible(&dev_priv->wait_queue); + clear_bit(MGA_BUF_SWAP_PENDING, &prim->buffer_status); + clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); +} + +int mga_advance_primary(drm_device_t *dev) +{ + DECLARE_WAITQUEUE(entry, current); + drm_mga_private_t *dev_priv = dev->dev_private; + drm_mga_prim_buf_t *prim_buffer; + drm_device_dma_t *dma = dev->dma; + int next_prim_idx; + int ret = 0; + + /* This needs to reset the primary buffer if available, + * we should collect stats on how many times it bites + * it's tail */ + + next_prim_idx = dev_priv->current_prim_idx + 1; + if(next_prim_idx >= MGA_NUM_PRIM_BUFS) + next_prim_idx = 0; + prim_buffer = dev_priv->prim_bufs[next_prim_idx]; + set_bit(MGA_IN_WAIT, &dev_priv->dispatch_status); + + /* In use is cleared in interrupt handler */ + + if(test_and_set_bit(MGA_BUF_IN_USE, &prim_buffer->buffer_status)) { + add_wait_queue(&dev_priv->wait_queue, &entry); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + mga_dma_schedule(dev, 0); + if(!test_and_set_bit(MGA_BUF_IN_USE, + &prim_buffer->buffer_status)) + break; + atomic_inc(&dev->total_sleeps); + atomic_inc(&dma->total_missed_sched); + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev_priv->wait_queue, &entry); + if(ret) return ret; + } + clear_bit(MGA_IN_WAIT, &dev_priv->dispatch_status); + + /* This primary buffer is now free to use */ + prim_buffer->current_dma_ptr = prim_buffer->head; + prim_buffer->num_dwords = 0; + prim_buffer->sec_used = 0; + prim_buffer->prim_age = dev_priv->next_prim_age++; + if(prim_buffer->prim_age == 0 || prim_buffer->prim_age == 0xffffffff) { + mga_flush_queue(dev); + mga_dma_quiescent(dev); + mga_reset_freelist(dev); + prim_buffer->prim_age = (dev_priv->next_prim_age += 2); + } + + /* Reset all buffer status stuff */ + clear_bit(MGA_BUF_NEEDS_OVERFLOW, &prim_buffer->buffer_status); + clear_bit(MGA_BUF_FORCE_FIRE, &prim_buffer->buffer_status); + clear_bit(MGA_BUF_SWAP_PENDING, &prim_buffer->buffer_status); + + dev_priv->current_prim = prim_buffer; + dev_priv->current_prim_idx = next_prim_idx; + return 0; +} + +/* More dynamic performance decisions */ +static inline int mga_decide_to_fire(drm_device_t *dev) +{ + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + + if(test_bit(MGA_BUF_FORCE_FIRE, &dev_priv->next_prim->buffer_status)) { + return 1; + } + + if (test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status) && + dev_priv->next_prim->num_dwords) { + return 1; + } + + if (test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) && + dev_priv->next_prim->num_dwords) { + return 1; + } + + if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS - 1) { + if(test_bit(MGA_BUF_SWAP_PENDING, + &dev_priv->next_prim->buffer_status)) { + return 1; + } + } + + if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS / 2) { + if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 8) { + return 1; + } + } + + if(atomic_read(&dev_priv->pending_bufs) >= MGA_NUM_PRIM_BUFS / 2) { + if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 4) { + return 1; + } + } + + return 0; +} + +int mga_dma_schedule(drm_device_t *dev, int locked) +{ + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + int retval = 0; + + if (!dev_priv) return -EBUSY; + + if (test_and_set_bit(0, &dev->dma_flag)) { + retval = -EBUSY; + goto sch_out_wakeup; + } + + if(test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) || + test_bit(MGA_IN_WAIT, &dev_priv->dispatch_status) || + test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status)) { + locked = 1; + } + + if (!locked && + !drm_lock_take(&dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) { + clear_bit(0, &dev->dma_flag); + retval = -EBUSY; + goto sch_out_wakeup; + } + + if(!test_and_set_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status)) { + /* Fire dma buffer */ + if(mga_decide_to_fire(dev)) { + clear_bit(MGA_BUF_FORCE_FIRE, + &dev_priv->next_prim->buffer_status); + if(dev_priv->current_prim == dev_priv->next_prim) { + /* Schedule overflow for a later time */ + set_bit(MGA_BUF_NEEDS_OVERFLOW, + &dev_priv->next_prim->buffer_status); + } + mga_fire_primary(dev, dev_priv->next_prim); + } else { + clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); + } + } + + if (!locked) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } + + clear_bit(0, &dev->dma_flag); + +sch_out_wakeup: + if(test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) && + atomic_read(&dev_priv->pending_bufs) == 0) { + /* Everything has been processed by the hardware */ + clear_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status); + wake_up_interruptible(&dev_priv->flush_queue); + } + + if(test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status) + && dev_priv->tail->age < dev_priv->last_prim_age) + wake_up_interruptible(&dev_priv->buf_queue); + + return retval; +} + +static void mga_dma_service(int irq, void *device, struct pt_regs *regs) +{ + drm_device_t *dev = (drm_device_t *)device; + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + drm_mga_prim_buf_t *last_prim_buffer; + + atomic_inc(&dev->total_irq); + if((MGA_READ(MGAREG_STATUS) & 0x00000001) != 0x00000001) return; + MGA_WRITE(MGAREG_ICLEAR, 0x00000001); + last_prim_buffer = dev_priv->last_prim; + last_prim_buffer->num_dwords = 0; + last_prim_buffer->sec_used = 0; + dev_priv->sarea_priv->last_dispatch = + dev_priv->last_prim_age = last_prim_buffer->prim_age; + clear_bit(MGA_BUF_IN_USE, &last_prim_buffer->buffer_status); + clear_bit(MGA_BUF_SWAP_PENDING, &last_prim_buffer->buffer_status); + clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); + atomic_dec(&dev_priv->pending_bufs); + queue_task(&dev->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + wake_up_interruptible(&dev_priv->wait_queue); +} + +static void mga_dma_task_queue(void *device) +{ + mga_dma_schedule((drm_device_t *)device, 0); +} + +int mga_dma_cleanup(drm_device_t *dev) +{ + if(dev->dev_private) { + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + + if (dev->irq) mga_flush_queue(dev); + mga_dma_quiescent(dev); + + if(dev_priv->ioremap) { + int temp = (dev_priv->warp_ucode_size + + dev_priv->primary_size + + PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE; + + drm_ioremapfree((void *) dev_priv->ioremap, temp); + } + if(dev_priv->status_page != NULL) { + iounmap(dev_priv->status_page); + } + if(dev_priv->real_status_page != 0UL) { + mga_free_page(dev, dev_priv->real_status_page); + } + if(dev_priv->prim_bufs != NULL) { + int i; + for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) { + if(dev_priv->prim_bufs[i] != NULL) { + drm_free(dev_priv->prim_bufs[i], + sizeof(drm_mga_prim_buf_t), + DRM_MEM_DRIVER); + } + } + drm_free(dev_priv->prim_bufs, sizeof(void *) * + (MGA_NUM_PRIM_BUFS + 1), + DRM_MEM_DRIVER); + } + if(dev_priv->head != NULL) { + mga_freelist_cleanup(dev); + } + + + drm_free(dev->dev_private, sizeof(drm_mga_private_t), + DRM_MEM_DRIVER); + dev->dev_private = NULL; + } + + return 0; +} + +static int mga_dma_initialize(drm_device_t *dev, drm_mga_init_t *init) { + drm_mga_private_t *dev_priv; + drm_map_t *sarea_map = NULL; + + dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER); + if(dev_priv == NULL) return -ENOMEM; + dev->dev_private = (void *) dev_priv; + + memset(dev_priv, 0, sizeof(drm_mga_private_t)); + + if((init->reserved_map_idx >= dev->map_count) || + (init->buffer_map_idx >= dev->map_count)) { + mga_dma_cleanup(dev); + return -EINVAL; + } + + dev_priv->reserved_map_idx = init->reserved_map_idx; + dev_priv->buffer_map_idx = init->buffer_map_idx; + sarea_map = dev->maplist[0]; + dev_priv->sarea_priv = (drm_mga_sarea_t *) + ((u8 *)sarea_map->handle + + init->sarea_priv_offset); + + /* Scale primary size to the next page */ + dev_priv->chipset = init->chipset; + dev_priv->frontOffset = init->frontOffset; + dev_priv->backOffset = init->backOffset; + dev_priv->depthOffset = init->depthOffset; + dev_priv->textureOffset = init->textureOffset; + dev_priv->textureSize = init->textureSize; + dev_priv->cpp = init->cpp; + dev_priv->sgram = init->sgram; + dev_priv->stride = init->stride; + + dev_priv->mAccess = init->mAccess; + init_waitqueue_head(&dev_priv->flush_queue); + init_waitqueue_head(&dev_priv->buf_queue); + dev_priv->WarpPipe = 0xff000000; + dev_priv->vertexsize = 0; + + DRM_DEBUG("chipset=%d ucode_size=%d backOffset=%x depthOffset=%x\n", + dev_priv->chipset, dev_priv->warp_ucode_size, + dev_priv->backOffset, dev_priv->depthOffset); + DRM_DEBUG("cpp: %d sgram: %d stride: %d maccess: %x\n", + dev_priv->cpp, dev_priv->sgram, dev_priv->stride, + dev_priv->mAccess); + + memcpy(&dev_priv->WarpIndex, &init->WarpIndex, + sizeof(drm_mga_warp_index_t) * MGA_MAX_WARP_PIPES); + + if(mga_init_primary_bufs(dev, init) != 0) { + DRM_ERROR("Can not initialize primary buffers\n"); + mga_dma_cleanup(dev); + return -ENOMEM; + } + dev_priv->real_status_page = mga_alloc_page(dev); + if(dev_priv->real_status_page == 0UL) { + mga_dma_cleanup(dev); + DRM_ERROR("Can not allocate status page\n"); + return -ENOMEM; + } + + dev_priv->status_page = + ioremap_nocache(virt_to_bus((void *)dev_priv->real_status_page), + PAGE_SIZE); + + if(dev_priv->status_page == NULL) { + mga_dma_cleanup(dev); + DRM_ERROR("Can not remap status page\n"); + return -ENOMEM; + } + + /* Write status page when secend or softrap occurs */ + MGA_WRITE(MGAREG_PRIMPTR, + virt_to_bus((void *)dev_priv->real_status_page) | 0x00000003); + + + /* Private is now filled in, initialize the hardware */ + { + PRIMLOCALS; + PRIMGETPTR( dev_priv ); + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DWGSYNC, 0x0100); + PRIMOUTREG(MGAREG_SOFTRAP, 0); + /* Poll for the first buffer to insure that + * the status register will be correct + */ + + mga_flush_write_combine(); + MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL); + + MGA_WRITE(MGAREG_PRIMEND, ((phys_head + num_dwords * 4) | + PDEA_pagpxfer_enable)); + + while(MGA_READ(MGAREG_DWGSYNC) != 0x0100) ; + } + + if(mga_freelist_init(dev) != 0) { + DRM_ERROR("Could not initialize freelist\n"); + mga_dma_cleanup(dev); + return -ENOMEM; + } + return 0; +} + +int mga_dma_init(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_mga_init_t init; + + if (copy_from_user(&init, (drm_mga_init_t *)arg, sizeof(init))) + return -EFAULT; + + switch(init.func) { + case MGA_INIT_DMA: + return mga_dma_initialize(dev, &init); + case MGA_CLEANUP_DMA: + return mga_dma_cleanup(dev); + } + + return -EINVAL; +} + +int mga_irq_install(drm_device_t *dev, int irq) +{ + int retcode; + + if (!irq) return -EINVAL; + + down(&dev->struct_sem); + if (dev->irq) { + up(&dev->struct_sem); + return -EBUSY; + } + dev->irq = irq; + up(&dev->struct_sem); + + DRM_DEBUG("install irq handler %d\n", irq); + + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + dev->dma->next_buffer = NULL; + dev->dma->next_queue = NULL; + dev->dma->this_buffer = NULL; + INIT_LIST_HEAD(&dev->tq.list); + dev->tq.sync = 0; + dev->tq.routine = mga_dma_task_queue; + dev->tq.data = dev; + + /* Before installing handler */ + MGA_WRITE(MGAREG_IEN, 0); + /* Install handler */ + if ((retcode = request_irq(dev->irq, + mga_dma_service, + SA_SHIRQ, + dev->devname, + dev))) { + down(&dev->struct_sem); + dev->irq = 0; + up(&dev->struct_sem); + return retcode; + } + /* After installing handler */ + MGA_WRITE(MGAREG_ICLEAR, 0x00000001); + MGA_WRITE(MGAREG_IEN, 0x00000001); + return 0; +} + +int mga_irq_uninstall(drm_device_t *dev) +{ + int irq; + + down(&dev->struct_sem); + irq = dev->irq; + dev->irq = 0; + up(&dev->struct_sem); + + if (!irq) return -EINVAL; + DRM_DEBUG("remove irq handler %d\n", irq); + MGA_WRITE(MGAREG_ICLEAR, 0x00000001); + MGA_WRITE(MGAREG_IEN, 0); + free_irq(irq, dev); + return 0; +} + +int mga_control(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_control_t ctl; + + if (copy_from_user(&ctl, (drm_control_t *)arg, sizeof(ctl))) + return -EFAULT; + + switch (ctl.func) { + case DRM_INST_HANDLER: + return mga_irq_install(dev, ctl.irq); + case DRM_UNINST_HANDLER: + return mga_irq_uninstall(dev); + default: + return -EINVAL; + } +} + +static int mga_flush_queue(drm_device_t *dev) +{ + DECLARE_WAITQUEUE(entry, current); + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + int ret = 0; + + if(!dev_priv) return 0; + + if(dev_priv->next_prim->num_dwords != 0) { + add_wait_queue(&dev_priv->flush_queue, &entry); + if (test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status)) + DRM_ERROR("Incorrect mga_flush_queue logic\n"); + set_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status); + mga_dma_schedule(dev, 0); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!test_bit(MGA_IN_FLUSH, + &dev_priv->dispatch_status)) + break; + atomic_inc(&dev->total_sleeps); + schedule(); + if (signal_pending(current)) { + ret = -EINTR; /* Can't restart */ + clear_bit(MGA_IN_FLUSH, + &dev_priv->dispatch_status); + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev_priv->flush_queue, &entry); + } + return ret; +} + +/* Must be called with the lock held */ +void mga_reclaim_buffers(drm_device_t *dev, pid_t pid) +{ + drm_device_dma_t *dma = dev->dma; + int i; + + if (!dma) return; + if(dev->dev_private == NULL) return; + if(dma->buflist == NULL) return; + + DRM_DEBUG("buf_count=%d\n", dma->buf_count); + + mga_flush_queue(dev); + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_mga_buf_priv_t *buf_priv = buf->dev_private; + + /* Only buffers that need to get reclaimed ever + * get set to free + */ + if (buf->pid == pid && buf_priv) { + if(buf_priv->my_freelist->age == MGA_BUF_USED) + buf_priv->my_freelist->age = MGA_BUF_FREE; + } + } +} + +int mga_lock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + if (lock.context < 0) return -EINVAL; + + /* Only one queue: + */ + + if (!ret) { + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + } + + if (!ret) { + sigemptyset(&dev->sigmask); + sigaddset(&dev->sigmask, SIGSTOP); + sigaddset(&dev->sigmask, SIGTSTP); + sigaddset(&dev->sigmask, SIGTTIN); + sigaddset(&dev->sigmask, SIGTTOU); + dev->sigdata.context = lock.context; + dev->sigdata.lock = dev->lock.hw_lock; + block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask); + + if (lock.flags & _DRM_LOCK_QUIESCENT) { + DRM_DEBUG("_DRM_LOCK_QUIESCENT\n"); + mga_flush_queue(dev); + mga_dma_quiescent(dev); + } + } + + if (ret) DRM_DEBUG("%d %s\n", lock.context, + ret ? "interrupted" : "has lock"); + return ret; +} + +int mga_flush_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("lock not held\n"); + return -EINVAL; + } + + if(lock.flags & _DRM_LOCK_FLUSH || lock.flags & _DRM_LOCK_FLUSH_ALL) { + drm_mga_prim_buf_t *temp_buf; + + temp_buf = dev_priv->current_prim; + + if(temp_buf && temp_buf->num_dwords) { + set_bit(MGA_BUF_FORCE_FIRE, &temp_buf->buffer_status); + mga_advance_primary(dev); + } + mga_dma_schedule(dev, 1); + } + if(lock.flags & _DRM_LOCK_QUIESCENT) { + mga_flush_queue(dev); + mga_dma_quiescent(dev); + } + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/mga_drm.h linux.ac/drivers/char/drm-4.0/mga_drm.h --- linux.vanilla/drivers/char/drm-4.0/mga_drm.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/mga_drm.h Wed Oct 10 01:47:41 2001 @@ -0,0 +1,274 @@ +/* mga_drm.h -- Public header for the Matrox g200/g400 driver -*- linux-c -*- + * Created: Tue Jan 25 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Jeff Hartmann + * Keith Whitwell + * + */ + +#ifndef _MGA_DRM_H_ +#define _MGA_DRM_H_ + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (xf86drmMga.h) + */ +#ifndef _MGA_DEFINES_ +#define _MGA_DEFINES_ + +#define MGA_F 0x1 /* fog */ +#define MGA_A 0x2 /* alpha */ +#define MGA_S 0x4 /* specular */ +#define MGA_T2 0x8 /* multitexture */ + +#define MGA_WARP_TGZ 0 +#define MGA_WARP_TGZF (MGA_F) +#define MGA_WARP_TGZA (MGA_A) +#define MGA_WARP_TGZAF (MGA_F|MGA_A) +#define MGA_WARP_TGZS (MGA_S) +#define MGA_WARP_TGZSF (MGA_S|MGA_F) +#define MGA_WARP_TGZSA (MGA_S|MGA_A) +#define MGA_WARP_TGZSAF (MGA_S|MGA_F|MGA_A) +#define MGA_WARP_T2GZ (MGA_T2) +#define MGA_WARP_T2GZF (MGA_T2|MGA_F) +#define MGA_WARP_T2GZA (MGA_T2|MGA_A) +#define MGA_WARP_T2GZAF (MGA_T2|MGA_A|MGA_F) +#define MGA_WARP_T2GZS (MGA_T2|MGA_S) +#define MGA_WARP_T2GZSF (MGA_T2|MGA_S|MGA_F) +#define MGA_WARP_T2GZSA (MGA_T2|MGA_S|MGA_A) +#define MGA_WARP_T2GZSAF (MGA_T2|MGA_S|MGA_F|MGA_A) + +#define MGA_MAX_G400_PIPES 16 +#define MGA_MAX_G200_PIPES 8 /* no multitex */ +#define MGA_MAX_WARP_PIPES MGA_MAX_G400_PIPES + +#define MGA_CARD_TYPE_G200 1 +#define MGA_CARD_TYPE_G400 2 + +#define MGA_FRONT 0x1 +#define MGA_BACK 0x2 +#define MGA_DEPTH 0x4 + +/* 3d state excluding texture units: + */ +#define MGA_CTXREG_DSTORG 0 /* validated */ +#define MGA_CTXREG_MACCESS 1 +#define MGA_CTXREG_PLNWT 2 +#define MGA_CTXREG_DWGCTL 3 +#define MGA_CTXREG_ALPHACTRL 4 +#define MGA_CTXREG_FOGCOLOR 5 +#define MGA_CTXREG_WFLAG 6 +#define MGA_CTXREG_TDUAL0 7 +#define MGA_CTXREG_TDUAL1 8 +#define MGA_CTXREG_FCOL 9 +#define MGA_CTXREG_STENCIL 10 +#define MGA_CTXREG_STENCILCTL 11 +#define MGA_CTX_SETUP_SIZE 12 + +/* 2d state + */ +#define MGA_2DREG_PITCH 0 +#define MGA_2D_SETUP_SIZE 1 + +/* Each texture unit has a state: + */ +#define MGA_TEXREG_CTL 0 +#define MGA_TEXREG_CTL2 1 +#define MGA_TEXREG_FILTER 2 +#define MGA_TEXREG_BORDERCOL 3 +#define MGA_TEXREG_ORG 4 /* validated */ +#define MGA_TEXREG_ORG1 5 +#define MGA_TEXREG_ORG2 6 +#define MGA_TEXREG_ORG3 7 +#define MGA_TEXREG_ORG4 8 +#define MGA_TEXREG_WIDTH 9 +#define MGA_TEXREG_HEIGHT 10 +#define MGA_TEX_SETUP_SIZE 11 + +/* What needs to be changed for the current vertex dma buffer? + */ +#define MGA_UPLOAD_CTX 0x1 +#define MGA_UPLOAD_TEX0 0x2 +#define MGA_UPLOAD_TEX1 0x4 +#define MGA_UPLOAD_PIPE 0x8 +#define MGA_UPLOAD_TEX0IMAGE 0x10 /* handled client-side */ +#define MGA_UPLOAD_TEX1IMAGE 0x20 /* handled client-side */ +#define MGA_UPLOAD_2D 0x40 +#define MGA_WAIT_AGE 0x80 /* handled client-side */ +#define MGA_UPLOAD_CLIPRECTS 0x100 /* handled client-side */ +#define MGA_DMA_FLUSH 0x200 /* set when someone gets the lock + quiescent */ + +/* 32 buffers of 64k each, total 2 meg. + */ +#define MGA_DMA_BUF_ORDER 16 +#define MGA_DMA_BUF_SZ (1< + * Jeff Hartmann + * + * + */ + +#include +#include "drmP.h" +#include "mga_drv.h" + +#define MGA_NAME "mga" +#define MGA_DESC "Matrox G200/G400" +#define MGA_DATE "20000928" +#define MGA_MAJOR 2 +#define MGA_MINOR 0 +#define MGA_PATCHLEVEL 1 + +static drm_device_t mga_device; +drm_ctx_t mga_res_ctx; + +static struct file_operations mga_fops = { +#if LINUX_VERSION_CODE >= 0x020400 + /* This started being used during 2.4.0-test */ + owner: THIS_MODULE, +#endif + open: mga_open, + flush: drm_flush, + release: mga_release, + ioctl: mga_ioctl, + mmap: drm_mmap, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +static struct miscdevice mga_misc = { + minor: MISC_DYNAMIC_MINOR, + name: MGA_NAME, + fops: &mga_fops, +}; + +static drm_ioctl_desc_t mga_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { mga_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)] = { mga_control, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { mga_addbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { mga_markbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { mga_infobufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)] = { mga_mapbufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { mga_freebufs, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { mga_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { mga_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { mga_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { mga_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { mga_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { mga_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { mga_resctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { mga_dma, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { mga_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { mga_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = { drm_agp_enable, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = { drm_agp_info, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = { drm_agp_alloc, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = { drm_agp_free, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { drm_agp_bind, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { drm_agp_unbind, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MGA_INIT)] = { mga_dma_init, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MGA_SWAP)] = { mga_swap_bufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MGA_CLEAR)] = { mga_clear_bufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MGA_ILOAD)] = { mga_iload, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MGA_VERTEX)] = { mga_vertex, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MGA_FLUSH)] = { mga_flush_ioctl, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MGA_INDICES)] = { mga_indices, 1, 0 }, +}; + +#define MGA_IOCTL_COUNT DRM_ARRAY_SIZE(mga_ioctls) + +#ifdef MODULE +static char *mga = NULL; +#endif + +MODULE_AUTHOR("VA Linux Systems, Inc."); +MODULE_DESCRIPTION("Matrox G200/G400"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_PARM(mga, "s"); + +#ifndef MODULE +/* mga_options is called by the kernel to parse command-line options passed + * via the boot-loader (e.g., LILO). It calls the insmod option routine, + * drm_parse_drm. + */ + +static int __init mga_options(char *str) +{ + drm_parse_options(str); + return 1; +} + +__setup("mga=", mga_options); +#endif + +static int mga_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + drm_dma_setup(dev); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); + + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + DRM_DEBUG("\n"); + + /* The kernel's context could be created here, but is now created + in drm_dma_enqueue. This is more resource-efficient for + hardware that does not do DMA, but may mean that + drm_select_queue fails between the time the interrupt is + initialized and the time the queues are initialized. */ + + return 0; +} + + +static int mga_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + if (dev->dev_private) mga_dma_cleanup(dev); + if (dev->irq) mga_irq_uninstall(dev); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } + /* Clear AGP information */ + if (dev->agp) { + drm_agp_mem_t *entry; + drm_agp_mem_t *nexte; + + /* Remove AGP resources, but leave dev->agp + intact until cleanup is called. */ + for (entry = dev->agp->memory; entry; entry = nexte) { + nexte = entry->next; + if (entry->bound) drm_unbind_agp(entry->memory); + drm_free_agp(entry->memory, entry->pages); + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + } + dev->agp->memory = NULL; + + if (dev->agp->acquired) _drm_agp_release(); + + dev->agp->acquired = 0; + dev->agp->enabled = 0; + } + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area and mtrr information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifdef CONFIG_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + drm_ioremapfree(map->handle, map->size); + break; + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + case _DRM_AGP: + break; + } + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + if (dev->queuelist) { + for (i = 0; i < dev->queue_count; i++) { + drm_waitlist_destroy(&dev->queuelist[i]->waitlist); + if (dev->queuelist[i]) { + drm_free(dev->queuelist[i], + sizeof(*dev->queuelist[0]), + DRM_MEM_QUEUES); + dev->queuelist[i] = NULL; + } + } + drm_free(dev->queuelist, + dev->queue_slots * sizeof(*dev->queuelist), + DRM_MEM_QUEUES); + dev->queuelist = NULL; + } + + drm_dma_takedown(dev); + + dev->queue_count = 0; + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +/* mga_init is called via init_module at module load time, or via + * linux/init/main.c (this is not currently supported). */ + +static int __init mga_init(void) +{ + int retcode; + drm_device_t *dev = &mga_device; + + DRM_DEBUG("\n"); + + memset((void *)dev, 0, sizeof(*dev)); + dev->count_lock = SPIN_LOCK_UNLOCKED; + sema_init(&dev->struct_sem, 1); + +#ifdef MODULE + drm_parse_options(mga); +#endif + if ((retcode = misc_register(&mga_misc))) { + DRM_ERROR("Cannot register \"%s\"\n", MGA_NAME); + return retcode; + } + dev->device = MKDEV(MISC_MAJOR, mga_misc.minor); + dev->name = MGA_NAME; + + drm_mem_init(); + drm_proc_init(dev); + dev->agp = drm_agp_init(); + if(dev->agp == NULL) { + DRM_INFO("The mga drm module requires the agpgart module" + " to function correctly\nPlease load the agpgart" + " module before you load the mga module\n"); + drm_proc_cleanup(); + misc_deregister(&mga_misc); + mga_takedown(dev); + return -ENOMEM; + } +#ifdef CONFIG_MTRR + dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base, + dev->agp->agp_info.aper_size * 1024 * 1024, + MTRR_TYPE_WRCOMB, + 1); +#endif + if((retcode = drm_ctxbitmap_init(dev))) { + DRM_ERROR("Cannot allocate memory for context bitmap.\n"); + drm_proc_cleanup(); + misc_deregister(&mga_misc); + mga_takedown(dev); + return retcode; + } + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", + MGA_NAME, + MGA_MAJOR, + MGA_MINOR, + MGA_PATCHLEVEL, + MGA_DATE, + mga_misc.minor); + + return 0; +} + +/* mga_cleanup is called via cleanup_module at module unload time. */ + +static void __exit mga_cleanup(void) +{ + drm_device_t *dev = &mga_device; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + if (misc_deregister(&mga_misc)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + drm_ctxbitmap_cleanup(dev); +#ifdef CONFIG_MTRR + if(dev->agp && dev->agp->agp_mtrr) { + int retval; + retval = mtrr_del(dev->agp->agp_mtrr, + dev->agp->agp_info.aper_base, + dev->agp->agp_info.aper_size * 1024*1024); + DRM_DEBUG("mtrr_del = %d\n", retval); + } +#endif + + mga_takedown(dev); + if (dev->agp) { + drm_agp_uninit(); + drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS); + dev->agp = NULL; + } +} + +module_init(mga_init); +module_exit(mga_cleanup); + + +int mga_version(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_version_t version; + int len; + + if (copy_from_user(&version, + (drm_version_t *)arg, + sizeof(version))) + return -EFAULT; + +#define DRM_COPY(name,value) \ + len = strlen(value); \ + if (len > name##_len) len = name##_len; \ + name##_len = strlen(value); \ + if (len && name) { \ + if (copy_to_user(name, value, len)) \ + return -EFAULT; \ + } + + version.version_major = MGA_MAJOR; + version.version_minor = MGA_MINOR; + version.version_patchlevel = MGA_PATCHLEVEL; + + DRM_COPY(version.name, MGA_NAME); + DRM_COPY(version.date, MGA_DATE); + DRM_COPY(version.desc, MGA_DESC); + + if (copy_to_user((drm_version_t *)arg, + &version, + sizeof(version))) + return -EFAULT; + return 0; +} + +int mga_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev = &mga_device; + int retcode = 0; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_open_helper(inode, filp, dev))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return mga_setup(dev); + } + spin_unlock(&dev->count_lock); + } + return retcode; +} + +int mga_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + int retcode = 0; + + lock_kernel(); + dev = priv->dev; + DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", + current->pid, dev->device, dev->open_count); + + if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) + && dev->lock.pid == current->pid) { + mga_reclaim_buffers(dev, priv->pid); + DRM_INFO("Process %d dead (ctx %d, d_s = 0x%02lx)\n", + current->pid, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock), + dev->dev_private ? + ((drm_mga_private_t *)dev->dev_private) + ->dispatch_status + : 0); + + if (dev->dev_private) + ((drm_mga_private_t *)dev->dev_private) + ->dispatch_status &= MGA_IN_DISPATCH; + + drm_lock_free(dev, + &dev->lock.hw_lock->lock, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + } else if (dev->lock.hw_lock) { + /* The lock is required to reclaim buffers */ + DECLARE_WAITQUEUE(entry, current); + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + retcode = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + dev->lock.pid = priv->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + /* Contention */ + atomic_inc(&dev->total_sleeps); + schedule(); + if (signal_pending(current)) { + retcode = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + if(!retcode) { + mga_reclaim_buffers(dev, priv->pid); + if (dev->dev_private) + ((drm_mga_private_t *)dev->dev_private) + ->dispatch_status &= MGA_IN_DISPATCH; + drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT); + } + } + drm_fasync(-1, filp, 0); + + down(&dev->struct_sem); + if (priv->remove_auth_on_close == 1) { + drm_file_t *temp = dev->file_first; + while(temp) { + temp->authenticated = 0; + temp = temp->next; + } + } + if (priv->prev) priv->prev->next = priv->next; + else dev->file_first = priv->next; + if (priv->next) priv->next->prev = priv->prev; + else dev->file_last = priv->prev; + up(&dev->struct_sem); + + drm_free(priv, sizeof(*priv), DRM_MEM_FILES); +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + unlock_kernel(); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return mga_takedown(dev); + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return retcode; +} + + +/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */ + +int mga_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + if (nr >= MGA_IOCTL_COUNT) { + retcode = -EINVAL; + } else { + ioctl = &mga_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function: pid = %d, cmd = 0x%02x," + " nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, + priv->authenticated); + retcode = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + retcode = -EACCES; + } else { + retcode = (func)(inode, filp, cmd, arg); + } + } + + atomic_dec(&dev->ioctl_count); + return retcode; +} + +int mga_unlock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); + mga_dma_schedule(dev, 1); + + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) DRM_ERROR("\n"); + + unblock_all_signals(); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/mga_drv.h linux.ac/drivers/char/drm-4.0/mga_drv.h --- linux.vanilla/drivers/char/drm-4.0/mga_drv.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/mga_drv.h Wed Oct 10 01:47:41 2001 @@ -0,0 +1,520 @@ +/* mga_drv.h -- Private header for the Matrox g200/g400 driver -*- linux-c -*- + * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#ifndef _MGA_DRV_H_ +#define _MGA_DRV_H_ + +#define MGA_BUF_IN_USE 0 +#define MGA_BUF_SWAP_PENDING 1 +#define MGA_BUF_FORCE_FIRE 2 +#define MGA_BUF_NEEDS_OVERFLOW 3 + +typedef struct { + long buffer_status; /* long req'd for set_bit() --RR */ + int num_dwords; + int max_dwords; + u32 *current_dma_ptr; + u32 *head; + u32 phys_head; + unsigned int prim_age; + int sec_used; + int idx; +} drm_mga_prim_buf_t; + +typedef struct _drm_mga_freelist { + __volatile__ unsigned int age; + drm_buf_t *buf; + struct _drm_mga_freelist *next; + struct _drm_mga_freelist *prev; +} drm_mga_freelist_t; + +#define MGA_IN_DISPATCH 0 +#define MGA_IN_FLUSH 1 +#define MGA_IN_WAIT 2 +#define MGA_IN_GETBUF 3 + +typedef struct _drm_mga_private { + long dispatch_status; /* long req'd for set_bit() --RR */ + unsigned int next_prim_age; + __volatile__ unsigned int last_prim_age; + int reserved_map_idx; + int buffer_map_idx; + drm_mga_sarea_t *sarea_priv; + int primary_size; + int warp_ucode_size; + int chipset; + unsigned int frontOffset; + unsigned int backOffset; + unsigned int depthOffset; + unsigned int textureOffset; + unsigned int textureSize; + int cpp; + unsigned int stride; + int sgram; + int use_agp; + drm_mga_warp_index_t WarpIndex[MGA_MAX_G400_PIPES]; + unsigned int WarpPipe; + unsigned int vertexsize; + atomic_t pending_bufs; + void *status_page; + unsigned long real_status_page; + u8 *ioremap; + drm_mga_prim_buf_t **prim_bufs; + drm_mga_prim_buf_t *next_prim; + drm_mga_prim_buf_t *last_prim; + drm_mga_prim_buf_t *current_prim; + int current_prim_idx; + drm_mga_freelist_t *head; + drm_mga_freelist_t *tail; + wait_queue_head_t flush_queue; /* Processes waiting until flush */ + wait_queue_head_t wait_queue; /* Processes waiting until interrupt */ + wait_queue_head_t buf_queue; /* Processes waiting for a free buf */ + /* Some validated register values: + */ + u32 mAccess; +} drm_mga_private_t; + + /* mga_drv.c */ +extern int mga_version(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_open(struct inode *inode, struct file *filp); +extern int mga_release(struct inode *inode, struct file *filp); +extern int mga_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_unlock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* mga_dma.c */ +extern int mga_dma_schedule(drm_device_t *dev, int locked); +extern int mga_dma(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_irq_install(drm_device_t *dev, int irq); +extern int mga_irq_uninstall(drm_device_t *dev); +extern int mga_control(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_lock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +/* mga_dma_init does init and release */ +extern int mga_dma_init(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_dma_cleanup(drm_device_t *dev); +extern int mga_flush_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern unsigned int mga_create_sync_tag(drm_device_t *dev); +extern drm_buf_t *mga_freelist_get(drm_device_t *dev); +extern int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf); +extern int mga_advance_primary(drm_device_t *dev); +extern void mga_reclaim_buffers(drm_device_t *dev, pid_t pid); + + + /* mga_bufs.c */ +extern int mga_addbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_infobufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_markbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_freebufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_mapbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_addmap(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + /* mga_state.c */ +extern int mga_clear_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_swap_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_iload(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_vertex(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_indices(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + /* mga_context.c */ +extern int mga_resctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_addctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_modctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_getctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_switchctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_newctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_rmctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int mga_context_switch(drm_device_t *dev, int old, int new); +extern int mga_context_switch_complete(drm_device_t *dev, int new); + +#define mga_flush_write_combine() mb() + +typedef enum { + TT_GENERAL, + TT_BLIT, + TT_VECTOR, + TT_VERTEX +} transferType_t; + +typedef struct { + drm_mga_freelist_t *my_freelist; + int discard; + int dispatched; +} drm_mga_buf_priv_t; + +#define DWGREG0 0x1c00 +#define DWGREG0_END 0x1dff +#define DWGREG1 0x2c00 +#define DWGREG1_END 0x2dff + +#define ISREG0(r) (r >= DWGREG0 && r <= DWGREG0_END) +#define ADRINDEX0(r) (u8)((r - DWGREG0) >> 2) +#define ADRINDEX1(r) (u8)(((r - DWGREG1) >> 2) | 0x80) +#define ADRINDEX(r) (ISREG0(r) ? ADRINDEX0(r) : ADRINDEX1(r)) + +#define MGA_VERBOSE 0 +#define MGA_NUM_PRIM_BUFS 8 + +#define PRIMLOCALS u8 tempIndex[4]; u32 *dma_ptr; u32 phys_head; \ + int outcount, num_dwords + +#define PRIM_OVERFLOW(dev, dev_priv, length) do { \ + drm_mga_prim_buf_t *tmp_buf = \ + dev_priv->prim_bufs[dev_priv->current_prim_idx]; \ + if( test_bit(MGA_BUF_NEEDS_OVERFLOW, &tmp_buf->buffer_status)) { \ + mga_advance_primary(dev); \ + mga_dma_schedule(dev, 1); \ + tmp_buf = dev_priv->prim_bufs[dev_priv->current_prim_idx]; \ + } else if( tmp_buf->max_dwords - tmp_buf->num_dwords < length || \ + tmp_buf->sec_used > MGA_DMA_BUF_NR/2) { \ + set_bit(MGA_BUF_FORCE_FIRE, &tmp_buf->buffer_status); \ + mga_advance_primary(dev); \ + mga_dma_schedule(dev, 1); \ + tmp_buf = dev_priv->prim_bufs[dev_priv->current_prim_idx]; \ + } \ + if(MGA_VERBOSE) \ + DRM_DEBUG("PRIMGETPTR in %s\n", __FUNCTION__); \ + dma_ptr = tmp_buf->current_dma_ptr; \ + num_dwords = tmp_buf->num_dwords; \ + phys_head = tmp_buf->phys_head; \ + outcount = 0; \ +} while(0) + +#define PRIMGETPTR(dev_priv) do { \ + drm_mga_prim_buf_t *tmp_buf = \ + dev_priv->prim_bufs[dev_priv->current_prim_idx]; \ + if(MGA_VERBOSE) \ + DRM_DEBUG("PRIMGETPTR in %s\n", __FUNCTION__); \ + dma_ptr = tmp_buf->current_dma_ptr; \ + num_dwords = tmp_buf->num_dwords; \ + phys_head = tmp_buf->phys_head; \ + outcount = 0; \ +} while(0) + +#define PRIMPTR(prim_buf) do { \ + if(MGA_VERBOSE) \ + DRM_DEBUG("PRIMPTR in %s\n", __FUNCTION__); \ + dma_ptr = prim_buf->current_dma_ptr; \ + num_dwords = prim_buf->num_dwords; \ + phys_head = prim_buf->phys_head; \ + outcount = 0; \ +} while(0) + +#define PRIMFINISH(prim_buf) do { \ + if (MGA_VERBOSE) { \ + DRM_DEBUG( "PRIMFINISH in %s\n", __FUNCTION__); \ + if (outcount & 3) \ + DRM_DEBUG(" --- truncation\n"); \ + } \ + prim_buf->num_dwords = num_dwords; \ + prim_buf->current_dma_ptr = dma_ptr; \ +} while(0) + +#define PRIMADVANCE(dev_priv) do { \ +drm_mga_prim_buf_t *tmp_buf = \ + dev_priv->prim_bufs[dev_priv->current_prim_idx]; \ + if (MGA_VERBOSE) { \ + DRM_DEBUG("PRIMADVANCE in %s\n", __FUNCTION__); \ + if (outcount & 3) \ + DRM_DEBUG(" --- truncation\n"); \ + } \ + tmp_buf->num_dwords = num_dwords; \ + tmp_buf->current_dma_ptr = dma_ptr; \ +} while (0) + +#define PRIMUPDATE(dev_priv) do { \ + drm_mga_prim_buf_t *tmp_buf = \ + dev_priv->prim_bufs[dev_priv->current_prim_idx]; \ + tmp_buf->sec_used++; \ +} while (0) + +#define AGEBUF(dev_priv, buf_priv) do { \ + drm_mga_prim_buf_t *tmp_buf = \ + dev_priv->prim_bufs[dev_priv->current_prim_idx]; \ + buf_priv->my_freelist->age = tmp_buf->prim_age; \ +} while (0) + + +#define PRIMOUTREG(reg, val) do { \ + tempIndex[outcount]=ADRINDEX(reg); \ + dma_ptr[1+outcount] = val; \ + if (MGA_VERBOSE) \ + DRM_DEBUG(" PRIMOUT %d: 0x%x -- 0x%x\n", \ + num_dwords + 1 + outcount, ADRINDEX(reg), val); \ + if( ++outcount == 4) { \ + outcount = 0; \ + dma_ptr[0] = *(unsigned long *)tempIndex; \ + dma_ptr+=5; \ + num_dwords += 5; \ + } \ +}while (0) + +/* A reduced set of the mga registers. + */ + +#define MGAREG_MGA_EXEC 0x0100 +#define MGAREG_ALPHACTRL 0x2c7c +#define MGAREG_AR0 0x1c60 +#define MGAREG_AR1 0x1c64 +#define MGAREG_AR2 0x1c68 +#define MGAREG_AR3 0x1c6c +#define MGAREG_AR4 0x1c70 +#define MGAREG_AR5 0x1c74 +#define MGAREG_AR6 0x1c78 +#define MGAREG_CXBNDRY 0x1c80 +#define MGAREG_CXLEFT 0x1ca0 +#define MGAREG_CXRIGHT 0x1ca4 +#define MGAREG_DMAPAD 0x1c54 +#define MGAREG_DSTORG 0x2cb8 +#define MGAREG_DWGCTL 0x1c00 +#define MGAREG_DWGSYNC 0x2c4c +#define MGAREG_FCOL 0x1c24 +#define MGAREG_FIFOSTATUS 0x1e10 +#define MGAREG_FOGCOL 0x1cf4 +#define MGAREG_FXBNDRY 0x1c84 +#define MGAREG_FXLEFT 0x1ca8 +#define MGAREG_FXRIGHT 0x1cac +#define MGAREG_ICLEAR 0x1e18 +#define MGAREG_IEN 0x1e1c +#define MGAREG_LEN 0x1c5c +#define MGAREG_MACCESS 0x1c04 +#define MGAREG_PITCH 0x1c8c +#define MGAREG_PLNWT 0x1c1c +#define MGAREG_PRIMADDRESS 0x1e58 +#define MGAREG_PRIMEND 0x1e5c +#define MGAREG_PRIMPTR 0x1e50 +#define MGAREG_SECADDRESS 0x2c40 +#define MGAREG_SECEND 0x2c44 +#define MGAREG_SETUPADDRESS 0x2cd0 +#define MGAREG_SETUPEND 0x2cd4 +#define MGAREG_SOFTRAP 0x2c48 +#define MGAREG_SRCORG 0x2cb4 +#define MGAREG_STATUS 0x1e14 +#define MGAREG_STENCIL 0x2cc8 +#define MGAREG_STENCILCTL 0x2ccc +#define MGAREG_TDUALSTAGE0 0x2cf8 +#define MGAREG_TDUALSTAGE1 0x2cfc +#define MGAREG_TEXBORDERCOL 0x2c5c +#define MGAREG_TEXCTL 0x2c30 +#define MGAREG_TEXCTL2 0x2c3c +#define MGAREG_TEXFILTER 0x2c58 +#define MGAREG_TEXHEIGHT 0x2c2c +#define MGAREG_TEXORG 0x2c24 +#define MGAREG_TEXORG1 0x2ca4 +#define MGAREG_TEXORG2 0x2ca8 +#define MGAREG_TEXORG3 0x2cac +#define MGAREG_TEXORG4 0x2cb0 +#define MGAREG_TEXTRANS 0x2c34 +#define MGAREG_TEXTRANSHIGH 0x2c38 +#define MGAREG_TEXWIDTH 0x2c28 +#define MGAREG_WACCEPTSEQ 0x1dd4 +#define MGAREG_WCODEADDR 0x1e6c +#define MGAREG_WFLAG 0x1dc4 +#define MGAREG_WFLAG1 0x1de0 +#define MGAREG_WFLAGNB 0x1e64 +#define MGAREG_WFLAGNB1 0x1e08 +#define MGAREG_WGETMSB 0x1dc8 +#define MGAREG_WIADDR 0x1dc0 +#define MGAREG_WIADDR2 0x1dd8 +#define MGAREG_WMISC 0x1e70 +#define MGAREG_WVRTXSZ 0x1dcc +#define MGAREG_YBOT 0x1c9c +#define MGAREG_YDST 0x1c90 +#define MGAREG_YDSTLEN 0x1c88 +#define MGAREG_YDSTORG 0x1c94 +#define MGAREG_YTOP 0x1c98 +#define MGAREG_ZORG 0x1c0c + +/* Warp registers */ +#define MGAREG_WR0 0x2d00 +#define MGAREG_WR1 0x2d04 +#define MGAREG_WR2 0x2d08 +#define MGAREG_WR3 0x2d0c +#define MGAREG_WR4 0x2d10 +#define MGAREG_WR5 0x2d14 +#define MGAREG_WR6 0x2d18 +#define MGAREG_WR7 0x2d1c +#define MGAREG_WR8 0x2d20 +#define MGAREG_WR9 0x2d24 +#define MGAREG_WR10 0x2d28 +#define MGAREG_WR11 0x2d2c +#define MGAREG_WR12 0x2d30 +#define MGAREG_WR13 0x2d34 +#define MGAREG_WR14 0x2d38 +#define MGAREG_WR15 0x2d3c +#define MGAREG_WR16 0x2d40 +#define MGAREG_WR17 0x2d44 +#define MGAREG_WR18 0x2d48 +#define MGAREG_WR19 0x2d4c +#define MGAREG_WR20 0x2d50 +#define MGAREG_WR21 0x2d54 +#define MGAREG_WR22 0x2d58 +#define MGAREG_WR23 0x2d5c +#define MGAREG_WR24 0x2d60 +#define MGAREG_WR25 0x2d64 +#define MGAREG_WR26 0x2d68 +#define MGAREG_WR27 0x2d6c +#define MGAREG_WR28 0x2d70 +#define MGAREG_WR29 0x2d74 +#define MGAREG_WR30 0x2d78 +#define MGAREG_WR31 0x2d7c +#define MGAREG_WR32 0x2d80 +#define MGAREG_WR33 0x2d84 +#define MGAREG_WR34 0x2d88 +#define MGAREG_WR35 0x2d8c +#define MGAREG_WR36 0x2d90 +#define MGAREG_WR37 0x2d94 +#define MGAREG_WR38 0x2d98 +#define MGAREG_WR39 0x2d9c +#define MGAREG_WR40 0x2da0 +#define MGAREG_WR41 0x2da4 +#define MGAREG_WR42 0x2da8 +#define MGAREG_WR43 0x2dac +#define MGAREG_WR44 0x2db0 +#define MGAREG_WR45 0x2db4 +#define MGAREG_WR46 0x2db8 +#define MGAREG_WR47 0x2dbc +#define MGAREG_WR48 0x2dc0 +#define MGAREG_WR49 0x2dc4 +#define MGAREG_WR50 0x2dc8 +#define MGAREG_WR51 0x2dcc +#define MGAREG_WR52 0x2dd0 +#define MGAREG_WR53 0x2dd4 +#define MGAREG_WR54 0x2dd8 +#define MGAREG_WR55 0x2ddc +#define MGAREG_WR56 0x2de0 +#define MGAREG_WR57 0x2de4 +#define MGAREG_WR58 0x2de8 +#define MGAREG_WR59 0x2dec +#define MGAREG_WR60 0x2df0 +#define MGAREG_WR61 0x2df4 +#define MGAREG_WR62 0x2df8 +#define MGAREG_WR63 0x2dfc + +#define PDEA_pagpxfer_enable 0x2 + +#define WIA_wmode_suspend 0x0 +#define WIA_wmode_start 0x3 +#define WIA_wagp_agp 0x4 + +#define DC_opcod_line_open 0x0 +#define DC_opcod_autoline_open 0x1 +#define DC_opcod_line_close 0x2 +#define DC_opcod_autoline_close 0x3 +#define DC_opcod_trap 0x4 +#define DC_opcod_texture_trap 0x6 +#define DC_opcod_bitblt 0x8 +#define DC_opcod_iload 0x9 +#define DC_atype_rpl 0x0 +#define DC_atype_rstr 0x10 +#define DC_atype_zi 0x30 +#define DC_atype_blk 0x40 +#define DC_atype_i 0x70 +#define DC_linear_xy 0x0 +#define DC_linear_linear 0x80 +#define DC_zmode_nozcmp 0x0 +#define DC_zmode_ze 0x200 +#define DC_zmode_zne 0x300 +#define DC_zmode_zlt 0x400 +#define DC_zmode_zlte 0x500 +#define DC_zmode_zgt 0x600 +#define DC_zmode_zgte 0x700 +#define DC_solid_disable 0x0 +#define DC_solid_enable 0x800 +#define DC_arzero_disable 0x0 +#define DC_arzero_enable 0x1000 +#define DC_sgnzero_disable 0x0 +#define DC_sgnzero_enable 0x2000 +#define DC_shftzero_disable 0x0 +#define DC_shftzero_enable 0x4000 +#define DC_bop_SHIFT 16 +#define DC_trans_SHIFT 20 +#define DC_bltmod_bmonolef 0x0 +#define DC_bltmod_bmonowf 0x8000000 +#define DC_bltmod_bplan 0x2000000 +#define DC_bltmod_bfcol 0x4000000 +#define DC_bltmod_bu32bgr 0x6000000 +#define DC_bltmod_bu32rgb 0xe000000 +#define DC_bltmod_bu24bgr 0x16000000 +#define DC_bltmod_bu24rgb 0x1e000000 +#define DC_pattern_disable 0x0 +#define DC_pattern_enable 0x20000000 +#define DC_transc_disable 0x0 +#define DC_transc_enable 0x40000000 +#define DC_clipdis_disable 0x0 +#define DC_clipdis_enable 0x80000000 + + +#define SETADD_mode_vertlist 0x0 + + +#define MGA_CLEAR_CMD (DC_opcod_trap | DC_arzero_enable | \ + DC_sgnzero_enable | DC_shftzero_enable | \ + (0xC << DC_bop_SHIFT) | DC_clipdis_enable | \ + DC_solid_enable | DC_transc_enable) + + +#define MGA_COPY_CMD (DC_opcod_bitblt | DC_atype_rpl | DC_linear_xy | \ + DC_solid_disable | DC_arzero_disable | \ + DC_sgnzero_enable | DC_shftzero_enable | \ + (0xC << DC_bop_SHIFT) | DC_bltmod_bfcol | \ + DC_pattern_disable | DC_transc_disable | \ + DC_clipdis_enable) \ + +#define MGA_FLUSH_CMD (DC_opcod_texture_trap | (0xF << DC_trans_SHIFT) |\ + DC_arzero_enable | DC_sgnzero_enable | \ + DC_atype_i) + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/mga_state.c linux.ac/drivers/char/drm-4.0/mga_state.c --- linux.vanilla/drivers/char/drm-4.0/mga_state.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/mga_state.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,1017 @@ +/* mga_state.c -- State support for mga g200/g400 -*- linux-c -*- + * Created: Thu Jan 27 02:53:43 2000 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Jeff Hartmann + * Keith Whitwell + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "mga_drv.h" +#include "drm.h" + +/* If you change the functions to set state, PLEASE + * change these values + */ + +#define MGAEMITCLIP_SIZE 10 +#define MGAEMITCTX_SIZE 20 +#define MGAG200EMITTEX_SIZE 20 +#define MGAG400EMITTEX0_SIZE 30 +#define MGAG400EMITTEX1_SIZE 25 +#define MGAG400EMITPIPE_SIZE 50 +#define MGAG200EMITPIPE_SIZE 15 + +#define MAX_STATE_SIZE ((MGAEMITCLIP_SIZE * MGA_NR_SAREA_CLIPRECTS) + \ + MGAEMITCTX_SIZE + MGAG400EMITTEX0_SIZE + \ + MGAG400EMITTEX1_SIZE + MGAG400EMITPIPE_SIZE) + +static void mgaEmitClipRect(drm_mga_private_t * dev_priv, + drm_clip_rect_t * box) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->ContextState; + PRIMLOCALS; + + /* This takes 10 dwords */ + PRIMGETPTR(dev_priv); + + /* Force reset of dwgctl on G400 (eliminates clip disable bit) */ + if (dev_priv->chipset == MGA_CARD_TYPE_G400) { +#if 0 + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DWGSYNC, 0); + PRIMOUTREG(MGAREG_DWGSYNC, 0); + PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]); +#else + PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]); + PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0x80000000); + PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]); + PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0x80000000); +#endif + } + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_CXBNDRY, ((box->x2) << 16) | (box->x1)); + PRIMOUTREG(MGAREG_YTOP, box->y1 * dev_priv->stride / dev_priv->cpp); + PRIMOUTREG(MGAREG_YBOT, box->y2 * dev_priv->stride / dev_priv->cpp); + + PRIMADVANCE(dev_priv); +} + +static void mgaEmitContext(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->ContextState; + PRIMLOCALS; + + /* This takes a max of 20 dwords */ + PRIMGETPTR(dev_priv); + + PRIMOUTREG(MGAREG_DSTORG, regs[MGA_CTXREG_DSTORG]); + PRIMOUTREG(MGAREG_MACCESS, regs[MGA_CTXREG_MACCESS]); + PRIMOUTREG(MGAREG_PLNWT, regs[MGA_CTXREG_PLNWT]); + PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]); + + PRIMOUTREG(MGAREG_ALPHACTRL, regs[MGA_CTXREG_ALPHACTRL]); + PRIMOUTREG(MGAREG_FOGCOL, regs[MGA_CTXREG_FOGCOLOR]); + PRIMOUTREG(MGAREG_WFLAG, regs[MGA_CTXREG_WFLAG]); + PRIMOUTREG(MGAREG_ZORG, dev_priv->depthOffset); /* invarient */ + + if (dev_priv->chipset == MGA_CARD_TYPE_G400) { + PRIMOUTREG(MGAREG_WFLAG1, regs[MGA_CTXREG_WFLAG]); + PRIMOUTREG(MGAREG_TDUALSTAGE0, regs[MGA_CTXREG_TDUAL0]); + PRIMOUTREG(MGAREG_TDUALSTAGE1, regs[MGA_CTXREG_TDUAL1]); + PRIMOUTREG(MGAREG_FCOL, regs[MGA_CTXREG_FCOL]); + + PRIMOUTREG(MGAREG_STENCIL, regs[MGA_CTXREG_STENCIL]); + PRIMOUTREG(MGAREG_STENCILCTL, regs[MGA_CTXREG_STENCILCTL]); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + } else { + PRIMOUTREG(MGAREG_FCOL, regs[MGA_CTXREG_FCOL]); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + } + + PRIMADVANCE(dev_priv); +} + +static void mgaG200EmitTex(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->TexState[0]; + PRIMLOCALS; + + PRIMGETPTR(dev_priv); + + /* This takes 20 dwords */ + + PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2]); + PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]); + PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]); + PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]); + + PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]); + PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]); + PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]); + PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]); + + PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]); + PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]); + PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]); + PRIMOUTREG(MGAREG_WR24, regs[MGA_TEXREG_WIDTH]); + + PRIMOUTREG(MGAREG_WR34, regs[MGA_TEXREG_HEIGHT]); + PRIMOUTREG(MGAREG_TEXTRANS, 0xffff); + PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff); + PRIMOUTREG(MGAREG_DMAPAD, 0); + + PRIMADVANCE(dev_priv); +} + +#define TMC_dualtex_enable 0x80 + +static void mgaG400EmitTex0(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->TexState[0]; + PRIMLOCALS; + + PRIMGETPTR(dev_priv); + + /* This takes 30 dwords */ + + PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2] | 0x00008000); + PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]); + PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]); + PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]); + + PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]); + PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]); + PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]); + PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]); + + PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]); + PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]); + PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]); + PRIMOUTREG(MGAREG_WR49, 0); + + PRIMOUTREG(MGAREG_WR57, 0); + PRIMOUTREG(MGAREG_WR53, 0); + PRIMOUTREG(MGAREG_WR61, 0); + PRIMOUTREG(MGAREG_WR52, 0x40); + + PRIMOUTREG(MGAREG_WR60, 0x40); + PRIMOUTREG(MGAREG_WR54, regs[MGA_TEXREG_WIDTH] | 0x40); + PRIMOUTREG(MGAREG_WR62, regs[MGA_TEXREG_HEIGHT] | 0x40); + PRIMOUTREG(MGAREG_DMAPAD, 0); + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_TEXTRANS, 0xffff); + PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff); + + PRIMADVANCE(dev_priv); +} + +#define TMC_map1_enable 0x80000000 + +static void mgaG400EmitTex1(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->TexState[1]; + PRIMLOCALS; + + PRIMGETPTR(dev_priv); + + /* This takes 25 dwords */ + + PRIMOUTREG(MGAREG_TEXCTL2, + regs[MGA_TEXREG_CTL2] | TMC_map1_enable | 0x00008000); + PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]); + PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]); + PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]); + + PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]); + PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]); + PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]); + PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]); + + PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]); + PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]); + PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]); + PRIMOUTREG(MGAREG_WR49, 0); + + PRIMOUTREG(MGAREG_WR57, 0); + PRIMOUTREG(MGAREG_WR53, 0); + PRIMOUTREG(MGAREG_WR61, 0); + PRIMOUTREG(MGAREG_WR52, regs[MGA_TEXREG_WIDTH] | 0x40); + + PRIMOUTREG(MGAREG_WR60, regs[MGA_TEXREG_HEIGHT] | 0x40); + PRIMOUTREG(MGAREG_TEXTRANS, 0xffff); + PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff); + PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2] | 0x00008000); + + PRIMADVANCE(dev_priv); +} + +#define MAGIC_FPARAM_HEX_VALUE 0x46480000 +/* This is the hex value of 12800.0f which is a magic value we must + * set in wr56. + */ + +static void mgaG400EmitPipe(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int pipe = sarea_priv->WarpPipe; + PRIMLOCALS; + + PRIMGETPTR(dev_priv); + + /* This takes 50 dwords */ + + /* Establish vertex size. + */ + PRIMOUTREG(MGAREG_WIADDR2, WIA_wmode_suspend); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + + if (pipe & MGA_T2) { + PRIMOUTREG(MGAREG_WVRTXSZ, 0x00001e09); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0); + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0); + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0); + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0x1e000000); + } else { + if (dev_priv->WarpPipe & MGA_T2) { + /* Flush the WARP pipe */ + PRIMOUTREG(MGAREG_YDST, 0); + PRIMOUTREG(MGAREG_FXLEFT, 0); + PRIMOUTREG(MGAREG_FXRIGHT, 1); + PRIMOUTREG(MGAREG_DWGCTL, MGA_FLUSH_CMD); + + PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 1); + PRIMOUTREG(MGAREG_DWGSYNC, 0x7000); + PRIMOUTREG(MGAREG_TEXCTL2, 0x00008000); + PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0); + + PRIMOUTREG(MGAREG_TEXCTL2, 0x80 | 0x00008000); + PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0); + PRIMOUTREG(MGAREG_TEXCTL2, 0x00008000); + PRIMOUTREG(MGAREG_DMAPAD, 0); + } + + PRIMOUTREG(MGAREG_WVRTXSZ, 0x00001807); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0); + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0); + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0); + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0x18000000); + } + + PRIMOUTREG(MGAREG_WFLAG, 0); + PRIMOUTREG(MGAREG_WFLAG1, 0); + PRIMOUTREG(MGAREG_WR56, MAGIC_FPARAM_HEX_VALUE); + PRIMOUTREG(MGAREG_DMAPAD, 0); + + PRIMOUTREG(MGAREG_WR49, 0); /* Tex stage 0 */ + PRIMOUTREG(MGAREG_WR57, 0); /* Tex stage 0 */ + PRIMOUTREG(MGAREG_WR53, 0); /* Tex stage 1 */ + PRIMOUTREG(MGAREG_WR61, 0); /* Tex stage 1 */ + + PRIMOUTREG(MGAREG_WR54, 0x40); /* Tex stage 0 : w */ + PRIMOUTREG(MGAREG_WR62, 0x40); /* Tex stage 0 : h */ + PRIMOUTREG(MGAREG_WR52, 0x40); /* Tex stage 1 : w */ + PRIMOUTREG(MGAREG_WR60, 0x40); /* Tex stage 1 : h */ + + /* Dma pading required due to hw bug */ + PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff); + PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff); + PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff); + PRIMOUTREG(MGAREG_WIADDR2, + (u32) (dev_priv->WarpIndex[pipe]. + phys_addr | WIA_wmode_start | WIA_wagp_agp)); + PRIMADVANCE(dev_priv); +} + +static void mgaG200EmitPipe(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int pipe = sarea_priv->WarpPipe; + PRIMLOCALS; + + PRIMGETPTR(dev_priv); + + /* This takes 15 dwords */ + + PRIMOUTREG(MGAREG_WIADDR, WIA_wmode_suspend); + PRIMOUTREG(MGAREG_WVRTXSZ, 7); + PRIMOUTREG(MGAREG_WFLAG, 0); + PRIMOUTREG(MGAREG_WR24, 0); /* tex w/h */ + + PRIMOUTREG(MGAREG_WR25, 0x100); + PRIMOUTREG(MGAREG_WR34, 0); /* tex w/h */ + PRIMOUTREG(MGAREG_WR42, 0xFFFF); + PRIMOUTREG(MGAREG_WR60, 0xFFFF); + + /* Dma pading required due to hw bug */ + PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff); + PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff); + PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff); + PRIMOUTREG(MGAREG_WIADDR, + (u32) (dev_priv->WarpIndex[pipe]. + phys_addr | WIA_wmode_start | WIA_wagp_agp)); + + PRIMADVANCE( dev_priv ); +} + +static void mgaEmitState(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int dirty = sarea_priv->dirty; + + if (dev_priv->chipset == MGA_CARD_TYPE_G400) { + int multitex = sarea_priv->WarpPipe & MGA_T2; + + if (sarea_priv->WarpPipe != dev_priv->WarpPipe) { + mgaG400EmitPipe(dev_priv); + dev_priv->WarpPipe = sarea_priv->WarpPipe; + } + + if (dirty & MGA_UPLOAD_CTX) { + mgaEmitContext(dev_priv); + sarea_priv->dirty &= ~MGA_UPLOAD_CTX; + } + + if (dirty & MGA_UPLOAD_TEX0) { + mgaG400EmitTex0(dev_priv); + sarea_priv->dirty &= ~MGA_UPLOAD_TEX0; + } + + if ((dirty & MGA_UPLOAD_TEX1) && multitex) { + mgaG400EmitTex1(dev_priv); + sarea_priv->dirty &= ~MGA_UPLOAD_TEX1; + } + } else { + if (sarea_priv->WarpPipe != dev_priv->WarpPipe) { + mgaG200EmitPipe(dev_priv); + dev_priv->WarpPipe = sarea_priv->WarpPipe; + } + + if (dirty & MGA_UPLOAD_CTX) { + mgaEmitContext(dev_priv); + sarea_priv->dirty &= ~MGA_UPLOAD_CTX; + } + + if (dirty & MGA_UPLOAD_TEX0) { + mgaG200EmitTex(dev_priv); + sarea_priv->dirty &= ~MGA_UPLOAD_TEX0; + } + } +} + +/* Disallow all write destinations except the front and backbuffer. + */ +static int mgaVerifyContext(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->ContextState; + + if (regs[MGA_CTXREG_DSTORG] != dev_priv->frontOffset && + regs[MGA_CTXREG_DSTORG] != dev_priv->backOffset) { + DRM_DEBUG("BAD DSTORG: %x (front %x, back %x)\n\n", + regs[MGA_CTXREG_DSTORG], dev_priv->frontOffset, + dev_priv->backOffset); + regs[MGA_CTXREG_DSTORG] = 0; + return -1; + } + + return 0; +} + +/* Disallow texture reads from PCI space. + */ +static int mgaVerifyTex(drm_mga_private_t * dev_priv, int unit) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + + if ((sarea_priv->TexState[unit][MGA_TEXREG_ORG] & 0x3) == 0x1) { + DRM_DEBUG("BAD TEXREG_ORG: %x, unit %d\n", + sarea_priv->TexState[unit][MGA_TEXREG_ORG], + unit); + sarea_priv->TexState[unit][MGA_TEXREG_ORG] = 0; + return -1; + } + + return 0; +} + +static int mgaVerifyState(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int dirty = sarea_priv->dirty; + int rv = 0; + + if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS) + sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS; + + if (dirty & MGA_UPLOAD_CTX) + rv |= mgaVerifyContext(dev_priv); + + if (dirty & MGA_UPLOAD_TEX0) + rv |= mgaVerifyTex(dev_priv, 0); + + if (dev_priv->chipset == MGA_CARD_TYPE_G400) { + if (dirty & MGA_UPLOAD_TEX1) + rv |= mgaVerifyTex(dev_priv, 1); + + if (dirty & MGA_UPLOAD_PIPE) + rv |= (sarea_priv->WarpPipe > MGA_MAX_G400_PIPES); + } else { + if (dirty & MGA_UPLOAD_PIPE) + rv |= (sarea_priv->WarpPipe > MGA_MAX_G200_PIPES); + } + + return rv == 0; +} + +static int mgaVerifyIload(drm_mga_private_t * dev_priv, + unsigned long bus_address, + unsigned int dstOrg, int length) +{ + if (dstOrg < dev_priv->textureOffset || + dstOrg + length > + (dev_priv->textureOffset + dev_priv->textureSize)) { + return -EINVAL; + } + if (length % 64) { + return -EINVAL; + } + return 0; +} + +/* This copies a 64 byte aligned agp region to the frambuffer + * with a standard blit, the ioctl needs to do checking */ + +static void mga_dma_dispatch_tex_blit(drm_device_t * dev, + unsigned long bus_address, + int length, unsigned int destOrg) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + int use_agp = PDEA_pagpxfer_enable | 0x00000001; + u16 y2; + PRIMLOCALS; + + y2 = length / 64; + + PRIM_OVERFLOW(dev, dev_priv, 30); + + PRIMOUTREG(MGAREG_DSTORG, destOrg); + PRIMOUTREG(MGAREG_MACCESS, 0x00000000); + PRIMOUTREG(MGAREG_SRCORG, (u32) bus_address | use_agp); + PRIMOUTREG(MGAREG_AR5, 64); + + PRIMOUTREG(MGAREG_PITCH, 64); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DWGCTL, MGA_COPY_CMD); + + PRIMOUTREG(MGAREG_AR0, 63); + PRIMOUTREG(MGAREG_AR3, 0); + PRIMOUTREG(MGAREG_FXBNDRY, (63 << 16)); + PRIMOUTREG(MGAREG_YDSTLEN + MGAREG_MGA_EXEC, y2); + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_SRCORG, 0); + PRIMOUTREG(MGAREG_PITCH, dev_priv->stride / dev_priv->cpp); + PRIMOUTREG(MGAREG_DWGSYNC, 0x7000); + PRIMADVANCE(dev_priv); +} + +static void mga_dma_dispatch_vertex(drm_device_t * dev, drm_buf_t * buf) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + drm_mga_buf_priv_t *buf_priv = buf->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned long address = (unsigned long) buf->bus_address; + int length = buf->used; + int use_agp = PDEA_pagpxfer_enable; + int i = 0; + PRIMLOCALS; + + if (buf->used) { + /* WARNING: if you change any of the state functions verify + * these numbers (Overestimating this doesn't hurt). + */ + buf_priv->dispatched = 1; + PRIM_OVERFLOW(dev, dev_priv, + (MAX_STATE_SIZE + (5 * MGA_NR_SAREA_CLIPRECTS))); + mgaEmitState(dev_priv); + +#if 0 + length = dev_priv->vertexsize * 3 * 4; +#endif + + do { + if (i < sarea_priv->nbox) { + mgaEmitClipRect(dev_priv, + &sarea_priv->boxes[i]); + } + + PRIMGETPTR(dev_priv); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_SECADDRESS, + ((u32) address) | TT_VERTEX); + PRIMOUTREG(MGAREG_SECEND, + (((u32) (address + length)) | use_agp)); + PRIMADVANCE(dev_priv); + } while (++i < sarea_priv->nbox); + } + if (buf_priv->discard) { + if (buf_priv->dispatched == 1) + AGEBUF(dev_priv, buf_priv); + buf_priv->dispatched = 0; + mga_freelist_put(dev, buf); + } + + +} + + +static void mga_dma_dispatch_indices(drm_device_t * dev, + drm_buf_t * buf, + unsigned int start, unsigned int end) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + drm_mga_buf_priv_t *buf_priv = buf->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int address = (unsigned int) buf->bus_address; + int use_agp = PDEA_pagpxfer_enable; + int i = 0; + PRIMLOCALS; + + if (start != end) { + /* WARNING: if you change any of the state functions verify + * these numbers (Overestimating this doesn't hurt). + */ + buf_priv->dispatched = 1; + PRIM_OVERFLOW(dev, dev_priv, + (MAX_STATE_SIZE + (5 * MGA_NR_SAREA_CLIPRECTS))); + mgaEmitState(dev_priv); + + do { + if (i < sarea_priv->nbox) { + mgaEmitClipRect(dev_priv, + &sarea_priv->boxes[i]); + } + + PRIMGETPTR(dev_priv); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_SETUPADDRESS, + ((address + start) | + SETADD_mode_vertlist)); + PRIMOUTREG(MGAREG_SETUPEND, + ((address + end) | use_agp)); +/* ((address + start + 12) | use_agp)); */ + PRIMADVANCE(dev_priv); + } while (++i < sarea_priv->nbox); + } + if (buf_priv->discard) { + if (buf_priv->dispatched == 1) + AGEBUF(dev_priv, buf_priv); + buf_priv->dispatched = 0; + mga_freelist_put(dev, buf); + } +} + + +static void mga_dma_dispatch_clear(drm_device_t * dev, int flags, + unsigned int clear_color, + unsigned int clear_zval, + unsigned int clear_colormask, + unsigned int clear_depthmask) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->ContextState; + int nbox = sarea_priv->nbox; + drm_clip_rect_t *pbox = sarea_priv->boxes; + unsigned int cmd; + int i; + PRIMLOCALS; + + if (dev_priv->sgram) + cmd = MGA_CLEAR_CMD | DC_atype_blk; + else + cmd = MGA_CLEAR_CMD | DC_atype_rstr; + + PRIM_OVERFLOW(dev, dev_priv, 35 * MGA_NR_SAREA_CLIPRECTS); + + for (i = 0; i < nbox; i++) { + unsigned int height = pbox[i].y2 - pbox[i].y1; + + if (flags & MGA_FRONT) { + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_PLNWT, clear_colormask); + PRIMOUTREG(MGAREG_YDSTLEN, + (pbox[i].y1 << 16) | height); + PRIMOUTREG(MGAREG_FXBNDRY, + (pbox[i].x2 << 16) | pbox[i].x1); + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_FCOL, clear_color); + PRIMOUTREG(MGAREG_DSTORG, dev_priv->frontOffset); + PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd); + } + + if (flags & MGA_BACK) { + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_PLNWT, clear_colormask); + PRIMOUTREG(MGAREG_YDSTLEN, + (pbox[i].y1 << 16) | height); + PRIMOUTREG(MGAREG_FXBNDRY, + (pbox[i].x2 << 16) | pbox[i].x1); + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_FCOL, clear_color); + PRIMOUTREG(MGAREG_DSTORG, dev_priv->backOffset); + PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd); + } + + if (flags & MGA_DEPTH) { + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_PLNWT, clear_depthmask); + PRIMOUTREG(MGAREG_YDSTLEN, + (pbox[i].y1 << 16) | height); + PRIMOUTREG(MGAREG_FXBNDRY, + (pbox[i].x2 << 16) | pbox[i].x1); + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_FCOL, clear_zval); + PRIMOUTREG(MGAREG_DSTORG, dev_priv->depthOffset); + PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd); + } + } + + /* Force reset of DWGCTL */ + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]); + PRIMADVANCE(dev_priv); +} + +static void mga_dma_dispatch_swap(drm_device_t * dev) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->ContextState; + int nbox = sarea_priv->nbox; + drm_clip_rect_t *pbox = sarea_priv->boxes; + int i; + int pixel_stride = dev_priv->stride / dev_priv->cpp; + + PRIMLOCALS; + + PRIM_OVERFLOW(dev, dev_priv, (MGA_NR_SAREA_CLIPRECTS * 5) + 20); + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DWGSYNC, 0x7100); + PRIMOUTREG(MGAREG_DWGSYNC, 0x7000); + + PRIMOUTREG(MGAREG_DSTORG, dev_priv->frontOffset); + PRIMOUTREG(MGAREG_MACCESS, dev_priv->mAccess); + PRIMOUTREG(MGAREG_SRCORG, dev_priv->backOffset); + PRIMOUTREG(MGAREG_AR5, pixel_stride); + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DWGCTL, MGA_COPY_CMD); + + for (i = 0; i < nbox; i++) { + unsigned int h = pbox[i].y2 - pbox[i].y1; + unsigned int start = pbox[i].y1 * pixel_stride; + + PRIMOUTREG(MGAREG_AR0, start + pbox[i].x2 - 1); + PRIMOUTREG(MGAREG_AR3, start + pbox[i].x1); + PRIMOUTREG(MGAREG_FXBNDRY, + pbox[i].x1 | ((pbox[i].x2 - 1) << 16)); + PRIMOUTREG(MGAREG_YDSTLEN + MGAREG_MGA_EXEC, + (pbox[i].y1 << 16) | h); + } + + /* Force reset of DWGCTL */ + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_SRCORG, 0); + PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]); + + PRIMADVANCE(dev_priv); +} + +int mga_clear_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_mga_clear_t clear; + + if (copy_from_user(&clear, (drm_mga_clear_t *) arg, sizeof(clear))) + return -EFAULT; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("mga_clear_bufs called without lock held\n"); + return -EINVAL; + } + + if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS) + sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS; + + /* Make sure we restore the 3D state next time. + */ + dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CTX; + mga_dma_dispatch_clear(dev, clear.flags, + clear.clear_color, + clear.clear_depth, + clear.clear_color_mask, + clear.clear_depth_mask); + PRIMUPDATE(dev_priv); + mga_flush_write_combine(); + mga_dma_schedule(dev, 1); + return 0; +} + +int mga_swap_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("mga_swap_bufs called without lock held\n"); + return -EINVAL; + } + + if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS) + sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS; + + /* Make sure we restore the 3D state next time. + */ + dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CTX; + mga_dma_dispatch_swap(dev); + PRIMUPDATE(dev_priv); + set_bit(MGA_BUF_SWAP_PENDING, + &dev_priv->current_prim->buffer_status); + mga_flush_write_combine(); + mga_dma_schedule(dev, 1); + return 0; +} + +int mga_iload(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_buf_t *buf; + drm_mga_buf_priv_t *buf_priv; + drm_mga_iload_t iload; + unsigned long bus_address; + + if (copy_from_user(&iload, (drm_mga_iload_t *) arg, sizeof(iload))) + return -EFAULT; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("mga_iload called without lock held\n"); + return -EINVAL; + } + + if(iload.idx < 0 || iload.idx > dma->buf_count) return -EINVAL; + buf = dma->buflist[iload.idx]; + buf_priv = buf->dev_private; + bus_address = buf->bus_address; + + if (mgaVerifyIload(dev_priv, + bus_address, iload.destOrg, iload.length)) { + mga_freelist_put(dev, buf); + return -EINVAL; + } + + sarea_priv->dirty |= MGA_UPLOAD_CTX; + + mga_dma_dispatch_tex_blit(dev, bus_address, iload.length, + iload.destOrg); + AGEBUF(dev_priv, buf_priv); + buf_priv->discard = 1; + mga_freelist_put(dev, buf); + mga_flush_write_combine(); + mga_dma_schedule(dev, 1); + return 0; +} + +int mga_vertex(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_mga_buf_priv_t *buf_priv; + drm_mga_vertex_t vertex; + + if (copy_from_user(&vertex, (drm_mga_vertex_t *) arg, sizeof(vertex))) + return -EFAULT; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("mga_vertex called without lock held\n"); + return -EINVAL; + } + + if(vertex.idx < 0 || vertex.idx > dma->buf_count) return -EINVAL; + + buf = dma->buflist[vertex.idx]; + buf_priv = buf->dev_private; + + buf->used = vertex.used; + buf_priv->discard = vertex.discard; + + if (!mgaVerifyState(dev_priv)) { + if (vertex.discard) { + if (buf_priv->dispatched == 1) + AGEBUF(dev_priv, buf_priv); + buf_priv->dispatched = 0; + mga_freelist_put(dev, buf); + } + return -EINVAL; + } + + mga_dma_dispatch_vertex(dev, buf); + + PRIMUPDATE(dev_priv); + mga_flush_write_combine(); + mga_dma_schedule(dev, 1); + return 0; +} + + +int mga_indices(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_mga_buf_priv_t *buf_priv; + drm_mga_indices_t indices; + + if (copy_from_user(&indices, + (drm_mga_indices_t *)arg, sizeof(indices))) + return -EFAULT; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("mga_indices called without lock held\n"); + return -EINVAL; + } + + if(indices.idx < 0 || indices.idx > dma->buf_count) return -EINVAL; + buf = dma->buflist[indices.idx]; + buf_priv = buf->dev_private; + + buf_priv->discard = indices.discard; + + if (!mgaVerifyState(dev_priv)) { + if (indices.discard) { + if (buf_priv->dispatched == 1) + AGEBUF(dev_priv, buf_priv); + buf_priv->dispatched = 0; + mga_freelist_put(dev, buf); + } + return -EINVAL; + } + + mga_dma_dispatch_indices(dev, buf, indices.start, indices.end); + + PRIMUPDATE(dev_priv); + mga_flush_write_combine(); + mga_dma_schedule(dev, 1); + return 0; +} + + + +static int mga_dma_get_buffers(drm_device_t * dev, drm_dma_t * d) +{ + int i; + drm_buf_t *buf; + + for (i = d->granted_count; i < d->request_count; i++) { + buf = mga_freelist_get(dev); + if (!buf) + break; + buf->pid = current->pid; + if (copy_to_user(&d->request_indices[i], + &buf->idx, sizeof(buf->idx))) + return -EFAULT; + if (copy_to_user(&d->request_sizes[i], + &buf->total, sizeof(buf->total))) + return -EFAULT; + ++d->granted_count; + } + return 0; +} + +int mga_dma(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + drm_dma_t d; + + if (copy_from_user(&d, (drm_dma_t *) arg, sizeof(d))) + return -EFAULT; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("mga_dma called without lock held\n"); + return -EINVAL; + } + + /* Please don't send us buffers. + */ + if (d.send_count != 0) { + DRM_ERROR + ("Process %d trying to send %d buffers via drmDMA\n", + current->pid, d.send_count); + return -EINVAL; + } + + /* We'll send you buffers. + */ + if (d.request_count < 0 || d.request_count > dma->buf_count) { + DRM_ERROR + ("Process %d trying to get %d buffers (of %d max)\n", + current->pid, d.request_count, dma->buf_count); + return -EINVAL; + } + + d.granted_count = 0; + + if (d.request_count) { + retcode = mga_dma_get_buffers(dev, &d); + } + + if (copy_to_user((drm_dma_t *) arg, &d, sizeof(d))) + return -EFAULT; + return retcode; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/proc.c linux.ac/drivers/char/drm-4.0/proc.c --- linux.vanilla/drivers/char/drm-4.0/proc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/proc.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,578 @@ +/* proc.c -- /proc support for DRM -*- linux-c -*- + * Created: Mon Jan 11 09:48:47 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + */ + +#define __NO_VERSION__ +#include "drmP.h" + +static struct proc_dir_entry *drm_root = NULL; +static struct proc_dir_entry *drm_dev_root = NULL; +static char drm_slot_name[64]; + +static int drm_name_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_vm_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_clients_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_queues_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_bufs_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +#if DRM_DEBUG_CODE +static int drm_vma_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +#endif +#if DRM_DMA_HISTOGRAM +static int drm_histo_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +#endif + +struct drm_proc_list { + const char *name; + int (*f)(char *, char **, off_t, int, int *, void *); +} drm_proc_list[] = { + { "name", drm_name_info }, + { "mem", drm_mem_info }, + { "vm", drm_vm_info }, + { "clients", drm_clients_info }, + { "queues", drm_queues_info }, + { "bufs", drm_bufs_info }, +#if DRM_DEBUG_CODE + { "vma", drm_vma_info }, +#endif +#if DRM_DMA_HISTOGRAM + { "histo", drm_histo_info }, +#endif +}; +#define DRM_PROC_ENTRIES (sizeof(drm_proc_list)/sizeof(drm_proc_list[0])) + +int drm_proc_init(drm_device_t *dev) +{ + struct proc_dir_entry *ent; + int i, j; + + drm_root = create_proc_entry("dri", S_IFDIR, NULL); + if (!drm_root) { + DRM_ERROR("Cannot create /proc/dri\n"); + return -1; + } + + /* Instead of doing this search, we should + add some global support for /proc/dri. */ + for (i = 0; i < 8; i++) { + sprintf(drm_slot_name, "dri/%d", i); + drm_dev_root = create_proc_entry(drm_slot_name, S_IFDIR, NULL); + if (!drm_dev_root) { + DRM_ERROR("Cannot create /proc/%s\n", drm_slot_name); + remove_proc_entry("dri", NULL); + break; + } + if (drm_dev_root->nlink == 2) break; + drm_dev_root = NULL; + } + if (!drm_dev_root) { + DRM_ERROR("Cannot find slot in /proc/dri\n"); + return -1; + } + + for (i = 0; i < DRM_PROC_ENTRIES; i++) { + ent = create_proc_entry(drm_proc_list[i].name, + S_IFREG|S_IRUGO, drm_dev_root); + if (!ent) { + DRM_ERROR("Cannot create /proc/%s/%s\n", + drm_slot_name, drm_proc_list[i].name); + for (j = 0; j < i; j++) + remove_proc_entry(drm_proc_list[i].name, + drm_dev_root); + remove_proc_entry(drm_slot_name, NULL); + remove_proc_entry("dri", NULL); + return -1; + } + ent->read_proc = drm_proc_list[i].f; + ent->data = dev; + } + + return 0; +} + + +int drm_proc_cleanup(void) +{ + int i; + + if (drm_root) { + if (drm_dev_root) { + for (i = 0; i < DRM_PROC_ENTRIES; i++) { + remove_proc_entry(drm_proc_list[i].name, + drm_dev_root); + } + remove_proc_entry(drm_slot_name, NULL); + } + remove_proc_entry("dri", NULL); + remove_proc_entry(DRM_NAME, NULL); + } + drm_root = drm_dev_root = NULL; + return 0; +} + +static int drm_name_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + + if (dev->unique) { + DRM_PROC_PRINT("%s 0x%x %s\n", + dev->name, dev->device, dev->unique); + } else { + DRM_PROC_PRINT("%s 0x%x\n", dev->name, dev->device); + } + return len; +} + +static int _drm_vm_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_map_t *map; + /* Hardcoded from _DRM_FRAME_BUFFER, + _DRM_REGISTERS, _DRM_SHM, and + _DRM_AGP. */ + const char *types[] = { "FB", "REG", "SHM", "AGP" }; + const char *type; + int i; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT("slot offset size type flags " + "address mtrr\n\n"); + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + if (map->type < 0 || map->type > 3) type = "??"; + else type = types[map->type]; + DRM_PROC_PRINT("%4d 0x%08lx 0x%08lx %4.4s 0x%02x 0x%08lx ", + i, + map->offset, + map->size, + type, + map->flags, + (unsigned long)map->handle); + if (map->mtrr < 0) { + DRM_PROC_PRINT("none\n"); + } else { + DRM_PROC_PRINT("%4d\n", map->mtrr); + } + } + + return len; +} + +static int drm_vm_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_vm_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + + +static int _drm_queues_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int i; + drm_queue_t *q; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT(" ctx/flags use fin" + " blk/rw/rwf wait flushed queued" + " locks\n\n"); + for (i = 0; i < dev->queue_count; i++) { + q = dev->queuelist[i]; + atomic_inc(&q->use_count); + DRM_PROC_PRINT_RET(atomic_dec(&q->use_count), + "%5d/0x%03x %5d %5d" + " %5d/%c%c/%c%c%c %5Zd %10d %10d %10d\n", + i, + q->flags, + atomic_read(&q->use_count), + atomic_read(&q->finalization), + atomic_read(&q->block_count), + atomic_read(&q->block_read) ? 'r' : '-', + atomic_read(&q->block_write) ? 'w' : '-', + waitqueue_active(&q->read_queue) ? 'r':'-', + waitqueue_active(&q->write_queue) ? 'w':'-', + waitqueue_active(&q->flush_queue) ? 'f':'-', + DRM_BUFCOUNT(&q->waitlist), + atomic_read(&q->total_flushed), + atomic_read(&q->total_queued), + atomic_read(&q->total_locks)); + atomic_dec(&q->use_count); + } + + return len; +} + +static int drm_queues_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_queues_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + +/* drm_bufs_info is called whenever a process reads + /dev/drm//bufs. */ + +static int _drm_bufs_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_device_dma_t *dma = dev->dma; + int i; + + if (!dma) return 0; + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT(" o size count free segs pages kB\n\n"); + for (i = 0; i <= DRM_MAX_ORDER; i++) { + if (dma->bufs[i].buf_count) + DRM_PROC_PRINT("%2d %8d %5d %5d %5d %5d %5ld\n", + i, + dma->bufs[i].buf_size, + dma->bufs[i].buf_count, + atomic_read(&dma->bufs[i] + .freelist.count), + dma->bufs[i].seg_count, + dma->bufs[i].seg_count + *(1 << dma->bufs[i].page_order), + (dma->bufs[i].seg_count + * (1 << dma->bufs[i].page_order)) + * PAGE_SIZE / 1024); + } + DRM_PROC_PRINT("\n"); + for (i = 0; i < dma->buf_count; i++) { + if (i && !(i%32)) DRM_PROC_PRINT("\n"); + DRM_PROC_PRINT(" %d", dma->buflist[i]->list); + } + DRM_PROC_PRINT("\n"); + + return len; +} + +static int drm_bufs_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_bufs_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + + +static int _drm_clients_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_file_t *priv; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT("a dev pid uid magic ioctls\n\n"); + for (priv = dev->file_first; priv; priv = priv->next) { + DRM_PROC_PRINT("%c %3d %5d %5d %10u %10lu\n", + priv->authenticated ? 'y' : 'n', + priv->minor, + priv->pid, + priv->uid, + priv->magic, + priv->ioctl_count); + } + + return len; +} + +static int drm_clients_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_clients_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + +#if DRM_DEBUG_CODE + +#define DRM_VMA_VERBOSE 0 + +static int _drm_vma_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_vma_entry_t *pt; + struct vm_area_struct *vma; +#if DRM_VMA_VERBOSE + unsigned long i; + unsigned long address; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; +#endif +#if defined(__i386__) + unsigned int pgprot; +#endif + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT("vma use count: %d, high_memory = %p, 0x%08lx\n", + atomic_read(&dev->vma_count), + high_memory, virt_to_phys(high_memory)); + for (pt = dev->vmalist; pt; pt = pt->next) { + if (!(vma = pt->vma)) continue; + DRM_PROC_PRINT("\n%5d 0x%08lx-0x%08lx %c%c%c%c%c%c 0x%08lx", + pt->pid, + vma->vm_start, + vma->vm_end, + vma->vm_flags & VM_READ ? 'r' : '-', + vma->vm_flags & VM_WRITE ? 'w' : '-', + vma->vm_flags & VM_EXEC ? 'x' : '-', + vma->vm_flags & VM_MAYSHARE ? 's' : 'p', + vma->vm_flags & VM_LOCKED ? 'l' : '-', + vma->vm_flags & VM_IO ? 'i' : '-', + VM_OFFSET(vma)); + +#if defined(__i386__) + pgprot = pgprot_val(vma->vm_page_prot); + DRM_PROC_PRINT(" %c%c%c%c%c%c%c%c%c", + pgprot & _PAGE_PRESENT ? 'p' : '-', + pgprot & _PAGE_RW ? 'w' : 'r', + pgprot & _PAGE_USER ? 'u' : 's', + pgprot & _PAGE_PWT ? 't' : 'b', + pgprot & _PAGE_PCD ? 'u' : 'c', + pgprot & _PAGE_ACCESSED ? 'a' : '-', + pgprot & _PAGE_DIRTY ? 'd' : '-', + pgprot & _PAGE_PSE ? 'm' : 'k', + pgprot & _PAGE_GLOBAL ? 'g' : 'l' ); +#endif + DRM_PROC_PRINT("\n"); +#if 0 + for (i = vma->vm_start; i < vma->vm_end; i += PAGE_SIZE) { + pgd = pgd_offset(vma->vm_mm, i); + pmd = pmd_offset(pgd, i); + pte = pte_offset(pmd, i); + if (pte_present(*pte)) { + address = __pa(pte_page(*pte)) + + (i & (PAGE_SIZE-1)); + DRM_PROC_PRINT(" 0x%08lx -> 0x%08lx" + " %c%c%c%c%c\n", + i, + address, + pte_read(*pte) ? 'r' : '-', + pte_write(*pte) ? 'w' : '-', + pte_exec(*pte) ? 'x' : '-', + pte_dirty(*pte) ? 'd' : '-', + pte_young(*pte) ? 'a' : '-' ); + } else { + DRM_PROC_PRINT(" 0x%08lx\n", i); + } + } +#endif + } + + return len; +} + +static int drm_vma_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_vma_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} +#endif + + +#if DRM_DMA_HISTOGRAM +static int _drm_histo_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_device_dma_t *dma = dev->dma; + int i; + unsigned long slot_value = DRM_DMA_HISTOGRAM_INITIAL; + unsigned long prev_value = 0; + drm_buf_t *buffer; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + + DRM_PROC_PRINT("general statistics:\n"); + DRM_PROC_PRINT("total %10u\n", atomic_read(&dev->histo.total)); + DRM_PROC_PRINT("open %10u\n", atomic_read(&dev->total_open)); + DRM_PROC_PRINT("close %10u\n", atomic_read(&dev->total_close)); + DRM_PROC_PRINT("ioctl %10u\n", atomic_read(&dev->total_ioctl)); + DRM_PROC_PRINT("irq %10u\n", atomic_read(&dev->total_irq)); + DRM_PROC_PRINT("ctx %10u\n", atomic_read(&dev->total_ctx)); + + DRM_PROC_PRINT("\nlock statistics:\n"); + DRM_PROC_PRINT("locks %10u\n", atomic_read(&dev->total_locks)); + DRM_PROC_PRINT("unlocks %10u\n", atomic_read(&dev->total_unlocks)); + DRM_PROC_PRINT("contends %10u\n", atomic_read(&dev->total_contends)); + DRM_PROC_PRINT("sleeps %10u\n", atomic_read(&dev->total_sleeps)); + + + if (dma) { + DRM_PROC_PRINT("\ndma statistics:\n"); + DRM_PROC_PRINT("prio %10u\n", + atomic_read(&dma->total_prio)); + DRM_PROC_PRINT("bytes %10u\n", + atomic_read(&dma->total_bytes)); + DRM_PROC_PRINT("dmas %10u\n", + atomic_read(&dma->total_dmas)); + DRM_PROC_PRINT("missed:\n"); + DRM_PROC_PRINT(" dma %10u\n", + atomic_read(&dma->total_missed_dma)); + DRM_PROC_PRINT(" lock %10u\n", + atomic_read(&dma->total_missed_lock)); + DRM_PROC_PRINT(" free %10u\n", + atomic_read(&dma->total_missed_free)); + DRM_PROC_PRINT(" sched %10u\n", + atomic_read(&dma->total_missed_sched)); + DRM_PROC_PRINT("tried %10u\n", + atomic_read(&dma->total_tried)); + DRM_PROC_PRINT("hit %10u\n", + atomic_read(&dma->total_hit)); + DRM_PROC_PRINT("lost %10u\n", + atomic_read(&dma->total_lost)); + + buffer = dma->next_buffer; + if (buffer) { + DRM_PROC_PRINT("next_buffer %7d\n", buffer->idx); + } else { + DRM_PROC_PRINT("next_buffer none\n"); + } + buffer = dma->this_buffer; + if (buffer) { + DRM_PROC_PRINT("this_buffer %7d\n", buffer->idx); + } else { + DRM_PROC_PRINT("this_buffer none\n"); + } + } + + + DRM_PROC_PRINT("\nvalues:\n"); + if (dev->lock.hw_lock) { + DRM_PROC_PRINT("lock 0x%08x\n", + dev->lock.hw_lock->lock); + } else { + DRM_PROC_PRINT("lock none\n"); + } + DRM_PROC_PRINT("context_flag 0x%08lx\n", dev->context_flag); + DRM_PROC_PRINT("interrupt_flag 0x%08lx\n", dev->interrupt_flag); + DRM_PROC_PRINT("dma_flag 0x%08lx\n", dev->dma_flag); + + DRM_PROC_PRINT("queue_count %10d\n", dev->queue_count); + DRM_PROC_PRINT("last_context %10d\n", dev->last_context); + DRM_PROC_PRINT("last_switch %10lu\n", dev->last_switch); + DRM_PROC_PRINT("last_checked %10d\n", dev->last_checked); + + + DRM_PROC_PRINT("\n q2d d2c c2f" + " q2c q2f dma sch" + " ctx lacq lhld\n\n"); + for (i = 0; i < DRM_DMA_HISTOGRAM_SLOTS; i++) { + DRM_PROC_PRINT("%s %10lu %10u %10u %10u %10u %10u" + " %10u %10u %10u %10u %10u\n", + i == DRM_DMA_HISTOGRAM_SLOTS - 1 ? ">=" : "< ", + i == DRM_DMA_HISTOGRAM_SLOTS - 1 + ? prev_value : slot_value , + + atomic_read(&dev->histo + .queued_to_dispatched[i]), + atomic_read(&dev->histo + .dispatched_to_completed[i]), + atomic_read(&dev->histo + .completed_to_freed[i]), + + atomic_read(&dev->histo + .queued_to_completed[i]), + atomic_read(&dev->histo + .queued_to_freed[i]), + atomic_read(&dev->histo.dma[i]), + atomic_read(&dev->histo.schedule[i]), + atomic_read(&dev->histo.ctx[i]), + atomic_read(&dev->histo.lacq[i]), + atomic_read(&dev->histo.lhld[i])); + prev_value = slot_value; + slot_value = DRM_DMA_HISTOGRAM_NEXT(slot_value); + } + return len; +} + +static int drm_histo_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_histo_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/r128_bufs.c linux.ac/drivers/char/drm-4.0/r128_bufs.c --- linux.vanilla/drivers/char/drm-4.0/r128_bufs.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/r128_bufs.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,319 @@ +/* r128_bufs.c -- IOCTLs to manage buffers -*- linux-c -*- + * Created: Wed Apr 12 16:19:08 2000 by kevin@precisioninsight.com + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Kevin E. Martin + * Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#define __NO_VERSION__ +#include +#include "drmP.h" +#include "r128_drv.h" +#include "linux/un.h" + + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) +int r128_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + drm_buf_entry_t *entry; + drm_buf_t *buf; + unsigned long offset; + unsigned long agp_offset; + int count; + int order; + int size; + int alignment; + int page_order; + int total; + int byte_count; + int i; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, + (drm_buf_desc_t *)arg, + sizeof(request))) + return -EFAULT; + + count = request.count; + order = drm_order(request.size); + size = 1 << order; + + alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + byte_count = 0; + agp_offset = dev->agp->base + request.agp_start; + + DRM_DEBUG("count: %d\n", count); + DRM_DEBUG("order: %d\n", order); + DRM_DEBUG("size: %d\n", size); + DRM_DEBUG("agp_offset: %ld\n", agp_offset); + DRM_DEBUG("alignment: %d\n", alignment); + DRM_DEBUG("page_order: %d\n", page_order); + DRM_DEBUG("total: %d\n", total); + + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + if (dev->queue_count) return -EBUSY; /* Not while in use */ + + spin_lock(&dev->count_lock); + if (dev->buf_use) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + atomic_inc(&dev->buf_alloc); + spin_unlock(&dev->count_lock); + + down(&dev->struct_sem); + entry = &dma->bufs[order]; + if (entry->buf_count) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; /* May only call once for each order */ + } + + /* Might be a poor limit, but take that up with XFree86 + if its a problem */ + + if(count < 0 || count > 4096) + { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -EINVAL; + } + + entry->buflist = drm_alloc(count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + if (!entry->buflist) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->buflist, 0, count * sizeof(*entry->buflist)); + + entry->buf_size = size; + entry->page_order = page_order; + offset = 0; + + for (offset = 0; + entry->buf_count < count; + offset += alignment, ++entry->buf_count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + buf->offset = (dma->byte_count + offset); + buf->address = (void *)(agp_offset + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->pid = 0; + + buf->dev_priv_size = sizeof(drm_r128_buf_priv_t); + buf->dev_private = drm_alloc(sizeof(drm_r128_buf_priv_t), + DRM_MEM_BUFS); + memset(buf->dev_private, 0, buf->dev_priv_size); + +#if DRM_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + + byte_count += PAGE_SIZE << page_order; + + DRM_DEBUG("buffer %d @ %p\n", + entry->buf_count, buf->address); + } + + DRM_DEBUG("byte_count: %d\n", byte_count); + + dma->buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS); + for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++) + dma->buflist[i] = &entry->buflist[i - dma->buf_count]; + + dma->buf_count += entry->buf_count; + dma->byte_count += byte_count; + + drm_freelist_create(&entry->freelist, entry->buf_count); + for (i = 0; i < entry->buf_count; i++) { + drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]); + } + + up(&dev->struct_sem); + + request.count = entry->buf_count; + request.size = size; + + if (copy_to_user((drm_buf_desc_t *)arg, + &request, + sizeof(request))) + return -EFAULT; + + dma->flags = _DRM_DMA_USE_AGP; + + atomic_dec(&dev->buf_alloc); + return 0; +} +#endif + +int r128_addbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_buf_desc_t request; + + if (!dev_priv || dev_priv->is_pci) return -EINVAL; + + if (copy_from_user(&request, + (drm_buf_desc_t *)arg, + sizeof(request))) + return -EFAULT; + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + if (request.flags & _DRM_AGP_BUFFER) + return r128_addbufs_agp(inode, filp, cmd, arg); + else +#endif + return -EINVAL; +} + +int r128_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + const int zero = 0; + unsigned long virtual; + unsigned long address; + drm_buf_map_t request; + int i; + + if (!dma || !dev_priv || dev_priv->is_pci) return -EINVAL; + + DRM_DEBUG("\n"); + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + if (copy_from_user(&request, + (drm_buf_map_t *)arg, + sizeof(request))) + return -EFAULT; + + if (request.count >= dma->buf_count) { + if (dma->flags & _DRM_DMA_USE_AGP) { + drm_map_t *map; + + map = dev_priv->buffers; + if (!map) { + retcode = -EINVAL; + goto done; + } + + down_write(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, map->size, + PROT_READ|PROT_WRITE, + MAP_SHARED, + (unsigned long)map->offset); + up_write(¤t->mm->mmap_sem); + } else { + down_write(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, dma->byte_count, + PROT_READ|PROT_WRITE, MAP_SHARED, 0); + up_write(¤t->mm->mmap_sem); + } + if (virtual > -1024UL) { + /* Real error */ + retcode = (signed long)virtual; + goto done; + } + request.virtual = (void *)virtual; + + for (i = 0; i < dma->buf_count; i++) { + if (copy_to_user(&request.list[i].idx, + &dma->buflist[i]->idx, + sizeof(request.list[0].idx))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].total, + &dma->buflist[i]->total, + sizeof(request.list[0].total))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].used, + &zero, + sizeof(zero))) { + retcode = -EFAULT; + goto done; + } + address = virtual + dma->buflist[i]->offset; + if (copy_to_user(&request.list[i].address, + &address, + sizeof(address))) { + retcode = -EFAULT; + goto done; + } + } + } + done: + request.count = dma->buf_count; + DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode); + + if (copy_to_user((drm_buf_map_t *)arg, + &request, + sizeof(request))) + return -EFAULT; + + return retcode; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/r128_cce.c linux.ac/drivers/char/drm-4.0/r128_cce.c --- linux.vanilla/drivers/char/drm-4.0/r128_cce.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/r128_cce.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,1253 @@ +/* r128_cce.c -- ATI Rage 128 driver -*- linux-c -*- + * Created: Wed Apr 5 19:24:19 2000 by kevin@precisioninsight.com + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Gareth Hughes + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "r128_drv.h" + +#include /* For task queue support */ +#include + + +/* FIXME: Temporary CCE packet buffer */ +u32 r128_cce_buffer[(1 << 14)] __attribute__ ((aligned (32))); + +/* CCE microcode (from ATI) */ +static u32 r128_cce_microcode[] = { + 0, 276838400, 0, 268449792, 2, 142, 2, 145, 0, 1076765731, 0, + 1617039951, 0, 774592877, 0, 1987540286, 0, 2307490946U, 0, + 599558925, 0, 589505315, 0, 596487092, 0, 589505315, 1, + 11544576, 1, 206848, 1, 311296, 1, 198656, 2, 912273422, 11, + 262144, 0, 0, 1, 33559837, 1, 7438, 1, 14809, 1, 6615, 12, 28, + 1, 6614, 12, 28, 2, 23, 11, 18874368, 0, 16790922, 1, 409600, 9, + 30, 1, 147854772, 16, 420483072, 3, 8192, 0, 10240, 1, 198656, + 1, 15630, 1, 51200, 10, 34858, 9, 42, 1, 33559823, 2, 10276, 1, + 15717, 1, 15718, 2, 43, 1, 15936948, 1, 570480831, 1, 14715071, + 12, 322123831, 1, 33953125, 12, 55, 1, 33559908, 1, 15718, 2, + 46, 4, 2099258, 1, 526336, 1, 442623, 4, 4194365, 1, 509952, 1, + 459007, 3, 0, 12, 92, 2, 46, 12, 176, 1, 15734, 1, 206848, 1, + 18432, 1, 133120, 1, 100670734, 1, 149504, 1, 165888, 1, + 15975928, 1, 1048576, 6, 3145806, 1, 15715, 16, 2150645232U, 2, + 268449859, 2, 10307, 12, 176, 1, 15734, 1, 15735, 1, 15630, 1, + 15631, 1, 5253120, 6, 3145810, 16, 2150645232U, 1, 15864, 2, 82, + 1, 343310, 1, 1064207, 2, 3145813, 1, 15728, 1, 7817, 1, 15729, + 3, 15730, 12, 92, 2, 98, 1, 16168, 1, 16167, 1, 16002, 1, 16008, + 1, 15974, 1, 15975, 1, 15990, 1, 15976, 1, 15977, 1, 15980, 0, + 15981, 1, 10240, 1, 5253120, 1, 15720, 1, 198656, 6, 110, 1, + 180224, 1, 103824738, 2, 112, 2, 3145839, 0, 536885440, 1, + 114880, 14, 125, 12, 206975, 1, 33559995, 12, 198784, 0, + 33570236, 1, 15803, 0, 15804, 3, 294912, 1, 294912, 3, 442370, + 1, 11544576, 0, 811612160, 1, 12593152, 1, 11536384, 1, + 14024704, 7, 310382726, 0, 10240, 1, 14796, 1, 14797, 1, 14793, + 1, 14794, 0, 14795, 1, 268679168, 1, 9437184, 1, 268449792, 1, + 198656, 1, 9452827, 1, 1075854602, 1, 1075854603, 1, 557056, 1, + 114880, 14, 159, 12, 198784, 1, 1109409213, 12, 198783, 1, + 1107312059, 12, 198784, 1, 1109409212, 2, 162, 1, 1075854781, 1, + 1073757627, 1, 1075854780, 1, 540672, 1, 10485760, 6, 3145894, + 16, 274741248, 9, 168, 3, 4194304, 3, 4209949, 0, 0, 0, 256, 14, + 174, 1, 114857, 1, 33560007, 12, 176, 0, 10240, 1, 114858, 1, + 33560018, 1, 114857, 3, 33560007, 1, 16008, 1, 114874, 1, + 33560360, 1, 114875, 1, 33560154, 0, 15963, 0, 256, 0, 4096, 1, + 409611, 9, 188, 0, 10240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + + +#define DO_REMAP(_m) (_m)->handle = drm_ioremap((_m)->offset, (_m)->size) + +#define DO_REMAPFREE(_m) \ + do { \ + if ((_m)->handle && (_m)->size) \ + drm_ioremapfree((_m)->handle, (_m)->size); \ + } while (0) + +#define DO_FIND_MAP(_m, _o) \ + do { \ + int _i; \ + for (_i = 0; _i < dev->map_count; _i++) { \ + if (dev->maplist[_i]->offset == _o) { \ + _m = dev->maplist[_i]; \ + break; \ + } \ + } \ + } while (0) + + +int R128_READ_PLL(drm_device_t *dev, int addr) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + + R128_WRITE8(R128_CLOCK_CNTL_INDEX, addr & 0x1f); + return R128_READ(R128_CLOCK_CNTL_DATA); +} + +#if 0 +static void r128_status( drm_r128_private_t *dev_priv ) +{ + printk( "GUI_STAT = 0x%08x\n", + (unsigned int)R128_READ( R128_GUI_STAT ) ); + printk( "PM4_STAT = 0x%08x\n", + (unsigned int)R128_READ( R128_PM4_STAT ) ); + printk( "PM4_BUFFER_DL_WPTR = 0x%08x\n", + (unsigned int)R128_READ( R128_PM4_BUFFER_DL_WPTR ) ); + printk( "PM4_BUFFER_DL_RPTR = 0x%08x\n", + (unsigned int)R128_READ( R128_PM4_BUFFER_DL_RPTR ) ); + printk( "PM4_MICRO_CNTL = 0x%08x\n", + (unsigned int)R128_READ( R128_PM4_MICRO_CNTL ) ); + printk( "PM4_BUFFER_CNTL = 0x%08x\n", + (unsigned int)R128_READ( R128_PM4_BUFFER_CNTL ) ); +} +#endif + + +/* ================================================================ + * Engine, FIFO control + */ + +static int r128_do_pixcache_flush( drm_r128_private_t *dev_priv ) +{ + u32 tmp; + int i; + + tmp = R128_READ( R128_PC_NGUI_CTLSTAT ) | R128_PC_FLUSH_ALL; + R128_WRITE( R128_PC_NGUI_CTLSTAT, tmp ); + + for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { + if ( !(R128_READ( R128_PC_NGUI_CTLSTAT ) & R128_PC_BUSY) ) { + return 0; + } + udelay( 1 ); + } + + DRM_ERROR( "%s failed!\n", __FUNCTION__ ); + return -EBUSY; +} + +static int r128_do_wait_for_fifo( drm_r128_private_t *dev_priv, int entries ) +{ + int i; + + for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { + int slots = R128_READ( R128_GUI_STAT ) & R128_GUI_FIFOCNT_MASK; + if ( slots >= entries ) return 0; + udelay( 1 ); + } + + DRM_ERROR( "%s failed!\n", __FUNCTION__ ); + return -EBUSY; +} + +static int r128_do_wait_for_idle( drm_r128_private_t *dev_priv ) +{ + int i, ret; + + ret = r128_do_wait_for_fifo( dev_priv, 64 ); + if ( !ret ) return ret; + + for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { + if ( !(R128_READ( R128_GUI_STAT ) & R128_GUI_ACTIVE) ) { + r128_do_pixcache_flush( dev_priv ); + return 0; + } + udelay( 1 ); + } + + DRM_ERROR( "%s failed!\n", __FUNCTION__ ); + return -EBUSY; +} + + +/* ================================================================ + * CCE control, initialization + */ + +/* Load the microcode for the CCE */ +static void r128_cce_load_microcode( drm_r128_private_t *dev_priv ) +{ + int i; + + r128_do_wait_for_idle( dev_priv ); + + R128_WRITE( R128_PM4_MICROCODE_ADDR, 0 ); + for ( i = 0 ; i < 256 ; i++ ) { + R128_WRITE( R128_PM4_MICROCODE_DATAH, + r128_cce_microcode[i * 2] ); + R128_WRITE( R128_PM4_MICROCODE_DATAL, + r128_cce_microcode[i * 2 + 1] ); + } +} + +/* Flush any pending commands to the CCE. This should only be used just + * prior to a wait for idle, as it informs the engine that the command + * stream is ending. + */ +static void r128_do_cce_flush( drm_r128_private_t *dev_priv ) +{ + u32 tmp; + + tmp = R128_READ( R128_PM4_BUFFER_DL_WPTR ) | R128_PM4_BUFFER_DL_DONE; + R128_WRITE( R128_PM4_BUFFER_DL_WPTR, tmp ); +} + +/* Wait for the CCE to go idle. + */ +static int r128_do_cce_idle( drm_r128_private_t *dev_priv ) +{ + int i; + + for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { + if ( *dev_priv->ring.head == dev_priv->ring.tail ) { + int pm4stat = R128_READ( R128_PM4_STAT ); + if ( ( (pm4stat & R128_PM4_FIFOCNT_MASK) >= + dev_priv->cce_fifo_size ) && + !(pm4stat & (R128_PM4_BUSY | + R128_PM4_GUI_ACTIVE)) ) { + return r128_do_pixcache_flush( dev_priv ); + } + } + udelay( 1 ); + } + +#if 0 + DRM_ERROR( "failed!\n" ); + r128_status( dev_priv ); +#endif + return -EBUSY; +} + +/* Start the Concurrent Command Engine. + */ +static void r128_do_cce_start( drm_r128_private_t *dev_priv ) +{ + r128_do_wait_for_idle( dev_priv ); + + R128_WRITE( R128_PM4_BUFFER_CNTL, + dev_priv->cce_mode | dev_priv->ring.size_l2qw ); + R128_READ( R128_PM4_BUFFER_ADDR ); /* as per the sample code */ + R128_WRITE( R128_PM4_MICRO_CNTL, R128_PM4_MICRO_FREERUN ); + + dev_priv->cce_running = 1; +} + +/* Reset the Concurrent Command Engine. This will not flush any pending + * commangs, so you must wait for the CCE command stream to complete + * before calling this routine. + */ +static void r128_do_cce_reset( drm_r128_private_t *dev_priv ) +{ + R128_WRITE( R128_PM4_BUFFER_DL_WPTR, 0 ); + R128_WRITE( R128_PM4_BUFFER_DL_RPTR, 0 ); + *dev_priv->ring.head = 0; + dev_priv->ring.tail = 0; +} + +/* Stop the Concurrent Command Engine. This will not flush any pending + * commangs, so you must flush the command stream and wait for the CCE + * to go idle before calling this routine. + */ +static void r128_do_cce_stop( drm_r128_private_t *dev_priv ) +{ + R128_WRITE( R128_PM4_MICRO_CNTL, 0 ); + R128_WRITE( R128_PM4_BUFFER_CNTL, R128_PM4_NONPM4 ); + + dev_priv->cce_running = 0; +} + +/* Reset the engine. This will stop the CCE if it is running. + */ +static int r128_do_engine_reset( drm_device_t *dev ) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + u32 clock_cntl_index, mclk_cntl, gen_reset_cntl; + + r128_do_pixcache_flush( dev_priv ); + + clock_cntl_index = R128_READ( R128_CLOCK_CNTL_INDEX ); + mclk_cntl = R128_READ_PLL( dev, R128_MCLK_CNTL ); + + R128_WRITE_PLL( R128_MCLK_CNTL, + mclk_cntl | R128_FORCE_GCP | R128_FORCE_PIPE3D_CP ); + + gen_reset_cntl = R128_READ( R128_GEN_RESET_CNTL ); + + /* Taken from the sample code - do not change */ + R128_WRITE( R128_GEN_RESET_CNTL, + gen_reset_cntl | R128_SOFT_RESET_GUI ); + R128_READ( R128_GEN_RESET_CNTL ); + R128_WRITE( R128_GEN_RESET_CNTL, + gen_reset_cntl & ~R128_SOFT_RESET_GUI ); + R128_READ( R128_GEN_RESET_CNTL ); + + R128_WRITE_PLL( R128_MCLK_CNTL, mclk_cntl ); + R128_WRITE( R128_CLOCK_CNTL_INDEX, clock_cntl_index ); + R128_WRITE( R128_GEN_RESET_CNTL, gen_reset_cntl ); + + /* Reset the CCE ring */ + r128_do_cce_reset( dev_priv ); + + /* The CCE is no longer running after an engine reset */ + dev_priv->cce_running = 0; + + /* Reset any pending vertex, indirect buffers */ + r128_freelist_reset( dev ); + + return 0; +} + +static void r128_cce_init_ring_buffer( drm_device_t *dev ) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + u32 ring_start; + u32 tmp; + + /* The manual (p. 2) says this address is in "VM space". This + * means it's an offset from the start of AGP space. + */ + ring_start = dev_priv->cce_ring->offset - dev->agp->base; + R128_WRITE( R128_PM4_BUFFER_OFFSET, ring_start | R128_AGP_OFFSET ); + + R128_WRITE( R128_PM4_BUFFER_DL_WPTR, 0 ); + R128_WRITE( R128_PM4_BUFFER_DL_RPTR, 0 ); + + /* DL_RPTR_ADDR is a physical address in AGP space. */ + *dev_priv->ring.head = 0; + R128_WRITE( R128_PM4_BUFFER_DL_RPTR_ADDR, + dev_priv->ring_rptr->offset ); + + /* Set watermark control */ + R128_WRITE( R128_PM4_BUFFER_WM_CNTL, + ((R128_WATERMARK_L/4) << R128_WMA_SHIFT) + | ((R128_WATERMARK_M/4) << R128_WMB_SHIFT) + | ((R128_WATERMARK_N/4) << R128_WMC_SHIFT) + | ((R128_WATERMARK_K/64) << R128_WB_WM_SHIFT) ); + + /* Force read. Why? Because it's in the examples... */ + R128_READ( R128_PM4_BUFFER_ADDR ); + + /* Turn on bus mastering */ + tmp = R128_READ( R128_BUS_CNTL ) & ~R128_BUS_MASTER_DIS; + R128_WRITE( R128_BUS_CNTL, tmp ); +} + +static int r128_do_init_cce( drm_device_t *dev, drm_r128_init_t *init ) +{ + drm_r128_private_t *dev_priv; + int i; + + dev_priv = drm_alloc( sizeof(drm_r128_private_t), DRM_MEM_DRIVER ); + if ( dev_priv == NULL ) + return -ENOMEM; + dev->dev_private = (void *)dev_priv; + + memset( dev_priv, 0, sizeof(drm_r128_private_t) ); + + dev_priv->is_pci = init->is_pci; + + /* GH: We don't support PCI cards until PCI GART is implemented. + * Fail here so we can remove all checks for PCI cards around + * the CCE ring code. + */ + if ( dev_priv->is_pci ) { + drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); + dev->dev_private = NULL; + return -EINVAL; + } + + dev_priv->usec_timeout = init->usec_timeout; + if ( dev_priv->usec_timeout < 1 || + dev_priv->usec_timeout > R128_MAX_USEC_TIMEOUT ) { + drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); + dev->dev_private = NULL; + return -EINVAL; + } + + dev_priv->cce_mode = init->cce_mode; + dev_priv->cce_secure = init->cce_secure; + + /* GH: Simple idle check. + */ + atomic_set( &dev_priv->idle_count, 0 ); + + /* We don't support anything other than bus-mastering ring mode, + * but the ring can be in either AGP or PCI space for the ring + * read pointer. + */ + if ( ( init->cce_mode != R128_PM4_192BM ) && + ( init->cce_mode != R128_PM4_128BM_64INDBM ) && + ( init->cce_mode != R128_PM4_64BM_128INDBM ) && + ( init->cce_mode != R128_PM4_64BM_64VCBM_64INDBM ) ) { + drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); + dev->dev_private = NULL; + return -EINVAL; + } + + switch ( init->cce_mode ) { + case R128_PM4_NONPM4: + dev_priv->cce_fifo_size = 0; + break; + case R128_PM4_192PIO: + case R128_PM4_192BM: + dev_priv->cce_fifo_size = 192; + break; + case R128_PM4_128PIO_64INDBM: + case R128_PM4_128BM_64INDBM: + dev_priv->cce_fifo_size = 128; + break; + case R128_PM4_64PIO_128INDBM: + case R128_PM4_64BM_128INDBM: + case R128_PM4_64PIO_64VCBM_64INDBM: + case R128_PM4_64BM_64VCBM_64INDBM: + case R128_PM4_64PIO_64VCPIO_64INDPIO: + dev_priv->cce_fifo_size = 64; + break; + } + + dev_priv->fb_bpp = init->fb_bpp; + dev_priv->front_offset = init->front_offset; + dev_priv->front_pitch = init->front_pitch; + dev_priv->back_offset = init->back_offset; + dev_priv->back_pitch = init->back_pitch; + + dev_priv->depth_bpp = init->depth_bpp; + dev_priv->depth_offset = init->depth_offset; + dev_priv->depth_pitch = init->depth_pitch; + dev_priv->span_offset = init->span_offset; + + dev_priv->front_pitch_offset_c = (((dev_priv->front_pitch/8) << 21) | + (dev_priv->front_offset >> 5)); + dev_priv->back_pitch_offset_c = (((dev_priv->back_pitch/8) << 21) | + (dev_priv->back_offset >> 5)); + dev_priv->depth_pitch_offset_c = (((dev_priv->depth_pitch/8) << 21) | + (dev_priv->depth_offset >> 5) | + R128_DST_TILE); + dev_priv->span_pitch_offset_c = (((dev_priv->depth_pitch/8) << 21) | + (dev_priv->span_offset >> 5)); + + /* FIXME: We want multiple shared areas, including one shared + * only by the X Server and kernel module. + */ + for ( i = 0 ; i < dev->map_count ; i++ ) { + if ( dev->maplist[i]->type == _DRM_SHM ) { + dev_priv->sarea = dev->maplist[i]; + break; + } + } + + DO_FIND_MAP( dev_priv->fb, init->fb_offset ); + DO_FIND_MAP( dev_priv->mmio, init->mmio_offset ); + DO_FIND_MAP( dev_priv->cce_ring, init->ring_offset ); + DO_FIND_MAP( dev_priv->ring_rptr, init->ring_rptr_offset ); + DO_FIND_MAP( dev_priv->buffers, init->buffers_offset ); + + if ( !dev_priv->is_pci ) { + DO_FIND_MAP( dev_priv->agp_textures, + init->agp_textures_offset ); + } + + dev_priv->sarea_priv = + (drm_r128_sarea_t *)((u8 *)dev_priv->sarea->handle + + init->sarea_priv_offset); + + DO_REMAP( dev_priv->cce_ring ); + DO_REMAP( dev_priv->ring_rptr ); + DO_REMAP( dev_priv->buffers ); +#if 0 + if ( !dev_priv->is_pci ) { + DO_REMAP( dev_priv->agp_textures ); + } +#endif + + dev_priv->ring.head = ((__volatile__ u32 *) + dev_priv->ring_rptr->handle); + + dev_priv->ring.start = (u32 *)dev_priv->cce_ring->handle; + dev_priv->ring.end = ((u32 *)dev_priv->cce_ring->handle + + init->ring_size / sizeof(u32)); + dev_priv->ring.size = init->ring_size; + dev_priv->ring.size_l2qw = drm_order( init->ring_size / 8 ); + + dev_priv->ring.tail_mask = + (dev_priv->ring.size / sizeof(u32)) - 1; + + dev_priv->sarea_priv->last_frame = 0; + R128_WRITE( R128_LAST_FRAME_REG, dev_priv->sarea_priv->last_frame ); + + dev_priv->sarea_priv->last_dispatch = 0; + R128_WRITE( R128_LAST_DISPATCH_REG, + dev_priv->sarea_priv->last_dispatch ); + + r128_cce_init_ring_buffer( dev ); + r128_cce_load_microcode( dev_priv ); + r128_do_engine_reset( dev ); + + return 0; +} + +static int r128_do_cleanup_cce( drm_device_t *dev ) +{ + if ( dev->dev_private ) { + drm_r128_private_t *dev_priv = dev->dev_private; + + DO_REMAPFREE( dev_priv->cce_ring ); + DO_REMAPFREE( dev_priv->ring_rptr ); + DO_REMAPFREE( dev_priv->buffers ); +#if 0 + if ( !dev_priv->is_pci ) { + DO_REMAPFREE( dev_priv->agp_textures ); + } +#endif + + drm_free( dev->dev_private, sizeof(drm_r128_private_t), + DRM_MEM_DRIVER ); + dev->dev_private = NULL; + } + + return 0; +} + +int r128_cce_init( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_init_t init; + + if ( copy_from_user( &init, (drm_r128_init_t *)arg, sizeof(init) ) ) + return -EFAULT; + + switch ( init.func ) { + case R128_INIT_CCE: + return r128_do_init_cce( dev, &init ); + case R128_CLEANUP_CCE: + return r128_do_cleanup_cce( dev ); + } + + return -EINVAL; +} + +int r128_cce_start( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + if ( dev_priv->cce_running || dev_priv->cce_mode == R128_PM4_NONPM4 ) { + DRM_DEBUG( "%s while CCE running\n", __FUNCTION__ ); + return 0; + } + + r128_do_cce_start( dev_priv ); + + return 0; +} + +/* Stop the CCE. The engine must have been idled before calling this + * routine. + */ +int r128_cce_stop( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_cce_stop_t stop; + int ret; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &stop, (drm_r128_init_t *)arg, sizeof(stop) ) ) + return -EFAULT; + + /* Flush any pending CCE commands. This ensures any outstanding + * commands are exectuted by the engine before we turn it off. + */ + if ( stop.flush ) { + r128_do_cce_flush( dev_priv ); + } + + /* If we fail to make the engine go idle, we return an error + * code so that the DRM ioctl wrapper can try again. + */ + if ( stop.idle ) { + ret = r128_do_cce_idle( dev_priv ); + if ( ret < 0 ) return ret; + } + + /* Finally, we can turn off the CCE. If the engine isn't idle, + * we will get some dropped triangles as they won't be fully + * rendered before the CCE is shut down. + */ + r128_do_cce_stop( dev_priv ); + + /* Reset the engine */ + r128_do_engine_reset( dev ); + + return 0; +} + +/* Just reset the CCE ring. Called as part of an X Server engine reset. + */ +int r128_cce_reset( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + if ( !dev_priv ) { + DRM_DEBUG( "%s called before init done\n", __FUNCTION__ ); + return -EINVAL; + } + + r128_do_cce_reset( dev_priv ); + + /* The CCE is no longer running after an engine reset */ + dev_priv->cce_running = 0; + + return 0; +} + +int r128_cce_idle( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( dev_priv->cce_running ) { + r128_do_cce_flush( dev_priv ); + } + + return r128_do_cce_idle( dev_priv ); +} + +int r128_engine_reset( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + return r128_do_engine_reset( dev ); +} + + +/* ================================================================ + * Freelist management + */ +#define R128_BUFFER_USED 0xffffffff +#define R128_BUFFER_FREE 0 + +#if 0 +static int r128_freelist_init( drm_device_t *dev ) +{ + drm_device_dma_t *dma = dev->dma; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_buf_t *buf; + drm_r128_buf_priv_t *buf_priv; + drm_r128_freelist_t *entry; + int i; + + dev_priv->head = drm_alloc( sizeof(drm_r128_freelist_t), + DRM_MEM_DRIVER ); + if ( dev_priv->head == NULL ) + return -ENOMEM; + + memset( dev_priv->head, 0, sizeof(drm_r128_freelist_t) ); + dev_priv->head->age = R128_BUFFER_USED; + + for ( i = 0 ; i < dma->buf_count ; i++ ) { + buf = dma->buflist[i]; + buf_priv = buf->dev_private; + + entry = drm_alloc( sizeof(drm_r128_freelist_t), + DRM_MEM_DRIVER ); + if ( !entry ) return -ENOMEM; + + entry->age = R128_BUFFER_FREE; + entry->buf = buf; + entry->prev = dev_priv->head; + entry->next = dev_priv->head->next; + if ( !entry->next ) + dev_priv->tail = entry; + + buf_priv->discard = 0; + buf_priv->dispatched = 0; + buf_priv->list_entry = entry; + + dev_priv->head->next = entry; + + if ( dev_priv->head->next ) + dev_priv->head->next->prev = entry; + } + + return 0; + +} +#endif + +drm_buf_t *r128_freelist_get( drm_device_t *dev ) +{ + drm_device_dma_t *dma = dev->dma; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_buf_priv_t *buf_priv; + drm_buf_t *buf; + int i, t; + + /* FIXME: Optimize -- use freelist code */ + + for ( i = 0 ; i < dma->buf_count ; i++ ) { + buf = dma->buflist[i]; + buf_priv = buf->dev_private; + if ( buf->pid == 0 ) + return buf; + } + + for ( t = 0 ; t < dev_priv->usec_timeout ; t++ ) { + u32 done_age = R128_READ( R128_LAST_DISPATCH_REG ); + + for ( i = 0 ; i < dma->buf_count ; i++ ) { + buf = dma->buflist[i]; + buf_priv = buf->dev_private; + if ( buf->pending && buf_priv->age <= done_age ) { + /* The buffer has been processed, so it + * can now be used. + */ + buf->pending = 0; + return buf; + } + } + udelay( 1 ); + } + + DRM_ERROR( "returning NULL!\n" ); + return NULL; +} + +void r128_freelist_reset( drm_device_t *dev ) +{ + drm_device_dma_t *dma = dev->dma; + int i; + + for ( i = 0 ; i < dma->buf_count ; i++ ) { + drm_buf_t *buf = dma->buflist[i]; + drm_r128_buf_priv_t *buf_priv = buf->dev_private; + buf_priv->age = 0; + } +} + + +/* ================================================================ + * CCE packet submission + */ + +int r128_wait_ring( drm_r128_private_t *dev_priv, int n ) +{ + drm_r128_ring_buffer_t *ring = &dev_priv->ring; + int i; + + for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { + ring->space = *ring->head - ring->tail; + if ( ring->space <= 0 ) + ring->space += ring->size; + + if ( ring->space >= n ) + return 0; + + udelay( 1 ); + } + + return -EBUSY; +} + +void r128_update_ring_snapshot( drm_r128_private_t *dev_priv ) +{ + drm_r128_ring_buffer_t *ring = &dev_priv->ring; + + ring->space = *ring->head - ring->tail; +#if R128_PERFORMANCE_BOXES + if ( ring->space == 0 ) + atomic_inc( &dev_priv->idle_count ); +#endif + if ( ring->space <= 0 ) + ring->space += ring->size; +} + +#if 0 +static int r128_verify_command( drm_r128_private_t *dev_priv, + u32 cmd, int *size ) +{ + int writing = 1; + + *size = 0; + + switch ( cmd & R128_CCE_PACKET_MASK ) { + case R128_CCE_PACKET0: + if ( (cmd & R128_CCE_PACKET0_REG_MASK) <= (0x1004 >> 2) && + (cmd & R128_CCE_PACKET0_REG_MASK) != + (R128_PM4_VC_FPU_SETUP >> 2) ) { + writing = 0; + } + *size = ((cmd & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2; + break; + + case R128_CCE_PACKET1: + if ( (cmd & R128_CCE_PACKET1_REG0_MASK) <= (0x1004 >> 2) && + (cmd & R128_CCE_PACKET1_REG0_MASK) != + (R128_PM4_VC_FPU_SETUP >> 2) ) { + writing = 0; + } + if ( (cmd & R128_CCE_PACKET1_REG1_MASK) <= (0x1004 << 9) && + (cmd & R128_CCE_PACKET1_REG1_MASK) != + (R128_PM4_VC_FPU_SETUP << 9) ) { + writing = 0; + } + *size = 3; + break; + + case R128_CCE_PACKET2: + break; + + case R128_CCE_PACKET3: + *size = ((cmd & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2; + break; + + } + + return writing; +} + +static int r128_submit_packet_ring_secure( drm_r128_private_t *dev_priv, + u32 *commands, int *count ) +{ +#if 0 + int write = dev_priv->sarea_priv->ring_write; + int *write_ptr = dev_priv->ring_start + write; + int c = *count; + u32 tmp = 0; + int psize = 0; + int writing = 1; + int timeout; + + while ( c > 0 ) { + tmp = *commands++; + if ( !psize ) { + writing = r128_verify_command( dev_priv, tmp, &psize ); + } + psize--; + + if ( writing ) { + write++; + *write_ptr++ = tmp; + } + if ( write >= dev_priv->ring_entries ) { + write = 0; + write_ptr = dev_priv->ring_start; + } + timeout = 0; + while ( write == *dev_priv->ring_read_ptr ) { + R128_READ( R128_PM4_BUFFER_DL_RPTR ); + if ( timeout++ >= dev_priv->usec_timeout ) + return -EBUSY; + udelay( 1 ); + } + c--; + } + + if ( write < 32 ) { + memcpy( dev_priv->ring_end, + dev_priv->ring_start, + write * sizeof(u32) ); + } + + /* Make sure WC cache has been flushed */ + r128_flush_write_combine(); + + dev_priv->sarea_priv->ring_write = write; + R128_WRITE( R128_PM4_BUFFER_DL_WPTR, write ); + + *count = 0; +#endif + return 0; +} + +static int r128_submit_packet_ring_insecure( drm_r128_private_t *dev_priv, + u32 *commands, int *count ) +{ +#if 0 + int write = dev_priv->sarea_priv->ring_write; + int *write_ptr = dev_priv->ring_start + write; + int c = *count; + int timeout; + + while ( c > 0 ) { + write++; + *write_ptr++ = *commands++; + if ( write >= dev_priv->ring_entries ) { + write = 0; + write_ptr = dev_priv->ring_start; + } + + timeout = 0; + while ( write == *dev_priv->ring_read_ptr ) { + R128_READ( R128_PM4_BUFFER_DL_RPTR ); + if ( timeout++ >= dev_priv->usec_timeout ) + return -EBUSY; + udelay( 1 ); + } + c--; + } + + if ( write < 32 ) { + memcpy( dev_priv->ring_end, + dev_priv->ring_start, + write * sizeof(u32) ); + } + + /* Make sure WC cache has been flushed */ + r128_flush_write_combine(); + + dev_priv->sarea_priv->ring_write = write; + R128_WRITE( R128_PM4_BUFFER_DL_WPTR, write ); + + *count = 0; +#endif + return 0; +} +#endif + +/* Internal packet submission routine. This uses the insecure versions + * of the packet submission functions, and thus should only be used for + * packets generated inside the kernel module. + */ +int r128_do_submit_packet( drm_r128_private_t *dev_priv, + u32 *buffer, int count ) +{ + int c = count; + int ret = 0; + +#if 0 + int left = 0; + + if ( c >= dev_priv->ring_entries ) { + c = dev_priv->ring_entries - 1; + left = count - c; + } + + /* Since this is only used by the kernel we can use the + * insecure ring buffer submit packet routine. + */ + ret = r128_submit_packet_ring_insecure( dev_priv, buffer, &c ); + c += left; +#endif + + return ( ret < 0 ) ? ret : c; +} + +/* External packet submission routine. This uses the secure versions + * by default, and can thus submit packets received from user space. + */ +int r128_cce_packet( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_packet_t packet; + u32 *buffer; + int c; + int size; + int ret = 0; + +#if 0 + /* GH: Disable packet submission for now. + */ + return -EINVAL; +#endif + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "r128_submit_packet called without lock held\n" ); + return -EINVAL; + } + + if ( copy_from_user( &packet, (drm_r128_packet_t *)arg, + sizeof(packet) ) ) + return -EFAULT; + +#if 0 + c = packet.count; + size = c * sizeof(*buffer); + + { + int left = 0; + + if ( c >= dev_priv->ring_entries ) { + c = dev_priv->ring_entries - 1; + size = c * sizeof(*buffer); + left = packet.count - c; + } + + buffer = kmalloc( size, 0 ); + if ( buffer == NULL) + return -ENOMEM; + if ( copy_from_user( buffer, packet.buffer, size ) ) + return -EFAULT; + + if ( dev_priv->cce_secure ) { + ret = r128_submit_packet_ring_secure( dev_priv, + buffer, &c ); + } else { + ret = r128_submit_packet_ring_insecure( dev_priv, + buffer, &c ); + } + c += left; + } + + kfree( buffer ); +#else + c = 0; +#endif + + packet.count = c; + if ( copy_to_user( (drm_r128_packet_t *)arg, &packet, + sizeof(packet) ) ) + return -EFAULT; + + if ( ret ) { + return ret; + } else if ( c > 0 ) { + return -EAGAIN; + } + return 0; +} + +#if 0 +static int r128_send_vertbufs( drm_device_t *dev, drm_r128_vertex_t *v ) +{ + drm_device_dma_t *dma = dev->dma; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_buf_priv_t *buf_priv; + drm_buf_t *buf; + int i, ret; + RING_LOCALS; + + /* Make sure we have valid data */ + for (i = 0; i < v->send_count; i++) { + int idx = v->send_indices[i]; + + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("Index %d (of %d max)\n", + idx, dma->buf_count - 1); + return -EINVAL; + } + buf = dma->buflist[idx]; + if (buf->pid != current->pid) { + DRM_ERROR("Process %d using buffer owned by %d\n", + current->pid, buf->pid); + return -EINVAL; + } + if (buf->pending) { + DRM_ERROR("Sending pending buffer:" + " buffer %d, offset %d\n", + v->send_indices[i], i); + return -EINVAL; + } + } + + /* Wait for idle, if we've wrapped to make sure that all pending + buffers have been processed */ + if (dev_priv->submit_age == R128_MAX_VBUF_AGE) { + if ((ret = r128_do_cce_idle(dev)) < 0) return ret; + dev_priv->submit_age = 0; + r128_freelist_reset(dev); + } + + /* Make sure WC cache has been flushed (if in PIO mode) */ + if (!dev_priv->cce_is_bm_mode) r128_flush_write_combine(); + + /* FIXME: Add support for sending vertex buffer to the CCE here + instead of in client code. The v->prim holds the primitive + type that should be drawn. Loop over the list buffers in + send_indices[] and submit a packet for each VB. + + This will require us to loop over the clip rects here as + well, which implies that we extend the kernel driver to allow + cliprects to be stored here. Note that the cliprects could + possibly come from the X server instead of the client, but + this will require additional changes to the DRI to allow for + this optimization. */ + + /* Submit a CCE packet that writes submit_age to R128_VB_AGE_REG */ +#if 0 + cce_buffer[0] = R128CCE0(R128_CCE_PACKET0, R128_VB_AGE_REG, 0); + cce_buffer[1] = dev_priv->submit_age; + + if ((ret = r128_do_submit_packet(dev, cce_buffer, 2)) < 0) { + /* Until we add support for sending VBs to the CCE in + this routine, we can recover from this error. After + we add that support, we won't be able to easily + recover, so we will probably have to implement + another mechanism for handling timeouts from packets + submitted directly by the kernel. */ + return ret; + } +#else + BEGIN_RING( 2 ); + + OUT_RING( CCE_PACKET0( R128_VB_AGE_REG, 0 ) ); + OUT_RING( dev_priv->submit_age ); + + ADVANCE_RING(); +#endif + /* Now that the submit packet request has succeeded, we can mark + the buffers as pending */ + for (i = 0; i < v->send_count; i++) { + buf = dma->buflist[v->send_indices[i]]; + buf->pending = 1; + + buf_priv = buf->dev_private; + buf_priv->age = dev_priv->submit_age; + } + + dev_priv->submit_age++; + + return 0; +} +#endif + + + + +static int r128_cce_get_buffers( drm_device_t *dev, drm_dma_t *d ) +{ + int i; + drm_buf_t *buf; + + for ( i = d->granted_count ; i < d->request_count ; i++ ) { + buf = r128_freelist_get( dev ); + if ( !buf ) return -EAGAIN; + + buf->pid = current->pid; + + if ( copy_to_user( &d->request_indices[i], &buf->idx, + sizeof(buf->idx) ) ) + return -EFAULT; + if ( copy_to_user( &d->request_sizes[i], &buf->total, + sizeof(buf->total) ) ) + return -EFAULT; + + d->granted_count++; + } + return 0; +} + +int r128_cce_buffers( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int ret = 0; + drm_dma_t d; + + if ( copy_from_user( &d, (drm_dma_t *) arg, sizeof(d) ) ) + return -EFAULT; + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + /* Please don't send us buffers. + */ + if ( d.send_count != 0 ) { + DRM_ERROR( "Process %d trying to send %d buffers via drmDMA\n", + current->pid, d.send_count ); + return -EINVAL; + } + + /* We'll send you buffers. + */ + if ( d.request_count < 0 || d.request_count > dma->buf_count ) { + DRM_ERROR( "Process %d trying to get %d buffers (of %d max)\n", + current->pid, d.request_count, dma->buf_count ); + return -EINVAL; + } + + d.granted_count = 0; + + if ( d.request_count ) { + ret = r128_cce_get_buffers( dev, &d ); + } + + if ( copy_to_user( (drm_dma_t *) arg, &d, sizeof(d) ) ) + return -EFAULT; + + return ret; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/r128_context.c linux.ac/drivers/char/drm-4.0/r128_context.c --- linux.vanilla/drivers/char/drm-4.0/r128_context.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/r128_context.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,217 @@ +/* r128_context.c -- IOCTLs for r128 contexts -*- linux-c -*- + * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "r128_drv.h" + +extern drm_ctx_t r128_res_ctx; + +static int r128_alloc_queue(drm_device_t *dev) +{ + return drm_ctxbitmap_next(dev); +} + +int r128_context_switch(drm_device_t *dev, int old, int new) +{ + char buf[64]; + + atomic_inc(&dev->total_ctx); + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + if (drm_flags & DRM_FLAG_NOCTX) { + r128_context_switch_complete(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + drm_write_string(dev, buf); + } + + return 0; +} + +int r128_context_switch_complete(drm_device_t *dev, int new) +{ + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + /* If a context switch is ever initiated + when the kernel holds the lock, release + that lock here. */ +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + wake_up(&dev->context_wait); + + return 0; +} + + +int r128_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res))) + return -EFAULT; + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + if (copy_to_user(&res.contexts[i], + &i, + sizeof(i))) + return -EFAULT; + } + } + res.count = DRM_RESERVED_CONTEXTS; + if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res))) + return -EFAULT; + return 0; +} + + +int r128_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + if ((ctx.handle = r128_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) { + /* Skip kernel's context and get a new one. */ + ctx.handle = r128_alloc_queue(dev); + } + DRM_DEBUG("%d\n", ctx.handle); + if (ctx.handle == -1) { + DRM_DEBUG("Not enough free contexts.\n"); + /* Should this return -EBUSY instead? */ + return -ENOMEM; + } + + if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx))) + return -EFAULT; + return 0; +} + +int r128_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx))) + return -EFAULT; + if (ctx.flags==_DRM_CONTEXT_PRESERVED) + r128_res_ctx.handle=ctx.handle; + return 0; +} + +int r128_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx))) + return -EFAULT; + /* This is 0, because we don't hanlde any context flags */ + ctx.flags = 0; + if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx))) + return -EFAULT; + return 0; +} + +int r128_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + return r128_context_switch(dev, dev->last_context, ctx.handle); +} + +int r128_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + r128_context_switch_complete(dev, ctx.handle); + + return 0; +} + +int r128_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + drm_ctxbitmap_free(dev, ctx.handle); + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/r128_drm.h linux.ac/drivers/char/drm-4.0/r128_drm.h --- linux.vanilla/drivers/char/drm-4.0/r128_drm.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/r128_drm.h Wed Oct 10 01:47:41 2001 @@ -0,0 +1,272 @@ +/* r128_drm.h -- Public header for the r128 driver -*- linux-c -*- + * Created: Wed Apr 5 19:24:19 2000 by kevin@precisioninsight.com + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Kevin E. Martin + * Gareth Hughes + * + */ + +#ifndef _R128_DRM_H_ +#define _R128_DRM_H_ + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the X server file (r128_sarea.h) + */ +#ifndef __R128_SAREA_DEFINES__ +#define __R128_SAREA_DEFINES__ + +/* What needs to be changed for the current vertex buffer? + */ +#define R128_UPLOAD_CONTEXT 0x001 +#define R128_UPLOAD_SETUP 0x002 +#define R128_UPLOAD_TEX0 0x004 +#define R128_UPLOAD_TEX1 0x008 +#define R128_UPLOAD_TEX0IMAGES 0x010 +#define R128_UPLOAD_TEX1IMAGES 0x020 +#define R128_UPLOAD_CORE 0x040 +#define R128_UPLOAD_MASKS 0x080 +#define R128_UPLOAD_WINDOW 0x100 +#define R128_UPLOAD_CLIPRECTS 0x200 /* handled client-side */ +#define R128_REQUIRE_QUIESCENCE 0x400 +#define R128_UPLOAD_ALL 0x7ff + +#define R128_FRONT 0x1 +#define R128_BACK 0x2 +#define R128_DEPTH 0x4 + +/* Primitive types + */ +#define R128_POINTS 0x1 +#define R128_LINES 0x2 +#define R128_LINE_STRIP 0x3 +#define R128_TRIANGLES 0x4 +#define R128_TRIANGLE_FAN 0x5 +#define R128_TRIANGLE_STRIP 0x6 + +/* Vertex/indirect buffer size + */ +#if 1 +#define R128_BUFFER_SIZE 16384 +#else +#define R128_BUFFER_SIZE (128 * 1024) +#endif + +/* Byte offsets for indirect buffer data + */ +#define R128_INDEX_PRIM_OFFSET 20 +#define R128_HOSTDATA_BLIT_OFFSET 32 + +/* 2048x2048 @ 32bpp texture requires this many indirect buffers + */ +#define R128_MAX_BLIT_BUFFERS ((2048 * 2048 * 4) / R128_BUFFER_SIZE) + +/* Keep these small for testing. + */ +#define R128_NR_SAREA_CLIPRECTS 12 + +/* There are 2 heaps (local/AGP). Each region within a heap is a + * minimum of 64k, and there are at most 64 of them per heap. + */ +#define R128_LOCAL_TEX_HEAP 0 +#define R128_AGP_TEX_HEAP 1 +#define R128_NR_TEX_HEAPS 2 +#define R128_NR_TEX_REGIONS 64 +#define R128_LOG_TEX_GRANULARITY 16 + +#define R128_NR_CONTEXT_REGS 12 +#define R128_TEX_MAXLEVELS 11 + +#endif /* __R128_SAREA_DEFINES__ */ + +typedef struct { + /* Context state - can be written in one large chunk */ + unsigned int dst_pitch_offset_c; + unsigned int dp_gui_master_cntl_c; + unsigned int sc_top_left_c; + unsigned int sc_bottom_right_c; + unsigned int z_offset_c; + unsigned int z_pitch_c; + unsigned int z_sten_cntl_c; + unsigned int tex_cntl_c; + unsigned int misc_3d_state_cntl_reg; + unsigned int texture_clr_cmp_clr_c; + unsigned int texture_clr_cmp_msk_c; + unsigned int fog_color_c; + + /* Texture state */ + unsigned int tex_size_pitch_c; + unsigned int constant_color_c; + + /* Setup state */ + unsigned int pm4_vc_fpu_setup; + unsigned int setup_cntl; + + /* Mask state */ + unsigned int dp_write_mask; + unsigned int sten_ref_mask_c; + unsigned int plane_3d_mask_c; + + /* Window state */ + unsigned int window_xy_offset; + + /* Core state */ + unsigned int scale_3d_cntl; +} drm_r128_context_regs_t; + +/* Setup registers for each texture unit */ +typedef struct { + unsigned int tex_cntl; + unsigned int tex_combine_cntl; + unsigned int tex_size_pitch; + unsigned int tex_offset[R128_TEX_MAXLEVELS]; + unsigned int tex_border_color; +} drm_r128_texture_regs_t; + + +typedef struct drm_tex_region { + unsigned char next, prev; + unsigned char in_use; + int age; +} drm_tex_region_t; + +typedef struct drm_r128_sarea { + /* The channel for communication of state information to the kernel + * on firing a vertex buffer. + */ + drm_r128_context_regs_t context_state; + drm_r128_texture_regs_t tex_state[R128_NR_TEX_HEAPS]; + unsigned int dirty; + unsigned int vertsize; + unsigned int vc_format; + + /* The current cliprects, or a subset thereof. + */ + drm_clip_rect_t boxes[R128_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Counters for client-side throttling of rendering clients. + */ + unsigned int last_frame; + unsigned int last_dispatch; + + drm_tex_region_t tex_list[R128_NR_TEX_HEAPS][R128_NR_TEX_REGIONS+1]; + int tex_age[R128_NR_TEX_HEAPS]; + int ctx_owner; +} drm_r128_sarea_t; + + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (xf86drmR128.h) + */ +typedef struct drm_r128_init { + enum { + R128_INIT_CCE = 0x01, + R128_CLEANUP_CCE = 0x02 + } func; + int sarea_priv_offset; + int is_pci; + int cce_mode; + int cce_secure; + int ring_size; + int usec_timeout; + + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + unsigned int span_offset; + + unsigned int fb_offset; + unsigned int mmio_offset; + unsigned int ring_offset; + unsigned int ring_rptr_offset; + unsigned int buffers_offset; + unsigned int agp_textures_offset; +} drm_r128_init_t; + +typedef struct drm_r128_cce_stop { + int flush; + int idle; +} drm_r128_cce_stop_t; + +typedef struct drm_r128_clear { + unsigned int flags; + int x, y, w, h; + unsigned int clear_color; + unsigned int clear_depth; +} drm_r128_clear_t; + +typedef struct drm_r128_vertex { + int prim; + int idx; /* Index of vertex buffer */ + int count; /* Number of vertices in buffer */ + int discard; /* Client finished with buffer? */ +} drm_r128_vertex_t; + +typedef struct drm_r128_indices { + int prim; + int idx; + int start; + int end; + int discard; /* Client finished with buffer? */ +} drm_r128_indices_t; + +typedef struct drm_r128_blit { + int idx; + int pitch; + int offset; + int format; + unsigned short x, y; + unsigned short width, height; +} drm_r128_blit_t; + +typedef struct drm_r128_depth { + enum { + R128_WRITE_SPAN = 0x01, + R128_WRITE_PIXELS = 0x02, + R128_READ_SPAN = 0x03, + R128_READ_PIXELS = 0x04 + } func; + int n; + int *x; + int *y; + unsigned int *buffer; + unsigned char *mask; +} drm_r128_depth_t; + +typedef struct drm_r128_stipple { + unsigned int *mask; +} drm_r128_stipple_t; + +typedef struct drm_r128_packet { + unsigned int *buffer; + int count; + int flags; +} drm_r128_packet_t; + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/r128_drv.c linux.ac/drivers/char/drm-4.0/r128_drv.c --- linux.vanilla/drivers/char/drm-4.0/r128_drv.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/r128_drv.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,700 @@ +/* r128_drv.c -- ATI Rage 128 driver -*- linux-c -*- + * Created: Mon Dec 13 09:47:27 1999 by faith@precisioninsight.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * Kevin E. Martin + * Gareth Hughes + * + */ + +#include +#include "drmP.h" +#include "r128_drv.h" + +#define R128_NAME "r128" +#define R128_DESC "ATI Rage 128" +#define R128_DATE "20001215" +#define R128_MAJOR 2 +#define R128_MINOR 1 +#define R128_PATCHLEVEL 2 + +static drm_device_t r128_device; +drm_ctx_t r128_res_ctx; + +static struct file_operations r128_fops = { +#if LINUX_VERSION_CODE >= 0x020400 + /* This started being used during 2.4.0-test */ + owner: THIS_MODULE, +#endif + open: r128_open, + flush: drm_flush, + release: r128_release, + ioctl: r128_ioctl, + mmap: drm_mmap, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +static struct miscdevice r128_misc = { + minor: MISC_DYNAMIC_MINOR, + name: R128_NAME, + fops: &r128_fops, +}; + +static drm_ioctl_desc_t r128_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { r128_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { r128_addbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { drm_markbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { drm_infobufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)] = { r128_mapbufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { drm_freebufs, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { r128_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { r128_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { r128_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { r128_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { r128_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { r128_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { r128_resctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { r128_cce_buffers, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { r128_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { r128_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = { drm_agp_enable, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = { drm_agp_info, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = { drm_agp_alloc, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = { drm_agp_free, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { drm_agp_bind, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { drm_agp_unbind, 1, 1 }, +#endif + + [DRM_IOCTL_NR(DRM_IOCTL_R128_INIT)] = { r128_cce_init, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_START)] = { r128_cce_start, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_STOP)] = { r128_cce_stop, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_RESET)] = { r128_cce_reset, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_IDLE)] = { r128_cce_idle, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_RESET)] = { r128_engine_reset, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_SWAP)] = { r128_cce_swap, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_CLEAR)] = { r128_cce_clear, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_VERTEX)] = { r128_cce_vertex, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_INDICES)] = { r128_cce_indices, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_BLIT)] = { r128_cce_blit, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_DEPTH)] = { r128_cce_depth, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_STIPPLE)] = { r128_cce_stipple, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_PACKET)] = { r128_cce_packet, 1, 0 }, +}; +#define R128_IOCTL_COUNT DRM_ARRAY_SIZE(r128_ioctls) + +#ifdef MODULE +static char *r128 = NULL; +#endif + +MODULE_AUTHOR("VA Linux Systems, Inc."); +MODULE_DESCRIPTION("r128"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_PARM(r128, "s"); + +#ifndef MODULE +/* r128_options is called by the kernel to parse command-line options + * passed via the boot-loader (e.g., LILO). It calls the insmod option + * routine, drm_parse_drm. + */ + +static int __init r128_options(char *str) +{ + drm_parse_options(str); + return 1; +} + +__setup("r128=", r128_options); +#endif + +static int r128_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + drm_dma_setup(dev); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); + + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + r128_res_ctx.handle=-1; + + DRM_DEBUG("\n"); + + /* The kernel's context could be created here, but is now created + in drm_dma_enqueue. This is more resource-efficient for + hardware that does not do DMA, but may mean that + drm_select_queue fails between the time the interrupt is + initialized and the time the queues are initialized. */ + + return 0; +} + + +static int r128_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + /* Clear AGP information */ + if (dev->agp) { + drm_agp_mem_t *entry; + drm_agp_mem_t *nexte; + + /* Remove AGP resources, but leave dev->agp + intact until r128_cleanup is called. */ + for (entry = dev->agp->memory; entry; entry = nexte) { + nexte = entry->next; + if (entry->bound) drm_unbind_agp(entry->memory); + drm_free_agp(entry->memory, entry->pages); + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + } + dev->agp->memory = NULL; + + if (dev->agp->acquired) _drm_agp_release(); + + dev->agp->acquired = 0; + dev->agp->enabled = 0; + } +#endif + + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area and mtrr information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifdef CONFIG_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + drm_ioremapfree(map->handle, map->size); + break; + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + case _DRM_AGP: + /* Do nothing here, because this is all + handled in the AGP/GART driver. */ + break; + } + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + drm_dma_takedown(dev); + + dev->queue_count = 0; + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +/* r128_init is called via init_module at module load time, or via + * linux/init/main.c (this is not currently supported). */ + +static int __init r128_init(void) +{ + int retcode; + drm_device_t *dev = &r128_device; + + DRM_DEBUG("\n"); + + memset((void *)dev, 0, sizeof(*dev)); + dev->count_lock = SPIN_LOCK_UNLOCKED; + sema_init(&dev->struct_sem, 1); + +#ifdef MODULE + drm_parse_options(r128); +#endif + + if ((retcode = misc_register(&r128_misc))) { + DRM_ERROR("Cannot register \"%s\"\n", R128_NAME); + return retcode; + } + dev->device = MKDEV(MISC_MAJOR, r128_misc.minor); + dev->name = R128_NAME; + + drm_mem_init(); + drm_proc_init(dev); + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + dev->agp = drm_agp_init(); + if (dev->agp == NULL) { + DRM_ERROR("Cannot initialize agpgart module.\n"); + drm_proc_cleanup(); + misc_deregister(&r128_misc); + r128_takedown(dev); + return -ENOMEM; + } + +#ifdef CONFIG_MTRR + dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base, + dev->agp->agp_info.aper_size*1024*1024, + MTRR_TYPE_WRCOMB, + 1); +#endif +#endif + + if((retcode = drm_ctxbitmap_init(dev))) { + DRM_ERROR("Cannot allocate memory for context bitmap.\n"); + drm_proc_cleanup(); + misc_deregister(&r128_misc); + r128_takedown(dev); + return retcode; + } + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", + R128_NAME, + R128_MAJOR, + R128_MINOR, + R128_PATCHLEVEL, + R128_DATE, + r128_misc.minor); + + return 0; +} + +/* r128_cleanup is called via cleanup_module at module unload time. */ + +static void __exit r128_cleanup(void) +{ + drm_device_t *dev = &r128_device; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + if (misc_deregister(&r128_misc)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + drm_ctxbitmap_cleanup(dev); + r128_takedown(dev); +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + if (dev->agp) { + drm_agp_uninit(); + drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS); + dev->agp = NULL; + } +#endif +} + +module_init(r128_init); +module_exit(r128_cleanup); + + +int r128_version(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_version_t version; + int len; + + if (copy_from_user(&version, + (drm_version_t *)arg, + sizeof(version))) + return -EFAULT; + +#define DRM_COPY(name,value) \ + len = strlen(value); \ + if (len > name##_len) len = name##_len; \ + name##_len = strlen(value); \ + if (len && name) { \ + if (copy_to_user(name, value, len)) \ + return -EFAULT; \ + } + + version.version_major = R128_MAJOR; + version.version_minor = R128_MINOR; + version.version_patchlevel = R128_PATCHLEVEL; + + DRM_COPY(version.name, R128_NAME); + DRM_COPY(version.date, R128_DATE); + DRM_COPY(version.desc, R128_DESC); + + if (copy_to_user((drm_version_t *)arg, + &version, + sizeof(version))) + return -EFAULT; + return 0; +} + +int r128_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev = &r128_device; + int retcode = 0; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_open_helper(inode, filp, dev))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return r128_setup(dev); + } + spin_unlock(&dev->count_lock); + } + + return retcode; +} + +int r128_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + int retcode = 0; + + lock_kernel(); + dev = priv->dev; + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_release(inode, filp))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + unlock_kernel(); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return r128_takedown(dev); + } + spin_unlock(&dev->count_lock); + } + + unlock_kernel(); + return retcode; +} + +/* r128_ioctl is called whenever a process performs an ioctl on /dev/drm. */ +int r128_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, priv->authenticated); + + if (nr >= R128_IOCTL_COUNT) { + retcode = -EINVAL; + } else { + ioctl = &r128_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function\n"); + retcode = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + retcode = -EACCES; + } else { + retcode = (func)(inode, filp, cmd, arg); + } + } + +#if 0 + if ( retcode ) { + DRM_INFO( "%s 0x%x ret = %d\n", __FUNCTION__, nr, retcode ); + } +#endif + + atomic_dec(&dev->ioctl_count); + return retcode; +} + +int r128_lock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; +#if DRM_DMA_HISTOGRAM + cycles_t start; + + dev->lck_start = start = get_cycles(); +#endif + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, current->pid, dev->lock.hw_lock->lock, + lock.flags); + + if (lock.context < 0) + return -EINVAL; + + if (!ret) { + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + } + + if (!ret) { + sigemptyset(&dev->sigmask); + sigaddset(&dev->sigmask, SIGSTOP); + sigaddset(&dev->sigmask, SIGTSTP); + sigaddset(&dev->sigmask, SIGTTIN); + sigaddset(&dev->sigmask, SIGTTOU); + dev->sigdata.context = lock.context; + dev->sigdata.lock = dev->lock.hw_lock; + block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask); + if (lock.flags & _DRM_LOCK_READY) { + /* Wait for space in DMA/FIFO */ + } + if (lock.flags & _DRM_LOCK_QUIESCENT) { + /* Make hardware quiescent */ + DRM_DEBUG( "not quiescent!\n" ); +#if 0 + r128_quiescent(dev); +#endif + } + } + +#if LINUX_VERSION_CODE < 0x020400 + if (lock.context != r128_res_ctx.handle) { + current->counter = 5; + current->priority = DEF_PRIORITY/4; + } +#endif + DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]); +#endif + + return ret; +} + + +int r128_unlock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d frees lock (%d holds)\n", + lock.context, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); + /* FIXME: Try to send data to card here */ + if (!dev->context_flag) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } + +#if LINUX_VERSION_CODE < 0x020400 + if (lock.context != r128_res_ctx.handle) { + current->counter = 5; + current->priority = DEF_PRIORITY; + } +#endif + unblock_all_signals(); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/r128_drv.h linux.ac/drivers/char/drm-4.0/r128_drv.h --- linux.vanilla/drivers/char/drm-4.0/r128_drv.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/r128_drv.h Wed Oct 10 01:47:41 2001 @@ -0,0 +1,469 @@ +/* r128_drv.h -- Private header for r128 driver -*- linux-c -*- + * Created: Mon Dec 13 09:51:11 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * Kevin E. Martin + * Gareth Hughes + * + */ + +#ifndef __R128_DRV_H__ +#define __R128_DRV_H__ + +typedef struct drm_r128_freelist { + unsigned int age; + drm_buf_t *buf; + struct drm_r128_freelist *next; + struct drm_r128_freelist *prev; +} drm_r128_freelist_t; + +typedef struct drm_r128_ring_buffer { + u32 *start; + u32 *end; + int size; + int size_l2qw; + + volatile u32 *head; + u32 tail; + u32 tail_mask; + int space; +} drm_r128_ring_buffer_t; + +typedef struct drm_r128_private { + drm_r128_ring_buffer_t ring; + drm_r128_sarea_t *sarea_priv; + + int cce_mode; + int cce_fifo_size; + int cce_secure; + int cce_running; + + drm_r128_freelist_t *head; + drm_r128_freelist_t *tail; + + int usec_timeout; + int is_pci; + + atomic_t idle_count; + + unsigned int fb_bpp; + unsigned int front_offset; + unsigned int front_pitch; + unsigned int back_offset; + unsigned int back_pitch; + + unsigned int depth_bpp; + unsigned int depth_offset; + unsigned int depth_pitch; + unsigned int span_offset; + + u32 front_pitch_offset_c; + u32 back_pitch_offset_c; + u32 depth_pitch_offset_c; + u32 span_pitch_offset_c; + + drm_map_t *sarea; + drm_map_t *fb; + drm_map_t *mmio; + drm_map_t *cce_ring; + drm_map_t *ring_rptr; + drm_map_t *buffers; + drm_map_t *agp_textures; +} drm_r128_private_t; + +typedef struct drm_r128_buf_priv { + u32 age; + int prim; + int discard; + int dispatched; + drm_r128_freelist_t *list_entry; +} drm_r128_buf_priv_t; + + /* r128_drv.c */ +extern int r128_version( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_open( struct inode *inode, struct file *filp ); +extern int r128_release( struct inode *inode, struct file *filp ); +extern int r128_ioctl( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_lock( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_unlock( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); + + /* r128_cce.c */ +extern int r128_cce_init( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_cce_start( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_cce_stop( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_cce_reset( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_cce_idle( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_engine_reset( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_cce_packet( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_cce_buffers( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); + +extern void r128_freelist_reset( drm_device_t *dev ); +extern drm_buf_t *r128_freelist_get( drm_device_t *dev ); + +extern int r128_wait_ring( drm_r128_private_t *dev_priv, int n ); +extern void r128_update_ring_snapshot( drm_r128_private_t *dev_priv ); + + /* r128_state.c */ +extern int r128_cce_clear( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_cce_swap( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_cce_vertex( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_cce_indices( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_cce_blit( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_cce_depth( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int r128_cce_stipple( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); + + /* r128_bufs.c */ +extern int r128_addbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_mapbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* r128_context.c */ +extern int r128_resctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_addctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_modctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_getctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_switchctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_newctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_rmctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int r128_context_switch(drm_device_t *dev, int old, int new); +extern int r128_context_switch_complete(drm_device_t *dev, int new); + + +/* Register definitions, register access macros and drmAddMap constants + * for Rage 128 kernel driver. + */ + +#define R128_AUX_SC_CNTL 0x1660 +# define R128_AUX1_SC_EN (1 << 0) +# define R128_AUX1_SC_MODE_OR (0 << 1) +# define R128_AUX1_SC_MODE_NAND (1 << 1) +# define R128_AUX2_SC_EN (1 << 2) +# define R128_AUX2_SC_MODE_OR (0 << 3) +# define R128_AUX2_SC_MODE_NAND (1 << 3) +# define R128_AUX3_SC_EN (1 << 4) +# define R128_AUX3_SC_MODE_OR (0 << 5) +# define R128_AUX3_SC_MODE_NAND (1 << 5) +#define R128_AUX1_SC_LEFT 0x1664 +#define R128_AUX1_SC_RIGHT 0x1668 +#define R128_AUX1_SC_TOP 0x166c +#define R128_AUX1_SC_BOTTOM 0x1670 +#define R128_AUX2_SC_LEFT 0x1674 +#define R128_AUX2_SC_RIGHT 0x1678 +#define R128_AUX2_SC_TOP 0x167c +#define R128_AUX2_SC_BOTTOM 0x1680 +#define R128_AUX3_SC_LEFT 0x1684 +#define R128_AUX3_SC_RIGHT 0x1688 +#define R128_AUX3_SC_TOP 0x168c +#define R128_AUX3_SC_BOTTOM 0x1690 + +#define R128_BRUSH_DATA0 0x1480 +#define R128_BUS_CNTL 0x0030 +# define R128_BUS_MASTER_DIS (1 << 6) + +#define R128_CLOCK_CNTL_INDEX 0x0008 +#define R128_CLOCK_CNTL_DATA 0x000c +# define R128_PLL_WR_EN (1 << 7) + +#define R128_CONSTANT_COLOR_C 0x1d34 + +#define R128_DP_GUI_MASTER_CNTL 0x146c +# define R128_GMC_SRC_PITCH_OFFSET_CNTL (1 << 0) +# define R128_GMC_DST_PITCH_OFFSET_CNTL (1 << 1) +# define R128_GMC_BRUSH_SOLID_COLOR (13 << 4) +# define R128_GMC_BRUSH_NONE (15 << 4) +# define R128_GMC_DST_16BPP (4 << 8) +# define R128_GMC_DST_24BPP (5 << 8) +# define R128_GMC_DST_32BPP (6 << 8) +# define R128_GMC_DST_DATATYPE_SHIFT 8 +# define R128_GMC_SRC_DATATYPE_COLOR (3 << 12) +# define R128_DP_SRC_SOURCE_MEMORY (2 << 24) +# define R128_DP_SRC_SOURCE_HOST_DATA (3 << 24) +# define R128_GMC_CLR_CMP_CNTL_DIS (1 << 28) +# define R128_GMC_AUX_CLIP_DIS (1 << 29) +# define R128_GMC_WR_MSK_DIS (1 << 30) +# define R128_ROP3_S 0x00cc0000 +# define R128_ROP3_P 0x00f00000 +#define R128_DP_WRITE_MASK 0x16cc +#define R128_DST_PITCH_OFFSET_C 0x1c80 +# define R128_DST_TILE (1 << 31) + +#define R128_GEN_RESET_CNTL 0x00f0 +# define R128_SOFT_RESET_GUI (1 << 0) + +#define R128_GUI_SCRATCH_REG0 0x15e0 +#define R128_GUI_SCRATCH_REG1 0x15e4 +#define R128_GUI_SCRATCH_REG2 0x15e8 +#define R128_GUI_SCRATCH_REG3 0x15ec +#define R128_GUI_SCRATCH_REG4 0x15f0 +#define R128_GUI_SCRATCH_REG5 0x15f4 + +#define R128_GUI_STAT 0x1740 +# define R128_GUI_FIFOCNT_MASK 0x0fff +# define R128_GUI_ACTIVE (1 << 31) + +#define R128_MCLK_CNTL 0x000f +# define R128_FORCE_GCP (1 << 16) +# define R128_FORCE_PIPE3D_CP (1 << 17) +# define R128_FORCE_RCP (1 << 18) + +#define R128_PC_GUI_CTLSTAT 0x1748 +#define R128_PC_NGUI_CTLSTAT 0x0184 +# define R128_PC_FLUSH_GUI (3 << 0) +# define R128_PC_RI_GUI (1 << 2) +# define R128_PC_FLUSH_ALL 0x00ff +# define R128_PC_BUSY (1 << 31) + +#define R128_PRIM_TEX_CNTL_C 0x1cb0 + +#define R128_SCALE_3D_CNTL 0x1a00 +#define R128_SEC_TEX_CNTL_C 0x1d00 +#define R128_SEC_TEXTURE_BORDER_COLOR_C 0x1d3c +#define R128_SETUP_CNTL 0x1bc4 +#define R128_STEN_REF_MASK_C 0x1d40 + +#define R128_TEX_CNTL_C 0x1c9c +# define R128_TEX_CACHE_FLUSH (1 << 23) + +#define R128_WINDOW_XY_OFFSET 0x1bcc + + +/* CCE registers + */ +#define R128_PM4_BUFFER_OFFSET 0x0700 +#define R128_PM4_BUFFER_CNTL 0x0704 +# define R128_PM4_MASK (15 << 28) +# define R128_PM4_NONPM4 (0 << 28) +# define R128_PM4_192PIO (1 << 28) +# define R128_PM4_192BM (2 << 28) +# define R128_PM4_128PIO_64INDBM (3 << 28) +# define R128_PM4_128BM_64INDBM (4 << 28) +# define R128_PM4_64PIO_128INDBM (5 << 28) +# define R128_PM4_64BM_128INDBM (6 << 28) +# define R128_PM4_64PIO_64VCBM_64INDBM (7 << 28) +# define R128_PM4_64BM_64VCBM_64INDBM (8 << 28) +# define R128_PM4_64PIO_64VCPIO_64INDPIO (15 << 28) + +#define R128_PM4_BUFFER_WM_CNTL 0x0708 +# define R128_WMA_SHIFT 0 +# define R128_WMB_SHIFT 8 +# define R128_WMC_SHIFT 16 +# define R128_WB_WM_SHIFT 24 + +#define R128_PM4_BUFFER_DL_RPTR_ADDR 0x070c +#define R128_PM4_BUFFER_DL_RPTR 0x0710 +#define R128_PM4_BUFFER_DL_WPTR 0x0714 +# define R128_PM4_BUFFER_DL_DONE (1 << 31) + +#define R128_PM4_VC_FPU_SETUP 0x071c + +#define R128_PM4_IW_INDOFF 0x0738 +#define R128_PM4_IW_INDSIZE 0x073c + +#define R128_PM4_STAT 0x07b8 +# define R128_PM4_FIFOCNT_MASK 0x0fff +# define R128_PM4_BUSY (1 << 16) +# define R128_PM4_GUI_ACTIVE (1 << 31) + +#define R128_PM4_MICROCODE_ADDR 0x07d4 +#define R128_PM4_MICROCODE_RADDR 0x07d8 +#define R128_PM4_MICROCODE_DATAH 0x07dc +#define R128_PM4_MICROCODE_DATAL 0x07e0 + +#define R128_PM4_BUFFER_ADDR 0x07f0 +#define R128_PM4_MICRO_CNTL 0x07fc +# define R128_PM4_MICRO_FREERUN (1 << 30) + +#define R128_PM4_FIFO_DATA_EVEN 0x1000 +#define R128_PM4_FIFO_DATA_ODD 0x1004 + + +/* CCE command packets + */ +#define R128_CCE_PACKET0 0x00000000 +#define R128_CCE_PACKET1 0x40000000 +#define R128_CCE_PACKET2 0x80000000 +#define R128_CCE_PACKET3 0xC0000000 +# define R128_CNTL_HOSTDATA_BLT 0x00009400 +# define R128_CNTL_PAINT_MULTI 0x00009A00 +# define R128_CNTL_BITBLT_MULTI 0x00009B00 +# define R128_3D_RNDR_GEN_INDX_PRIM 0x00002300 + +#define R128_CCE_PACKET_MASK 0xC0000000 +#define R128_CCE_PACKET_COUNT_MASK 0x3fff0000 +#define R128_CCE_PACKET0_REG_MASK 0x000007ff +#define R128_CCE_PACKET1_REG0_MASK 0x000007ff +#define R128_CCE_PACKET1_REG1_MASK 0x003ff800 + +#define R128_CCE_VC_CNTL_PRIM_TYPE_NONE 0x00000000 +#define R128_CCE_VC_CNTL_PRIM_TYPE_POINT 0x00000001 +#define R128_CCE_VC_CNTL_PRIM_TYPE_LINE 0x00000002 +#define R128_CCE_VC_CNTL_PRIM_TYPE_POLY_LINE 0x00000003 +#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_LIST 0x00000004 +#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_FAN 0x00000005 +#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_STRIP 0x00000006 +#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_TYPE2 0x00000007 +#define R128_CCE_VC_CNTL_PRIM_WALK_IND 0x00000010 +#define R128_CCE_VC_CNTL_PRIM_WALK_LIST 0x00000020 +#define R128_CCE_VC_CNTL_PRIM_WALK_RING 0x00000030 +#define R128_CCE_VC_CNTL_NUM_SHIFT 16 + +#define R128_DATATYPE_CI8 2 +#define R128_DATATYPE_ARGB1555 3 +#define R128_DATATYPE_RGB565 4 +#define R128_DATATYPE_RGB888 5 +#define R128_DATATYPE_ARGB8888 6 +#define R128_DATATYPE_RGB332 7 +#define R128_DATATYPE_RGB8 9 +#define R128_DATATYPE_ARGB4444 15 + +/* Constants */ +#define R128_AGP_OFFSET 0x02000000 + +#define R128_WATERMARK_L 16 +#define R128_WATERMARK_M 8 +#define R128_WATERMARK_N 8 +#define R128_WATERMARK_K 128 + +#define R128_MAX_USEC_TIMEOUT 100000 /* 100 ms */ + +#define R128_LAST_FRAME_REG R128_GUI_SCRATCH_REG0 +#define R128_LAST_DISPATCH_REG R128_GUI_SCRATCH_REG1 +#define R128_MAX_VB_AGE 0xffffffff + +#define R128_MAX_VB_VERTS (0xffff) + + +#define R128_BASE(reg) ((unsigned long)(dev_priv->mmio->handle)) +#define R128_ADDR(reg) (R128_BASE(reg) + reg) + +#define R128_READ(reg) readl(R128_ADDR(reg)) +#define R128_WRITE(reg,val) writel(val,R128_ADDR(reg)) + +#define R128_READ8(reg) readb(R128_ADDR(reg)) +#define R128_WRITE8(reg,val) writeb(val,R128_ADDR(reg)) + +#define R128_WRITE_PLL(addr,val) \ +do { \ + R128_WRITE8(R128_CLOCK_CNTL_INDEX, ((addr) & 0x1f) | R128_PLL_WR_EN); \ + R128_WRITE(R128_CLOCK_CNTL_DATA, (val)); \ +} while (0) + +extern int R128_READ_PLL(drm_device_t *dev, int addr); + +#define R128CCE0(p,r,n) ((p) | ((n) << 16) | ((r) >> 2)) +#define R128CCE1(p,r1,r2) ((p) | (((r2) >> 2) << 11) | ((r1) >> 2)) +#define R128CCE2(p) ((p)) +#define R128CCE3(p,n) ((p) | ((n) << 16)) + + + + +#define CCE_PACKET0( reg, n ) (R128_CCE_PACKET0 | \ + ((n) << 16) | ((reg) >> 2)) +#define CCE_PACKET1( reg0, reg1 ) (R128_CCE_PACKET1 | \ + (((reg1) >> 2) << 11) | ((reg0) >> 2)) +#define CCE_PACKET2() (R128_CCE_PACKET2) +#define CCE_PACKET3( pkt, n ) (R128_CCE_PACKET3 | \ + (pkt) | ((n) << 16)) + + +#define r128_flush_write_combine() mb() + + +#define R128_VERBOSE 0 + +#define RING_LOCALS int write; unsigned int tail_mask; volatile u32 *ring; + +#define BEGIN_RING( n ) do { \ + if ( R128_VERBOSE ) { \ + DRM_INFO( "BEGIN_RING( %d ) in %s\n", \ + n, __FUNCTION__ ); \ + } \ + if ( dev_priv->ring.space < n * sizeof(u32) ) { \ + r128_wait_ring( dev_priv, n * sizeof(u32) ); \ + } \ + dev_priv->ring.space -= n * sizeof(u32); \ + ring = dev_priv->ring.start; \ + write = dev_priv->ring.tail; \ + tail_mask = dev_priv->ring.tail_mask; \ +} while (0) + +#define ADVANCE_RING() do { \ + if ( R128_VERBOSE ) { \ + DRM_INFO( "ADVANCE_RING() tail=0x%06x wr=0x%06x\n", \ + write, dev_priv->ring.tail ); \ + } \ + if ( write < 32 ) { \ + memcpy( dev_priv->ring.end, \ + dev_priv->ring.start, \ + write * sizeof(u32) ); \ + } \ + r128_flush_write_combine(); \ + dev_priv->ring.tail = write; \ + R128_WRITE( R128_PM4_BUFFER_DL_WPTR, write ); \ +} while (0) + +#define OUT_RING( x ) do { \ + if ( R128_VERBOSE ) { \ + DRM_INFO( " OUT_RING( 0x%08x ) at 0x%x\n", \ + (unsigned int)(x), write ); \ + } \ + ring[write++] = x; \ + write &= tail_mask; \ +} while (0) + +#define R128_PERFORMANCE_BOXES 0 + +#endif /* __R128_DRV_H__ */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/r128_state.c linux.ac/drivers/char/drm-4.0/r128_state.c --- linux.vanilla/drivers/char/drm-4.0/r128_state.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/r128_state.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,1605 @@ +/* r128_state.c -- State support for r128 -*- linux-c -*- + * Created: Thu Jan 27 02:53:43 2000 by gareth@valinux.com + * + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Gareth Hughes + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "r128_drv.h" +#include "drm.h" + + +/* ================================================================ + * CCE hardware state programming functions + */ + +static void r128_emit_clip_rects( drm_r128_private_t *dev_priv, + drm_clip_rect_t *boxes, int count ) +{ + u32 aux_sc_cntl = 0x00000000; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 17 ); + + if ( count >= 1 ) { + OUT_RING( CCE_PACKET0( R128_AUX1_SC_LEFT, 3 ) ); + OUT_RING( boxes[0].x1 ); + OUT_RING( boxes[0].x2 - 1 ); + OUT_RING( boxes[0].y1 ); + OUT_RING( boxes[0].y2 - 1 ); + + aux_sc_cntl |= (R128_AUX1_SC_EN | R128_AUX1_SC_MODE_OR); + } + if ( count >= 2 ) { + OUT_RING( CCE_PACKET0( R128_AUX2_SC_LEFT, 3 ) ); + OUT_RING( boxes[1].x1 ); + OUT_RING( boxes[1].x2 - 1 ); + OUT_RING( boxes[1].y1 ); + OUT_RING( boxes[1].y2 - 1 ); + + aux_sc_cntl |= (R128_AUX2_SC_EN | R128_AUX2_SC_MODE_OR); + } + if ( count >= 3 ) { + OUT_RING( CCE_PACKET0( R128_AUX3_SC_LEFT, 3 ) ); + OUT_RING( boxes[2].x1 ); + OUT_RING( boxes[2].x2 - 1 ); + OUT_RING( boxes[2].y1 ); + OUT_RING( boxes[2].y2 - 1 ); + + aux_sc_cntl |= (R128_AUX3_SC_EN | R128_AUX3_SC_MODE_OR); + } + + OUT_RING( CCE_PACKET0( R128_AUX_SC_CNTL, 0 ) ); + OUT_RING( aux_sc_cntl ); + + ADVANCE_RING(); +} + +static inline void r128_emit_core( drm_r128_private_t *dev_priv ) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 2 ); + + OUT_RING( CCE_PACKET0( R128_SCALE_3D_CNTL, 0 ) ); + OUT_RING( ctx->scale_3d_cntl ); + + ADVANCE_RING(); +} + +static inline void r128_emit_context( drm_r128_private_t *dev_priv ) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 13 ); + + OUT_RING( CCE_PACKET0( R128_DST_PITCH_OFFSET_C, 11 ) ); + OUT_RING( ctx->dst_pitch_offset_c ); + OUT_RING( ctx->dp_gui_master_cntl_c ); + OUT_RING( ctx->sc_top_left_c ); + OUT_RING( ctx->sc_bottom_right_c ); + OUT_RING( ctx->z_offset_c ); + OUT_RING( ctx->z_pitch_c ); + OUT_RING( ctx->z_sten_cntl_c ); + OUT_RING( ctx->tex_cntl_c ); + OUT_RING( ctx->misc_3d_state_cntl_reg ); + OUT_RING( ctx->texture_clr_cmp_clr_c ); + OUT_RING( ctx->texture_clr_cmp_msk_c ); + OUT_RING( ctx->fog_color_c ); + + ADVANCE_RING(); +} + +static inline void r128_emit_setup( drm_r128_private_t *dev_priv ) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 3 ); + + OUT_RING( CCE_PACKET1( R128_SETUP_CNTL, R128_PM4_VC_FPU_SETUP ) ); + OUT_RING( ctx->setup_cntl ); + OUT_RING( ctx->pm4_vc_fpu_setup ); + + ADVANCE_RING(); +} + +static inline void r128_emit_masks( drm_r128_private_t *dev_priv ) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 5 ); + + OUT_RING( CCE_PACKET0( R128_DP_WRITE_MASK, 0 ) ); + OUT_RING( ctx->dp_write_mask ); + + OUT_RING( CCE_PACKET0( R128_STEN_REF_MASK_C, 1 ) ); + OUT_RING( ctx->sten_ref_mask_c ); + OUT_RING( ctx->plane_3d_mask_c ); + + ADVANCE_RING(); +} + +static inline void r128_emit_window( drm_r128_private_t *dev_priv ) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 2 ); + + OUT_RING( CCE_PACKET0( R128_WINDOW_XY_OFFSET, 0 ) ); + OUT_RING( ctx->window_xy_offset ); + + ADVANCE_RING(); +} + +static inline void r128_emit_tex0( drm_r128_private_t *dev_priv ) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_context_regs_t *ctx = &sarea_priv->context_state; + drm_r128_texture_regs_t *tex = &sarea_priv->tex_state[0]; + int i; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 7 + R128_TEX_MAXLEVELS ); + + OUT_RING( CCE_PACKET0( R128_PRIM_TEX_CNTL_C, + 2 + R128_TEX_MAXLEVELS ) ); + OUT_RING( tex->tex_cntl ); + OUT_RING( tex->tex_combine_cntl ); + OUT_RING( ctx->tex_size_pitch_c ); + for ( i = 0 ; i < R128_TEX_MAXLEVELS ; i++ ) { + OUT_RING( tex->tex_offset[i] ); + } + + OUT_RING( CCE_PACKET0( R128_CONSTANT_COLOR_C, 1 ) ); + OUT_RING( ctx->constant_color_c ); + OUT_RING( tex->tex_border_color ); + + ADVANCE_RING(); +} + +static inline void r128_emit_tex1( drm_r128_private_t *dev_priv ) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_texture_regs_t *tex = &sarea_priv->tex_state[1]; + int i; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 5 + R128_TEX_MAXLEVELS ); + + OUT_RING( CCE_PACKET0( R128_SEC_TEX_CNTL_C, + 1 + R128_TEX_MAXLEVELS ) ); + OUT_RING( tex->tex_cntl ); + OUT_RING( tex->tex_combine_cntl ); + for ( i = 0 ; i < R128_TEX_MAXLEVELS ; i++ ) { + OUT_RING( tex->tex_offset[i] ); + } + + OUT_RING( CCE_PACKET0( R128_SEC_TEXTURE_BORDER_COLOR_C, 0 ) ); + OUT_RING( tex->tex_border_color ); + + ADVANCE_RING(); +} + +static inline void r128_emit_state( drm_r128_private_t *dev_priv ) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int dirty = sarea_priv->dirty; + + DRM_DEBUG( "%s: dirty=0x%08x\n", __FUNCTION__, dirty ); + + if ( dirty & R128_UPLOAD_CORE ) { + r128_emit_core( dev_priv ); + sarea_priv->dirty &= ~R128_UPLOAD_CORE; + } + + if ( dirty & R128_UPLOAD_CONTEXT ) { + r128_emit_context( dev_priv ); + sarea_priv->dirty &= ~R128_UPLOAD_CONTEXT; + } + + if ( dirty & R128_UPLOAD_SETUP ) { + r128_emit_setup( dev_priv ); + sarea_priv->dirty &= ~R128_UPLOAD_SETUP; + } + + if ( dirty & R128_UPLOAD_MASKS ) { + r128_emit_masks( dev_priv ); + sarea_priv->dirty &= ~R128_UPLOAD_MASKS; + } + + if ( dirty & R128_UPLOAD_WINDOW ) { + r128_emit_window( dev_priv ); + sarea_priv->dirty &= ~R128_UPLOAD_WINDOW; + } + + if ( dirty & R128_UPLOAD_TEX0 ) { + r128_emit_tex0( dev_priv ); + sarea_priv->dirty &= ~R128_UPLOAD_TEX0; + } + + if ( dirty & R128_UPLOAD_TEX1 ) { + r128_emit_tex1( dev_priv ); + sarea_priv->dirty &= ~R128_UPLOAD_TEX1; + } + + /* Turn off the texture cache flushing */ + sarea_priv->context_state.tex_cntl_c &= ~R128_TEX_CACHE_FLUSH; + + sarea_priv->dirty &= ~R128_REQUIRE_QUIESCENCE; +} + + +#if R128_PERFORMANCE_BOXES +/* ================================================================ + * Performance monitoring functions + */ + +static void r128_clear_box( drm_r128_private_t *dev_priv, + int x, int y, int w, int h, + int r, int g, int b ) +{ + u32 pitch, offset; + u32 fb_bpp, color; + RING_LOCALS; + + switch ( dev_priv->fb_bpp ) { + case 16: + fb_bpp = R128_GMC_DST_16BPP; + color = (((r & 0xf8) << 8) | + ((g & 0xfc) << 3) | + ((b & 0xf8) >> 3)); + break; + case 24: + fb_bpp = R128_GMC_DST_24BPP; + color = ((r << 16) | (g << 8) | b); + break; + case 32: + fb_bpp = R128_GMC_DST_32BPP; + color = (((0xff) << 24) | (r << 16) | (g << 8) | b); + break; + default: + return; + } + + offset = dev_priv->back_offset; + pitch = dev_priv->back_pitch >> 3; + + BEGIN_RING( 6 ); + + OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) ); + OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL + | R128_GMC_BRUSH_SOLID_COLOR + | fb_bpp + | R128_GMC_SRC_DATATYPE_COLOR + | R128_ROP3_P + | R128_GMC_CLR_CMP_CNTL_DIS + | R128_GMC_AUX_CLIP_DIS ); + + OUT_RING( (pitch << 21) | (offset >> 5) ); + OUT_RING( color ); + + OUT_RING( (x << 16) | y ); + OUT_RING( (w << 16) | h ); + + ADVANCE_RING(); +} + +static void r128_cce_performance_boxes( drm_r128_private_t *dev_priv ) +{ + if ( atomic_read( &dev_priv->idle_count ) == 0 ) { + r128_clear_box( dev_priv, 64, 4, 8, 8, 0, 255, 0 ); + } else { + atomic_set( &dev_priv->idle_count, 0 ); + } +} + +#endif + + +/* ================================================================ + * CCE command dispatch functions + */ + +static void r128_print_dirty( const char *msg, unsigned int flags ) +{ + DRM_INFO( "%s: (0x%x) %s%s%s%s%s%s%s%s%s\n", + msg, + flags, + (flags & R128_UPLOAD_CORE) ? "core, " : "", + (flags & R128_UPLOAD_CONTEXT) ? "context, " : "", + (flags & R128_UPLOAD_SETUP) ? "setup, " : "", + (flags & R128_UPLOAD_TEX0) ? "tex0, " : "", + (flags & R128_UPLOAD_TEX1) ? "tex1, " : "", + (flags & R128_UPLOAD_MASKS) ? "masks, " : "", + (flags & R128_UPLOAD_WINDOW) ? "window, " : "", + (flags & R128_UPLOAD_CLIPRECTS) ? "cliprects, " : "", + (flags & R128_REQUIRE_QUIESCENCE) ? "quiescence, " : "" ); +} + +static void r128_cce_dispatch_clear( drm_device_t *dev, + unsigned int flags, + int cx, int cy, int cw, int ch, + unsigned int clear_color, + unsigned int clear_depth ) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + int nbox = sarea_priv->nbox; + drm_clip_rect_t *pbox = sarea_priv->boxes; + u32 fb_bpp, depth_bpp; + int i; + RING_LOCALS; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + r128_update_ring_snapshot( dev_priv ); + + switch ( dev_priv->fb_bpp ) { + case 16: + fb_bpp = R128_GMC_DST_16BPP; + break; + case 32: + fb_bpp = R128_GMC_DST_32BPP; + break; + default: + return; + } + switch ( dev_priv->depth_bpp ) { + case 16: + depth_bpp = R128_GMC_DST_16BPP; + break; + case 24: + case 32: + depth_bpp = R128_GMC_DST_32BPP; + break; + default: + return; + } + + for ( i = 0 ; i < nbox ; i++ ) { + int x = pbox[i].x1; + int y = pbox[i].y1; + int w = pbox[i].x2 - x; + int h = pbox[i].y2 - y; + + DRM_DEBUG( "dispatch clear %d,%d-%d,%d flags 0x%x\n", + pbox[i].x1, pbox[i].y1, pbox[i].x2, + pbox[i].y2, flags ); + + if ( flags & (R128_FRONT | R128_BACK) ) { + BEGIN_RING( 2 ); + + OUT_RING( CCE_PACKET0( R128_DP_WRITE_MASK, 0 ) ); + OUT_RING( sarea_priv->context_state.plane_3d_mask_c ); + + ADVANCE_RING(); + } + + if ( flags & R128_FRONT ) { + BEGIN_RING( 6 ); + + OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) ); + OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL + | R128_GMC_BRUSH_SOLID_COLOR + | fb_bpp + | R128_GMC_SRC_DATATYPE_COLOR + | R128_ROP3_P + | R128_GMC_CLR_CMP_CNTL_DIS + | R128_GMC_AUX_CLIP_DIS ); + + OUT_RING( dev_priv->front_pitch_offset_c ); + OUT_RING( clear_color ); + + OUT_RING( (x << 16) | y ); + OUT_RING( (w << 16) | h ); + + ADVANCE_RING(); + } + + if ( flags & R128_BACK ) { + BEGIN_RING( 6 ); + + OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) ); + OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL + | R128_GMC_BRUSH_SOLID_COLOR + | fb_bpp + | R128_GMC_SRC_DATATYPE_COLOR + | R128_ROP3_P + | R128_GMC_CLR_CMP_CNTL_DIS + | R128_GMC_AUX_CLIP_DIS ); + + OUT_RING( dev_priv->back_pitch_offset_c ); + OUT_RING( clear_color ); + + OUT_RING( (x << 16) | y ); + OUT_RING( (w << 16) | h ); + + ADVANCE_RING(); + } + + if ( flags & R128_DEPTH ) { + BEGIN_RING( 6 ); + + OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) ); + OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL + | R128_GMC_BRUSH_SOLID_COLOR + | depth_bpp + | R128_GMC_SRC_DATATYPE_COLOR + | R128_ROP3_P + | R128_GMC_CLR_CMP_CNTL_DIS + | R128_GMC_AUX_CLIP_DIS + | R128_GMC_WR_MSK_DIS ); + + OUT_RING( dev_priv->depth_pitch_offset_c ); + OUT_RING( clear_depth ); + + OUT_RING( (x << 16) | y ); + OUT_RING( (w << 16) | h ); + + ADVANCE_RING(); + } + } +} + +static void r128_cce_dispatch_swap( drm_device_t *dev ) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + int nbox = sarea_priv->nbox; + drm_clip_rect_t *pbox = sarea_priv->boxes; + u32 fb_bpp; + int i; + RING_LOCALS; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + r128_update_ring_snapshot( dev_priv ); + +#if R128_PERFORMANCE_BOXES + /* Do some trivial performance monitoring... + */ + r128_cce_performance_boxes( dev_priv ); +#endif + + switch ( dev_priv->fb_bpp ) { + case 16: + fb_bpp = R128_GMC_DST_16BPP; + break; + case 32: + default: + fb_bpp = R128_GMC_DST_32BPP; + break; + } + + for ( i = 0 ; i < nbox ; i++ ) { + int x = pbox[i].x1; + int y = pbox[i].y1; + int w = pbox[i].x2 - x; + int h = pbox[i].y2 - y; + + BEGIN_RING( 7 ); + + OUT_RING( CCE_PACKET3( R128_CNTL_BITBLT_MULTI, 5 ) ); + OUT_RING( R128_GMC_SRC_PITCH_OFFSET_CNTL + | R128_GMC_DST_PITCH_OFFSET_CNTL + | R128_GMC_BRUSH_NONE + | fb_bpp + | R128_GMC_SRC_DATATYPE_COLOR + | R128_ROP3_S + | R128_DP_SRC_SOURCE_MEMORY + | R128_GMC_CLR_CMP_CNTL_DIS + | R128_GMC_AUX_CLIP_DIS + | R128_GMC_WR_MSK_DIS ); + + OUT_RING( dev_priv->back_pitch_offset_c ); + OUT_RING( dev_priv->front_pitch_offset_c ); + + OUT_RING( (x << 16) | y ); + OUT_RING( (x << 16) | y ); + OUT_RING( (w << 16) | h ); + + ADVANCE_RING(); + } + + /* Increment the frame counter. The client-side 3D driver must + * throttle the framerate by waiting for this value before + * performing the swapbuffer ioctl. + */ + dev_priv->sarea_priv->last_frame++; + + BEGIN_RING( 2 ); + + OUT_RING( CCE_PACKET0( R128_LAST_FRAME_REG, 0 ) ); + OUT_RING( dev_priv->sarea_priv->last_frame ); + + ADVANCE_RING(); +} + +static void r128_cce_dispatch_vertex( drm_device_t *dev, + drm_buf_t *buf ) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_buf_priv_t *buf_priv = buf->dev_private; + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + int format = sarea_priv->vc_format; + int offset = dev_priv->buffers->offset + buf->offset - dev->agp->base; + int size = buf->used; + int prim = buf_priv->prim; + int i = 0; + RING_LOCALS; + DRM_DEBUG( "%s: buf=%d nbox=%d\n", + __FUNCTION__, buf->idx, sarea_priv->nbox ); + + r128_update_ring_snapshot( dev_priv ); + + if ( 0 ) + r128_print_dirty( "dispatch_vertex", sarea_priv->dirty ); + + if ( buf->used ) { + buf_priv->dispatched = 1; + + if ( sarea_priv->dirty & ~R128_UPLOAD_CLIPRECTS ) { + r128_emit_state( dev_priv ); + } + + do { + /* Emit the next set of up to three cliprects */ + if ( i < sarea_priv->nbox ) { + r128_emit_clip_rects( dev_priv, + &sarea_priv->boxes[i], + sarea_priv->nbox - i ); + } + + /* Emit the vertex buffer rendering commands */ + BEGIN_RING( 5 ); + + OUT_RING( CCE_PACKET3( R128_3D_RNDR_GEN_INDX_PRIM, 3 ) ); + OUT_RING( offset ); + OUT_RING( size ); + OUT_RING( format ); + OUT_RING( prim | R128_CCE_VC_CNTL_PRIM_WALK_LIST | + (size << R128_CCE_VC_CNTL_NUM_SHIFT) ); + + ADVANCE_RING(); + + i += 3; + } while ( i < sarea_priv->nbox ); + } + + if ( buf_priv->discard ) { + buf_priv->age = dev_priv->sarea_priv->last_dispatch; + + /* Emit the vertex buffer age */ + BEGIN_RING( 2 ); + + OUT_RING( CCE_PACKET0( R128_LAST_DISPATCH_REG, 0 ) ); + OUT_RING( buf_priv->age ); + + ADVANCE_RING(); + + buf->pending = 1; + buf->used = 0; + /* FIXME: Check dispatched field */ + buf_priv->dispatched = 0; + } + + dev_priv->sarea_priv->last_dispatch++; + +#if 0 + if ( dev_priv->submit_age == R128_MAX_VB_AGE ) { + ret = r128_do_cce_idle( dev_priv ); + if ( ret < 0 ) return ret; + dev_priv->submit_age = 0; + r128_freelist_reset( dev ); + } +#endif + + sarea_priv->dirty &= ~R128_UPLOAD_CLIPRECTS; + sarea_priv->nbox = 0; +} + + + + +static void r128_cce_dispatch_indirect( drm_device_t *dev, + drm_buf_t *buf, + int start, int end ) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_buf_priv_t *buf_priv = buf->dev_private; + RING_LOCALS; + DRM_DEBUG( "indirect: buf=%d s=0x%x e=0x%x\n", + buf->idx, start, end ); + + r128_update_ring_snapshot( dev_priv ); + + if ( start != end ) { + int offset = (dev_priv->buffers->offset - dev->agp->base + + buf->offset + start); + int dwords = (end - start + 3) / sizeof(u32); + + /* Indirect buffer data must be an even number of + * dwords, so if we've been given an odd number we must + * pad the data with a Type-2 CCE packet. + */ + if ( dwords & 1 ) { + u32 *data = (u32 *) + ((char *)dev_priv->buffers->handle + + buf->offset + start); + data[dwords++] = R128_CCE_PACKET2; + } + + buf_priv->dispatched = 1; + + /* Fire off the indirect buffer */ + BEGIN_RING( 3 ); + + OUT_RING( CCE_PACKET0( R128_PM4_IW_INDOFF, 1 ) ); + OUT_RING( offset ); + OUT_RING( dwords ); + + ADVANCE_RING(); + } + + if ( buf_priv->discard ) { + buf_priv->age = dev_priv->sarea_priv->last_dispatch; + + /* Emit the indirect buffer age */ + BEGIN_RING( 2 ); + + OUT_RING( CCE_PACKET0( R128_LAST_DISPATCH_REG, 0 ) ); + OUT_RING( buf_priv->age ); + + ADVANCE_RING(); + + buf->pending = 1; + buf->used = 0; + /* FIXME: Check dispatched field */ + buf_priv->dispatched = 0; + } + + dev_priv->sarea_priv->last_dispatch++; + +#if 0 + if ( dev_priv->submit_age == R128_MAX_VB_AGE ) { + ret = r128_do_cce_idle( dev_priv ); + if ( ret < 0 ) return ret; + dev_priv->submit_age = 0; + r128_freelist_reset( dev ); + } +#endif +} + +static void r128_cce_dispatch_indices( drm_device_t *dev, + drm_buf_t *buf, + int start, int end, + int count ) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_buf_priv_t *buf_priv = buf->dev_private; + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + int format = sarea_priv->vc_format; + int offset = dev_priv->buffers->offset - dev->agp->base; + int prim = buf_priv->prim; + u32 *data; + int dwords; + int i = 0; + RING_LOCALS; + DRM_DEBUG( "indices: s=%d e=%d c=%d\n", start, end, count ); + + r128_update_ring_snapshot( dev_priv ); + + if ( 0 ) + r128_print_dirty( "dispatch_indices", sarea_priv->dirty ); + + if ( start != end ) { + buf_priv->dispatched = 1; + + if ( sarea_priv->dirty & ~R128_UPLOAD_CLIPRECTS ) { + r128_emit_state( dev_priv ); + } + + dwords = (end - start + 3) / sizeof(u32); + + data = (u32 *)((char *)dev_priv->buffers->handle + + buf->offset + start); + + data[0] = CCE_PACKET3( R128_3D_RNDR_GEN_INDX_PRIM, dwords-2 ); + + data[1] = offset; + data[2] = R128_MAX_VB_VERTS; + data[3] = format; + data[4] = (prim | R128_CCE_VC_CNTL_PRIM_WALK_IND | + (count << 16)); + + if ( count & 0x1 ) { + data[dwords-1] &= 0x0000ffff; + } + + do { + /* Emit the next set of up to three cliprects */ + if ( i < sarea_priv->nbox ) { + r128_emit_clip_rects( dev_priv, + &sarea_priv->boxes[i], + sarea_priv->nbox - i ); + } + + r128_cce_dispatch_indirect( dev, buf, start, end ); + + i += 3; + } while ( i < sarea_priv->nbox ); + } + + if ( buf_priv->discard ) { + buf_priv->age = dev_priv->sarea_priv->last_dispatch; + + /* Emit the vertex buffer age */ + BEGIN_RING( 2 ); + + OUT_RING( CCE_PACKET0( R128_LAST_DISPATCH_REG, 0 ) ); + OUT_RING( buf_priv->age ); + + ADVANCE_RING(); + + buf->pending = 1; + /* FIXME: Check dispatched field */ + buf_priv->dispatched = 0; + } + + dev_priv->sarea_priv->last_dispatch++; + +#if 0 + if ( dev_priv->submit_age == R128_MAX_VB_AGE ) { + ret = r128_do_cce_idle( dev_priv ); + if ( ret < 0 ) return ret; + dev_priv->submit_age = 0; + r128_freelist_reset( dev ); + } +#endif + + sarea_priv->dirty &= ~R128_UPLOAD_CLIPRECTS; + sarea_priv->nbox = 0; +} + +static int r128_cce_dispatch_blit( drm_device_t *dev, + drm_r128_blit_t *blit ) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_r128_buf_priv_t *buf_priv; + u32 *data; + int dword_shift, dwords; + RING_LOCALS; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + r128_update_ring_snapshot( dev_priv ); + + /* The compiler won't optimize away a division by a variable, + * even if the only legal values are powers of two. Thus, we'll + * use a shift instead. + */ + switch ( blit->format ) { + case R128_DATATYPE_ARGB1555: + case R128_DATATYPE_RGB565: + case R128_DATATYPE_ARGB4444: + dword_shift = 1; + break; + case R128_DATATYPE_ARGB8888: + dword_shift = 0; + break; + default: + DRM_ERROR( "invalid blit format %d\n", blit->format ); + return -EINVAL; + } + + /* Flush the pixel cache, and mark the contents as Read Invalid. + * This ensures no pixel data gets mixed up with the texture + * data from the host data blit, otherwise part of the texture + * image may be corrupted. + */ + BEGIN_RING( 2 ); + + OUT_RING( CCE_PACKET0( R128_PC_GUI_CTLSTAT, 0 ) ); + OUT_RING( R128_PC_RI_GUI | R128_PC_FLUSH_GUI ); + + ADVANCE_RING(); + + /* Dispatch the indirect buffer. + */ + buf = dma->buflist[blit->idx]; + buf_priv = buf->dev_private; + + if ( buf->pid != current->pid ) { + DRM_ERROR( "process %d using buffer owned by %d\n", + current->pid, buf->pid ); + return -EINVAL; + } + if ( buf->pending ) { + DRM_ERROR( "sending pending buffer %d\n", blit->idx ); + return -EINVAL; + } + + buf_priv->discard = 1; + + dwords = (blit->width * blit->height) >> dword_shift; + + data = (u32 *)((char *)dev_priv->buffers->handle + buf->offset); + + data[0] = CCE_PACKET3( R128_CNTL_HOSTDATA_BLT, dwords + 6 ); + data[1] = ( R128_GMC_DST_PITCH_OFFSET_CNTL + | R128_GMC_BRUSH_NONE + | (blit->format << 8) + | R128_GMC_SRC_DATATYPE_COLOR + | R128_ROP3_S + | R128_DP_SRC_SOURCE_HOST_DATA + | R128_GMC_CLR_CMP_CNTL_DIS + | R128_GMC_AUX_CLIP_DIS + | R128_GMC_WR_MSK_DIS ); + + data[2] = (blit->pitch << 21) | (blit->offset >> 5); + data[3] = 0xffffffff; + data[4] = 0xffffffff; + data[5] = (blit->y << 16) | blit->x; + data[6] = (blit->height << 16) | blit->width; + data[7] = dwords; + + buf->used = (dwords + 8) * sizeof(u32); + + r128_cce_dispatch_indirect( dev, buf, 0, buf->used ); + + /* Flush the pixel cache after the blit completes. This ensures + * the texture data is written out to memory before rendering + * continues. + */ + BEGIN_RING( 2 ); + + OUT_RING( CCE_PACKET0( R128_PC_GUI_CTLSTAT, 0 ) ); + OUT_RING( R128_PC_FLUSH_GUI ); + + ADVANCE_RING(); + + return 0; +} + + +/* ================================================================ + * Tiled depth buffer management + * + * FIXME: These should all set the destination write mask for when we + * have hardware stencil support. + */ + +static int r128_cce_dispatch_write_span( drm_device_t *dev, + drm_r128_depth_t *depth ) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int count, x, y; + u32 *buffer; + u8 *mask; + u32 depth_bpp; + int i; + RING_LOCALS; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + r128_update_ring_snapshot( dev_priv ); + + switch ( dev_priv->depth_bpp ) { + case 16: + depth_bpp = R128_GMC_DST_16BPP; + break; + case 24: + case 32: + depth_bpp = R128_GMC_DST_32BPP; + break; + default: + return -EINVAL; + } + + count = depth->n; + if ( copy_from_user( &x, depth->x, sizeof(x) ) ) { + return -EFAULT; + } + if ( copy_from_user( &y, depth->y, sizeof(y) ) ) { + return -EFAULT; + } + + buffer = kmalloc( depth->n * sizeof(u32), 0 ); + if ( buffer == NULL ) + return -ENOMEM; + if ( copy_from_user( buffer, depth->buffer, + depth->n * sizeof(u32) ) ) { + kfree( buffer ); + return -EFAULT; + } + + if ( depth->mask ) { + mask = kmalloc( depth->n * sizeof(u8), 0 ); + if ( mask == NULL ) { + kfree( buffer ); + return -ENOMEM; + } + if ( copy_from_user( mask, depth->mask, + depth->n * sizeof(u8) ) ) { + kfree( buffer ); + kfree( mask ); + return -EFAULT; + } + + for ( i = 0 ; i < count ; i++, x++ ) { + if ( mask[i] ) { + BEGIN_RING( 6 ); + + OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, + 4 ) ); + OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL + | R128_GMC_BRUSH_SOLID_COLOR + | depth_bpp + | R128_GMC_SRC_DATATYPE_COLOR + | R128_ROP3_P + | R128_GMC_CLR_CMP_CNTL_DIS + | R128_GMC_WR_MSK_DIS ); + + OUT_RING( dev_priv->depth_pitch_offset_c ); + OUT_RING( buffer[i] ); + + OUT_RING( (x << 16) | y ); + OUT_RING( (1 << 16) | 1 ); + + ADVANCE_RING(); + } + } + + kfree( mask ); + } else { + for ( i = 0 ; i < count ; i++, x++ ) { + BEGIN_RING( 6 ); + + OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) ); + OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL + | R128_GMC_BRUSH_SOLID_COLOR + | depth_bpp + | R128_GMC_SRC_DATATYPE_COLOR + | R128_ROP3_P + | R128_GMC_CLR_CMP_CNTL_DIS + | R128_GMC_WR_MSK_DIS ); + + OUT_RING( dev_priv->depth_pitch_offset_c ); + OUT_RING( buffer[i] ); + + OUT_RING( (x << 16) | y ); + OUT_RING( (1 << 16) | 1 ); + + ADVANCE_RING(); + } + } + + kfree( buffer ); + + return 0; +} + +static int r128_cce_dispatch_write_pixels( drm_device_t *dev, + drm_r128_depth_t *depth ) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int count, *x, *y; + u32 *buffer; + u8 *mask; + u32 depth_bpp; + int i; + RING_LOCALS; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + r128_update_ring_snapshot( dev_priv ); + + switch ( dev_priv->depth_bpp ) { + case 16: + depth_bpp = R128_GMC_DST_16BPP; + break; + case 24: + case 32: + depth_bpp = R128_GMC_DST_32BPP; + break; + default: + return -EINVAL; + } + + count = depth->n; + + x = kmalloc( count * sizeof(*x), 0 ); + if ( x == NULL ) { + return -ENOMEM; + } + y = kmalloc( count * sizeof(*y), 0 ); + if ( y == NULL ) { + kfree( x ); + return -ENOMEM; + } + if ( copy_from_user( x, depth->x, count * sizeof(int) ) ) { + kfree( x ); + kfree( y ); + return -EFAULT; + } + if ( copy_from_user( y, depth->y, count * sizeof(int) ) ) { + kfree( x ); + kfree( y ); + return -EFAULT; + } + + buffer = kmalloc( depth->n * sizeof(u32), 0 ); + if ( buffer == NULL ) { + kfree( x ); + kfree( y ); + return -ENOMEM; + } + if ( copy_from_user( buffer, depth->buffer, + depth->n * sizeof(u32) ) ) { + kfree( x ); + kfree( y ); + kfree( buffer ); + return -EFAULT; + } + + if ( depth->mask ) { + mask = kmalloc( depth->n * sizeof(u8), 0 ); + if ( mask == NULL ) { + kfree( x ); + kfree( y ); + kfree( buffer ); + return -ENOMEM; + } + if ( copy_from_user( mask, depth->mask, + depth->n * sizeof(u8) ) ) { + kfree( x ); + kfree( y ); + kfree( buffer ); + kfree( mask ); + return -EFAULT; + } + + for ( i = 0 ; i < count ; i++ ) { + if ( mask[i] ) { + BEGIN_RING( 6 ); + + OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, + 4 ) ); + OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL + | R128_GMC_BRUSH_SOLID_COLOR + | depth_bpp + | R128_GMC_SRC_DATATYPE_COLOR + | R128_ROP3_P + | R128_GMC_CLR_CMP_CNTL_DIS + | R128_GMC_WR_MSK_DIS ); + + OUT_RING( dev_priv->depth_pitch_offset_c ); + OUT_RING( buffer[i] ); + + OUT_RING( (x[i] << 16) | y[i] ); + OUT_RING( (1 << 16) | 1 ); + + ADVANCE_RING(); + } + } + + kfree( mask ); + } else { + for ( i = 0 ; i < count ; i++ ) { + BEGIN_RING( 6 ); + + OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) ); + OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL + | R128_GMC_BRUSH_SOLID_COLOR + | depth_bpp + | R128_GMC_SRC_DATATYPE_COLOR + | R128_ROP3_P + | R128_GMC_CLR_CMP_CNTL_DIS + | R128_GMC_WR_MSK_DIS ); + + OUT_RING( dev_priv->depth_pitch_offset_c ); + OUT_RING( buffer[i] ); + + OUT_RING( (x[i] << 16) | y[i] ); + OUT_RING( (1 << 16) | 1 ); + + ADVANCE_RING(); + } + } + + kfree( x ); + kfree( y ); + kfree( buffer ); + + return 0; +} + +static int r128_cce_dispatch_read_span( drm_device_t *dev, + drm_r128_depth_t *depth ) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int count, x, y; + u32 depth_bpp; + RING_LOCALS; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + r128_update_ring_snapshot( dev_priv ); + + switch ( dev_priv->depth_bpp ) { + case 16: + depth_bpp = R128_GMC_DST_16BPP; + break; + case 24: + case 32: + depth_bpp = R128_GMC_DST_32BPP; + break; + default: + return -EINVAL; + } + + count = depth->n; + if ( copy_from_user( &x, depth->x, sizeof(x) ) ) { + return -EFAULT; + } + if ( copy_from_user( &y, depth->y, sizeof(y) ) ) { + return -EFAULT; + } + + BEGIN_RING( 7 ); + + OUT_RING( CCE_PACKET3( R128_CNTL_BITBLT_MULTI, 5 ) ); + OUT_RING( R128_GMC_SRC_PITCH_OFFSET_CNTL + | R128_GMC_DST_PITCH_OFFSET_CNTL + | R128_GMC_BRUSH_NONE + | depth_bpp + | R128_GMC_SRC_DATATYPE_COLOR + | R128_ROP3_S + | R128_DP_SRC_SOURCE_MEMORY + | R128_GMC_CLR_CMP_CNTL_DIS + | R128_GMC_WR_MSK_DIS ); + + OUT_RING( dev_priv->depth_pitch_offset_c ); + OUT_RING( dev_priv->span_pitch_offset_c ); + + OUT_RING( (x << 16) | y ); + OUT_RING( (0 << 16) | 0 ); + OUT_RING( (count << 16) | 1 ); + + ADVANCE_RING(); + + return 0; +} + +static int r128_cce_dispatch_read_pixels( drm_device_t *dev, + drm_r128_depth_t *depth ) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int count, *x, *y; + u32 depth_bpp; + int i; + RING_LOCALS; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + r128_update_ring_snapshot( dev_priv ); + + switch ( dev_priv->depth_bpp ) { + case 16: + depth_bpp = R128_GMC_DST_16BPP; + break; + case 24: + case 32: + depth_bpp = R128_GMC_DST_32BPP; + break; + default: + return -EINVAL; + } + + count = depth->n; + if ( count > dev_priv->depth_pitch ) { + count = dev_priv->depth_pitch; + } + + x = kmalloc( count * sizeof(*x), 0 ); + if ( x == NULL ) { + return -ENOMEM; + } + y = kmalloc( count * sizeof(*y), 0 ); + if ( y == NULL ) { + kfree( x ); + return -ENOMEM; + } + if ( copy_from_user( x, depth->x, count * sizeof(int) ) ) { + kfree( x ); + kfree( y ); + return -EFAULT; + } + if ( copy_from_user( y, depth->y, count * sizeof(int) ) ) { + kfree( x ); + kfree( y ); + return -EFAULT; + } + + for ( i = 0 ; i < count ; i++ ) { + BEGIN_RING( 7 ); + + OUT_RING( CCE_PACKET3( R128_CNTL_BITBLT_MULTI, 5 ) ); + OUT_RING( R128_GMC_SRC_PITCH_OFFSET_CNTL + | R128_GMC_DST_PITCH_OFFSET_CNTL + | R128_GMC_BRUSH_NONE + | depth_bpp + | R128_GMC_SRC_DATATYPE_COLOR + | R128_ROP3_S + | R128_DP_SRC_SOURCE_MEMORY + | R128_GMC_CLR_CMP_CNTL_DIS + | R128_GMC_WR_MSK_DIS ); + + OUT_RING( dev_priv->depth_pitch_offset_c ); + OUT_RING( dev_priv->span_pitch_offset_c ); + + OUT_RING( (x[i] << 16) | y[i] ); + OUT_RING( (i << 16) | 0 ); + OUT_RING( (1 << 16) | 1 ); + + ADVANCE_RING(); + } + + kfree( x ); + kfree( y ); + + return 0; +} + + +/* ================================================================ + * Polygon stipple + */ + +static void r128_cce_dispatch_stipple( drm_device_t *dev, u32 *stipple ) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int i; + RING_LOCALS; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + r128_update_ring_snapshot( dev_priv ); + + BEGIN_RING( 33 ); + + OUT_RING( CCE_PACKET0( R128_BRUSH_DATA0, 31 ) ); + for ( i = 0 ; i < 32 ; i++ ) { + OUT_RING( stipple[i] ); + } + + ADVANCE_RING(); +} + + +/* ================================================================ + * IOCTL functions + */ + +int r128_cce_clear( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_clear_t clear; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "r128_cce_clear called without lock held\n" ); + return -EINVAL; + } + + if ( copy_from_user( &clear, (drm_r128_clear_t *) arg, + sizeof(clear) ) ) + return -EFAULT; + + if ( sarea_priv->nbox > R128_NR_SAREA_CLIPRECTS ) + sarea_priv->nbox = R128_NR_SAREA_CLIPRECTS; + + r128_cce_dispatch_clear( dev, clear.flags, + clear.x, clear.y, clear.w, clear.h, + clear.clear_color, clear.clear_depth ); + + /* Make sure we restore the 3D state next time. + */ + dev_priv->sarea_priv->dirty |= R128_UPLOAD_CONTEXT | R128_UPLOAD_MASKS; + + return 0; +} + +int r128_cce_swap( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "r128_cce_swap called without lock held\n" ); + return -EINVAL; + } + + if ( sarea_priv->nbox > R128_NR_SAREA_CLIPRECTS ) + sarea_priv->nbox = R128_NR_SAREA_CLIPRECTS; + + r128_cce_dispatch_swap( dev ); + + /* Make sure we restore the 3D state next time. + */ + dev_priv->sarea_priv->dirty |= R128_UPLOAD_CONTEXT | R128_UPLOAD_MASKS; + + return 0; +} + +int r128_cce_vertex( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_r128_buf_priv_t *buf_priv; + drm_r128_vertex_t vertex; + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + if ( !dev_priv || dev_priv->is_pci ) { + DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &vertex, (drm_r128_vertex_t *)arg, + sizeof(vertex) ) ) + return -EFAULT; + + DRM_DEBUG( "%s: pid=%d index=%d count=%d discard=%d\n", + __FUNCTION__, current->pid, + vertex.idx, vertex.count, vertex.discard ); + + if ( vertex.idx < 0 || vertex.idx >= dma->buf_count ) { + DRM_ERROR( "buffer index %d (of %d max)\n", + vertex.idx, dma->buf_count - 1 ); + return -EINVAL; + } + if ( vertex.prim < 0 || + vertex.prim > R128_CCE_VC_CNTL_PRIM_TYPE_TRI_TYPE2 ) { + DRM_ERROR( "buffer prim %d\n", vertex.prim ); + return -EINVAL; + } + + buf = dma->buflist[vertex.idx]; + buf_priv = buf->dev_private; + + if ( buf->pid != current->pid ) { + DRM_ERROR( "process %d using buffer owned by %d\n", + current->pid, buf->pid ); + return -EINVAL; + } + if ( buf->pending ) { + DRM_ERROR( "sending pending buffer %d\n", vertex.idx ); + return -EINVAL; + } + + buf->used = vertex.count; + buf_priv->prim = vertex.prim; + buf_priv->discard = vertex.discard; + + r128_cce_dispatch_vertex( dev, buf ); + + return 0; +} + +int r128_cce_indices( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_r128_buf_priv_t *buf_priv; + drm_r128_indices_t elts; + int count; + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + if ( !dev_priv || dev_priv->is_pci ) { + DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &elts, (drm_r128_indices_t *)arg, + sizeof(elts) ) ) + return -EFAULT; + + DRM_DEBUG( "%s: pid=%d buf=%d s=%d e=%d d=%d\n", + __FUNCTION__, current->pid, + elts.idx, elts.start, elts.end, elts.discard ); + + if ( elts.idx < 0 || elts.idx >= dma->buf_count ) { + DRM_ERROR( "buffer index %d (of %d max)\n", + elts.idx, dma->buf_count - 1 ); + return -EINVAL; + } + if ( elts.prim < 0 || + elts.prim > R128_CCE_VC_CNTL_PRIM_TYPE_TRI_TYPE2 ) { + DRM_ERROR( "buffer prim %d\n", elts.prim ); + return -EINVAL; + } + + buf = dma->buflist[elts.idx]; + buf_priv = buf->dev_private; + + if ( buf->pid != current->pid ) { + DRM_ERROR( "process %d using buffer owned by %d\n", + current->pid, buf->pid ); + return -EINVAL; + } + if ( buf->pending ) { + DRM_ERROR( "sending pending buffer %d\n", elts.idx ); + return -EINVAL; + } + + count = (elts.end - elts.start) / sizeof(u16); + elts.start -= R128_INDEX_PRIM_OFFSET; + + if ( elts.start & 0x7 ) { + DRM_ERROR( "misaligned buffer 0x%x\n", elts.start ); + return -EINVAL; + } + if ( elts.start < buf->used ) { + DRM_ERROR( "no header 0x%x - 0x%x\n", elts.start, buf->used ); + return -EINVAL; + } + + buf->used = elts.end; + buf_priv->prim = elts.prim; + buf_priv->discard = elts.discard; + + r128_cce_dispatch_indices( dev, buf, elts.start, elts.end, count ); + + return 0; +} + +int r128_cce_blit( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_r128_blit_t blit; + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &blit, (drm_r128_blit_t *)arg, + sizeof(blit) ) ) + return -EFAULT; + + DRM_DEBUG( "%s: pid=%d index=%d\n", + __FUNCTION__, current->pid, blit.idx ); + + if ( blit.idx < 0 || blit.idx >= dma->buf_count ) { + DRM_ERROR( "buffer index %d (of %d max)\n", + blit.idx, dma->buf_count - 1 ); + return -EINVAL; + } + + return r128_cce_dispatch_blit( dev, &blit ); +} + +int r128_cce_depth( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_depth_t depth; + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &depth, (drm_r128_depth_t *)arg, + sizeof(depth) ) ) + return -EFAULT; + + switch ( depth.func ) { + case R128_WRITE_SPAN: + return r128_cce_dispatch_write_span( dev, &depth ); + case R128_WRITE_PIXELS: + return r128_cce_dispatch_write_pixels( dev, &depth ); + case R128_READ_SPAN: + return r128_cce_dispatch_read_span( dev, &depth ); + case R128_READ_PIXELS: + return r128_cce_dispatch_read_pixels( dev, &depth ); + } + + return -EINVAL; +} + +int r128_cce_stipple( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_stipple_t stipple; + u32 mask[32]; + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &stipple, (drm_r128_stipple_t *)arg, + sizeof(stipple) ) ) + return -EFAULT; + + if ( copy_from_user( &mask, stipple.mask, + 32 * sizeof(u32) ) ) + return -EFAULT; + + r128_cce_dispatch_stipple( dev, mask ); + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/radeon_bufs.c linux.ac/drivers/char/drm-4.0/radeon_bufs.c --- linux.vanilla/drivers/char/drm-4.0/radeon_bufs.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/radeon_bufs.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,312 @@ +/* radeon_bufs.c -- IOCTLs to manage buffers -*- linux-c -*- + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Kevin E. Martin + * Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#define __NO_VERSION__ +#include +#include "drmP.h" +#include "radeon_drv.h" +#include "linux/un.h" + + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) +int radeon_addbufs_agp(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + drm_buf_entry_t *entry; + drm_buf_t *buf; + unsigned long offset; + unsigned long agp_offset; + int count; + int order; + int size; + int alignment; + int page_order; + int total; + int byte_count; + int i; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, (drm_buf_desc_t *)arg, sizeof(request))) + return -EFAULT; + + count = request.count; + order = drm_order(request.size); + size = 1 << order; + + alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + byte_count = 0; + agp_offset = dev->agp->base + request.agp_start; + + DRM_DEBUG("count: %d\n", count); + DRM_DEBUG("order: %d\n", order); + DRM_DEBUG("size: %d\n", size); + DRM_DEBUG("agp_offset: %ld\n", agp_offset); + DRM_DEBUG("alignment: %d\n", alignment); + DRM_DEBUG("page_order: %d\n", page_order); + DRM_DEBUG("total: %d\n", total); + + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + if (dev->queue_count) return -EBUSY; /* Not while in use */ + + spin_lock(&dev->count_lock); + if (dev->buf_use) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + atomic_inc(&dev->buf_alloc); + spin_unlock(&dev->count_lock); + + down(&dev->struct_sem); + entry = &dma->bufs[order]; + if (entry->buf_count) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; /* May only call once for each order */ + } + + /* Might be too low a limit. XFree folks need to fix this properly */ + + if(count < 0 || count > 4096) + { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -EINVAL; + } + + entry->buflist = drm_alloc(count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + if (!entry->buflist) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->buflist, 0, count * sizeof(*entry->buflist)); + + entry->buf_size = size; + entry->page_order = page_order; + offset = 0; + + for (offset = 0; + entry->buf_count < count; + offset += alignment, ++entry->buf_count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + buf->offset = (dma->byte_count + offset); + buf->address = (void *)(agp_offset + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->pid = 0; + + buf->dev_priv_size = sizeof(drm_radeon_buf_priv_t); + buf->dev_private = drm_alloc(sizeof(drm_radeon_buf_priv_t), + DRM_MEM_BUFS); + if (!buf->dev_private) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(buf->dev_private, 0, buf->dev_priv_size); + +#if DRM_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + + byte_count += PAGE_SIZE << page_order; + + DRM_DEBUG("buffer %d @ %p\n", + entry->buf_count, buf->address); + } + + DRM_DEBUG("byte_count: %d\n", byte_count); + + dma->buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS); + for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++) + dma->buflist[i] = &entry->buflist[i - dma->buf_count]; + + dma->buf_count += entry->buf_count; + dma->byte_count += byte_count; + + drm_freelist_create(&entry->freelist, entry->buf_count); + for (i = 0; i < entry->buf_count; i++) { + drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]); + } + + up(&dev->struct_sem); + + request.count = entry->buf_count; + request.size = size; + + if (copy_to_user((drm_buf_desc_t *)arg, &request, sizeof(request))) + return -EFAULT; + + dma->flags = _DRM_DMA_USE_AGP; + + atomic_dec(&dev->buf_alloc); + return 0; +} +#endif + +int radeon_addbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_buf_desc_t request; + + if (!dev_priv || dev_priv->is_pci) return -EINVAL; + + if (copy_from_user(&request, (drm_buf_desc_t *)arg, sizeof(request))) + return -EFAULT; + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + if (request.flags & _DRM_AGP_BUFFER) + return radeon_addbufs_agp(inode, filp, cmd, arg); + else +#endif + return -EINVAL; +} + +int radeon_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + const int zero = 0; + unsigned long virtual; + unsigned long address; + drm_buf_map_t request; + int i; + + if (!dma || !dev_priv || dev_priv->is_pci) return -EINVAL; + + DRM_DEBUG("\n"); + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + if (copy_from_user(&request, (drm_buf_map_t *)arg, sizeof(request))) + return -EFAULT; + + if (request.count >= dma->buf_count) { + if (dma->flags & _DRM_DMA_USE_AGP) { + drm_map_t *map; + + map = dev_priv->buffers; + if (!map) { + retcode = -EINVAL; + goto done; + } + + down_write(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, map->size, + PROT_READ|PROT_WRITE, + MAP_SHARED, + (unsigned long)map->offset); + up_write(¤t->mm->mmap_sem); + } else { + down_write(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, dma->byte_count, + PROT_READ|PROT_WRITE, MAP_SHARED, 0); + up_write(¤t->mm->mmap_sem); + } + if (virtual > -1024UL) { + /* Real error */ + retcode = (signed long)virtual; + goto done; + } + request.virtual = (void *)virtual; + + for (i = 0; i < dma->buf_count; i++) { + if (copy_to_user(&request.list[i].idx, + &dma->buflist[i]->idx, + sizeof(request.list[0].idx))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].total, + &dma->buflist[i]->total, + sizeof(request.list[0].total))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].used, + &zero, + sizeof(zero))) { + retcode = -EFAULT; + goto done; + } + address = virtual + dma->buflist[i]->offset; + if (copy_to_user(&request.list[i].address, + &address, + sizeof(address))) { + retcode = -EFAULT; + goto done; + } + } + } + done: + request.count = dma->buf_count; + DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode); + + if (copy_to_user((drm_buf_map_t *)arg, &request, sizeof(request))) + return -EFAULT; + + return retcode; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/radeon_context.c linux.ac/drivers/char/drm-4.0/radeon_context.c --- linux.vanilla/drivers/char/drm-4.0/radeon_context.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/radeon_context.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,215 @@ +/* radeon_context.c -- IOCTLs for Radeon contexts -*- linux-c -*- + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Kevin E. Martin + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "radeon_drv.h" + +extern drm_ctx_t radeon_res_ctx; + +static int radeon_alloc_queue(drm_device_t *dev) +{ + return drm_ctxbitmap_next(dev); +} + +int radeon_context_switch(drm_device_t *dev, int old, int new) +{ + char buf[64]; + + atomic_inc(&dev->total_ctx); + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + if (drm_flags & DRM_FLAG_NOCTX) { + radeon_context_switch_complete(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + drm_write_string(dev, buf); + } + + return 0; +} + +int radeon_context_switch_complete(drm_device_t *dev, int new) +{ + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + /* If a context switch is ever initiated + when the kernel holds the lock, release + that lock here. */ +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + wake_up(&dev->context_wait); + + return 0; +} + + +int radeon_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res))) + return -EFAULT; + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + if (copy_to_user(&res.contexts[i], &i, sizeof(i))) + return -EFAULT; + } + } + res.count = DRM_RESERVED_CONTEXTS; + if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res))) + return -EFAULT; + return 0; +} + + +int radeon_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + if ((ctx.handle = radeon_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) { + /* Skip kernel's context and get a new one. */ + ctx.handle = radeon_alloc_queue(dev); + } + DRM_DEBUG("%d\n", ctx.handle); + if (ctx.handle == -1) { + DRM_DEBUG("Not enough free contexts.\n"); + /* Should this return -EBUSY instead? */ + return -ENOMEM; + } + + if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx))) + return -EFAULT; + return 0; +} + +int radeon_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx))) + return -EFAULT; + if (ctx.flags==_DRM_CONTEXT_PRESERVED) + radeon_res_ctx.handle=ctx.handle; + return 0; +} + +int radeon_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx))) + return -EFAULT; + /* This is 0, because we don't hanlde any context flags */ + ctx.flags = 0; + if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx))) + return -EFAULT; + return 0; +} + +int radeon_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + return radeon_context_switch(dev, dev->last_context, ctx.handle); +} + +int radeon_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + radeon_context_switch_complete(dev, ctx.handle); + + return 0; +} + +int radeon_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + drm_ctxbitmap_free(dev, ctx.handle); + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/radeon_cp.c linux.ac/drivers/char/drm-4.0/radeon_cp.c --- linux.vanilla/drivers/char/drm-4.0/radeon_cp.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/radeon_cp.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,1314 @@ +/* radeon_cp.c -- CP support for Radeon -*- linux-c -*- + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Kevin E. Martin + * Gareth Hughes + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "radeon_drv.h" + +#include /* For task queue support */ +#include + +#define RADEON_FIFO_DEBUG 0 + + +/* CP microcode (from ATI) */ +static u32 radeon_cp_microcode[][2] = { + { 0x21007000, 0000000000 }, + { 0x20007000, 0000000000 }, + { 0x000000b4, 0x00000004 }, + { 0x000000b8, 0x00000004 }, + { 0x6f5b4d4c, 0000000000 }, + { 0x4c4c427f, 0000000000 }, + { 0x5b568a92, 0000000000 }, + { 0x4ca09c6d, 0000000000 }, + { 0xad4c4c4c, 0000000000 }, + { 0x4ce1af3d, 0000000000 }, + { 0xd8afafaf, 0000000000 }, + { 0xd64c4cdc, 0000000000 }, + { 0x4cd10d10, 0000000000 }, + { 0x000f0000, 0x00000016 }, + { 0x362f242d, 0000000000 }, + { 0x00000012, 0x00000004 }, + { 0x000f0000, 0x00000016 }, + { 0x362f282d, 0000000000 }, + { 0x000380e7, 0x00000002 }, + { 0x04002c97, 0x00000002 }, + { 0x000f0001, 0x00000016 }, + { 0x333a3730, 0000000000 }, + { 0x000077ef, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x00000021, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00061000, 0x00000002 }, + { 0x00000021, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00061000, 0x00000002 }, + { 0x00000021, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00000017, 0x00000004 }, + { 0x0003802b, 0x00000002 }, + { 0x040067e0, 0x00000002 }, + { 0x00000017, 0x00000004 }, + { 0x000077e0, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x000037e1, 0x00000002 }, + { 0x040067e1, 0x00000006 }, + { 0x000077e0, 0x00000002 }, + { 0x000077e1, 0x00000002 }, + { 0x000077e1, 0x00000006 }, + { 0xffffffff, 0000000000 }, + { 0x10000000, 0000000000 }, + { 0x0003802b, 0x00000002 }, + { 0x040067e0, 0x00000006 }, + { 0x00007675, 0x00000002 }, + { 0x00007676, 0x00000002 }, + { 0x00007677, 0x00000002 }, + { 0x00007678, 0x00000006 }, + { 0x0003802c, 0x00000002 }, + { 0x04002676, 0x00000002 }, + { 0x00007677, 0x00000002 }, + { 0x00007678, 0x00000006 }, + { 0x0000002f, 0x00000018 }, + { 0x0000002f, 0x00000018 }, + { 0000000000, 0x00000006 }, + { 0x00000030, 0x00000018 }, + { 0x00000030, 0x00000018 }, + { 0000000000, 0x00000006 }, + { 0x01605000, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x00098000, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x64c0603e, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00080000, 0x00000016 }, + { 0000000000, 0000000000 }, + { 0x0400251d, 0x00000002 }, + { 0x00007580, 0x00000002 }, + { 0x00067581, 0x00000002 }, + { 0x04002580, 0x00000002 }, + { 0x00067581, 0x00000002 }, + { 0x00000049, 0x00000004 }, + { 0x00005000, 0000000000 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x0000750e, 0x00000002 }, + { 0x00019000, 0x00000002 }, + { 0x00011055, 0x00000014 }, + { 0x00000055, 0x00000012 }, + { 0x0400250f, 0x00000002 }, + { 0x0000504f, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00007565, 0x00000002 }, + { 0x00007566, 0x00000002 }, + { 0x00000058, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x01e655b4, 0x00000002 }, + { 0x4401b0e4, 0x00000002 }, + { 0x01c110e4, 0x00000002 }, + { 0x26667066, 0x00000018 }, + { 0x040c2565, 0x00000002 }, + { 0x00000066, 0x00000018 }, + { 0x04002564, 0x00000002 }, + { 0x00007566, 0x00000002 }, + { 0x0000005d, 0x00000004 }, + { 0x00401069, 0x00000008 }, + { 0x00101000, 0x00000002 }, + { 0x000d80ff, 0x00000002 }, + { 0x0080006c, 0x00000008 }, + { 0x000f9000, 0x00000002 }, + { 0x000e00ff, 0x00000002 }, + { 0000000000, 0x00000006 }, + { 0x0000008f, 0x00000018 }, + { 0x0000005b, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00007576, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x00009000, 0x00000002 }, + { 0x00041000, 0x00000002 }, + { 0x0c00350e, 0x00000002 }, + { 0x00049000, 0x00000002 }, + { 0x00051000, 0x00000002 }, + { 0x01e785f8, 0x00000002 }, + { 0x00200000, 0x00000002 }, + { 0x0060007e, 0x0000000c }, + { 0x00007563, 0x00000002 }, + { 0x006075f0, 0x00000021 }, + { 0x20007073, 0x00000004 }, + { 0x00005073, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00007576, 0x00000002 }, + { 0x00007577, 0x00000002 }, + { 0x0000750e, 0x00000002 }, + { 0x0000750f, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00600083, 0x0000000c }, + { 0x006075f0, 0x00000021 }, + { 0x000075f8, 0x00000002 }, + { 0x00000083, 0x00000004 }, + { 0x000a750e, 0x00000002 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x0020750f, 0x00000002 }, + { 0x00600086, 0x00000004 }, + { 0x00007570, 0x00000002 }, + { 0x00007571, 0x00000002 }, + { 0x00007572, 0x00000006 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00005000, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00007568, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x00000095, 0x0000000c }, + { 0x00058000, 0x00000002 }, + { 0x0c607562, 0x00000002 }, + { 0x00000097, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00600096, 0x00000004 }, + { 0x400070e5, 0000000000 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x000380e5, 0x00000002 }, + { 0x000000a8, 0x0000001c }, + { 0x000650aa, 0x00000018 }, + { 0x040025bb, 0x00000002 }, + { 0x000610ab, 0x00000018 }, + { 0x040075bc, 0000000000 }, + { 0x000075bb, 0x00000002 }, + { 0x000075bc, 0000000000 }, + { 0x00090000, 0x00000006 }, + { 0x00090000, 0x00000002 }, + { 0x000d8002, 0x00000006 }, + { 0x00007832, 0x00000002 }, + { 0x00005000, 0x00000002 }, + { 0x000380e7, 0x00000002 }, + { 0x04002c97, 0x00000002 }, + { 0x00007820, 0x00000002 }, + { 0x00007821, 0x00000002 }, + { 0x00007800, 0000000000 }, + { 0x01200000, 0x00000002 }, + { 0x20077000, 0x00000002 }, + { 0x01200000, 0x00000002 }, + { 0x20007000, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x0120751b, 0x00000002 }, + { 0x8040750a, 0x00000002 }, + { 0x8040750b, 0x00000002 }, + { 0x00110000, 0x00000002 }, + { 0x000380e5, 0x00000002 }, + { 0x000000c6, 0x0000001c }, + { 0x000610ab, 0x00000018 }, + { 0x844075bd, 0x00000002 }, + { 0x000610aa, 0x00000018 }, + { 0x840075bb, 0x00000002 }, + { 0x000610ab, 0x00000018 }, + { 0x844075bc, 0x00000002 }, + { 0x000000c9, 0x00000004 }, + { 0x804075bd, 0x00000002 }, + { 0x800075bb, 0x00000002 }, + { 0x804075bc, 0x00000002 }, + { 0x00108000, 0x00000002 }, + { 0x01400000, 0x00000002 }, + { 0x006000cd, 0x0000000c }, + { 0x20c07000, 0x00000020 }, + { 0x000000cf, 0x00000012 }, + { 0x00800000, 0x00000006 }, + { 0x0080751d, 0x00000006 }, + { 0000000000, 0000000000 }, + { 0x0000775c, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00661000, 0x00000002 }, + { 0x0460275d, 0x00000020 }, + { 0x00004000, 0000000000 }, + { 0x01e00830, 0x00000002 }, + { 0x21007000, 0000000000 }, + { 0x6464614d, 0000000000 }, + { 0x69687420, 0000000000 }, + { 0x00000073, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x00005000, 0x00000002 }, + { 0x000380d0, 0x00000002 }, + { 0x040025e0, 0x00000002 }, + { 0x000075e1, 0000000000 }, + { 0x00000001, 0000000000 }, + { 0x000380e0, 0x00000002 }, + { 0x04002394, 0x00000002 }, + { 0x00005000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x00000008, 0000000000 }, + { 0x00000004, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, +}; + + +#define DO_IOREMAP(_m) (_m)->handle = drm_ioremap((_m)->offset, (_m)->size) + +#define DO_IOREMAPFREE(_m) \ + do { \ + if ((_m)->handle && (_m)->size) \ + drm_ioremapfree((_m)->handle, (_m)->size); \ + } while (0) + +#define DO_FIND_MAP(_m, _o) \ + do { \ + int _i; \ + for (_i = 0; _i < dev->map_count; _i++) { \ + if (dev->maplist[_i]->offset == _o) { \ + _m = dev->maplist[_i]; \ + break; \ + } \ + } \ + } while (0) + + +int RADEON_READ_PLL(drm_device_t *dev, int addr) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + RADEON_WRITE8(RADEON_CLOCK_CNTL_INDEX, addr & 0x1f); + return RADEON_READ(RADEON_CLOCK_CNTL_DATA); +} + +#if RADEON_FIFO_DEBUG +static void radeon_status( drm_radeon_private_t *dev_priv ) +{ + printk( "%s:\n", __FUNCTION__ ); + printk( "RBBM_STATUS = 0x%08x\n", + (unsigned int)RADEON_READ( RADEON_RBBM_STATUS ) ); + printk( "CP_RB_RTPR = 0x%08x\n", + (unsigned int)RADEON_READ( RADEON_CP_RB_RPTR ) ); + printk( "CP_RB_WTPR = 0x%08x\n", + (unsigned int)RADEON_READ( RADEON_CP_RB_WPTR ) ); +} +#endif + + +/* ================================================================ + * Engine, FIFO control + */ + +static int radeon_do_pixcache_flush( drm_radeon_private_t *dev_priv ) +{ + u32 tmp; + int i; + + tmp = RADEON_READ( RADEON_RB2D_DSTCACHE_CTLSTAT ); + tmp |= RADEON_RB2D_DC_FLUSH_ALL; + RADEON_WRITE( RADEON_RB2D_DSTCACHE_CTLSTAT, tmp ); + + for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { + if ( !(RADEON_READ( RADEON_RB2D_DSTCACHE_CTLSTAT ) + & RADEON_RB2D_DC_BUSY) ) { + return 0; + } + udelay( 1 ); + } + +#if RADEON_FIFO_DEBUG + DRM_ERROR( "failed!\n" ); + radeon_status( dev_priv ); +#endif + return -EBUSY; +} + +static int radeon_do_wait_for_fifo( drm_radeon_private_t *dev_priv, + int entries ) +{ + int i; + + for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { + int slots = ( RADEON_READ( RADEON_RBBM_STATUS ) + & RADEON_RBBM_FIFOCNT_MASK ); + if ( slots >= entries ) return 0; + udelay( 1 ); + } + +#if RADEON_FIFO_DEBUG + DRM_ERROR( "failed!\n" ); + radeon_status( dev_priv ); +#endif + return -EBUSY; +} + +static int radeon_do_wait_for_idle( drm_radeon_private_t *dev_priv ) +{ + int i, ret; + + ret = radeon_do_wait_for_fifo( dev_priv, 64 ); + if ( ret < 0 ) return ret; + + for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { + if ( !(RADEON_READ( RADEON_RBBM_STATUS ) + & RADEON_RBBM_ACTIVE) ) { + radeon_do_pixcache_flush( dev_priv ); + return 0; + } + udelay( 1 ); + } + +#if RADEON_FIFO_DEBUG + DRM_ERROR( "failed!\n" ); + radeon_status( dev_priv ); +#endif + return -EBUSY; +} + + +/* ================================================================ + * CP control, initialization + */ + +/* Load the microcode for the CP */ +static void radeon_cp_load_microcode( drm_radeon_private_t *dev_priv ) +{ + int i; + + radeon_do_wait_for_idle( dev_priv ); + + RADEON_WRITE( RADEON_CP_ME_RAM_ADDR, 0 ); + for ( i = 0 ; i < 256 ; i++ ) { + RADEON_WRITE( RADEON_CP_ME_RAM_DATAH, + radeon_cp_microcode[i][1] ); + RADEON_WRITE( RADEON_CP_ME_RAM_DATAL, + radeon_cp_microcode[i][0] ); + } +} + +/* Flush any pending commands to the CP. This should only be used just + * prior to a wait for idle, as it informs the engine that the command + * stream is ending. + */ +static void radeon_do_cp_flush( drm_radeon_private_t *dev_priv ) +{ +#if 0 + u32 tmp; + + tmp = RADEON_READ( RADEON_CP_RB_WPTR ) | (1 << 31); + RADEON_WRITE( RADEON_CP_RB_WPTR, tmp ); +#endif +} + +/* Wait for the CP to go idle. + */ +int radeon_do_cp_idle( drm_radeon_private_t *dev_priv ) +{ + RING_LOCALS; + + BEGIN_RING( 6 ); + + RADEON_PURGE_CACHE(); + RADEON_PURGE_ZCACHE(); + RADEON_WAIT_UNTIL_IDLE(); + + ADVANCE_RING(); + + return radeon_do_wait_for_idle( dev_priv ); +} + +/* Start the Command Processor. + */ +static void radeon_do_cp_start( drm_radeon_private_t *dev_priv ) +{ + RING_LOCALS; + + radeon_do_wait_for_idle( dev_priv ); + + RADEON_WRITE( RADEON_CP_CSQ_CNTL, dev_priv->cp_mode ); + + dev_priv->cp_running = 1; + + BEGIN_RING( 6 ); + + RADEON_PURGE_CACHE(); + RADEON_PURGE_ZCACHE(); + RADEON_WAIT_UNTIL_IDLE(); + + ADVANCE_RING(); +} + +/* Reset the Command Processor. This will not flush any pending + * commands, so you must wait for the CP command stream to complete + * before calling this routine. + */ +static void radeon_do_cp_reset( drm_radeon_private_t *dev_priv ) +{ + u32 cur_read_ptr; + + cur_read_ptr = RADEON_READ( RADEON_CP_RB_RPTR ); + RADEON_WRITE( RADEON_CP_RB_WPTR, cur_read_ptr ); + *dev_priv->ring.head = cur_read_ptr; + dev_priv->ring.tail = cur_read_ptr; +} + +/* Stop the Command Processor. This will not flush any pending + * commands, so you must flush the command stream and wait for the CP + * to go idle before calling this routine. + */ +static void radeon_do_cp_stop( drm_radeon_private_t *dev_priv ) +{ + RADEON_WRITE( RADEON_CP_CSQ_CNTL, RADEON_CSQ_PRIDIS_INDDIS ); + + dev_priv->cp_running = 0; +} + +/* Reset the engine. This will stop the CP if it is running. + */ +static int radeon_do_engine_reset( drm_device_t *dev ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + radeon_do_pixcache_flush( dev_priv ); + + clock_cntl_index = RADEON_READ( RADEON_CLOCK_CNTL_INDEX ); + mclk_cntl = RADEON_READ_PLL( dev, RADEON_MCLK_CNTL ); + + /* FIXME: remove magic number here and in radeon ddx driver!!! */ + RADEON_WRITE_PLL( RADEON_MCLK_CNTL, mclk_cntl | 0x003f00000 ); + + rbbm_soft_reset = RADEON_READ( RADEON_RBBM_SOFT_RESET ); + + RADEON_WRITE( RADEON_RBBM_SOFT_RESET, ( rbbm_soft_reset | + RADEON_SOFT_RESET_CP | + RADEON_SOFT_RESET_HI | + RADEON_SOFT_RESET_SE | + RADEON_SOFT_RESET_RE | + RADEON_SOFT_RESET_PP | + RADEON_SOFT_RESET_E2 | + RADEON_SOFT_RESET_RB | + RADEON_SOFT_RESET_HDP ) ); + RADEON_READ( RADEON_RBBM_SOFT_RESET ); + RADEON_WRITE( RADEON_RBBM_SOFT_RESET, ( rbbm_soft_reset & + ~( RADEON_SOFT_RESET_CP | + RADEON_SOFT_RESET_HI | + RADEON_SOFT_RESET_SE | + RADEON_SOFT_RESET_RE | + RADEON_SOFT_RESET_PP | + RADEON_SOFT_RESET_E2 | + RADEON_SOFT_RESET_RB | + RADEON_SOFT_RESET_HDP ) ) ); + RADEON_READ( RADEON_RBBM_SOFT_RESET ); + + + RADEON_WRITE_PLL( RADEON_MCLK_CNTL, mclk_cntl ); + RADEON_WRITE( RADEON_CLOCK_CNTL_INDEX, clock_cntl_index ); + RADEON_WRITE( RADEON_RBBM_SOFT_RESET, rbbm_soft_reset ); + + /* Reset the CP ring */ + radeon_do_cp_reset( dev_priv ); + + /* The CP is no longer running after an engine reset */ + dev_priv->cp_running = 0; + + /* Reset any pending vertex, indirect buffers */ + radeon_freelist_reset( dev ); + + return 0; +} + +static void radeon_cp_init_ring_buffer( drm_device_t *dev ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + u32 ring_start, cur_read_ptr; + u32 tmp; + + /* Initialize the memory controller */ + RADEON_WRITE( RADEON_MC_FB_LOCATION, + (dev_priv->agp_vm_start - 1) & 0xffff0000 ); + RADEON_WRITE( RADEON_MC_AGP_LOCATION, + (((dev_priv->agp_vm_start - 1 + + dev_priv->agp_size) & 0xffff0000) | + (dev_priv->agp_vm_start >> 16)) ); + + ring_start = (dev_priv->cp_ring->offset + - dev->agp->base + + dev_priv->agp_vm_start); + + RADEON_WRITE( RADEON_CP_RB_BASE, ring_start ); + + /* Set the write pointer delay */ + RADEON_WRITE( RADEON_CP_RB_WPTR_DELAY, 0 ); + + /* Initialize the ring buffer's read and write pointers */ + cur_read_ptr = RADEON_READ( RADEON_CP_RB_RPTR ); + RADEON_WRITE( RADEON_CP_RB_WPTR, cur_read_ptr ); + *dev_priv->ring.head = cur_read_ptr; + dev_priv->ring.tail = cur_read_ptr; + + RADEON_WRITE( RADEON_CP_RB_RPTR_ADDR, dev_priv->ring_rptr->offset ); + + /* Set ring buffer size */ + RADEON_WRITE( RADEON_CP_RB_CNTL, dev_priv->ring.size_l2qw ); + + radeon_do_wait_for_idle( dev_priv ); + + /* Turn off PCI GART */ + tmp = RADEON_READ( RADEON_AIC_CNTL ) & ~RADEON_PCIGART_TRANSLATE_EN; + RADEON_WRITE( RADEON_AIC_CNTL, tmp ); + + /* Turn on bus mastering */ + tmp = RADEON_READ( RADEON_BUS_CNTL ) & ~RADEON_BUS_MASTER_DIS; + RADEON_WRITE( RADEON_BUS_CNTL, tmp ); + + /* Sync everything up */ + RADEON_WRITE( RADEON_ISYNC_CNTL, + (RADEON_ISYNC_ANY2D_IDLE3D | + RADEON_ISYNC_ANY3D_IDLE2D | + RADEON_ISYNC_WAIT_IDLEGUI | + RADEON_ISYNC_CPSCRATCH_IDLEGUI) ); +} + +static int radeon_do_init_cp( drm_device_t *dev, drm_radeon_init_t *init ) +{ + drm_radeon_private_t *dev_priv; + int i; + + dev_priv = drm_alloc( sizeof(drm_radeon_private_t), DRM_MEM_DRIVER ); + if ( dev_priv == NULL ) + return -ENOMEM; + dev->dev_private = (void *)dev_priv; + + memset( dev_priv, 0, sizeof(drm_radeon_private_t) ); + + dev_priv->is_pci = init->is_pci; + + /* We don't support PCI cards until PCI GART is implemented. + * Fail here so we can remove all checks for PCI cards around + * the CP ring code. + */ + if ( dev_priv->is_pci ) { + drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); + dev->dev_private = NULL; + return -EINVAL; + } + + dev_priv->usec_timeout = init->usec_timeout; + if ( dev_priv->usec_timeout < 1 || + dev_priv->usec_timeout > RADEON_MAX_USEC_TIMEOUT ) { + drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); + dev->dev_private = NULL; + return -EINVAL; + } + + dev_priv->cp_mode = init->cp_mode; + + /* Simple idle check. + */ + atomic_set( &dev_priv->idle_count, 0 ); + + /* We don't support anything other than bus-mastering ring mode, + * but the ring can be in either AGP or PCI space for the ring + * read pointer. + */ + if ( ( init->cp_mode != RADEON_CSQ_PRIBM_INDDIS ) && + ( init->cp_mode != RADEON_CSQ_PRIBM_INDBM ) ) { + drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); + dev->dev_private = NULL; + return -EINVAL; + } + + switch ( init->fb_bpp ) { + case 16: + dev_priv->color_fmt = RADEON_COLOR_FORMAT_RGB565; + break; + case 32: + default: + dev_priv->color_fmt = RADEON_COLOR_FORMAT_ARGB8888; + break; + } + dev_priv->front_offset = init->front_offset; + dev_priv->front_pitch = init->front_pitch; + dev_priv->back_offset = init->back_offset; + dev_priv->back_pitch = init->back_pitch; + + switch ( init->depth_bpp ) { + case 16: + dev_priv->depth_fmt = RADEON_DEPTH_FORMAT_16BIT_INT_Z; + break; + case 32: + default: + dev_priv->depth_fmt = RADEON_DEPTH_FORMAT_24BIT_INT_Z; + break; + } + dev_priv->depth_offset = init->depth_offset; + dev_priv->depth_pitch = init->depth_pitch; + + dev_priv->front_pitch_offset = (((dev_priv->front_pitch/64) << 22) | + (dev_priv->front_offset >> 10)); + dev_priv->back_pitch_offset = (((dev_priv->back_pitch/64) << 22) | + (dev_priv->back_offset >> 10)); + dev_priv->depth_pitch_offset = (((dev_priv->depth_pitch/64) << 22) | + (dev_priv->depth_offset >> 10)); + + /* Hardware state for depth clears. Remove this if/when we no + * longer clear the depth buffer with a 3D rectangle. Hard-code + * all values to prevent unwanted 3D state from slipping through + * and screwing with the clear operation. + */ + dev_priv->depth_clear.rb3d_cntl = (RADEON_PLANE_MASK_ENABLE | + RADEON_Z_ENABLE | + (dev_priv->color_fmt << 10) | + RADEON_ZBLOCK16); + + dev_priv->depth_clear.rb3d_zstencilcntl = (dev_priv->depth_fmt | + RADEON_Z_TEST_ALWAYS | + RADEON_STENCIL_TEST_ALWAYS | + RADEON_STENCIL_S_FAIL_KEEP | + RADEON_STENCIL_ZPASS_KEEP | + RADEON_STENCIL_ZFAIL_KEEP | + RADEON_Z_WRITE_ENABLE); + + dev_priv->depth_clear.se_cntl = (RADEON_FFACE_CULL_CW | + RADEON_BFACE_SOLID | + RADEON_FFACE_SOLID | + RADEON_FLAT_SHADE_VTX_LAST | + + RADEON_DIFFUSE_SHADE_FLAT | + RADEON_ALPHA_SHADE_FLAT | + RADEON_SPECULAR_SHADE_FLAT | + RADEON_FOG_SHADE_FLAT | + + RADEON_VTX_PIX_CENTER_OGL | + RADEON_ROUND_MODE_TRUNC | + RADEON_ROUND_PREC_8TH_PIX); + + /* FIXME: We want multiple shared areas, including one shared + * only by the X Server and kernel module. + */ + for ( i = 0 ; i < dev->map_count ; i++ ) { + if ( dev->maplist[i]->type == _DRM_SHM ) { + dev_priv->sarea = dev->maplist[i]; + break; + } + } + + DO_FIND_MAP( dev_priv->fb, init->fb_offset ); + DO_FIND_MAP( dev_priv->mmio, init->mmio_offset ); + DO_FIND_MAP( dev_priv->cp_ring, init->ring_offset ); + DO_FIND_MAP( dev_priv->ring_rptr, init->ring_rptr_offset ); + DO_FIND_MAP( dev_priv->buffers, init->buffers_offset ); + + if ( !dev_priv->is_pci ) { + DO_FIND_MAP( dev_priv->agp_textures, + init->agp_textures_offset ); + } + + dev_priv->sarea_priv = + (drm_radeon_sarea_t *)((u8 *)dev_priv->sarea->handle + + init->sarea_priv_offset); + + DO_IOREMAP( dev_priv->cp_ring ); + DO_IOREMAP( dev_priv->ring_rptr ); + DO_IOREMAP( dev_priv->buffers ); +#if 0 + if ( !dev_priv->is_pci ) { + DO_IOREMAP( dev_priv->agp_textures ); + } +#endif + + dev_priv->agp_size = init->agp_size; + dev_priv->agp_vm_start = RADEON_READ( RADEON_CONFIG_APER_SIZE ); + dev_priv->agp_buffers_offset = (dev_priv->buffers->offset + - dev->agp->base + + dev_priv->agp_vm_start); + + dev_priv->ring.head = ((__volatile__ u32 *) + dev_priv->ring_rptr->handle); + + dev_priv->ring.start = (u32 *)dev_priv->cp_ring->handle; + dev_priv->ring.end = ((u32 *)dev_priv->cp_ring->handle + + init->ring_size / sizeof(u32)); + dev_priv->ring.size = init->ring_size; + dev_priv->ring.size_l2qw = drm_order( init->ring_size / 8 ); + + dev_priv->ring.tail_mask = + (dev_priv->ring.size / sizeof(u32)) - 1; + +#if 0 + /* Initialize the scratch register pointer. This will cause + * the scratch register values to be written out to memory + * whenever they are updated. + * FIXME: This doesn't quite work yet, so we're disabling it + * for the release. + */ + RADEON_WRITE( RADEON_SCRATCH_ADDR, (dev_priv->ring_rptr->offset + + RADEON_SCRATCH_REG_OFFSET) ); + RADEON_WRITE( RADEON_SCRATCH_UMSK, 0x7 ); +#endif + + dev_priv->scratch = ((__volatile__ u32 *) + dev_priv->ring_rptr->handle + + (RADEON_SCRATCH_REG_OFFSET / sizeof(u32))); + + dev_priv->sarea_priv->last_frame = 0; + RADEON_WRITE( RADEON_LAST_FRAME_REG, + dev_priv->sarea_priv->last_frame ); + + dev_priv->sarea_priv->last_dispatch = 0; + RADEON_WRITE( RADEON_LAST_DISPATCH_REG, + dev_priv->sarea_priv->last_dispatch ); + + dev_priv->sarea_priv->last_clear = 0; + RADEON_WRITE( RADEON_LAST_CLEAR_REG, + dev_priv->sarea_priv->last_clear ); + + radeon_cp_load_microcode( dev_priv ); + radeon_cp_init_ring_buffer( dev ); + radeon_do_engine_reset( dev ); + +#if ROTATE_BUFS + dev_priv->last_buf = 0; +#endif + + return 0; +} + +static int radeon_do_cleanup_cp( drm_device_t *dev ) +{ + if ( dev->dev_private ) { + drm_radeon_private_t *dev_priv = dev->dev_private; + + DO_IOREMAPFREE( dev_priv->cp_ring ); + DO_IOREMAPFREE( dev_priv->ring_rptr ); + DO_IOREMAPFREE( dev_priv->buffers ); +#if 0 + if ( !dev_priv->is_pci ) { + DO_IOREMAPFREE( dev_priv->agp_textures ); + } +#endif + + drm_free( dev->dev_private, sizeof(drm_radeon_private_t), + DRM_MEM_DRIVER ); + dev->dev_private = NULL; + } + + return 0; +} + +int radeon_cp_init( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_init_t init; + + if ( copy_from_user( &init, (drm_radeon_init_t *)arg, sizeof(init) ) ) + return -EFAULT; + + switch ( init.func ) { + case RADEON_INIT_CP: + return radeon_do_init_cp( dev, &init ); + case RADEON_CLEANUP_CP: + return radeon_do_cleanup_cp( dev ); + } + + return -EINVAL; +} + +int radeon_cp_start( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + if ( dev_priv->cp_running ) { + DRM_DEBUG( "%s while CP running\n", __FUNCTION__ ); + return 0; + } + if ( dev_priv->cp_mode == RADEON_CSQ_PRIDIS_INDDIS ) { + DRM_DEBUG( "%s called with bogus CP mode (%d)\n", + __FUNCTION__, dev_priv->cp_mode ); + return 0; + } + + radeon_do_cp_start( dev_priv ); + + return 0; +} + +/* Stop the CP. The engine must have been idled before calling this + * routine. + */ +int radeon_cp_stop( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_cp_stop_t stop; + int ret; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &stop, (drm_radeon_init_t *)arg, sizeof(stop) ) ) + return -EFAULT; + + /* Flush any pending CP commands. This ensures any outstanding + * commands are exectuted by the engine before we turn it off. + */ + if ( stop.flush ) { + radeon_do_cp_flush( dev_priv ); + } + + /* If we fail to make the engine go idle, we return an error + * code so that the DRM ioctl wrapper can try again. + */ + if ( stop.idle ) { + ret = radeon_do_cp_idle( dev_priv ); + if ( ret < 0 ) return ret; + } + + /* Finally, we can turn off the CP. If the engine isn't idle, + * we will get some dropped triangles as they won't be fully + * rendered before the CP is shut down. + */ + radeon_do_cp_stop( dev_priv ); + + /* Reset the engine */ + radeon_do_engine_reset( dev ); + + return 0; +} + +/* Just reset the CP ring. Called as part of an X Server engine reset. + */ +int radeon_cp_reset( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + if ( !dev_priv ) { + DRM_DEBUG( "%s called before init done\n", __FUNCTION__ ); + return -EINVAL; + } + + radeon_do_cp_reset( dev_priv ); + + /* The CP is no longer running after an engine reset */ + dev_priv->cp_running = 0; + + return 0; +} + +int radeon_cp_idle( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + return radeon_do_cp_idle( dev_priv ); +} + +int radeon_engine_reset( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + return radeon_do_engine_reset( dev ); +} + + +/* ================================================================ + * Fullscreen mode + */ + +static int radeon_do_init_pageflip( drm_device_t *dev ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + dev_priv->crtc_offset = RADEON_READ( RADEON_CRTC_OFFSET ); + dev_priv->crtc_offset_cntl = RADEON_READ( RADEON_CRTC_OFFSET_CNTL ); + + RADEON_WRITE( RADEON_CRTC_OFFSET, dev_priv->front_offset ); + RADEON_WRITE( RADEON_CRTC_OFFSET_CNTL, + dev_priv->crtc_offset_cntl | + RADEON_CRTC_OFFSET_FLIP_CNTL ); + + dev_priv->page_flipping = 1; + dev_priv->current_page = 0; + + return 0; +} + +int radeon_do_cleanup_pageflip( drm_device_t *dev ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + RADEON_WRITE( RADEON_CRTC_OFFSET, dev_priv->crtc_offset ); + RADEON_WRITE( RADEON_CRTC_OFFSET_CNTL, dev_priv->crtc_offset_cntl ); + + dev_priv->page_flipping = 0; + dev_priv->current_page = 0; + + return 0; +} + +int radeon_fullscreen( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_fullscreen_t fs; + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &fs, (drm_radeon_fullscreen_t *)arg, + sizeof(fs) ) ) + return -EFAULT; + + switch ( fs.func ) { + case RADEON_INIT_FULLSCREEN: + return radeon_do_init_pageflip( dev ); + case RADEON_CLEANUP_FULLSCREEN: + return radeon_do_cleanup_pageflip( dev ); + } + + return -EINVAL; +} + + +/* ================================================================ + * Freelist management + */ +#define RADEON_BUFFER_USED 0xffffffff +#define RADEON_BUFFER_FREE 0 + +#if 0 +static int radeon_freelist_init( drm_device_t *dev ) +{ + drm_device_dma_t *dma = dev->dma; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_buf_t *buf; + drm_radeon_buf_priv_t *buf_priv; + drm_radeon_freelist_t *entry; + int i; + + dev_priv->head = drm_alloc( sizeof(drm_radeon_freelist_t), + DRM_MEM_DRIVER ); + if ( dev_priv->head == NULL ) + return -ENOMEM; + + memset( dev_priv->head, 0, sizeof(drm_radeon_freelist_t) ); + dev_priv->head->age = RADEON_BUFFER_USED; + + for ( i = 0 ; i < dma->buf_count ; i++ ) { + buf = dma->buflist[i]; + buf_priv = buf->dev_private; + + entry = drm_alloc( sizeof(drm_radeon_freelist_t), + DRM_MEM_DRIVER ); + if ( !entry ) return -ENOMEM; + + entry->age = RADEON_BUFFER_FREE; + entry->buf = buf; + entry->prev = dev_priv->head; + entry->next = dev_priv->head->next; + if ( !entry->next ) + dev_priv->tail = entry; + + buf_priv->discard = 0; + buf_priv->dispatched = 0; + buf_priv->list_entry = entry; + + dev_priv->head->next = entry; + + if ( dev_priv->head->next ) + dev_priv->head->next->prev = entry; + } + + return 0; + +} +#endif + +drm_buf_t *radeon_freelist_get( drm_device_t *dev ) +{ + drm_device_dma_t *dma = dev->dma; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_buf_priv_t *buf_priv; + drm_buf_t *buf; + int i, t; +#if ROTATE_BUFS + int start; +#endif + + /* FIXME: Optimize -- use freelist code */ + + for ( i = 0 ; i < dma->buf_count ; i++ ) { + buf = dma->buflist[i]; + buf_priv = buf->dev_private; + if ( buf->pid == 0 ) { + DRM_DEBUG( " ret buf=%d last=%d pid=0\n", + buf->idx, dev_priv->last_buf ); + return buf; + } + DRM_DEBUG( " skipping buf=%d pid=%d\n", + buf->idx, buf->pid ); + } + +#if ROTATE_BUFS + if ( ++dev_priv->last_buf >= dma->buf_count ) + dev_priv->last_buf = 0; + start = dev_priv->last_buf; +#endif + for ( t = 0 ; t < dev_priv->usec_timeout ; t++ ) { +#if 0 + /* FIXME: Disable this for now */ + u32 done_age = dev_priv->scratch[RADEON_LAST_DISPATCH]; +#else + u32 done_age = RADEON_READ( RADEON_LAST_DISPATCH_REG ); +#endif +#if ROTATE_BUFS + for ( i = start ; i < dma->buf_count ; i++ ) { +#else + for ( i = 0 ; i < dma->buf_count ; i++ ) { +#endif + buf = dma->buflist[i]; + buf_priv = buf->dev_private; + if ( buf->pending && buf_priv->age <= done_age ) { + /* The buffer has been processed, so it + * can now be used. + */ + buf->pending = 0; + DRM_DEBUG( " ret buf=%d last=%d age=%d done=%d\n", buf->idx, dev_priv->last_buf, buf_priv->age, done_age ); + return buf; + } + DRM_DEBUG( " skipping buf=%d age=%d done=%d\n", + buf->idx, buf_priv->age, + done_age ); +#if ROTATE_BUFS + start = 0; +#endif + } + udelay( 1 ); + } + + DRM_ERROR( "returning NULL!\n" ); + return NULL; +} + +void radeon_freelist_reset( drm_device_t *dev ) +{ + drm_device_dma_t *dma = dev->dma; +#if ROTATE_BUFS + drm_radeon_private_t *dev_priv = dev->dev_private; +#endif + int i; + +#if ROTATE_BUFS + dev_priv->last_buf = 0; +#endif + for ( i = 0 ; i < dma->buf_count ; i++ ) { + drm_buf_t *buf = dma->buflist[i]; + drm_radeon_buf_priv_t *buf_priv = buf->dev_private; + buf_priv->age = 0; + } +} + + +/* ================================================================ + * CP command submission + */ + +int radeon_wait_ring( drm_radeon_private_t *dev_priv, int n ) +{ + drm_radeon_ring_buffer_t *ring = &dev_priv->ring; + int i; + + for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { + ring->space = *ring->head - ring->tail; + if ( ring->space <= 0 ) + ring->space += ring->size; + + if ( ring->space >= n ) + return 0; + + udelay( 1 ); + } + + /* FIXME: This return value is ignored in the BEGIN_RING macro! */ + DRM_ERROR( "failed!\n" ); + return -EBUSY; +} + +void radeon_update_ring_snapshot( drm_radeon_private_t *dev_priv ) +{ + drm_radeon_ring_buffer_t *ring = &dev_priv->ring; + + ring->space = *ring->head - ring->tail; + if ( ring->space == 0 ) + atomic_inc( &dev_priv->idle_count ); + if ( ring->space <= 0 ) + ring->space += ring->size; +} + +static int radeon_cp_get_buffers( drm_device_t *dev, drm_dma_t *d ) +{ + int i; + drm_buf_t *buf; + + for ( i = d->granted_count ; i < d->request_count ; i++ ) { + buf = radeon_freelist_get( dev ); + if ( !buf ) return -EAGAIN; + + buf->pid = current->pid; + + if ( copy_to_user( &d->request_indices[i], &buf->idx, + sizeof(buf->idx) ) ) + return -EFAULT; + if ( copy_to_user( &d->request_sizes[i], &buf->total, + sizeof(buf->total) ) ) + return -EFAULT; + + d->granted_count++; + } + return 0; +} + +int radeon_cp_buffers( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int ret = 0; + drm_dma_t d; + + if ( copy_from_user( &d, (drm_dma_t *) arg, sizeof(d) ) ) + return -EFAULT; + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + /* Please don't send us buffers. + */ + if ( d.send_count != 0 ) { + DRM_ERROR( "Process %d trying to send %d buffers via drmDMA\n", + current->pid, d.send_count ); + return -EINVAL; + } + + /* We'll send you buffers. + */ + if ( d.request_count < 0 || d.request_count > dma->buf_count ) { + DRM_ERROR( "Process %d trying to get %d buffers (of %d max)\n", + current->pid, d.request_count, dma->buf_count ); + return -EINVAL; + } + + d.granted_count = 0; + + if ( d.request_count ) { + ret = radeon_cp_get_buffers( dev, &d ); + } + + if ( copy_to_user( (drm_dma_t *) arg, &d, sizeof(d) ) ) + return -EFAULT; + + return ret; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/radeon_drm.h linux.ac/drivers/char/drm-4.0/radeon_drm.h --- linux.vanilla/drivers/char/drm-4.0/radeon_drm.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/radeon_drm.h Wed Oct 10 01:47:41 2001 @@ -0,0 +1,325 @@ +/* radeon_drm.h -- Public header for the radeon driver -*- linux-c -*- + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Kevin E. Martin + * Gareth Hughes + * + */ + +#ifndef __RADEON_DRM_H__ +#define __RADEON_DRM_H__ + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the X server file (radeon_sarea.h) + */ +#ifndef __RADEON_SAREA_DEFINES__ +#define __RADEON_SAREA_DEFINES__ + +/* What needs to be changed for the current vertex buffer? + */ +#define RADEON_UPLOAD_CONTEXT 0x00000001 +#define RADEON_UPLOAD_VERTFMT 0x00000002 +#define RADEON_UPLOAD_LINE 0x00000004 +#define RADEON_UPLOAD_BUMPMAP 0x00000008 +#define RADEON_UPLOAD_MASKS 0x00000010 +#define RADEON_UPLOAD_VIEWPORT 0x00000020 +#define RADEON_UPLOAD_SETUP 0x00000040 +#define RADEON_UPLOAD_TCL 0x00000080 +#define RADEON_UPLOAD_MISC 0x00000100 +#define RADEON_UPLOAD_TEX0 0x00000200 +#define RADEON_UPLOAD_TEX1 0x00000400 +#define RADEON_UPLOAD_TEX2 0x00000800 +#define RADEON_UPLOAD_TEX0IMAGES 0x00001000 +#define RADEON_UPLOAD_TEX1IMAGES 0x00002000 +#define RADEON_UPLOAD_TEX2IMAGES 0x00004000 +#define RADEON_UPLOAD_CLIPRECTS 0x00008000 /* handled client-side */ +#define RADEON_REQUIRE_QUIESCENCE 0x00010000 +#define RADEON_UPLOAD_ALL 0x0001ffff + +#define RADEON_FRONT 0x1 +#define RADEON_BACK 0x2 +#define RADEON_DEPTH 0x4 + +/* Primitive types + */ +#define RADEON_POINTS 0x1 +#define RADEON_LINES 0x2 +#define RADEON_LINE_STRIP 0x3 +#define RADEON_TRIANGLES 0x4 +#define RADEON_TRIANGLE_FAN 0x5 +#define RADEON_TRIANGLE_STRIP 0x6 + +/* Vertex/indirect buffer size + */ +#define RADEON_BUFFER_SIZE 16384 + +/* Byte offsets for indirect buffer data + */ +#define RADEON_INDEX_PRIM_OFFSET 20 +#define RADEON_HOSTDATA_BLIT_OFFSET 32 + +#define RADEON_SCRATCH_REG_OFFSET 32 + +/* Keep these small for testing + */ +#define RADEON_NR_SAREA_CLIPRECTS 12 + +/* There are 2 heaps (local/AGP). Each region within a heap is a + * minimum of 64k, and there are at most 64 of them per heap. + */ +#define RADEON_LOCAL_TEX_HEAP 0 +#define RADEON_AGP_TEX_HEAP 1 +#define RADEON_NR_TEX_HEAPS 2 +#define RADEON_NR_TEX_REGIONS 64 +#define RADEON_LOG_TEX_GRANULARITY 16 + +#define RADEON_MAX_TEXTURE_LEVELS 11 +#define RADEON_MAX_TEXTURE_UNITS 3 + +#endif /* __RADEON_SAREA_DEFINES__ */ + +typedef struct { + unsigned int red; + unsigned int green; + unsigned int blue; + unsigned int alpha; +} radeon_color_regs_t; + +typedef struct { + /* Context state */ + unsigned int pp_misc; /* 0x1c14 */ + unsigned int pp_fog_color; + unsigned int re_solid_color; + unsigned int rb3d_blendcntl; + unsigned int rb3d_depthoffset; + unsigned int rb3d_depthpitch; + unsigned int rb3d_zstencilcntl; + + unsigned int pp_cntl; /* 0x1c38 */ + unsigned int rb3d_cntl; + unsigned int rb3d_coloroffset; + unsigned int re_width_height; + unsigned int rb3d_colorpitch; + unsigned int se_cntl; + + /* Vertex format state */ + unsigned int se_coord_fmt; /* 0x1c50 */ + + /* Line state */ + unsigned int re_line_pattern; /* 0x1cd0 */ + unsigned int re_line_state; + + unsigned int se_line_width; /* 0x1db8 */ + + /* Bumpmap state */ + unsigned int pp_lum_matrix; /* 0x1d00 */ + + unsigned int pp_rot_matrix_0; /* 0x1d58 */ + unsigned int pp_rot_matrix_1; + + /* Mask state */ + unsigned int rb3d_stencilrefmask; /* 0x1d7c */ + unsigned int rb3d_ropcntl; + unsigned int rb3d_planemask; + + /* Viewport state */ + unsigned int se_vport_xscale; /* 0x1d98 */ + unsigned int se_vport_xoffset; + unsigned int se_vport_yscale; + unsigned int se_vport_yoffset; + unsigned int se_vport_zscale; + unsigned int se_vport_zoffset; + + /* Setup state */ + unsigned int se_cntl_status; /* 0x2140 */ + +#ifdef TCL_ENABLE + /* TCL state */ + radeon_color_regs_t se_tcl_material_emmissive; /* 0x2210 */ + radeon_color_regs_t se_tcl_material_ambient; + radeon_color_regs_t se_tcl_material_diffuse; + radeon_color_regs_t se_tcl_material_specular; + unsigned int se_tcl_shininess; + unsigned int se_tcl_output_vtx_fmt; + unsigned int se_tcl_output_vtx_sel; + unsigned int se_tcl_matrix_select_0; + unsigned int se_tcl_matrix_select_1; + unsigned int se_tcl_ucp_vert_blend_ctl; + unsigned int se_tcl_texture_proc_ctl; + unsigned int se_tcl_light_model_ctl; + unsigned int se_tcl_per_light_ctl[4]; +#endif + + /* Misc state */ + unsigned int re_top_left; /* 0x26c0 */ + unsigned int re_misc; +} drm_radeon_context_regs_t; + +/* Setup registers for each texture unit + */ +typedef struct { + unsigned int pp_txfilter; + unsigned int pp_txformat; + unsigned int pp_txoffset; + unsigned int pp_txcblend; + unsigned int pp_txablend; + unsigned int pp_tfactor; + + unsigned int pp_border_color; + +#ifdef CUBIC_ENABLE + unsigned int pp_cubic_faces; + unsigned int pp_cubic_offset[5]; +#endif +} drm_radeon_texture_regs_t; + +typedef struct { + unsigned char next, prev; + unsigned char in_use; + int age; +} drm_radeon_tex_region_t; + +typedef struct { + /* The channel for communication of state information to the kernel + * on firing a vertex buffer. + */ + drm_radeon_context_regs_t context_state; + drm_radeon_texture_regs_t tex_state[RADEON_MAX_TEXTURE_UNITS]; + unsigned int dirty; + unsigned int vertsize; + unsigned int vc_format; + + /* The current cliprects, or a subset thereof. + */ + drm_clip_rect_t boxes[RADEON_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Counters for client-side throttling of rendering clients. + */ + unsigned int last_frame; + unsigned int last_dispatch; + unsigned int last_clear; + + drm_radeon_tex_region_t tex_list[RADEON_NR_TEX_HEAPS][RADEON_NR_TEX_REGIONS+1]; + int tex_age[RADEON_NR_TEX_HEAPS]; + int ctx_owner; +} drm_radeon_sarea_t; + + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (xf86drmRadeon.h) + */ +typedef struct drm_radeon_init { + enum { + RADEON_INIT_CP = 0x01, + RADEON_CLEANUP_CP = 0x02 + } func; + int sarea_priv_offset; + int is_pci; + int cp_mode; + int agp_size; + int ring_size; + int usec_timeout; + + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + + unsigned int fb_offset; + unsigned int mmio_offset; + unsigned int ring_offset; + unsigned int ring_rptr_offset; + unsigned int buffers_offset; + unsigned int agp_textures_offset; +} drm_radeon_init_t; + +typedef struct drm_radeon_cp_stop { + int flush; + int idle; +} drm_radeon_cp_stop_t; + +typedef struct drm_radeon_fullscreen { + enum { + RADEON_INIT_FULLSCREEN = 0x01, + RADEON_CLEANUP_FULLSCREEN = 0x02 + } func; +} drm_radeon_fullscreen_t; + +#define CLEAR_X1 0 +#define CLEAR_Y1 1 +#define CLEAR_X2 2 +#define CLEAR_Y2 3 +#define CLEAR_DEPTH 4 + +typedef struct drm_radeon_clear { + unsigned int flags; + int x, y, w, h; + unsigned int clear_color; + unsigned int clear_depth; + union { + float f[5]; + unsigned int ui[5]; + } rect; +} drm_radeon_clear_t; + +typedef struct drm_radeon_vertex { + int prim; + int idx; /* Index of vertex buffer */ + int count; /* Number of vertices in buffer */ + int discard; /* Client finished with buffer? */ +} drm_radeon_vertex_t; + +typedef struct drm_radeon_indices { + int prim; + int idx; + int start; + int end; + int discard; /* Client finished with buffer? */ +} drm_radeon_indices_t; + +typedef struct drm_radeon_blit { + int idx; + int pitch; + int offset; + int format; + unsigned short x, y; + unsigned short width, height; +} drm_radeon_blit_t; + +typedef struct drm_radeon_stipple { + unsigned int *mask; +} drm_radeon_stipple_t; + +typedef struct drm_radeon_indirect { + int idx; + int start; + int end; + int discard; +} drm_radeon_indirect_t; + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/radeon_drv.c linux.ac/drivers/char/drm-4.0/radeon_drv.c --- linux.vanilla/drivers/char/drm-4.0/radeon_drv.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/radeon_drv.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,703 @@ +/* radeon_drv.c -- ATI Radeon driver -*- linux-c -*- + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Kevin E. Martin + * Rickard E. (Rik) Faith + * + */ + +#include +#include "drmP.h" +#include "radeon_drv.h" + +#define RADEON_NAME "radeon" +#define RADEON_DESC "ATI Radeon" +#define RADEON_DATE "20010105" +#define RADEON_MAJOR 1 +#define RADEON_MINOR 0 +#define RADEON_PATCHLEVEL 0 + +static drm_device_t radeon_device; +drm_ctx_t radeon_res_ctx; + +static struct file_operations radeon_fops = { +#if LINUX_VERSION_CODE >= 0x020400 + /* This started being used during 2.4.0-test */ + owner: THIS_MODULE, +#endif + open: radeon_open, + flush: drm_flush, + release: radeon_release, + ioctl: radeon_ioctl, + mmap: drm_mmap, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +static struct miscdevice radeon_misc = { + minor: MISC_DYNAMIC_MINOR, + name: RADEON_NAME, + fops: &radeon_fops, +}; + +static drm_ioctl_desc_t radeon_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { radeon_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { radeon_addbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { drm_markbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { drm_infobufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)] = { radeon_mapbufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { drm_freebufs, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { radeon_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { radeon_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { radeon_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { radeon_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { radeon_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { radeon_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { radeon_resctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { radeon_cp_buffers, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { radeon_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { radeon_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = { drm_agp_enable, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = { drm_agp_info, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = { drm_agp_alloc, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = { drm_agp_free, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { drm_agp_bind, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { drm_agp_unbind, 1, 1 }, +#endif + + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_INIT)] = { radeon_cp_init, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_START)] = { radeon_cp_start, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_STOP)] = { radeon_cp_stop, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_RESET)] = { radeon_cp_reset, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_IDLE)] = { radeon_cp_idle, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_RESET)] = { radeon_engine_reset, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_FULLSCREEN)] = { radeon_fullscreen, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_SWAP)] = { radeon_cp_swap, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CLEAR)] = { radeon_cp_clear, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_VERTEX)] = { radeon_cp_vertex, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_INDICES)] = { radeon_cp_indices, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_BLIT)] = { radeon_cp_blit, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_STIPPLE)] = { radeon_cp_stipple, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_INDIRECT)]= { radeon_cp_indirect,1, 1 }, +}; +#define RADEON_IOCTL_COUNT DRM_ARRAY_SIZE(radeon_ioctls) + +#ifdef MODULE +static char *radeon = NULL; +#endif + +MODULE_AUTHOR("VA Linux Systems, Inc."); +MODULE_DESCRIPTION("radeon"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_PARM(radeon, "s"); + +#ifndef MODULE +/* radeon_options is called by the kernel to parse command-line options + * passed via the boot-loader (e.g., LILO). It calls the insmod option + * routine, drm_parse_drm. + */ + +static int __init radeon_options(char *str) +{ + drm_parse_options(str); + return 1; +} + +__setup("radeon=", radeon_options); +#endif + +static int radeon_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + drm_dma_setup(dev); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); + + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + radeon_res_ctx.handle = -1; + + DRM_DEBUG("\n"); + + /* The kernel's context could be created here, but is now created + in drm_dma_enqueue. This is more resource-efficient for + hardware that does not do DMA, but may mean that + drm_select_queue fails between the time the interrupt is + initialized and the time the queues are initialized. */ + + return 0; +} + + +static int radeon_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + /* Clear AGP information */ + if (dev->agp) { + drm_agp_mem_t *entry; + drm_agp_mem_t *nexte; + + /* Remove AGP resources, but leave dev->agp + intact until radeon_cleanup is called. */ + for (entry = dev->agp->memory; entry; entry = nexte) { + nexte = entry->next; + if (entry->bound) drm_unbind_agp(entry->memory); + drm_free_agp(entry->memory, entry->pages); + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + } + dev->agp->memory = NULL; + + if (dev->agp->acquired) _drm_agp_release(); + + dev->agp->acquired = 0; + dev->agp->enabled = 0; + } +#endif + + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area and mtrr information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifdef CONFIG_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + drm_ioremapfree(map->handle, map->size); + break; + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + case _DRM_AGP: + /* Do nothing here, because this is all + handled in the AGP/GART driver. */ + break; + } + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + drm_dma_takedown(dev); + + dev->queue_count = 0; + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +/* radeon_init is called via init_module at module load time, or via + * linux/init/main.c (this is not currently supported). */ + +static int __init radeon_init(void) +{ + int retcode; + drm_device_t *dev = &radeon_device; + + DRM_DEBUG("\n"); + + memset((void *)dev, 0, sizeof(*dev)); + dev->count_lock = SPIN_LOCK_UNLOCKED; + sema_init(&dev->struct_sem, 1); + +#ifdef MODULE + drm_parse_options(radeon); +#endif + + if ((retcode = misc_register(&radeon_misc))) { + DRM_ERROR("Cannot register \"%s\"\n", RADEON_NAME); + return retcode; + } + dev->device = MKDEV(MISC_MAJOR, radeon_misc.minor); + dev->name = RADEON_NAME; + + drm_mem_init(); + drm_proc_init(dev); + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + dev->agp = drm_agp_init(); + if (dev->agp == NULL) { + DRM_ERROR("Cannot initialize agpgart module.\n"); + drm_proc_cleanup(); + misc_deregister(&radeon_misc); + radeon_takedown(dev); + return -ENOMEM; + } + +#ifdef CONFIG_MTRR + dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base, + dev->agp->agp_info.aper_size*1024*1024, + MTRR_TYPE_WRCOMB, + 1); +#endif +#endif + + if((retcode = drm_ctxbitmap_init(dev))) { + DRM_ERROR("Cannot allocate memory for context bitmap.\n"); + drm_proc_cleanup(); + misc_deregister(&radeon_misc); + radeon_takedown(dev); + return retcode; + } + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", + RADEON_NAME, + RADEON_MAJOR, + RADEON_MINOR, + RADEON_PATCHLEVEL, + RADEON_DATE, + radeon_misc.minor); + + return 0; +} + +/* radeon_cleanup is called via cleanup_module at module unload time. */ + +static void __exit radeon_cleanup(void) +{ + drm_device_t *dev = &radeon_device; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + if (misc_deregister(&radeon_misc)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + drm_ctxbitmap_cleanup(dev); + radeon_takedown(dev); +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + if (dev->agp) { + drm_agp_uninit(); + drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS); + dev->agp = NULL; + } +#endif +} + +module_init(radeon_init); +module_exit(radeon_cleanup); + + +int radeon_version(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_version_t version; + int len; + + if (copy_from_user(&version, + (drm_version_t *)arg, + sizeof(version))) + return -EFAULT; + +#define DRM_COPY(name,value) \ + len = strlen(value); \ + if (len > name##_len) len = name##_len; \ + name##_len = strlen(value); \ + if (len && name) { \ + if (copy_to_user(name, value, len)) \ + return -EFAULT; \ + } + + version.version_major = RADEON_MAJOR; + version.version_minor = RADEON_MINOR; + version.version_patchlevel = RADEON_PATCHLEVEL; + + DRM_COPY(version.name, RADEON_NAME); + DRM_COPY(version.date, RADEON_DATE); + DRM_COPY(version.desc, RADEON_DESC); + + if (copy_to_user((drm_version_t *)arg, + &version, + sizeof(version))) + return -EFAULT; + return 0; +} + +int radeon_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev = &radeon_device; + int retcode = 0; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_open_helper(inode, filp, dev))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return radeon_setup(dev); + } + spin_unlock(&dev->count_lock); + } + + return retcode; +} + +int radeon_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + int retcode = 0; + + lock_kernel(); + dev = priv->dev; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + + /* Force the cleanup of page flipping when required */ + if ( dev->dev_private ) { + drm_radeon_private_t *dev_priv = dev->dev_private; + if ( dev_priv->page_flipping ) { + radeon_do_cleanup_pageflip( dev ); + } + } + + if (!(retcode = drm_release(inode, filp))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + unlock_kernel(); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return radeon_takedown(dev); + } + spin_unlock(&dev->count_lock); + } + + unlock_kernel(); + return retcode; +} + +/* radeon_ioctl is called whenever a process performs an ioctl on /dev/drm. */ + +int radeon_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, priv->authenticated); + + if (nr >= RADEON_IOCTL_COUNT) { + retcode = -EINVAL; + } else { + ioctl = &radeon_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function\n"); + retcode = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + retcode = -EACCES; + } else { + retcode = (func)(inode, filp, cmd, arg); + } + } + + atomic_dec(&dev->ioctl_count); + return retcode; +} + +int radeon_lock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; +#if DRM_DMA_HISTOGRAM + cycles_t start; + + dev->lck_start = start = get_cycles(); +#endif + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, current->pid, dev->lock.hw_lock->lock, + lock.flags); + + if (lock.context < 0 /* || lock.context >= dev->queue_count */) + return -EINVAL; + + if (!ret) { + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + } + + if (!ret) { + sigemptyset(&dev->sigmask); + sigaddset(&dev->sigmask, SIGSTOP); + sigaddset(&dev->sigmask, SIGTSTP); + sigaddset(&dev->sigmask, SIGTTIN); + sigaddset(&dev->sigmask, SIGTTOU); + dev->sigdata.context = lock.context; + dev->sigdata.lock = dev->lock.hw_lock; + block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask); + if (lock.flags & _DRM_LOCK_READY) { + /* Wait for space in DMA/FIFO */ + } + if (lock.flags & _DRM_LOCK_QUIESCENT) { + /* Make hardware quiescent */ + DRM_DEBUG("not quiescent!\n"); +#if 0 + radeon_quiescent(dev); +#endif + } + } + +#if LINUX_VERSION_CODE < 0x020400 + if (lock.context != radeon_res_ctx.handle) { + current->counter = 5; + current->priority = DEF_PRIORITY/4; + } +#endif + DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]); +#endif + + return ret; +} + + +int radeon_unlock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d frees lock (%d holds)\n", + lock.context, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); + /* FIXME: Try to send data to card here */ + if (!dev->context_flag) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } + +#if LINUX_VERSION_CODE < 0x020400 + if (lock.context != radeon_res_ctx.handle) { + current->counter = 5; + current->priority = DEF_PRIORITY; + } +#endif + unblock_all_signals(); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/radeon_drv.h linux.ac/drivers/char/drm-4.0/radeon_drv.h --- linux.vanilla/drivers/char/drm-4.0/radeon_drv.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/radeon_drv.h Wed Oct 10 01:47:41 2001 @@ -0,0 +1,709 @@ +/* radeon_drv.h -- Private header for radeon driver -*- linux-c -*- + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * Kevin E. Martin + * Gareth Hughes + * + */ + +#ifndef __RADEON_DRV_H__ +#define __RADEON_DRV_H__ + +typedef struct drm_radeon_freelist { + unsigned int age; + drm_buf_t *buf; + struct drm_radeon_freelist *next; + struct drm_radeon_freelist *prev; +} drm_radeon_freelist_t; + +typedef struct drm_radeon_ring_buffer { + u32 *start; + u32 *end; + int size; + int size_l2qw; + + volatile u32 *head; + u32 tail; + u32 tail_mask; + int space; +} drm_radeon_ring_buffer_t; + +typedef struct drm_radeon_depth_clear_t { + u32 rb3d_cntl; + u32 rb3d_zstencilcntl; + u32 se_cntl; +} drm_radeon_depth_clear_t; + +typedef struct drm_radeon_private { + drm_radeon_ring_buffer_t ring; + drm_radeon_sarea_t *sarea_priv; + + int agp_size; + u32 agp_vm_start; + u32 agp_buffers_offset; + + int cp_mode; + int cp_running; + + drm_radeon_freelist_t *head; + drm_radeon_freelist_t *tail; +/* FIXME: ROTATE_BUFS is a hask to cycle through bufs until freelist + code is used. Note this hides a problem with the scratch register + (used to keep track of last buffer completed) being written to before + the last buffer has actually completed rendering. */ +#define ROTATE_BUFS 1 +#if ROTATE_BUFS + int last_buf; +#endif + volatile u32 *scratch; + + int usec_timeout; + int is_pci; + + atomic_t idle_count; + + int page_flipping; + int current_page; + u32 crtc_offset; + u32 crtc_offset_cntl; + + unsigned int color_fmt; + unsigned int front_offset; + unsigned int front_pitch; + unsigned int back_offset; + unsigned int back_pitch; + + unsigned int depth_fmt; + unsigned int depth_offset; + unsigned int depth_pitch; + + u32 front_pitch_offset; + u32 back_pitch_offset; + u32 depth_pitch_offset; + + drm_radeon_depth_clear_t depth_clear; + + drm_map_t *sarea; + drm_map_t *fb; + drm_map_t *mmio; + drm_map_t *cp_ring; + drm_map_t *ring_rptr; + drm_map_t *buffers; + drm_map_t *agp_textures; +} drm_radeon_private_t; + +typedef struct drm_radeon_buf_priv { + u32 age; + int prim; + int discard; + int dispatched; + drm_radeon_freelist_t *list_entry; +} drm_radeon_buf_priv_t; + + /* radeon_drv.c */ +extern int radeon_version( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_open( struct inode *inode, struct file *filp ); +extern int radeon_release( struct inode *inode, struct file *filp ); +extern int radeon_ioctl( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_lock( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_unlock( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); + + /* radeon_cp.c */ +extern int radeon_cp_init( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_cp_start( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_cp_stop( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_cp_reset( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_cp_idle( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_engine_reset( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_fullscreen( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_cp_buffers( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); + +extern void radeon_freelist_reset( drm_device_t *dev ); +extern drm_buf_t *radeon_freelist_get( drm_device_t *dev ); + +extern int radeon_wait_ring( drm_radeon_private_t *dev_priv, int n ); +extern void radeon_update_ring_snapshot( drm_radeon_private_t *dev_priv ); + +extern int radeon_do_cp_idle( drm_radeon_private_t *dev_priv ); +extern int radeon_do_cleanup_pageflip( drm_device_t *dev ); + + /* radeon_state.c */ +extern int radeon_cp_clear( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_cp_swap( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_cp_vertex( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_cp_indices( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_cp_blit( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_cp_stipple( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int radeon_cp_indirect( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); + + /* radeon_bufs.c */ +extern int radeon_addbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int radeon_mapbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* radeon_context.c */ +extern int radeon_resctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int radeon_addctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int radeon_modctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int radeon_getctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int radeon_switchctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int radeon_newctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int radeon_rmctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int radeon_context_switch(drm_device_t *dev, int old, int new); +extern int radeon_context_switch_complete(drm_device_t *dev, int new); + + +/* Register definitions, register access macros and drmAddMap constants + * for Radeon kernel driver. + */ + +#define RADEON_AUX_SCISSOR_CNTL 0x26f0 +# define RADEON_EXCLUSIVE_SCISSOR_0 (1 << 24) +# define RADEON_EXCLUSIVE_SCISSOR_1 (1 << 25) +# define RADEON_EXCLUSIVE_SCISSOR_2 (1 << 26) +# define RADEON_SCISSOR_0_ENABLE (1 << 28) +# define RADEON_SCISSOR_1_ENABLE (1 << 29) +# define RADEON_SCISSOR_2_ENABLE (1 << 30) + +#define RADEON_BUS_CNTL 0x0030 +# define RADEON_BUS_MASTER_DIS (1 << 6) + +#define RADEON_CLOCK_CNTL_DATA 0x000c +# define RADEON_PLL_WR_EN (1 << 7) +#define RADEON_CLOCK_CNTL_INDEX 0x0008 +#define RADEON_CONFIG_APER_SIZE 0x0108 +#define RADEON_CRTC_OFFSET 0x0224 +#define RADEON_CRTC_OFFSET_CNTL 0x0228 +# define RADEON_CRTC_TILE_EN (1 << 15) +# define RADEON_CRTC_OFFSET_FLIP_CNTL (1 << 16) + +#define RADEON_RB3D_COLORPITCH 0x1c48 +#define RADEON_RB3D_DEPTHCLEARVALUE 0x1c30 +#define RADEON_RB3D_DEPTHXY_OFFSET 0x1c60 + +#define RADEON_DP_GUI_MASTER_CNTL 0x146c +# define RADEON_GMC_SRC_PITCH_OFFSET_CNTL (1 << 0) +# define RADEON_GMC_DST_PITCH_OFFSET_CNTL (1 << 1) +# define RADEON_GMC_BRUSH_SOLID_COLOR (13 << 4) +# define RADEON_GMC_BRUSH_NONE (15 << 4) +# define RADEON_GMC_DST_16BPP (4 << 8) +# define RADEON_GMC_DST_24BPP (5 << 8) +# define RADEON_GMC_DST_32BPP (6 << 8) +# define RADEON_GMC_DST_DATATYPE_SHIFT 8 +# define RADEON_GMC_SRC_DATATYPE_COLOR (3 << 12) +# define RADEON_DP_SRC_SOURCE_MEMORY (2 << 24) +# define RADEON_DP_SRC_SOURCE_HOST_DATA (3 << 24) +# define RADEON_GMC_CLR_CMP_CNTL_DIS (1 << 28) +# define RADEON_GMC_WR_MSK_DIS (1 << 30) +# define RADEON_ROP3_S 0x00cc0000 +# define RADEON_ROP3_P 0x00f00000 +#define RADEON_DP_WRITE_MASK 0x16cc +#define RADEON_DST_PITCH_OFFSET 0x142c +#define RADEON_DST_PITCH_OFFSET_C 0x1c80 +# define RADEON_DST_TILE_LINEAR (0 << 30) +# define RADEON_DST_TILE_MACRO (1 << 30) +# define RADEON_DST_TILE_MICRO (2 << 30) +# define RADEON_DST_TILE_BOTH (3 << 30) + +#define RADEON_SCRATCH_REG0 0x15e0 +#define RADEON_SCRATCH_REG1 0x15e4 +#define RADEON_SCRATCH_REG2 0x15e8 +#define RADEON_SCRATCH_REG3 0x15ec +#define RADEON_SCRATCH_REG4 0x15f0 +#define RADEON_SCRATCH_REG5 0x15f4 +#define RADEON_SCRATCH_UMSK 0x0770 +#define RADEON_SCRATCH_ADDR 0x0774 + +#define RADEON_HOST_PATH_CNTL 0x0130 +# define RADEON_HDP_SOFT_RESET (1 << 26) +# define RADEON_HDP_WC_TIMEOUT_MASK (7 << 28) +# define RADEON_HDP_WC_TIMEOUT_28BCLK (7 << 28) + +#define RADEON_ISYNC_CNTL 0x1724 +# define RADEON_ISYNC_ANY2D_IDLE3D (1 << 0) +# define RADEON_ISYNC_ANY3D_IDLE2D (1 << 1) +# define RADEON_ISYNC_TRIG2D_IDLE3D (1 << 2) +# define RADEON_ISYNC_TRIG3D_IDLE2D (1 << 3) +# define RADEON_ISYNC_WAIT_IDLEGUI (1 << 4) +# define RADEON_ISYNC_CPSCRATCH_IDLEGUI (1 << 5) + +#define RADEON_MC_AGP_LOCATION 0x014c +#define RADEON_MC_FB_LOCATION 0x0148 +#define RADEON_MCLK_CNTL 0x0012 + +#define RADEON_PP_BORDER_COLOR_0 0x1d40 +#define RADEON_PP_BORDER_COLOR_1 0x1d44 +#define RADEON_PP_BORDER_COLOR_2 0x1d48 +#define RADEON_PP_CNTL 0x1c38 +# define RADEON_SCISSOR_ENABLE (1 << 1) +#define RADEON_PP_LUM_MATRIX 0x1d00 +#define RADEON_PP_MISC 0x1c14 +#define RADEON_PP_ROT_MATRIX_0 0x1d58 +#define RADEON_PP_TXFILTER_0 0x1c54 +#define RADEON_PP_TXFILTER_1 0x1c6c +#define RADEON_PP_TXFILTER_2 0x1c84 + +#define RADEON_RB2D_DSTCACHE_CTLSTAT 0x342c +# define RADEON_RB2D_DC_FLUSH (3 << 0) +# define RADEON_RB2D_DC_FREE (3 << 2) +# define RADEON_RB2D_DC_FLUSH_ALL 0xf +# define RADEON_RB2D_DC_BUSY (1 << 31) +#define RADEON_RB3D_CNTL 0x1c3c +# define RADEON_ALPHA_BLEND_ENABLE (1 << 0) +# define RADEON_PLANE_MASK_ENABLE (1 << 1) +# define RADEON_DITHER_ENABLE (1 << 2) +# define RADEON_ROUND_ENABLE (1 << 3) +# define RADEON_SCALE_DITHER_ENABLE (1 << 4) +# define RADEON_DITHER_INIT (1 << 5) +# define RADEON_ROP_ENABLE (1 << 6) +# define RADEON_STENCIL_ENABLE (1 << 7) +# define RADEON_Z_ENABLE (1 << 8) +# define RADEON_DEPTH_XZ_OFFEST_ENABLE (1 << 9) +# define RADEON_ZBLOCK8 (0 << 15) +# define RADEON_ZBLOCK16 (1 << 15) +#define RADEON_RB3D_DEPTHOFFSET 0x1c24 +#define RADEON_RB3D_PLANEMASK 0x1d84 +#define RADEON_RB3D_STENCILREFMASK 0x1d7c +#define RADEON_RB3D_ZCACHE_MODE 0x3250 +#define RADEON_RB3D_ZCACHE_CTLSTAT 0x3254 +# define RADEON_RB3D_ZC_FLUSH (1 << 0) +# define RADEON_RB3D_ZC_FREE (1 << 2) +# define RADEON_RB3D_ZC_FLUSH_ALL 0x5 +# define RADEON_RB3D_ZC_BUSY (1 << 31) +#define RADEON_RB3D_ZSTENCILCNTL 0x1c2c +# define RADEON_Z_TEST_MASK (7 << 4) +# define RADEON_Z_TEST_ALWAYS (7 << 4) +# define RADEON_STENCIL_TEST_ALWAYS (7 << 12) +# define RADEON_STENCIL_S_FAIL_KEEP (0 << 16) +# define RADEON_STENCIL_ZPASS_KEEP (0 << 20) +# define RADEON_STENCIL_ZFAIL_KEEP (0 << 20) +# define RADEON_Z_WRITE_ENABLE (1 << 30) +#define RADEON_RBBM_SOFT_RESET 0x00f0 +# define RADEON_SOFT_RESET_CP (1 << 0) +# define RADEON_SOFT_RESET_HI (1 << 1) +# define RADEON_SOFT_RESET_SE (1 << 2) +# define RADEON_SOFT_RESET_RE (1 << 3) +# define RADEON_SOFT_RESET_PP (1 << 4) +# define RADEON_SOFT_RESET_E2 (1 << 5) +# define RADEON_SOFT_RESET_RB (1 << 6) +# define RADEON_SOFT_RESET_HDP (1 << 7) +#define RADEON_RBBM_STATUS 0x0e40 +# define RADEON_RBBM_FIFOCNT_MASK 0x007f +# define RADEON_RBBM_ACTIVE (1 << 31) +#define RADEON_RE_LINE_PATTERN 0x1cd0 +#define RADEON_RE_MISC 0x26c4 +#define RADEON_RE_TOP_LEFT 0x26c0 +#define RADEON_RE_WIDTH_HEIGHT 0x1c44 +#define RADEON_RE_STIPPLE_ADDR 0x1cc8 +#define RADEON_RE_STIPPLE_DATA 0x1ccc + +#define RADEON_SCISSOR_TL_0 0x1cd8 +#define RADEON_SCISSOR_BR_0 0x1cdc +#define RADEON_SCISSOR_TL_1 0x1ce0 +#define RADEON_SCISSOR_BR_1 0x1ce4 +#define RADEON_SCISSOR_TL_2 0x1ce8 +#define RADEON_SCISSOR_BR_2 0x1cec +#define RADEON_SE_COORD_FMT 0x1c50 +#define RADEON_SE_CNTL 0x1c4c +# define RADEON_FFACE_CULL_CW (0 << 0) +# define RADEON_BFACE_SOLID (3 << 1) +# define RADEON_FFACE_SOLID (3 << 3) +# define RADEON_FLAT_SHADE_VTX_LAST (3 << 6) +# define RADEON_DIFFUSE_SHADE_FLAT (1 << 8) +# define RADEON_DIFFUSE_SHADE_GOURAUD (2 << 8) +# define RADEON_ALPHA_SHADE_FLAT (1 << 10) +# define RADEON_ALPHA_SHADE_GOURAUD (2 << 10) +# define RADEON_SPECULAR_SHADE_FLAT (1 << 12) +# define RADEON_SPECULAR_SHADE_GOURAUD (2 << 12) +# define RADEON_FOG_SHADE_FLAT (1 << 14) +# define RADEON_FOG_SHADE_GOURAUD (2 << 14) +# define RADEON_VPORT_XY_XFORM_ENABLE (1 << 24) +# define RADEON_VPORT_Z_XFORM_ENABLE (1 << 25) +# define RADEON_VTX_PIX_CENTER_OGL (1 << 27) +# define RADEON_ROUND_MODE_TRUNC (0 << 28) +# define RADEON_ROUND_PREC_8TH_PIX (1 << 30) +#define RADEON_SE_CNTL_STATUS 0x2140 +#define RADEON_SE_LINE_WIDTH 0x1db8 +#define RADEON_SE_VPORT_XSCALE 0x1d98 +#define RADEON_SURFACE_ACCESS_FLAGS 0x0bf8 +#define RADEON_SURFACE_ACCESS_CLR 0x0bfc +#define RADEON_SURFACE_CNTL 0x0b00 +# define RADEON_SURF_TRANSLATION_DIS (1 << 8) +# define RADEON_NONSURF_AP0_SWP_MASK (3 << 20) +# define RADEON_NONSURF_AP0_SWP_LITTLE (0 << 20) +# define RADEON_NONSURF_AP0_SWP_BIG16 (1 << 20) +# define RADEON_NONSURF_AP0_SWP_BIG32 (2 << 20) +# define RADEON_NONSURF_AP1_SWP_MASK (3 << 22) +# define RADEON_NONSURF_AP1_SWP_LITTLE (0 << 22) +# define RADEON_NONSURF_AP1_SWP_BIG16 (1 << 22) +# define RADEON_NONSURF_AP1_SWP_BIG32 (2 << 22) +#define RADEON_SURFACE0_INFO 0x0b0c +# define RADEON_SURF_PITCHSEL_MASK (0x1ff << 0) +# define RADEON_SURF_TILE_MODE_MASK (3 << 16) +# define RADEON_SURF_TILE_MODE_MACRO (0 << 16) +# define RADEON_SURF_TILE_MODE_MICRO (1 << 16) +# define RADEON_SURF_TILE_MODE_32BIT_Z (2 << 16) +# define RADEON_SURF_TILE_MODE_16BIT_Z (3 << 16) +#define RADEON_SURFACE0_LOWER_BOUND 0x0b04 +#define RADEON_SURFACE0_UPPER_BOUND 0x0b08 +#define RADEON_SURFACE1_INFO 0x0b1c +#define RADEON_SURFACE1_LOWER_BOUND 0x0b14 +#define RADEON_SURFACE1_UPPER_BOUND 0x0b18 +#define RADEON_SURFACE2_INFO 0x0b2c +#define RADEON_SURFACE2_LOWER_BOUND 0x0b24 +#define RADEON_SURFACE2_UPPER_BOUND 0x0b28 +#define RADEON_SURFACE3_INFO 0x0b3c +#define RADEON_SURFACE3_LOWER_BOUND 0x0b34 +#define RADEON_SURFACE3_UPPER_BOUND 0x0b38 +#define RADEON_SURFACE4_INFO 0x0b4c +#define RADEON_SURFACE4_LOWER_BOUND 0x0b44 +#define RADEON_SURFACE4_UPPER_BOUND 0x0b48 +#define RADEON_SURFACE5_INFO 0x0b5c +#define RADEON_SURFACE5_LOWER_BOUND 0x0b54 +#define RADEON_SURFACE5_UPPER_BOUND 0x0b58 +#define RADEON_SURFACE6_INFO 0x0b6c +#define RADEON_SURFACE6_LOWER_BOUND 0x0b64 +#define RADEON_SURFACE6_UPPER_BOUND 0x0b68 +#define RADEON_SURFACE7_INFO 0x0b7c +#define RADEON_SURFACE7_LOWER_BOUND 0x0b74 +#define RADEON_SURFACE7_UPPER_BOUND 0x0b78 +#define RADEON_SW_SEMAPHORE 0x013c + +#define RADEON_WAIT_UNTIL 0x1720 +# define RADEON_WAIT_CRTC_PFLIP (1 << 0) +# define RADEON_WAIT_2D_IDLECLEAN (1 << 16) +# define RADEON_WAIT_3D_IDLECLEAN (1 << 17) +# define RADEON_WAIT_HOST_IDLECLEAN (1 << 18) + +#define RADEON_RB3D_ZMASKOFFSET 0x1c34 +#define RADEON_RB3D_ZSTENCILCNTL 0x1c2c +# define RADEON_DEPTH_FORMAT_16BIT_INT_Z (0 << 0) +# define RADEON_DEPTH_FORMAT_24BIT_INT_Z (2 << 0) + + +/* CP registers */ +#define RADEON_CP_ME_RAM_ADDR 0x07d4 +#define RADEON_CP_ME_RAM_RADDR 0x07d8 +#define RADEON_CP_ME_RAM_DATAH 0x07dc +#define RADEON_CP_ME_RAM_DATAL 0x07e0 + +#define RADEON_CP_RB_BASE 0x0700 +#define RADEON_CP_RB_CNTL 0x0704 +#define RADEON_CP_RB_RPTR_ADDR 0x070c +#define RADEON_CP_RB_RPTR 0x0710 +#define RADEON_CP_RB_WPTR 0x0714 + +#define RADEON_CP_RB_WPTR_DELAY 0x0718 +# define RADEON_PRE_WRITE_TIMER_SHIFT 0 +# define RADEON_PRE_WRITE_LIMIT_SHIFT 23 + +#define RADEON_CP_IB_BASE 0x0738 + +#define RADEON_CP_CSQ_CNTL 0x0740 +# define RADEON_CSQ_CNT_PRIMARY_MASK (0xff << 0) +# define RADEON_CSQ_PRIDIS_INDDIS (0 << 28) +# define RADEON_CSQ_PRIPIO_INDDIS (1 << 28) +# define RADEON_CSQ_PRIBM_INDDIS (2 << 28) +# define RADEON_CSQ_PRIPIO_INDBM (3 << 28) +# define RADEON_CSQ_PRIBM_INDBM (4 << 28) +# define RADEON_CSQ_PRIPIO_INDPIO (15 << 28) + +#define RADEON_AIC_CNTL 0x01d0 +# define RADEON_PCIGART_TRANSLATE_EN (1 << 0) + +/* CP command packets */ +#define RADEON_CP_PACKET0 0x00000000 +# define RADEON_ONE_REG_WR (1 << 15) +#define RADEON_CP_PACKET1 0x40000000 +#define RADEON_CP_PACKET2 0x80000000 +#define RADEON_CP_PACKET3 0xC0000000 +# define RADEON_3D_RNDR_GEN_INDX_PRIM 0x00002300 +# define RADEON_WAIT_FOR_IDLE 0x00002600 +# define RADEON_3D_DRAW_IMMD 0x00002900 +# define RADEON_3D_CLEAR_ZMASK 0x00003200 +# define RADEON_CNTL_HOSTDATA_BLT 0x00009400 +# define RADEON_CNTL_PAINT_MULTI 0x00009A00 +# define RADEON_CNTL_BITBLT_MULTI 0x00009B00 + +#define RADEON_CP_PACKET_MASK 0xC0000000 +#define RADEON_CP_PACKET_COUNT_MASK 0x3fff0000 +#define RADEON_CP_PACKET0_REG_MASK 0x000007ff +#define RADEON_CP_PACKET1_REG0_MASK 0x000007ff +#define RADEON_CP_PACKET1_REG1_MASK 0x003ff800 + +#define RADEON_VTX_Z_PRESENT (1 << 31) + +#define RADEON_PRIM_TYPE_NONE (0 << 0) +#define RADEON_PRIM_TYPE_POINT (1 << 0) +#define RADEON_PRIM_TYPE_LINE (2 << 0) +#define RADEON_PRIM_TYPE_LINE_STRIP (3 << 0) +#define RADEON_PRIM_TYPE_TRI_LIST (4 << 0) +#define RADEON_PRIM_TYPE_TRI_FAN (5 << 0) +#define RADEON_PRIM_TYPE_TRI_STRIP (6 << 0) +#define RADEON_PRIM_TYPE_TRI_TYPE2 (7 << 0) +#define RADEON_PRIM_TYPE_RECT_LIST (8 << 0) +#define RADEON_PRIM_TYPE_3VRT_POINT_LIST (9 << 0) +#define RADEON_PRIM_TYPE_3VRT_LINE_LIST (10 << 0) +#define RADEON_PRIM_WALK_IND (1 << 4) +#define RADEON_PRIM_WALK_LIST (2 << 4) +#define RADEON_PRIM_WALK_RING (3 << 4) +#define RADEON_COLOR_ORDER_BGRA (0 << 6) +#define RADEON_COLOR_ORDER_RGBA (1 << 6) +#define RADEON_MAOS_ENABLE (1 << 7) +#define RADEON_VTX_FMT_R128_MODE (0 << 8) +#define RADEON_VTX_FMT_RADEON_MODE (1 << 8) +#define RADEON_NUM_VERTICES_SHIFT 16 + +#define RADEON_COLOR_FORMAT_CI8 2 +#define RADEON_COLOR_FORMAT_ARGB1555 3 +#define RADEON_COLOR_FORMAT_RGB565 4 +#define RADEON_COLOR_FORMAT_ARGB8888 6 +#define RADEON_COLOR_FORMAT_RGB332 7 +#define RADEON_COLOR_FORMAT_RGB8 9 +#define RADEON_COLOR_FORMAT_ARGB4444 15 + +#define RADEON_TXF_8BPP_I 0 +#define RADEON_TXF_16BPP_AI88 1 +#define RADEON_TXF_8BPP_RGB332 2 +#define RADEON_TXF_16BPP_ARGB1555 3 +#define RADEON_TXF_16BPP_RGB565 4 +#define RADEON_TXF_16BPP_ARGB4444 5 +#define RADEON_TXF_32BPP_ARGB8888 6 +#define RADEON_TXF_32BPP_RGBA8888 7 + +/* Constants */ +#define RADEON_MAX_USEC_TIMEOUT 100000 /* 100 ms */ + +#define RADEON_LAST_FRAME_REG RADEON_SCRATCH_REG0 +#define RADEON_LAST_DISPATCH_REG RADEON_SCRATCH_REG1 +#define RADEON_LAST_CLEAR_REG RADEON_SCRATCH_REG2 +#define RADEON_LAST_DISPATCH 1 + +#define RADEON_MAX_VB_AGE 0x7fffffff +#define RADEON_MAX_VB_VERTS (0xffff) + + +#define RADEON_BASE(reg) ((u32)(dev_priv->mmio->handle)) +#define RADEON_ADDR(reg) (RADEON_BASE(reg) + reg) + +#define RADEON_DEREF(reg) *(__volatile__ u32 *)RADEON_ADDR(reg) +#define RADEON_READ(reg) RADEON_DEREF(reg) +#define RADEON_WRITE(reg,val) do { RADEON_DEREF(reg) = val; } while (0) + +#define RADEON_DEREF8(reg) *(__volatile__ u8 *)RADEON_ADDR(reg) +#define RADEON_READ8(reg) RADEON_DEREF8(reg) +#define RADEON_WRITE8(reg,val) do { RADEON_DEREF8(reg) = val; } while (0) + +#define RADEON_WRITE_PLL(addr,val) \ +do { \ + RADEON_WRITE8(RADEON_CLOCK_CNTL_INDEX, \ + ((addr) & 0x1f) | RADEON_PLL_WR_EN); \ + RADEON_WRITE(RADEON_CLOCK_CNTL_DATA, (val)); \ +} while (0) + +extern int RADEON_READ_PLL(drm_device_t *dev, int addr); + + + +#define CP_PACKET0( reg, n ) \ + (RADEON_CP_PACKET0 | ((n) << 16) | ((reg) >> 2)) +#define CP_PACKET0_TABLE( reg, n ) \ + (RADEON_CP_PACKET0 | RADEON_ONE_REG_WR | ((n) << 16) | ((reg) >> 2)) +#define CP_PACKET1( reg0, reg1 ) \ + (RADEON_CP_PACKET1 | (((reg1) >> 2) << 15) | ((reg0) >> 2)) +#define CP_PACKET2() \ + (RADEON_CP_PACKET2) +#define CP_PACKET3( pkt, n ) \ + (RADEON_CP_PACKET3 | (pkt) | ((n) << 16)) + + +/* ================================================================ + * Engine control helper macros + */ + +#define RADEON_WAIT_UNTIL_2D_IDLE() \ +do { \ + OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) ); \ + OUT_RING( (RADEON_WAIT_2D_IDLECLEAN | \ + RADEON_WAIT_HOST_IDLECLEAN) ); \ +} while (0) + +#define RADEON_WAIT_UNTIL_3D_IDLE() \ +do { \ + OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) ); \ + OUT_RING( (RADEON_WAIT_3D_IDLECLEAN | \ + RADEON_WAIT_HOST_IDLECLEAN) ); \ +} while (0) + +#define RADEON_WAIT_UNTIL_IDLE() \ +do { \ + OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) ); \ + OUT_RING( (RADEON_WAIT_2D_IDLECLEAN | \ + RADEON_WAIT_3D_IDLECLEAN | \ + RADEON_WAIT_HOST_IDLECLEAN) ); \ +} while (0) + +#define RADEON_WAIT_UNTIL_PAGE_FLIPPED() \ +do { \ + OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) ); \ + OUT_RING( RADEON_WAIT_CRTC_PFLIP ); \ +} while (0) + +#define RADEON_FLUSH_CACHE() \ +do { \ + OUT_RING( CP_PACKET0( RADEON_RB2D_DSTCACHE_CTLSTAT, 0 ) ); \ + OUT_RING( RADEON_RB2D_DC_FLUSH ); \ +} while (0) + +#define RADEON_PURGE_CACHE() \ +do { \ + OUT_RING( CP_PACKET0( RADEON_RB2D_DSTCACHE_CTLSTAT, 0 ) ); \ + OUT_RING( RADEON_RB2D_DC_FLUSH_ALL ); \ +} while (0) + +#define RADEON_FLUSH_ZCACHE() \ +do { \ + OUT_RING( CP_PACKET0( RADEON_RB3D_ZCACHE_CTLSTAT, 0 ) ); \ + OUT_RING( RADEON_RB3D_ZC_FLUSH ); \ +} while (0) + +#define RADEON_PURGE_ZCACHE() \ +do { \ + OUT_RING( CP_PACKET0( RADEON_RB3D_ZCACHE_CTLSTAT, 0 ) ); \ + OUT_RING( RADEON_RB3D_ZC_FLUSH_ALL ); \ +} while (0) + + +/* ================================================================ + * Misc helper macros + */ + +#define VB_AGE_CHECK_WITH_RET( dev_priv ) \ +do { \ + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; \ + if ( sarea_priv->last_dispatch >= RADEON_MAX_VB_AGE ) { \ + int __ret = radeon_do_cp_idle( dev_priv ); \ + if ( __ret < 0 ) return __ret; \ + sarea_priv->last_dispatch = 0; \ + radeon_freelist_reset( dev ); \ + } \ +} while (0) + +#define RADEON_DISPATCH_AGE( age ) \ +do { \ + OUT_RING( CP_PACKET0( RADEON_LAST_DISPATCH_REG, 0 ) ); \ + OUT_RING( age ); \ +} while (0) + +#define RADEON_FRAME_AGE( age ) \ +do { \ + OUT_RING( CP_PACKET0( RADEON_LAST_FRAME_REG, 0 ) ); \ + OUT_RING( age ); \ +} while (0) + +#define RADEON_CLEAR_AGE( age ) \ +do { \ + OUT_RING( CP_PACKET0( RADEON_LAST_CLEAR_REG, 0 ) ); \ + OUT_RING( age ); \ +} while (0) + + +/* ================================================================ + * Ring control + */ + +#define radeon_flush_write_combine() mb() + + +#define RADEON_VERBOSE 0 + +#define RING_LOCALS int write; unsigned int mask; volatile u32 *ring; + +#define BEGIN_RING( n ) do { \ + if ( RADEON_VERBOSE ) { \ + DRM_INFO( "BEGIN_RING( %d ) in %s\n", \ + n, __FUNCTION__ ); \ + } \ + if ( dev_priv->ring.space < (n) * sizeof(u32) ) { \ + radeon_wait_ring( dev_priv, (n) * sizeof(u32) ); \ + } \ + dev_priv->ring.space -= (n) * sizeof(u32); \ + ring = dev_priv->ring.start; \ + write = dev_priv->ring.tail; \ + mask = dev_priv->ring.tail_mask; \ +} while (0) + +#define ADVANCE_RING() do { \ + if ( RADEON_VERBOSE ) { \ + DRM_INFO( "ADVANCE_RING() tail=0x%06x wr=0x%06x\n", \ + write, dev_priv->ring.tail ); \ + } \ + radeon_flush_write_combine(); \ + dev_priv->ring.tail = write; \ + RADEON_WRITE( RADEON_CP_RB_WPTR, write ); \ +} while (0) + +#define OUT_RING( x ) do { \ + if ( RADEON_VERBOSE ) { \ + DRM_INFO( " OUT_RING( 0x%08x ) at 0x%x\n", \ + (unsigned int)(x), write ); \ + } \ + ring[write++] = (x); \ + write &= mask; \ +} while (0) + +#define RADEON_PERFORMANCE_BOXES 0 + +#endif /* __RADEON_DRV_H__ */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/radeon_state.c linux.ac/drivers/char/drm-4.0/radeon_state.c --- linux.vanilla/drivers/char/drm-4.0/radeon_state.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/radeon_state.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,1447 @@ +/* radeon_state.c -- State support for Radeon -*- linux-c -*- + * + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Kevin E. Martin + * Gareth Hughes + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "radeon_drv.h" +#include "drm.h" +#include + + +/* ================================================================ + * CP hardware state programming functions + */ + +static inline void radeon_emit_clip_rect( drm_radeon_private_t *dev_priv, + drm_clip_rect_t *box ) +{ + RING_LOCALS; + + DRM_DEBUG( " box: x1=%d y1=%d x2=%d y2=%d\n", + box->x1, box->y1, box->x2, box->y2 ); + + BEGIN_RING( 4 ); + + OUT_RING( CP_PACKET0( RADEON_RE_TOP_LEFT, 0 ) ); + OUT_RING( (box->y1 << 16) | box->x1 ); + + OUT_RING( CP_PACKET0( RADEON_RE_WIDTH_HEIGHT, 0 ) ); + OUT_RING( ((box->y2 - 1) << 16) | (box->x2 - 1) ); + + ADVANCE_RING(); +} + +static inline void radeon_emit_context( drm_radeon_private_t *dev_priv ) +{ + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 14 ); + + OUT_RING( CP_PACKET0( RADEON_PP_MISC, 6 ) ); + OUT_RING( ctx->pp_misc ); + OUT_RING( ctx->pp_fog_color ); + OUT_RING( ctx->re_solid_color ); + OUT_RING( ctx->rb3d_blendcntl ); + OUT_RING( ctx->rb3d_depthoffset ); + OUT_RING( ctx->rb3d_depthpitch ); + OUT_RING( ctx->rb3d_zstencilcntl ); + + OUT_RING( CP_PACKET0( RADEON_PP_CNTL, 2 ) ); + OUT_RING( ctx->pp_cntl ); + OUT_RING( ctx->rb3d_cntl ); + OUT_RING( ctx->rb3d_coloroffset ); + + OUT_RING( CP_PACKET0( RADEON_RB3D_COLORPITCH, 0 ) ); + OUT_RING( ctx->rb3d_colorpitch ); + + ADVANCE_RING(); +} + +static inline void radeon_emit_vertfmt( drm_radeon_private_t *dev_priv ) +{ + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 2 ); + + OUT_RING( CP_PACKET0( RADEON_SE_COORD_FMT, 0 ) ); + OUT_RING( ctx->se_coord_fmt ); + + ADVANCE_RING(); +} + +static inline void radeon_emit_line( drm_radeon_private_t *dev_priv ) +{ + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 5 ); + + OUT_RING( CP_PACKET0( RADEON_RE_LINE_PATTERN, 1 ) ); + OUT_RING( ctx->re_line_pattern ); + OUT_RING( ctx->re_line_state ); + + OUT_RING( CP_PACKET0( RADEON_SE_LINE_WIDTH, 0 ) ); + OUT_RING( ctx->se_line_width ); + + ADVANCE_RING(); +} + +static inline void radeon_emit_bumpmap( drm_radeon_private_t *dev_priv ) +{ + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 5 ); + + OUT_RING( CP_PACKET0( RADEON_PP_LUM_MATRIX, 0 ) ); + OUT_RING( ctx->pp_lum_matrix ); + + OUT_RING( CP_PACKET0( RADEON_PP_ROT_MATRIX_0, 1 ) ); + OUT_RING( ctx->pp_rot_matrix_0 ); + OUT_RING( ctx->pp_rot_matrix_1 ); + + ADVANCE_RING(); +} + +static inline void radeon_emit_masks( drm_radeon_private_t *dev_priv ) +{ + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 4 ); + + OUT_RING( CP_PACKET0( RADEON_RB3D_STENCILREFMASK, 2 ) ); + OUT_RING( ctx->rb3d_stencilrefmask ); + OUT_RING( ctx->rb3d_ropcntl ); + OUT_RING( ctx->rb3d_planemask ); + + ADVANCE_RING(); +} + +static inline void radeon_emit_viewport( drm_radeon_private_t *dev_priv ) +{ + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 7 ); + + OUT_RING( CP_PACKET0( RADEON_SE_VPORT_XSCALE, 5 ) ); + OUT_RING( ctx->se_vport_xscale ); + OUT_RING( ctx->se_vport_xoffset ); + OUT_RING( ctx->se_vport_yscale ); + OUT_RING( ctx->se_vport_yoffset ); + OUT_RING( ctx->se_vport_zscale ); + OUT_RING( ctx->se_vport_zoffset ); + + ADVANCE_RING(); +} + +static inline void radeon_emit_setup( drm_radeon_private_t *dev_priv ) +{ + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 4 ); + + OUT_RING( CP_PACKET0( RADEON_SE_CNTL, 0 ) ); + OUT_RING( ctx->se_cntl ); + OUT_RING( CP_PACKET0( RADEON_SE_CNTL_STATUS, 0 ) ); + OUT_RING( ctx->se_cntl_status ); + + ADVANCE_RING(); +} + +static inline void radeon_emit_tcl( drm_radeon_private_t *dev_priv ) +{ +#ifdef TCL_ENABLE + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 29 ); + + OUT_RING( CP_PACKET0( RADEON_SE_TCL_MATERIAL_EMMISSIVE_RED, 27 ) ); + OUT_RING( ctx->se_tcl_material_emmissive.red ); + OUT_RING( ctx->se_tcl_material_emmissive.green ); + OUT_RING( ctx->se_tcl_material_emmissive.blue ); + OUT_RING( ctx->se_tcl_material_emmissive.alpha ); + OUT_RING( ctx->se_tcl_material_ambient.red ); + OUT_RING( ctx->se_tcl_material_ambient.green ); + OUT_RING( ctx->se_tcl_material_ambient.blue ); + OUT_RING( ctx->se_tcl_material_ambient.alpha ); + OUT_RING( ctx->se_tcl_material_diffuse.red ); + OUT_RING( ctx->se_tcl_material_diffuse.green ); + OUT_RING( ctx->se_tcl_material_diffuse.blue ); + OUT_RING( ctx->se_tcl_material_diffuse.alpha ); + OUT_RING( ctx->se_tcl_material_specular.red ); + OUT_RING( ctx->se_tcl_material_specular.green ); + OUT_RING( ctx->se_tcl_material_specular.blue ); + OUT_RING( ctx->se_tcl_material_specular.alpha ); + OUT_RING( ctx->se_tcl_shininess ); + OUT_RING( ctx->se_tcl_output_vtx_fmt ); + OUT_RING( ctx->se_tcl_output_vtx_sel ); + OUT_RING( ctx->se_tcl_matrix_select_0 ); + OUT_RING( ctx->se_tcl_matrix_select_1 ); + OUT_RING( ctx->se_tcl_ucp_vert_blend_ctl ); + OUT_RING( ctx->se_tcl_texture_proc_ctl ); + OUT_RING( ctx->se_tcl_light_model_ctl ); + for ( i = 0 ; i < 4 ; i++ ) { + OUT_RING( ctx->se_tcl_per_light_ctl[i] ); + } + + ADVANCE_RING(); +#else + DRM_ERROR( "TCL not enabled!\n" ); +#endif +} + +static inline void radeon_emit_misc( drm_radeon_private_t *dev_priv ) +{ + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 2 ); + + OUT_RING( CP_PACKET0( RADEON_RE_MISC, 0 ) ); + OUT_RING( ctx->re_misc ); + + ADVANCE_RING(); +} + +static inline void radeon_emit_tex0( drm_radeon_private_t *dev_priv ) +{ + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_texture_regs_t *tex = &sarea_priv->tex_state[0]; + RING_LOCALS; + DRM_DEBUG( " %s: offset=0x%x\n", __FUNCTION__, tex->pp_txoffset ); + + BEGIN_RING( 9 ); + + OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_0, 5 ) ); + OUT_RING( tex->pp_txfilter ); + OUT_RING( tex->pp_txformat ); + OUT_RING( tex->pp_txoffset ); + OUT_RING( tex->pp_txcblend ); + OUT_RING( tex->pp_txablend ); + OUT_RING( tex->pp_tfactor ); + + OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_0, 0 ) ); + OUT_RING( tex->pp_border_color ); + + ADVANCE_RING(); +} + +static inline void radeon_emit_tex1( drm_radeon_private_t *dev_priv ) +{ + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_texture_regs_t *tex = &sarea_priv->tex_state[1]; + RING_LOCALS; + DRM_DEBUG( " %s: offset=0x%x\n", __FUNCTION__, tex->pp_txoffset ); + + BEGIN_RING( 9 ); + + OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_1, 5 ) ); + OUT_RING( tex->pp_txfilter ); + OUT_RING( tex->pp_txformat ); + OUT_RING( tex->pp_txoffset ); + OUT_RING( tex->pp_txcblend ); + OUT_RING( tex->pp_txablend ); + OUT_RING( tex->pp_tfactor ); + + OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_1, 0 ) ); + OUT_RING( tex->pp_border_color ); + + ADVANCE_RING(); +} + +static inline void radeon_emit_tex2( drm_radeon_private_t *dev_priv ) +{ + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_texture_regs_t *tex = &sarea_priv->tex_state[2]; + RING_LOCALS; + DRM_DEBUG( " %s\n", __FUNCTION__ ); + + BEGIN_RING( 9 ); + + OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_2, 5 ) ); + OUT_RING( tex->pp_txfilter ); + OUT_RING( tex->pp_txformat ); + OUT_RING( tex->pp_txoffset ); + OUT_RING( tex->pp_txcblend ); + OUT_RING( tex->pp_txablend ); + OUT_RING( tex->pp_tfactor ); + + OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_2, 0 ) ); + OUT_RING( tex->pp_border_color ); + + ADVANCE_RING(); +} + +static inline void radeon_emit_state( drm_radeon_private_t *dev_priv ) +{ + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int dirty = sarea_priv->dirty; + + DRM_DEBUG( "%s: dirty=0x%08x\n", __FUNCTION__, dirty ); + + if ( dirty & RADEON_UPLOAD_CONTEXT ) { + radeon_emit_context( dev_priv ); + sarea_priv->dirty &= ~RADEON_UPLOAD_CONTEXT; + } + + if ( dirty & RADEON_UPLOAD_VERTFMT ) { + radeon_emit_vertfmt( dev_priv ); + sarea_priv->dirty &= ~RADEON_UPLOAD_VERTFMT; + } + + if ( dirty & RADEON_UPLOAD_LINE ) { + radeon_emit_line( dev_priv ); + sarea_priv->dirty &= ~RADEON_UPLOAD_LINE; + } + + if ( dirty & RADEON_UPLOAD_BUMPMAP ) { + radeon_emit_bumpmap( dev_priv ); + sarea_priv->dirty &= ~RADEON_UPLOAD_BUMPMAP; + } + + if ( dirty & RADEON_UPLOAD_MASKS ) { + radeon_emit_masks( dev_priv ); + sarea_priv->dirty &= ~RADEON_UPLOAD_MASKS; + } + + if ( dirty & RADEON_UPLOAD_VIEWPORT ) { + radeon_emit_viewport( dev_priv ); + sarea_priv->dirty &= ~RADEON_UPLOAD_VIEWPORT; + } + + if ( dirty & RADEON_UPLOAD_SETUP ) { + radeon_emit_setup( dev_priv ); + sarea_priv->dirty &= ~RADEON_UPLOAD_SETUP; + } + + if ( dirty & RADEON_UPLOAD_TCL ) { +#ifdef TCL_ENABLE + radeon_emit_tcl( dev_priv ); +#endif + sarea_priv->dirty &= ~RADEON_UPLOAD_TCL; + } + + if ( dirty & RADEON_UPLOAD_MISC ) { + radeon_emit_misc( dev_priv ); + sarea_priv->dirty &= ~RADEON_UPLOAD_MISC; + } + + if ( dirty & RADEON_UPLOAD_TEX0 ) { + radeon_emit_tex0( dev_priv ); + sarea_priv->dirty &= ~RADEON_UPLOAD_TEX0; + } + + if ( dirty & RADEON_UPLOAD_TEX1 ) { + radeon_emit_tex1( dev_priv ); + sarea_priv->dirty &= ~RADEON_UPLOAD_TEX1; + } + + if ( dirty & RADEON_UPLOAD_TEX2 ) { +#if 0 + radeon_emit_tex2( dev_priv ); +#endif + sarea_priv->dirty &= ~RADEON_UPLOAD_TEX2; + } + + sarea_priv->dirty &= ~(RADEON_UPLOAD_TEX0IMAGES | + RADEON_UPLOAD_TEX1IMAGES | + RADEON_UPLOAD_TEX2IMAGES | + RADEON_REQUIRE_QUIESCENCE); +} + + +#if RADEON_PERFORMANCE_BOXES +/* ================================================================ + * Performance monitoring functions + */ + +static void radeon_clear_box( drm_radeon_private_t *dev_priv, + int x, int y, int w, int h, + int r, int g, int b ) +{ + u32 pitch, offset; + u32 color; + RING_LOCALS; + + switch ( dev_priv->color_fmt ) { + case RADEON_COLOR_FORMAT_RGB565: + color = (((r & 0xf8) << 8) | + ((g & 0xfc) << 3) | + ((b & 0xf8) >> 3)); + break; + case RADEON_COLOR_FORMAT_ARGB8888: + default: + color = (((0xff) << 24) | (r << 16) | (g << 8) | b); + break; + } + + offset = dev_priv->back_offset; + pitch = dev_priv->back_pitch >> 3; + + BEGIN_RING( 6 ); + + OUT_RING( CP_PACKET3( RADEON_CNTL_PAINT_MULTI, 4 ) ); + OUT_RING( RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_BRUSH_SOLID_COLOR | + (dev_priv->color_fmt << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_P | + RADEON_GMC_CLR_CMP_CNTL_DIS ); + + OUT_RING( (pitch << 22) | (offset >> 5) ); + OUT_RING( color ); + + OUT_RING( (x << 16) | y ); + OUT_RING( (w << 16) | h ); + + ADVANCE_RING(); +} + +static void radeon_cp_performance_boxes( drm_radeon_private_t *dev_priv ) +{ + if ( atomic_read( &dev_priv->idle_count ) == 0 ) { + radeon_clear_box( dev_priv, 64, 4, 8, 8, 0, 255, 0 ); + } else { + atomic_set( &dev_priv->idle_count, 0 ); + } +} + +#endif + + +/* ================================================================ + * CP command dispatch functions + */ + +static void radeon_print_dirty( const char *msg, unsigned int flags ) +{ + DRM_DEBUG( "%s: (0x%x) %s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + msg, + flags, + (flags & RADEON_UPLOAD_CONTEXT) ? "context, " : "", + (flags & RADEON_UPLOAD_VERTFMT) ? "vertfmt, " : "", + (flags & RADEON_UPLOAD_LINE) ? "line, " : "", + (flags & RADEON_UPLOAD_BUMPMAP) ? "bumpmap, " : "", + (flags & RADEON_UPLOAD_MASKS) ? "masks, " : "", + (flags & RADEON_UPLOAD_VIEWPORT) ? "viewport, " : "", + (flags & RADEON_UPLOAD_SETUP) ? "setup, " : "", + (flags & RADEON_UPLOAD_TCL) ? "tcl, " : "", + (flags & RADEON_UPLOAD_MISC) ? "misc, " : "", + (flags & RADEON_UPLOAD_TEX0) ? "tex0, " : "", + (flags & RADEON_UPLOAD_TEX1) ? "tex1, " : "", + (flags & RADEON_UPLOAD_TEX2) ? "tex2, " : "", + (flags & RADEON_UPLOAD_CLIPRECTS) ? "cliprects, " : "", + (flags & RADEON_REQUIRE_QUIESCENCE) ? "quiescence, " : "" ); +} + +static void radeon_cp_dispatch_clear( drm_device_t *dev, + drm_radeon_clear_t *clear ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + int nbox = sarea_priv->nbox; + drm_clip_rect_t *pbox = sarea_priv->boxes; + unsigned int flags = clear->flags; + int i; + RING_LOCALS; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + radeon_update_ring_snapshot( dev_priv ); + + if ( dev_priv->page_flipping && dev_priv->current_page == 1 ) { + unsigned int tmp = flags; + + flags &= ~(RADEON_FRONT | RADEON_BACK); + if ( tmp & RADEON_FRONT ) flags |= RADEON_BACK; + if ( tmp & RADEON_BACK ) flags |= RADEON_FRONT; + } + + for ( i = 0 ; i < nbox ; i++ ) { + int x = pbox[i].x1; + int y = pbox[i].y1; + int w = pbox[i].x2 - x; + int h = pbox[i].y2 - y; + + DRM_DEBUG( "dispatch clear %d,%d-%d,%d flags 0x%x\n", + x, y, w, h, flags ); + + if ( flags & (RADEON_FRONT | RADEON_BACK) ) { + BEGIN_RING( 4 ); + + /* Ensure the 3D stream is idle before doing a + * 2D fill to clear the front or back buffer. + */ + RADEON_WAIT_UNTIL_3D_IDLE(); + + OUT_RING( CP_PACKET0( RADEON_DP_WRITE_MASK, 0 ) ); + OUT_RING( sarea_priv->context_state.rb3d_planemask ); + + ADVANCE_RING(); + + /* Make sure we restore the 3D state next time. + */ + dev_priv->sarea_priv->dirty |= (RADEON_UPLOAD_CONTEXT | + RADEON_UPLOAD_MASKS); + } + + if ( flags & RADEON_FRONT ) { + BEGIN_RING( 6 ); + + OUT_RING( CP_PACKET3( RADEON_CNTL_PAINT_MULTI, 4 ) ); + OUT_RING( RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_BRUSH_SOLID_COLOR | + (dev_priv->color_fmt << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_P | + RADEON_GMC_CLR_CMP_CNTL_DIS ); + + OUT_RING( dev_priv->front_pitch_offset ); + OUT_RING( clear->clear_color ); + + OUT_RING( (x << 16) | y ); + OUT_RING( (w << 16) | h ); + + ADVANCE_RING(); + } + + if ( flags & RADEON_BACK ) { + BEGIN_RING( 6 ); + + OUT_RING( CP_PACKET3( RADEON_CNTL_PAINT_MULTI, 4 ) ); + OUT_RING( RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_BRUSH_SOLID_COLOR | + (dev_priv->color_fmt << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_P | + RADEON_GMC_CLR_CMP_CNTL_DIS ); + + OUT_RING( dev_priv->back_pitch_offset ); + OUT_RING( clear->clear_color ); + + OUT_RING( (x << 16) | y ); + OUT_RING( (w << 16) | h ); + + ADVANCE_RING(); + + } + + if ( flags & RADEON_DEPTH ) { + drm_radeon_depth_clear_t *depth_clear = + &dev_priv->depth_clear; + + if ( sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS ) { + radeon_emit_state( dev_priv ); + } + + /* FIXME: Render a rectangle to clear the depth + * buffer. So much for those "fast Z clears"... + */ + BEGIN_RING( 23 ); + + RADEON_WAIT_UNTIL_2D_IDLE(); + + OUT_RING( CP_PACKET0( RADEON_PP_CNTL, 1 ) ); + OUT_RING( 0x00000000 ); + OUT_RING( depth_clear->rb3d_cntl ); + OUT_RING( CP_PACKET0( RADEON_RB3D_ZSTENCILCNTL, 0 ) ); + OUT_RING( depth_clear->rb3d_zstencilcntl ); + OUT_RING( CP_PACKET0( RADEON_RB3D_PLANEMASK, 0 ) ); + OUT_RING( 0x00000000 ); + OUT_RING( CP_PACKET0( RADEON_SE_CNTL, 0 ) ); + OUT_RING( depth_clear->se_cntl ); + + OUT_RING( CP_PACKET3( RADEON_3D_DRAW_IMMD, 10 ) ); + OUT_RING( RADEON_VTX_Z_PRESENT ); + OUT_RING( (RADEON_PRIM_TYPE_RECT_LIST | + RADEON_PRIM_WALK_RING | + RADEON_MAOS_ENABLE | + RADEON_VTX_FMT_RADEON_MODE | + (3 << RADEON_NUM_VERTICES_SHIFT)) ); + + OUT_RING( clear->rect.ui[CLEAR_X1] ); + OUT_RING( clear->rect.ui[CLEAR_Y1] ); + OUT_RING( clear->rect.ui[CLEAR_DEPTH] ); + + OUT_RING( clear->rect.ui[CLEAR_X1] ); + OUT_RING( clear->rect.ui[CLEAR_Y2] ); + OUT_RING( clear->rect.ui[CLEAR_DEPTH] ); + + OUT_RING( clear->rect.ui[CLEAR_X2] ); + OUT_RING( clear->rect.ui[CLEAR_Y2] ); + OUT_RING( clear->rect.ui[CLEAR_DEPTH] ); + + ADVANCE_RING(); + + /* Make sure we restore the 3D state next time. + */ + dev_priv->sarea_priv->dirty |= (RADEON_UPLOAD_CONTEXT | + RADEON_UPLOAD_SETUP | + RADEON_UPLOAD_MASKS); + } + } + + /* Increment the clear counter. The client-side 3D driver must + * wait on this value before performing the clear ioctl. We + * need this because the card's so damned fast... + */ + dev_priv->sarea_priv->last_clear++; + + BEGIN_RING( 4 ); + + RADEON_CLEAR_AGE( dev_priv->sarea_priv->last_clear ); + RADEON_WAIT_UNTIL_IDLE(); + + ADVANCE_RING(); +} + +static void radeon_cp_dispatch_swap( drm_device_t *dev ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + int nbox = sarea_priv->nbox; + drm_clip_rect_t *pbox = sarea_priv->boxes; + int i; + RING_LOCALS; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + radeon_update_ring_snapshot( dev_priv ); + +#if RADEON_PERFORMANCE_BOXES + /* Do some trivial performance monitoring... + */ + radeon_cp_performance_boxes( dev_priv ); +#endif + + /* Wait for the 3D stream to idle before dispatching the bitblt. + * This will prevent data corruption between the two streams. + */ + BEGIN_RING( 2 ); + + RADEON_WAIT_UNTIL_3D_IDLE(); + + ADVANCE_RING(); + + for ( i = 0 ; i < nbox ; i++ ) { + int x = pbox[i].x1; + int y = pbox[i].y1; + int w = pbox[i].x2 - x; + int h = pbox[i].y2 - y; + + DRM_DEBUG( "dispatch swap %d,%d-%d,%d\n", + x, y, w, h ); + + BEGIN_RING( 7 ); + + OUT_RING( CP_PACKET3( RADEON_CNTL_BITBLT_MULTI, 5 ) ); + OUT_RING( RADEON_GMC_SRC_PITCH_OFFSET_CNTL | + RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_BRUSH_NONE | + (dev_priv->color_fmt << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_S | + RADEON_DP_SRC_SOURCE_MEMORY | + RADEON_GMC_CLR_CMP_CNTL_DIS | + RADEON_GMC_WR_MSK_DIS ); + + OUT_RING( dev_priv->back_pitch_offset ); + OUT_RING( dev_priv->front_pitch_offset ); + + OUT_RING( (x << 16) | y ); + OUT_RING( (x << 16) | y ); + OUT_RING( (w << 16) | h ); + + ADVANCE_RING(); + } + + /* Increment the frame counter. The client-side 3D driver must + * throttle the framerate by waiting for this value before + * performing the swapbuffer ioctl. + */ + dev_priv->sarea_priv->last_frame++; + + BEGIN_RING( 4 ); + + RADEON_FRAME_AGE( dev_priv->sarea_priv->last_frame ); + RADEON_WAIT_UNTIL_2D_IDLE(); + + ADVANCE_RING(); +} + +static void radeon_cp_dispatch_flip( drm_device_t *dev ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + DRM_DEBUG( "%s: page=%d\n", __FUNCTION__, dev_priv->current_page ); + + radeon_update_ring_snapshot( dev_priv ); + +#if RADEON_PERFORMANCE_BOXES + /* Do some trivial performance monitoring... + */ + radeon_cp_performance_boxes( dev_priv ); +#endif + + BEGIN_RING( 6 ); + + RADEON_WAIT_UNTIL_3D_IDLE(); + RADEON_WAIT_UNTIL_PAGE_FLIPPED(); + + OUT_RING( CP_PACKET0( RADEON_CRTC_OFFSET, 0 ) ); + + if ( dev_priv->current_page == 0 ) { + OUT_RING( dev_priv->back_offset ); + dev_priv->current_page = 1; + } else { + OUT_RING( dev_priv->front_offset ); + dev_priv->current_page = 0; + } + + ADVANCE_RING(); + + /* Increment the frame counter. The client-side 3D driver must + * throttle the framerate by waiting for this value before + * performing the swapbuffer ioctl. + */ + dev_priv->sarea_priv->last_frame++; + + BEGIN_RING( 2 ); + + RADEON_FRAME_AGE( dev_priv->sarea_priv->last_frame ); + + ADVANCE_RING(); +} + +static void radeon_cp_dispatch_vertex( drm_device_t *dev, + drm_buf_t *buf ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_buf_priv_t *buf_priv = buf->dev_private; + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + int format = sarea_priv->vc_format; + int offset = dev_priv->agp_buffers_offset + buf->offset; + int size = buf->used; + int prim = buf_priv->prim; + int i = 0; + RING_LOCALS; + DRM_DEBUG( "%s: nbox=%d\n", __FUNCTION__, sarea_priv->nbox ); + + radeon_update_ring_snapshot( dev_priv ); + + if ( 0 ) + radeon_print_dirty( "dispatch_vertex", sarea_priv->dirty ); + + if ( buf->used ) { + buf_priv->dispatched = 1; + + if ( sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS ) { + radeon_emit_state( dev_priv ); + } + + do { + /* Emit the next set of up to three cliprects */ + if ( i < sarea_priv->nbox ) { + radeon_emit_clip_rect( dev_priv, + &sarea_priv->boxes[i] ); + } + + /* Emit the vertex buffer rendering commands */ + BEGIN_RING( 5 ); + + OUT_RING( CP_PACKET3( RADEON_3D_RNDR_GEN_INDX_PRIM, 3 ) ); + OUT_RING( offset ); + OUT_RING( size ); + OUT_RING( format ); + OUT_RING( prim | RADEON_PRIM_WALK_LIST | + RADEON_COLOR_ORDER_RGBA | + RADEON_VTX_FMT_RADEON_MODE | + (size << RADEON_NUM_VERTICES_SHIFT) ); + + ADVANCE_RING(); + + i++; + } while ( i < sarea_priv->nbox ); + } + + if ( buf_priv->discard ) { + buf_priv->age = dev_priv->sarea_priv->last_dispatch; + + /* Emit the vertex buffer age */ + BEGIN_RING( 2 ); + RADEON_DISPATCH_AGE( buf_priv->age ); + ADVANCE_RING(); + + buf->pending = 1; + buf->used = 0; + /* FIXME: Check dispatched field */ + buf_priv->dispatched = 0; + } + + dev_priv->sarea_priv->last_dispatch++; + + sarea_priv->dirty &= ~RADEON_UPLOAD_CLIPRECTS; + sarea_priv->nbox = 0; +} + + +static void radeon_cp_dispatch_indirect( drm_device_t *dev, + drm_buf_t *buf, + int start, int end ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_buf_priv_t *buf_priv = buf->dev_private; + RING_LOCALS; + DRM_DEBUG( "indirect: buf=%d s=0x%x e=0x%x\n", + buf->idx, start, end ); + + radeon_update_ring_snapshot( dev_priv ); + + if ( start != end ) { + int offset = (dev_priv->agp_buffers_offset + + buf->offset + start); + int dwords = (end - start + 3) / sizeof(u32); + + /* Indirect buffer data must be an even number of + * dwords, so if we've been given an odd number we must + * pad the data with a Type-2 CP packet. + */ + if ( dwords & 1 ) { + u32 *data = (u32 *) + ((char *)dev_priv->buffers->handle + + buf->offset + start); + data[dwords++] = RADEON_CP_PACKET2; + } + + buf_priv->dispatched = 1; + + /* Fire off the indirect buffer */ + BEGIN_RING( 3 ); + + OUT_RING( CP_PACKET0( RADEON_CP_IB_BASE, 1 ) ); + OUT_RING( offset ); + OUT_RING( dwords ); + + ADVANCE_RING(); + } + + if ( buf_priv->discard ) { + buf_priv->age = dev_priv->sarea_priv->last_dispatch; + + /* Emit the indirect buffer age */ + BEGIN_RING( 2 ); + RADEON_DISPATCH_AGE( buf_priv->age ); + ADVANCE_RING(); + + buf->pending = 1; + buf->used = 0; + /* FIXME: Check dispatched field */ + buf_priv->dispatched = 0; + } + + dev_priv->sarea_priv->last_dispatch++; +} + +static void radeon_cp_dispatch_indices( drm_device_t *dev, + drm_buf_t *buf, + int start, int end, + int count ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_buf_priv_t *buf_priv = buf->dev_private; + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + int format = sarea_priv->vc_format; + int offset = dev_priv->agp_buffers_offset; + int prim = buf_priv->prim; + u32 *data; + int dwords; + int i = 0; + RING_LOCALS; + DRM_DEBUG( "indices: s=%d e=%d c=%d\n", start, end, count ); + + radeon_update_ring_snapshot( dev_priv ); + + if ( 0 ) + radeon_print_dirty( "dispatch_indices", sarea_priv->dirty ); + + if ( start != end ) { + buf_priv->dispatched = 1; + + if ( sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS ) { + radeon_emit_state( dev_priv ); + } + + dwords = (end - start + 3) / sizeof(u32); + + data = (u32 *)((char *)dev_priv->buffers->handle + + buf->offset + start); + + data[0] = CP_PACKET3( RADEON_3D_RNDR_GEN_INDX_PRIM, dwords-2 ); + + data[1] = offset; + data[2] = RADEON_MAX_VB_VERTS; + data[3] = format; + data[4] = (prim | RADEON_PRIM_WALK_IND | + RADEON_COLOR_ORDER_RGBA | + RADEON_VTX_FMT_RADEON_MODE | + (count << RADEON_NUM_VERTICES_SHIFT) ); + + if ( count & 0x1 ) { + data[dwords-1] &= 0x0000ffff; + } + + do { + /* Emit the next set of up to three cliprects */ + if ( i < sarea_priv->nbox ) { + radeon_emit_clip_rect( dev_priv, + &sarea_priv->boxes[i] ); + } + + radeon_cp_dispatch_indirect( dev, buf, start, end ); + + i++; + } while ( i < sarea_priv->nbox ); + } + + if ( buf_priv->discard ) { + buf_priv->age = dev_priv->sarea_priv->last_dispatch; + + /* Emit the vertex buffer age */ + BEGIN_RING( 2 ); + RADEON_DISPATCH_AGE( buf_priv->age ); + ADVANCE_RING(); + + buf->pending = 1; + /* FIXME: Check dispatched field */ + buf_priv->dispatched = 0; + } + + dev_priv->sarea_priv->last_dispatch++; + + sarea_priv->dirty &= ~RADEON_UPLOAD_CLIPRECTS; + sarea_priv->nbox = 0; +} + +static int radeon_cp_dispatch_blit( drm_device_t *dev, + drm_radeon_blit_t *blit ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_radeon_buf_priv_t *buf_priv; + u32 format; + u32 *data; + int dword_shift, dwords; + RING_LOCALS; + DRM_DEBUG( "blit: ofs=0x%x p=%d f=%d x=%hd y=%hd w=%hd h=%hd\n", + blit->offset >> 10, blit->pitch, blit->format, + blit->x, blit->y, blit->width, blit->height ); + + radeon_update_ring_snapshot( dev_priv ); + + /* The compiler won't optimize away a division by a variable, + * even if the only legal values are powers of two. Thus, we'll + * use a shift instead. + */ + switch ( blit->format ) { + case RADEON_TXF_32BPP_ARGB8888: + case RADEON_TXF_32BPP_RGBA8888: + format = RADEON_COLOR_FORMAT_ARGB8888; + dword_shift = 0; + break; + case RADEON_TXF_16BPP_AI88: + case RADEON_TXF_16BPP_ARGB1555: + case RADEON_TXF_16BPP_RGB565: + case RADEON_TXF_16BPP_ARGB4444: + format = RADEON_COLOR_FORMAT_RGB565; + dword_shift = 1; + break; + case RADEON_TXF_8BPP_I: + case RADEON_TXF_8BPP_RGB332: + format = RADEON_COLOR_FORMAT_CI8; + dword_shift = 2; + break; + default: + DRM_ERROR( "invalid blit format %d\n", blit->format ); + return -EINVAL; + } + + /* Flush the pixel cache. This ensures no pixel data gets mixed + * up with the texture data from the host data blit, otherwise + * part of the texture image may be corrupted. + */ + BEGIN_RING( 4 ); + + RADEON_FLUSH_CACHE(); + RADEON_WAIT_UNTIL_IDLE(); + + ADVANCE_RING(); + + /* Dispatch the indirect buffer. + */ + buf = dma->buflist[blit->idx]; + buf_priv = buf->dev_private; + + if ( buf->pid != current->pid ) { + DRM_ERROR( "process %d using buffer owned by %d\n", + current->pid, buf->pid ); + return -EINVAL; + } + if ( buf->pending ) { + DRM_ERROR( "sending pending buffer %d\n", blit->idx ); + return -EINVAL; + } + + buf_priv->discard = 1; + + dwords = (blit->width * blit->height) >> dword_shift; + if ( !dwords ) dwords = 1; + + data = (u32 *)((char *)dev_priv->buffers->handle + buf->offset); + + data[0] = CP_PACKET3( RADEON_CNTL_HOSTDATA_BLT, dwords + 6 ); + data[1] = (RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_BRUSH_NONE | + (format << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_S | + RADEON_DP_SRC_SOURCE_HOST_DATA | + RADEON_GMC_CLR_CMP_CNTL_DIS | + RADEON_GMC_WR_MSK_DIS); + + data[2] = (blit->pitch << 22) | (blit->offset >> 10); + data[3] = 0xffffffff; + data[4] = 0xffffffff; + data[5] = (blit->y << 16) | blit->x; + data[6] = (blit->height << 16) | blit->width; + data[7] = dwords; + + buf->used = (dwords + 8) * sizeof(u32); + + radeon_cp_dispatch_indirect( dev, buf, 0, buf->used ); + + /* Flush the pixel cache after the blit completes. This ensures + * the texture data is written out to memory before rendering + * continues. + */ + BEGIN_RING( 4 ); + + RADEON_FLUSH_CACHE(); + RADEON_WAIT_UNTIL_2D_IDLE(); + + ADVANCE_RING(); + + return 0; +} + +static void radeon_cp_dispatch_stipple( drm_device_t *dev, u32 *stipple ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + int i; + RING_LOCALS; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + radeon_update_ring_snapshot( dev_priv ); + + BEGIN_RING( 35 ); + + OUT_RING( CP_PACKET0( RADEON_RE_STIPPLE_ADDR, 0 ) ); + OUT_RING( 0x00000000 ); + + OUT_RING( CP_PACKET0_TABLE( RADEON_RE_STIPPLE_DATA, 31 ) ); + for ( i = 0 ; i < 32 ; i++ ) { + OUT_RING( stipple[i] ); + } + + ADVANCE_RING(); +} + + +/* ================================================================ + * IOCTL functions + */ + +int radeon_cp_clear( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_clear_t clear; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &clear, (drm_radeon_clear_t *) arg, + sizeof(clear) ) ) + return -EFAULT; + + if ( sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS ) + sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS; + + radeon_cp_dispatch_clear( dev, &clear ); + + return 0; +} + +int radeon_cp_swap( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS ) + sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS; + + if ( !dev_priv->page_flipping ) { + radeon_cp_dispatch_swap( dev ); + dev_priv->sarea_priv->dirty |= (RADEON_UPLOAD_CONTEXT | + RADEON_UPLOAD_MASKS); + } else { + radeon_cp_dispatch_flip( dev ); + } + + return 0; +} + +int radeon_cp_vertex( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_radeon_buf_priv_t *buf_priv; + drm_radeon_vertex_t vertex; + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + if ( !dev_priv || dev_priv->is_pci ) { + DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &vertex, (drm_radeon_vertex_t *)arg, + sizeof(vertex) ) ) + return -EFAULT; + + DRM_DEBUG( "%s: pid=%d index=%d count=%d discard=%d\n", + __FUNCTION__, current->pid, + vertex.idx, vertex.count, vertex.discard ); + + if ( vertex.idx < 0 || vertex.idx >= dma->buf_count ) { + DRM_ERROR( "buffer index %d (of %d max)\n", + vertex.idx, dma->buf_count - 1 ); + return -EINVAL; + } + if ( vertex.prim < 0 || + vertex.prim > RADEON_PRIM_TYPE_3VRT_LINE_LIST ) { + DRM_ERROR( "buffer prim %d\n", vertex.prim ); + return -EINVAL; + } + + VB_AGE_CHECK_WITH_RET( dev_priv ); + + buf = dma->buflist[vertex.idx]; + buf_priv = buf->dev_private; + + if ( buf->pid != current->pid ) { + DRM_ERROR( "process %d using buffer owned by %d\n", + current->pid, buf->pid ); + return -EINVAL; + } + if ( buf->pending ) { + DRM_ERROR( "sending pending buffer %d\n", vertex.idx ); + return -EINVAL; + } + + buf->used = vertex.count; + buf_priv->prim = vertex.prim; + buf_priv->discard = vertex.discard; + + radeon_cp_dispatch_vertex( dev, buf ); + + return 0; +} + +int radeon_cp_indices( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_radeon_buf_priv_t *buf_priv; + drm_radeon_indices_t elts; + int count; + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + if ( !dev_priv || dev_priv->is_pci ) { + DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &elts, (drm_radeon_indices_t *)arg, + sizeof(elts) ) ) + return -EFAULT; + + DRM_DEBUG( "%s: pid=%d index=%d start=%d end=%d discard=%d\n", + __FUNCTION__, current->pid, + elts.idx, elts.start, elts.end, elts.discard ); + + if ( elts.idx < 0 || elts.idx >= dma->buf_count ) { + DRM_ERROR( "buffer index %d (of %d max)\n", + elts.idx, dma->buf_count - 1 ); + return -EINVAL; + } + if ( elts.prim < 0 || + elts.prim > RADEON_PRIM_TYPE_3VRT_LINE_LIST ) { + DRM_ERROR( "buffer prim %d\n", elts.prim ); + return -EINVAL; + } + + VB_AGE_CHECK_WITH_RET( dev_priv ); + + buf = dma->buflist[elts.idx]; + buf_priv = buf->dev_private; + + if ( buf->pid != current->pid ) { + DRM_ERROR( "process %d using buffer owned by %d\n", + current->pid, buf->pid ); + return -EINVAL; + } + if ( buf->pending ) { + DRM_ERROR( "sending pending buffer %d\n", elts.idx ); + return -EINVAL; + } + + count = (elts.end - elts.start) / sizeof(u16); + elts.start -= RADEON_INDEX_PRIM_OFFSET; + + if ( elts.start & 0x7 ) { + DRM_ERROR( "misaligned buffer 0x%x\n", elts.start ); + return -EINVAL; + } + if ( elts.start < buf->used ) { + DRM_ERROR( "no header 0x%x - 0x%x\n", elts.start, buf->used ); + return -EINVAL; + } + + buf->used = elts.end; + buf_priv->prim = elts.prim; + buf_priv->discard = elts.discard; + + radeon_cp_dispatch_indices( dev, buf, elts.start, elts.end, count ); + + return 0; +} + +int radeon_cp_blit( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_radeon_blit_t blit; + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &blit, (drm_radeon_blit_t *)arg, + sizeof(blit) ) ) + return -EFAULT; + + DRM_DEBUG( "%s: pid=%d index=%d\n", + __FUNCTION__, current->pid, blit.idx ); + + if ( blit.idx < 0 || blit.idx > dma->buf_count ) { + DRM_ERROR( "sending %d buffers (of %d max)\n", + blit.idx, dma->buf_count ); + return -EINVAL; + } + + VB_AGE_CHECK_WITH_RET( dev_priv ); + + return radeon_cp_dispatch_blit( dev, &blit ); +} + +int radeon_cp_stipple( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_stipple_t stipple; + u32 mask[32]; + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &stipple, (drm_radeon_stipple_t *)arg, + sizeof(stipple) ) ) + return -EFAULT; + + if ( copy_from_user( &mask, stipple.mask, + 32 * sizeof(u32) ) ) + return -EFAULT; + + radeon_cp_dispatch_stipple( dev, mask ); + + return 0; +} + +int radeon_cp_indirect( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_radeon_buf_priv_t *buf_priv; + drm_radeon_indirect_t indirect; + RING_LOCALS; + + if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || + dev->lock.pid != current->pid ) { + DRM_ERROR( "%s called without lock held\n", __FUNCTION__ ); + return -EINVAL; + } + if ( !dev_priv || dev_priv->is_pci ) { + DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &indirect, (drm_radeon_indirect_t *)arg, + sizeof(indirect) ) ) + return -EFAULT; + + DRM_DEBUG( "indirect: idx=%d s=%d e=%d d=%d\n", + indirect.idx, indirect.start, + indirect.end, indirect.discard ); + + if ( indirect.idx < 0 || indirect.idx >= dma->buf_count ) { + DRM_ERROR( "buffer index %d (of %d max)\n", + indirect.idx, dma->buf_count - 1 ); + return -EINVAL; + } + + buf = dma->buflist[indirect.idx]; + buf_priv = buf->dev_private; + + if ( buf->pid != current->pid ) { + DRM_ERROR( "process %d using buffer owned by %d\n", + current->pid, buf->pid ); + return -EINVAL; + } + if ( buf->pending ) { + DRM_ERROR( "sending pending buffer %d\n", indirect.idx ); + return -EINVAL; + } + + if ( indirect.start < buf->used ) { + DRM_ERROR( "reusing indirect: start=0x%x actual=0x%x\n", + indirect.start, buf->used ); + return -EINVAL; + } + + VB_AGE_CHECK_WITH_RET( dev_priv ); + + buf->used = indirect.end; + buf_priv->discard = indirect.discard; + + /* Wait for the 3D stream to idle before the indirect buffer + * containing 2D acceleration commands is processed. + */ + BEGIN_RING( 2 ); + + RADEON_WAIT_UNTIL_3D_IDLE(); + + ADVANCE_RING(); + + /* Dispatch the indirect buffer full of commands from the + * X server. This is insecure and is thus only available to + * privileged clients. + */ + radeon_cp_dispatch_indirect( dev, buf, indirect.start, indirect.end ); + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/tdfx_context.c linux.ac/drivers/char/drm-4.0/tdfx_context.c --- linux.vanilla/drivers/char/drm-4.0/tdfx_context.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/tdfx_context.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,219 @@ +/* tdfx_context.c -- IOCTLs for tdfx contexts -*- linux-c -*- + * Created: Thu Oct 7 10:50:22 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * Daryll Strauss + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "tdfx_drv.h" + +extern drm_ctx_t tdfx_res_ctx; + +static int tdfx_alloc_queue(drm_device_t *dev) +{ + return drm_ctxbitmap_next(dev); +} + +int tdfx_context_switch(drm_device_t *dev, int old, int new) +{ + char buf[64]; + + atomic_inc(&dev->total_ctx); + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + if (drm_flags & DRM_FLAG_NOCTX) { + tdfx_context_switch_complete(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + drm_write_string(dev, buf); + } + + return 0; +} + +int tdfx_context_switch_complete(drm_device_t *dev, int new) +{ + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + /* If a context switch is ever initiated + when the kernel holds the lock, release + that lock here. */ +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + wake_up(&dev->context_wait); + + return 0; +} + + +int tdfx_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res))) + return -EFAULT; + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + if (copy_to_user(&res.contexts[i], + &i, + sizeof(i))) + return -EFAULT; + } + } + res.count = DRM_RESERVED_CONTEXTS; + if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res))) + return -EFAULT; + return 0; +} + + +int tdfx_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + if ((ctx.handle = tdfx_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) { + /* Skip kernel's context and get a new one. */ + ctx.handle = tdfx_alloc_queue(dev); + } + DRM_DEBUG("%d\n", ctx.handle); + if (ctx.handle == -1) { + DRM_DEBUG("Not enough free contexts.\n"); + /* Should this return -EBUSY instead? */ + return -ENOMEM; + } + + if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx))) + return -EFAULT; + return 0; +} + +int tdfx_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx))) + return -EFAULT; + if (ctx.flags==_DRM_CONTEXT_PRESERVED) + tdfx_res_ctx.handle=ctx.handle; + return 0; +} + +int tdfx_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx))) + return -EFAULT; + /* This is 0, because we don't handle any context flags */ + ctx.flags = 0; + if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx))) + return -EFAULT; + return 0; +} + +int tdfx_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + return tdfx_context_switch(dev, dev->last_context, ctx.handle); +} + +int tdfx_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + tdfx_context_switch_complete(dev, ctx.handle); + + return 0; +} + +int tdfx_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + drm_ctxbitmap_free(dev, ctx.handle); + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/tdfx_drv.c linux.ac/drivers/char/drm-4.0/tdfx_drv.c --- linux.vanilla/drivers/char/drm-4.0/tdfx_drv.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/tdfx_drv.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,697 @@ +/* tdfx_drv.c -- tdfx driver -*- linux-c -*- + * Created: Thu Oct 7 10:38:32 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * Daryll Strauss + * + */ + +#include +#include "drmP.h" +#include "tdfx_drv.h" + +#define TDFX_NAME "tdfx" +#define TDFX_DESC "3dfx Banshee/Voodoo3+" +#define TDFX_DATE "20000928" +#define TDFX_MAJOR 1 +#define TDFX_MINOR 0 +#define TDFX_PATCHLEVEL 0 + +static drm_device_t tdfx_device; +drm_ctx_t tdfx_res_ctx; + +static struct file_operations tdfx_fops = { +#if LINUX_VERSION_CODE >= 0x020400 + /* This started being used during 2.4.0-test */ + owner: THIS_MODULE, +#endif + open: tdfx_open, + flush: drm_flush, + release: tdfx_release, + ioctl: tdfx_ioctl, + mmap: drm_mmap, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +static struct miscdevice tdfx_misc = { + minor: MISC_DYNAMIC_MINOR, + name: TDFX_NAME, + fops: &tdfx_fops, +}; + +static drm_ioctl_desc_t tdfx_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { tdfx_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { tdfx_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { tdfx_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { tdfx_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { tdfx_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { tdfx_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { tdfx_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { tdfx_resctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { tdfx_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { tdfx_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = {drm_agp_acquire, 1, 1}, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = {drm_agp_release, 1, 1}, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = {drm_agp_enable, 1, 1}, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = {drm_agp_info, 1, 1}, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = {drm_agp_alloc, 1, 1}, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = {drm_agp_free, 1, 1}, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = {drm_agp_unbind, 1, 1}, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = {drm_agp_bind, 1, 1}, +#endif +}; +#define TDFX_IOCTL_COUNT DRM_ARRAY_SIZE(tdfx_ioctls) + +#ifdef MODULE +static char *tdfx = NULL; +#endif + +MODULE_AUTHOR("VA Linux Systems, Inc."); +MODULE_LICENSE("GPL and additional rights"); +MODULE_DESCRIPTION("tdfx"); +MODULE_PARM(tdfx, "s"); + +#ifndef MODULE +/* tdfx_options is called by the kernel to parse command-line options + * passed via the boot-loader (e.g., LILO). It calls the insmod option + * routine, drm_parse_drm. + */ + +static int __init tdfx_options(char *str) +{ + drm_parse_options(str); + return 1; +} + +__setup("tdfx=", tdfx_options); +#endif + +static int tdfx_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); + + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + tdfx_res_ctx.handle=-1; + + DRM_DEBUG("\n"); + + /* The kernel's context could be created here, but is now created + in drm_dma_enqueue. This is more resource-efficient for + hardware that does not do DMA, but may mean that + drm_select_queue fails between the time the interrupt is + initialized and the time the queues are initialized. */ + + return 0; +} + + +static int tdfx_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + /* Clear AGP information */ + if (dev->agp) { + drm_agp_mem_t *temp; + drm_agp_mem_t *temp_next; + + temp = dev->agp->memory; + while(temp != NULL) { + temp_next = temp->next; + drm_free_agp(temp->memory, temp->pages); + drm_free(temp, sizeof(*temp), DRM_MEM_AGPLISTS); + temp = temp_next; + } + if (dev->agp->acquired) _drm_agp_release(); + } +#endif + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area and mtrr information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifdef CONFIG_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + drm_ioremapfree(map->handle, map->size); + break; + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + case _DRM_AGP: + /* Do nothing here, because this is all + handled in the AGP/GART driver. */ + break; + } + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +/* tdfx_init is called via init_module at module load time, or via + * linux/init/main.c (this is not currently supported). */ + +static int __init tdfx_init(void) +{ + int retcode; + drm_device_t *dev = &tdfx_device; + + DRM_DEBUG("\n"); + + memset((void *)dev, 0, sizeof(*dev)); + dev->count_lock = SPIN_LOCK_UNLOCKED; + sema_init(&dev->struct_sem, 1); + +#ifdef MODULE + drm_parse_options(tdfx); +#endif + + if ((retcode = misc_register(&tdfx_misc))) { + DRM_ERROR("Cannot register \"%s\"\n", TDFX_NAME); + return retcode; + } + dev->device = MKDEV(MISC_MAJOR, tdfx_misc.minor); + dev->name = TDFX_NAME; + + drm_mem_init(); + drm_proc_init(dev); +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + dev->agp = drm_agp_init(); +#endif + if((retcode = drm_ctxbitmap_init(dev))) { + DRM_ERROR("Cannot allocate memory for context bitmap.\n"); + drm_proc_cleanup(); + misc_deregister(&tdfx_misc); + tdfx_takedown(dev); + return retcode; + } + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", + TDFX_NAME, + TDFX_MAJOR, + TDFX_MINOR, + TDFX_PATCHLEVEL, + TDFX_DATE, + tdfx_misc.minor); + + return 0; +} + +/* tdfx_cleanup is called via cleanup_module at module unload time. */ + +static void __exit tdfx_cleanup(void) +{ + drm_device_t *dev = &tdfx_device; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + if (misc_deregister(&tdfx_misc)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + drm_ctxbitmap_cleanup(dev); + tdfx_takedown(dev); +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + if (dev->agp) { + drm_agp_uninit(); + drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS); + dev->agp = NULL; + } +#endif +} + +module_init(tdfx_init); +module_exit(tdfx_cleanup); + + +int tdfx_version(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_version_t version; + int len; + + if (copy_from_user(&version, + (drm_version_t *)arg, + sizeof(version))) + return -EFAULT; + +#define DRM_COPY(name,value) \ + len = strlen(value); \ + if (len > name##_len) len = name##_len; \ + name##_len = strlen(value); \ + if (len && name) { \ + if (copy_to_user(name, value, len)) \ + return -EFAULT; \ + } + + version.version_major = TDFX_MAJOR; + version.version_minor = TDFX_MINOR; + version.version_patchlevel = TDFX_PATCHLEVEL; + + DRM_COPY(version.name, TDFX_NAME); + DRM_COPY(version.date, TDFX_DATE); + DRM_COPY(version.desc, TDFX_DESC); + + if (copy_to_user((drm_version_t *)arg, + &version, + sizeof(version))) + return -EFAULT; + return 0; +} + +int tdfx_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev = &tdfx_device; + int retcode = 0; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_open_helper(inode, filp, dev))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return tdfx_setup(dev); + } + spin_unlock(&dev->count_lock); + } + return retcode; +} + +int tdfx_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + int retcode = 0; + + lock_kernel(); + dev = priv->dev; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_release(inode, filp))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + unlock_kernel(); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return tdfx_takedown(dev); + } + spin_unlock(&dev->count_lock); + } + + unlock_kernel(); + return retcode; +} + +/* tdfx_ioctl is called whenever a process performs an ioctl on /dev/drm. */ + +int tdfx_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, priv->authenticated); + + if (nr >= TDFX_IOCTL_COUNT) { + retcode = -EINVAL; + } else { + ioctl = &tdfx_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function\n"); + retcode = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + retcode = -EACCES; + } else { + retcode = (func)(inode, filp, cmd, arg); + } + } + + atomic_dec(&dev->ioctl_count); + return retcode; +} + +int tdfx_lock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; +#if DRM_DMA_HISTOGRAM + cycles_t start; + + dev->lck_start = start = get_cycles(); +#endif + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, current->pid, dev->lock.hw_lock->lock, + lock.flags); + +#if 0 + /* dev->queue_count == 0 right now for + tdfx. FIXME? */ + if (lock.context < 0 || lock.context >= dev->queue_count) + return -EINVAL; +#endif + + if (!ret) { +#if 0 + if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) + != lock.context) { + long j = jiffies - dev->lock.lock_time; + + if (lock.context == tdfx_res_ctx.handle && + j >= 0 && j < DRM_LOCK_SLICE) { + /* Can't take lock if we just had it and + there is contention. */ + DRM_DEBUG("%d (pid %d) delayed j=%d dev=%d jiffies=%d\n", + lock.context, current->pid, j, + dev->lock.lock_time, jiffies); + current->state = TASK_INTERRUPTIBLE; + current->policy |= SCHED_YIELD; + schedule_timeout(DRM_LOCK_SLICE-j); + DRM_DEBUG("jiffies=%d\n", jiffies); + } + } +#endif + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); +#if 1 + current->policy |= SCHED_YIELD; +#endif + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + } + +#if 0 + if (!ret && dev->last_context != lock.context && + lock.context != tdfx_res_ctx.handle && + dev->last_context != tdfx_res_ctx.handle) { + add_wait_queue(&dev->context_wait, &entry); + current->state = TASK_INTERRUPTIBLE; + /* PRE: dev->last_context != lock.context */ + tdfx_context_switch(dev, dev->last_context, lock.context); + /* POST: we will wait for the context + switch and will dispatch on a later call + when dev->last_context == lock.context + NOTE WE HOLD THE LOCK THROUGHOUT THIS + TIME! */ + current->policy |= SCHED_YIELD; + schedule(); + current->state = TASK_RUNNING; + remove_wait_queue(&dev->context_wait, &entry); + if (signal_pending(current)) { + ret = -EINTR; + } else if (dev->last_context != lock.context) { + DRM_ERROR("Context mismatch: %d %d\n", + dev->last_context, lock.context); + } + } +#endif + + if (!ret) { + sigemptyset(&dev->sigmask); + sigaddset(&dev->sigmask, SIGSTOP); + sigaddset(&dev->sigmask, SIGTSTP); + sigaddset(&dev->sigmask, SIGTTIN); + sigaddset(&dev->sigmask, SIGTTOU); + dev->sigdata.context = lock.context; + dev->sigdata.lock = dev->lock.hw_lock; + block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask); + + if (lock.flags & _DRM_LOCK_READY) { + /* Wait for space in DMA/FIFO */ + } + if (lock.flags & _DRM_LOCK_QUIESCENT) { + /* Make hardware quiescent */ +#if 0 + tdfx_quiescent(dev); +#endif + } + } + +#if LINUX_VERSION_CODE < 0x020400 + if (lock.context != tdfx_res_ctx.handle) { + current->counter = 5; + current->priority = DEF_PRIORITY/4; + } +#endif + DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]); +#endif + + return ret; +} + + +int tdfx_unlock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d frees lock (%d holds)\n", + lock.context, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); + /* FIXME: Try to send data to card here */ + if (!dev->context_flag) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } + +#if LINUX_VERSION_CODE < 0x020400 + if (lock.context != tdfx_res_ctx.handle) { + current->counter = 5; + current->priority = DEF_PRIORITY; + } +#endif + + unblock_all_signals(); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/tdfx_drv.h linux.ac/drivers/char/drm-4.0/tdfx_drv.h --- linux.vanilla/drivers/char/drm-4.0/tdfx_drv.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/tdfx_drv.h Wed Oct 10 01:47:41 2001 @@ -0,0 +1,67 @@ +/* tdfx_drv.h -- Private header for tdfx driver -*- linux-c -*- + * Created: Thu Oct 7 10:40:04 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * Daryll Strauss + * + */ + +#ifndef _TDFX_DRV_H_ +#define _TDFX_DRV_H_ + + /* tdfx_drv.c */ +extern int tdfx_version(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_open(struct inode *inode, struct file *filp); +extern int tdfx_release(struct inode *inode, struct file *filp); +extern int tdfx_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_lock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_unlock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* tdfx_context.c */ + +extern int tdfx_resctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_addctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_modctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_getctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_switchctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_newctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_rmctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int tdfx_context_switch(drm_device_t *dev, int old, int new); +extern int tdfx_context_switch_complete(drm_device_t *dev, int new); +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm-4.0/vm.c linux.ac/drivers/char/drm-4.0/vm.c --- linux.vanilla/drivers/char/drm-4.0/vm.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm-4.0/vm.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,370 @@ +/* vm.c -- Memory mapping for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +struct vm_operations_struct drm_vm_ops = { + nopage: drm_vm_nopage, + open: drm_vm_open, + close: drm_vm_close, +}; + +struct vm_operations_struct drm_vm_shm_ops = { + nopage: drm_vm_shm_nopage, + open: drm_vm_open, + close: drm_vm_close, +}; + +struct vm_operations_struct drm_vm_shm_lock_ops = { + nopage: drm_vm_shm_nopage_lock, + open: drm_vm_open, + close: drm_vm_close, +}; + +struct vm_operations_struct drm_vm_dma_ops = { + nopage: drm_vm_dma_nopage, + open: drm_vm_open, + close: drm_vm_close, +}; + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long drm_vm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *drm_vm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ + return NOPAGE_SIGBUS; /* Disallow mremap */ +} + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long drm_vm_shm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *drm_vm_shm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ +#if LINUX_VERSION_CODE >= 0x020300 + drm_map_t *map = (drm_map_t *)vma->vm_private_data; +#else + drm_map_t *map = (drm_map_t *)vma->vm_pte; +#endif + unsigned long physical; + unsigned long offset; + + if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ + if (!map) return NOPAGE_OOM; /* Nothing allocated */ + + offset = address - vma->vm_start; + physical = (unsigned long)map->handle + offset; + atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */ + + DRM_DEBUG("0x%08lx => 0x%08lx\n", address, physical); +#if LINUX_VERSION_CODE < 0x020317 + return physical; +#else + return virt_to_page(physical); +#endif +} + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long drm_vm_shm_nopage_lock(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *drm_vm_shm_nopage_lock(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + unsigned long physical; + unsigned long offset; + unsigned long page; + + if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ + if (!dev->lock.hw_lock) return NOPAGE_OOM; /* Nothing allocated */ + + offset = address - vma->vm_start; + page = offset >> PAGE_SHIFT; + physical = (unsigned long)dev->lock.hw_lock + offset; + atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */ + + DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical); +#if LINUX_VERSION_CODE < 0x020317 + return physical; +#else + return virt_to_page(physical); +#endif +} + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long drm_vm_dma_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *drm_vm_dma_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + unsigned long physical; + unsigned long offset; + unsigned long page; + + if (!dma) return NOPAGE_SIGBUS; /* Error */ + if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ + if (!dma->pagelist) return NOPAGE_OOM ; /* Nothing allocated */ + + offset = address - vma->vm_start; /* vm_[pg]off[set] should be 0 */ + page = offset >> PAGE_SHIFT; + physical = dma->pagelist[page] + (offset & (~PAGE_MASK)); + atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */ + + DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical); +#if LINUX_VERSION_CODE < 0x020317 + return physical; +#else + return virt_to_page(physical); +#endif +} + +void drm_vm_open(struct vm_area_struct *vma) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; +#if DRM_DEBUG_CODE + drm_vma_entry_t *vma_entry; +#endif + + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); + atomic_inc(&dev->vma_count); +#if LINUX_VERSION_CODE < 0x020333 + /* The map can exist after the fd is closed. */ + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + + +#if DRM_DEBUG_CODE + vma_entry = drm_alloc(sizeof(*vma_entry), DRM_MEM_VMAS); + if (vma_entry) { + down(&dev->struct_sem); + vma_entry->vma = vma; + vma_entry->next = dev->vmalist; + vma_entry->pid = current->pid; + dev->vmalist = vma_entry; + up(&dev->struct_sem); + } +#endif +} + +void drm_vm_close(struct vm_area_struct *vma) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; +#if DRM_DEBUG_CODE + drm_vma_entry_t *pt, *prev; +#endif + + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_dec(&dev->vma_count); + +#if DRM_DEBUG_CODE + down(&dev->struct_sem); + for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { + if (pt->vma == vma) { + if (prev) { + prev->next = pt->next; + } else { + dev->vmalist = pt->next; + } + drm_free(pt, sizeof(*pt), DRM_MEM_VMAS); + break; + } + } + up(&dev->struct_sem); +#endif +} + +int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + drm_device_dma_t *dma; + unsigned long length = vma->vm_end - vma->vm_start; + + lock_kernel(); + dev = priv->dev; + dma = dev->dma; + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + + /* Length must match exact page count */ + if (!dma || (length >> PAGE_SHIFT) != dma->page_count) { + unlock_kernel(); + return -EINVAL; + } + unlock_kernel(); + + vma->vm_ops = &drm_vm_dma_ops; + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + +#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ + /* In Linux 2.2.3 and above, this is + handled in do_mmap() in mm/mmap.c. */ + ++filp->f_count; +#endif + vma->vm_file = filp; /* Needed for drm_vm_open() */ + drm_vm_open(vma); + return 0; +} + +int drm_mmap(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map = NULL; + int i; + + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + + if (!VM_OFFSET(vma)) return drm_mmap_dma(filp, vma); + + /* A sequential search of a linked list is + fine here because: 1) there will only be + about 5-10 entries in the list and, 2) a + DRI client only has to do this mapping + once, so it doesn't have to be optimized + for performance, even if the list was a + bit longer. */ + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + if (map->offset == VM_OFFSET(vma)) break; + } + + if (i >= dev->map_count) return -EINVAL; + if (!map || ((map->flags&_DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) + return -EPERM; + + /* Check for valid size. */ + if (map->size != vma->vm_end - vma->vm_start) return -EINVAL; + + if (!capable(CAP_SYS_ADMIN) && (map->flags & _DRM_READ_ONLY)) { + vma->vm_flags &= VM_MAYWRITE; +#if defined(__i386__) + pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW; +#else + /* Ye gads this is ugly. With more thought + we could move this up higher and use + `protection_map' instead. */ + vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect( + __pte(pgprot_val(vma->vm_page_prot))))); +#endif + } + + switch (map->type) { + case _DRM_FRAME_BUFFER: + case _DRM_REGISTERS: + case _DRM_AGP: + if (VM_OFFSET(vma) >= __pa(high_memory)) { +#if defined(__i386__) || defined(__x86_64__) + if (boot_cpu_data.x86 > 3 && map->type != _DRM_AGP) { + pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; + pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT; + } +#elif defined(__ia64__) + if (map->type != _DRM_AGP) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); +#endif + vma->vm_flags |= VM_IO; /* not in core dump */ + } + if (remap_page_range(vma->vm_start, + VM_OFFSET(vma), + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + DRM_DEBUG(" Type = %d; start = 0x%lx, end = 0x%lx," + " offset = 0x%lx\n", + map->type, + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + vma->vm_ops = &drm_vm_ops; + break; + case _DRM_SHM: + if (map->flags & _DRM_CONTAINS_LOCK) + vma->vm_ops = &drm_vm_shm_lock_ops; + else { + vma->vm_ops = &drm_vm_shm_ops; +#if LINUX_VERSION_CODE >= 0x020300 + vma->vm_private_data = (void *)map; +#else + vma->vm_pte = (unsigned long)map; +#endif + } + + /* Don't let this area swap. Change when + DRM_KERNEL advisory is supported. */ + vma->vm_flags |= VM_LOCKED; + break; + default: + return -EINVAL; /* This should never happen. */ + } + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + +#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ + /* In Linux 2.2.3 and above, this is + handled in do_mmap() in mm/mmap.c. */ + ++filp->f_count; +#endif + vma->vm_file = filp; /* Needed for drm_vm_open() */ + drm_vm_open(vma); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/epca.c linux.ac/drivers/char/epca.c --- linux.vanilla/drivers/char/epca.c Sun Sep 9 18:43:02 2001 +++ linux.ac/drivers/char/epca.c Wed Oct 10 01:47:41 2001 @@ -1572,7 +1572,8 @@ cli(); if ((tty_unregister_driver(&pc_driver)) || - (tty_unregister_driver(&pc_callout))) + (tty_unregister_driver(&pc_callout)) || + (tty_unregister_driver(&pc_info))) { printk(KERN_WARNING " - DIGI : cleanup_module failed to un-register tty driver\n"); restore_flags(flags); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/esp.c linux.ac/drivers/char/esp.c --- linux.vanilla/drivers/char/esp.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/char/esp.c Fri Oct 12 09:51:34 2001 @@ -2203,6 +2203,7 @@ DECLARE_WAITQUEUE(wait, current); int retval; int do_clocal = 0; + unsigned long flags; /* * If the device is in the middle of being closed, then block @@ -2274,12 +2275,14 @@ printk("block_til_ready before block: ttys%d, count = %d\n", info->line, info->count); #endif + save_flags(flags); cli(); if (!tty_hung_up_p(filp)) info->count--; - sti(); + restore_flags(flags); info->blocked_open++; while (1) { + save_flags(flags); cli(); if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && (tty->termios->c_cflag & CBAUD)) { @@ -2293,7 +2296,7 @@ serial_out(info, UART_ESI_CMD2, scratch | UART_MCR_DTR | UART_MCR_RTS); } - sti(); + restore_flags(flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/hp_keyb.c linux.ac/drivers/char/hp_keyb.c --- linux.vanilla/drivers/char/hp_keyb.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/hp_keyb.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,519 @@ +/* + * linux/drivers/char/hp_keyb.c + * helper-functions for the keyboard/psaux driver for HP-PARISC workstations + * + * based on pc_keyb.c by Geert Uytterhoeven & Martin Mares + * + * 2000/10/26 Debacker Xavier + * Marteau Thomas + * Djoudi Malek + * - fixed some keysym defines + * + * 2001/04/28 Debacker Xavier + * - scancode translation rewritten in handle_at_scancode() + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define KBD_REPORT_ERR +#define KBD_REPORT_UNKN + +#define KBD_ESCAPEE0 0xe0 /* in */ +#define KBD_ESCAPEE1 0xe1 /* in */ + +#define ESCE0(x) (0xe000|(x)) +#define ESCE1(x) (0xe100|(x)) + +#define KBD_BAT 0xaa /* in */ +#define KBD_SETLEDS 0xed /* out */ +#define KBD_ECHO 0xee /* in/out */ +#define KBD_BREAK 0xf0 /* in */ +#define KBD_TYPRATEDLY 0xf3 /* out */ +#define KBD_SCANENABLE 0xf4 /* out */ +#define KBD_DEFDISABLE 0xf5 /* out */ +#define KBD_DEFAULT 0xf6 /* out */ +#define KBD_ACK 0xfa /* in */ +#define KBD_DIAGFAIL 0xfd /* in */ +#define KBD_RESEND 0xfe /* in/out */ +#define KBD_RESET 0xff /* out */ + +#define CODE_BREAK 1 +#define CODE_ESCAPEE0 2 +#define CODE_ESCAPEE1 4 +#define CODE_ESCAPE12 8 + +#define K_NONE 0x7f +#define K_ESC 0x01 +#define K_F1 0x3b +#define K_F2 0x3c +#define K_F3 0x3d +#define K_F4 0x3e +#define K_F5 0x3f +#define K_F6 0x40 +#define K_F7 0x41 +#define K_F8 0x42 +#define K_F9 0x43 +#define K_F10 0x44 +#define K_F11 0x57 +#define K_F12 0x58 +#define K_PRNT 0x54 +#define K_SCRL 0x46 +#define K_BRK 0x77 +#define K_AGR 0x29 +#define K_1 0x02 +#define K_2 0x03 +#define K_3 0x04 +#define K_4 0x05 +#define K_5 0x06 +#define K_6 0x07 +#define K_7 0x08 +#define K_8 0x09 +#define K_9 0x0a +#define K_0 0x0b +#define K_MINS 0x0c +#define K_EQLS 0x0d +#define K_BKSP 0x0e +#define K_INS 0x6e +#define K_HOME 0x66 +#define K_PGUP 0x68 +#define K_NUML 0x45 +#define KP_SLH 0x62 +#define KP_STR 0x37 +#define KP_MNS 0x4a +#define K_TAB 0x0f +#define K_Q 0x10 +#define K_W 0x11 +#define K_E 0x12 +#define K_R 0x13 +#define K_T 0x14 +#define K_Y 0x15 +#define K_U 0x16 +#define K_I 0x17 +#define K_O 0x18 +#define K_P 0x19 +#define K_LSBK 0x1a +#define K_RSBK 0x1b +#define K_ENTR 0x1c +#define K_DEL 111 +#define K_END 0x6b +#define K_PGDN 0x6d +#define KP_7 0x47 +#define KP_8 0x48 +#define KP_9 0x49 +#define KP_PLS 0x4e +#define K_CAPS 0x3a +#define K_A 0x1e +#define K_S 0x1f +#define K_D 0x20 +#define K_F 0x21 +#define K_G 0x22 +#define K_H 0x23 +#define K_J 0x24 +#define K_K 0x25 +#define K_L 0x26 +#define K_SEMI 0x27 +#define K_SQOT 0x28 +#define K_HASH K_NONE +#define KP_4 0x4b +#define KP_5 0x4c +#define KP_6 0x4d +#define K_LSFT 0x2a +#define K_BSLH 0x2b +#define K_Z 0x2c +#define K_X 0x2d +#define K_C 0x2e +#define K_V 0x2f +#define K_B 0x30 +#define K_N 0x31 +#define K_M 0x32 +#define K_COMA 0x33 +#define K_DOT 0x34 +#define K_FSLH 0x35 +#define K_RSFT 0x36 +#define K_UP 0x67 +#define KP_1 0x4f +#define KP_2 0x50 +#define KP_3 0x51 +#define KP_ENT 0x60 +#define K_LCTL 0x1d +#define K_LALT 0x38 +#define K_SPCE 0x39 +#define K_RALT 0x64 +#define K_RCTL 0x61 +#define K_LEFT 0x69 +#define K_DOWN 0x6c +#define K_RGHT 0x6a +#define KP_0 0x52 +#define KP_DOT 0x53 + +static unsigned char keycode_translate[256] = +{ +/* 00 */ K_NONE, K_F9 , K_NONE, K_F5 , K_F3 , K_F1 , K_F2 , K_F12 , +/* 08 */ K_NONE, K_F10 , K_F8 , K_F6 , K_F4 , K_TAB , K_AGR , K_NONE, +/* 10 */ K_NONE, K_LALT, K_LSFT, K_NONE, K_LCTL, K_Q , K_1 , K_NONE, +/* 18 */ K_NONE, K_NONE, K_Z , K_S , K_A , K_W , K_2 , K_NONE, +/* 20 */ K_NONE, K_C , K_X , K_D , K_E , K_4 , K_3 , K_NONE, +/* 28 */ K_NONE, K_SPCE, K_V , K_F , K_T , K_R , K_5 , K_NONE, +/* 30 */ K_NONE, K_N , K_B , K_H , K_G , K_Y , K_6 , K_NONE, +/* 38 */ K_NONE, K_NONE, K_M , K_J , K_U , K_7 , K_8 , K_NONE, +/* 40 */ K_NONE, K_COMA, K_K , K_I , K_O , K_0 , K_9 , K_NONE, +/* 48 */ K_NONE, K_DOT , K_FSLH, K_L , K_SEMI, K_P , K_MINS, K_NONE, +/* 50 */ K_NONE, K_NONE, K_SQOT, K_NONE, K_LSBK, K_EQLS, K_NONE, K_NONE, +/* 58 */ K_CAPS, K_RSFT, K_ENTR, K_RSBK, K_NONE, K_BSLH, K_NONE, K_NONE, +/* 60 */ K_NONE, K_HASH, K_NONE, K_NONE, K_NONE, K_NONE, K_BKSP, K_NONE, +/* 68 */ K_NONE, KP_1 , K_NONE, KP_4 , KP_7 , K_NONE, K_NONE, K_NONE, +/* 70 */ KP_0 , KP_DOT, KP_2 , KP_5 , KP_6 , KP_8 , K_ESC , K_NUML, +/* 78 */ K_F11 , KP_PLS, KP_3 , KP_MNS, KP_STR, KP_9 , K_SCRL, K_PRNT, +/* 80 */ K_NONE, K_NONE, K_NONE, K_F7 , K_NONE, K_NONE, K_NONE, K_NONE, +/* 88 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, +/* 90 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, +/* 98 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, +/* a0 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, +/* a8 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, +/* b0 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, +/* b8 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, +/* c0 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, +/* c8 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, +/* d0 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, +/* d8 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, +/* e0 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, +/* e8 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, +/* f0 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, +/* f8 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, KBD_RESEND, K_NONE +}; + +/* ----- the following code stolen from pc_keyb.c */ + + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char hp_ps2kbd_sysrq_xlate[128] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ +#endif + +/* + * Translation of escaped scancodes to keycodes. + * This is now user-settable. + * The keycodes 1-88,96-111,119 are fairly standard, and + * should probably not be changed - changing might confuse X. + * X also interprets scancode 0x5d (KEY_Begin). + * + * For 1-88 keycode equals scancode. + */ + +#define E0_KPENTER 96 +#define E0_RCTRL 97 +#define E0_KPSLASH 98 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 102 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 105 +#define E0_RIGHT 106 +#define E0_END 107 +#define E0_DOWN 108 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 + +#define E1_PAUSE 119 + +/* + * The keycodes below are randomly located in 89-95,112-118,120-127. + * They could be thrown away (and all occurrences below replaced by 0), + * but that would force many users to use the `setkeycodes' utility, where + * they needed not before. It does not matter that there are duplicates, as + * long as no duplication occurs for any single keyboard. + */ +#define SC_LIM 89 /* 0x59 == 89 */ + +#define FOCUS_PF1 85 /* actual code! */ +#define FOCUS_PF2 89 +#define FOCUS_PF3 90 +#define FOCUS_PF4 91 +#define FOCUS_PF5 92 +#define FOCUS_PF6 93 +#define FOCUS_PF7 94 +#define FOCUS_PF8 95 +#define FOCUS_PF9 120 +#define FOCUS_PF10 121 +#define FOCUS_PF11 122 +#define FOCUS_PF12 123 + +#define JAP_86 124 +/* tfj@olivia.ping.dk: + * The four keys are located over the numeric keypad, and are + * labelled A1-A4. It's an rc930 keyboard, from + * Regnecentralen/RC International, Now ICL. + * Scancodes: 59, 5a, 5b, 5c. + */ +#define RGN1 124 +#define RGN2 125 +#define RGN3 126 +#define RGN4 127 + +static unsigned char high_keys[128 - SC_LIM] = { + RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ + 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ + FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ + FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */ +}; + +/* BTC */ +#define E0_MACRO 112 +/* LK450 */ +#define E0_F13 113 +#define E0_F14 114 +#define E0_HELP 115 +#define E0_DO 116 +#define E0_F17 117 +#define E0_KPMINPLUS 118 +/* + * My OmniKey generates e0 4c for the "OMNI" key and the + * right alt key does nada. [kkoller@nyx10.cs.du.edu] + */ +#define E0_OK 124 +/* + * New microsoft keyboard is rumoured to have + * e0 5b (left window button), e0 5c (right window button), + * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] + * [or: Windows_L, Windows_R, TaskMan] + */ +#define E0_MSLW 125 +#define E0_MSRW 126 +#define E0_MSTM 127 + +static unsigned char e0_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ + E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ + E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ + E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ +}; + +int pckbd_setkeycode(unsigned int scancode, unsigned int keycode) +{ + if (scancode < SC_LIM || scancode > 255 || keycode > 127) + return -EINVAL; + if (scancode < 128) + high_keys[scancode - SC_LIM] = keycode; + else + e0_keys[scancode - 128] = keycode; + return 0; +} + +int pckbd_getkeycode(unsigned int scancode) +{ + return + (scancode < SC_LIM || scancode > 255) ? -EINVAL : + (scancode < 128) ? high_keys[scancode - SC_LIM] : + e0_keys[scancode - 128]; +} + +int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + static int prev_scancode; + + /* special prefix scancodes.. */ + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + + /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */ + if (scancode == 0x00 || scancode == 0xff) { + prev_scancode = 0; + return 0; + } + scancode &= 0x7f; + + if (prev_scancode) { + /* + * usually it will be 0xe0, but a Pause key generates + * e1 1d 45 e1 9d c5 when pressed, and nothing when released + */ + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x1d) { + prev_scancode = 0x100; + return 0; + } else if (prev_scancode == 0x100 && scancode == 0x45) { + *keycode = E1_PAUSE; + prev_scancode = 0; + } else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); +#endif + prev_scancode = 0; + return 0; + } + } else { + prev_scancode = 0; + /* + * The keyboard maintains its own internal caps lock and + * num lock statuses. In caps lock mode E0 AA precedes make + * code and E0 2A follows break code. In num lock mode, + * E0 2A precedes make code and E0 AA follows break code. + * We do our own book-keeping, so we will just ignore these. + */ + /* + * For my keyboard there is no caps lock mode, but there are + * both Shift-L and Shift-R modes. The former mode generates + * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. + * So, we should also ignore the latter. - aeb@cwi.nl + */ + if (scancode == 0x2a || scancode == 0x36) + return 0; + + if (e0_keys[scancode]) + *keycode = e0_keys[scancode]; + else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", + scancode); +#endif + return 0; + } + } + } else if (scancode >= SC_LIM) { + /* This happens with the FOCUS 9000 keyboard + Its keys PF1..PF12 are reported to generate + 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f + Moreover, unless repeated, they do not generate + key-down events, so we have to zero up_flag below */ + /* Also, Japanese 86/106 keyboards are reported to + generate 0x73 and 0x7d for \ - and \ | respectively. */ + /* Also, some Brazilian keyboard is reported to produce + 0x73 and 0x7e for \ ? and KP-dot, respectively. */ + + *keycode = high_keys[scancode - SC_LIM]; + + if (!*keycode) { + if (!raw_mode) { +#ifdef KBD_REPORT_UNKN + printk(KERN_INFO "keyboard: unrecognized scancode (%02x)" + " - ignored\n", scancode); +#endif + } + return 0; + } + } else + *keycode = scancode; + + return 1; +} + +/* ----- end of stolen part ------ */ + + +void kbd_reset_setup(void) +{ +} + +void handle_at_scancode(int keyval) +{ + static int brk; + static int esc0; + static int esc1; + int scancode = 0; + + switch (keyval) { + case KBD_BREAK : + /* sets the "release_key" bit when a key is + released. HP keyboard send f0 followed by + the keycode while AT keyboard send the keycode + with this bit set. */ + brk = 0x80; + return; + case KBD_ESCAPEE0 : + /* 2chars sequence, commonly used to differenciate + the two ALT keys and the two ENTER keys and so + on... */ + esc0 = 2; /* e0-xx are 2 chars */ + scancode = keyval; + break; + case KBD_ESCAPEE1 : + /* 3chars sequence, only used by the Pause key. */ + esc1 = 3; /* e1-xx-xx are 3 chars */ + scancode = keyval; + break; +#if 0 + case KBD_RESEND : + /* dunno what to do when it happens. RFC */ + printk(KERN_INFO "keyboard: KBD_RESEND received.\n"); + return; +#endif + case 0x14 : + /* translate e1-14-77-e1-f0-14-f0-77 to + e1-1d-45-e1-9d-c5 (the Pause key) */ + if (esc1==2) scancode = brk | 0x1d; + break; + case 0x77 : + if (esc1==1) scancode = brk | 0x45; + break; + case 0x12 : + /* an extended key is e0-12-e0-xx e0-f0-xx-e0-f0-12 + on HP, while it is e0-2a-e0-xx e0-(xx|80)-f0-aa + on AT. */ + if (esc0==1) scancode = brk | 0x2a; + break; + } + + + /* translates HP scancodes to AT scancodes */ + if (!scancode) scancode = brk | keycode_translate[keyval]; + + + if (!scancode) printk(KERN_INFO "keyboard: unexpected key code %02x\n",keyval); + + /* now behave like an AT keyboard */ + handle_scancode(scancode,!(scancode&0x80)); + + if (esc0) esc0--; + if (esc1) esc1--; + + /* release key bit must be unset for the next key */ + brk = 0; +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/hp_psaux.c linux.ac/drivers/char/hp_psaux.c --- linux.vanilla/drivers/char/hp_psaux.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/hp_psaux.c Wed Oct 10 01:47:41 2001 @@ -0,0 +1,551 @@ +/* + * LASI PS/2 keyboard/psaux driver for HP-PARISC workstations + * + * (c) Copyright 1999 The Puffin Group Inc. + * by Alex deVries + * Copyright 1999, 2000 Philipp Rumpf + * + * 2000/10/26 Debacker Xavier (debackex@esiee.fr) + * Marteau Thomas (marteaut@esiee.fr) + * Djoudi Malek (djoudim@esiee.fr) + * fixed leds control + * implemented the psaux and controlled the mouse scancode based on pc_keyb.c + */ + +#include + +#include +#include +#include + +#include +#include /* interrupt.h wants struct pt_regs defined */ +#include +#include /* for request_irq/free_irq */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* mouse includes */ +#include +#include +#include +#include +#include +#include +#include + +/* HP specific LASI PS/2 keyboard and psaux constants */ +#define AUX_REPLY_ACK 0xFA /* Command byte ACK. */ +#define AUX_RECONNECT 0xAA /* scancode when ps2 device is plugged (back) in */ + +#define LASI_PSAUX_OFFSET 0x0100 /* offset from keyboard to psaux port */ + +#define LASI_ID 0x00 /* ID and reset port offsets */ +#define LASI_RESET 0x00 +#define LASI_RCVDATA 0x04 /* receive and transmit port offsets */ +#define LASI_XMTDATA 0x04 +#define LASI_CONTROL 0x08 /* see: control register bits */ +#define LASI_STATUS 0x0C /* see: status register bits */ + +/* control register bits */ +#define LASI_CTRL_ENBL 0x01 /* enable interface */ +#define LASI_CTRL_LPBXR 0x02 /* loopback operation */ +#define LASI_CTRL_DIAG 0x20 /* directly control clock/data line */ +#define LASI_CTRL_DATDIR 0x40 /* data line direct control */ +#define LASI_CTRL_CLKDIR 0x80 /* clock line direct control */ + +/* status register bits */ +#define LASI_STAT_RBNE 0x01 +#define LASI_STAT_TBNE 0x02 +#define LASI_STAT_TERR 0x04 +#define LASI_STAT_PERR 0x08 +#define LASI_STAT_CMPINTR 0x10 +#define LASI_STAT_DATSHD 0x40 +#define LASI_STAT_CLKSHD 0x80 + +static void *lasikbd_hpa; +static void *lasips2_hpa; + + +static inline u8 read_input(void *hpa) +{ + return gsc_readb(hpa+LASI_RCVDATA); +} + +static inline u8 read_control(void *hpa) +{ + return gsc_readb(hpa+LASI_CONTROL); +} + +static inline void write_control(u8 val, void *hpa) +{ + gsc_writeb(val, hpa+LASI_CONTROL); +} + +static inline u8 read_status(void *hpa) +{ + return gsc_readb(hpa+LASI_STATUS); +} + +static int write_output(u8 val, void *hpa) +{ + int wait = 0; + + while (read_status(hpa) & LASI_STAT_TBNE) { + wait++; + if (wait>10000) { + /* printk(KERN_WARNING "Lasi PS/2 transmit buffer timeout\n"); */ + return 0; + } + } + + if (wait) + printk(KERN_DEBUG "Lasi PS/2 wait %d\n", wait); + + gsc_writeb(val, hpa+LASI_XMTDATA); + + return 1; +} + +/* This function is the PA-RISC adaptation of i386 source */ + +static inline int aux_write_ack(u8 val) +{ + return write_output(val, lasikbd_hpa+LASI_PSAUX_OFFSET); +} + +static void lasikbd_leds(unsigned char leds) +{ + write_output(KBD_CMD_SET_LEDS, lasikbd_hpa); + write_output(leds, lasikbd_hpa); + write_output(KBD_CMD_ENABLE, lasikbd_hpa); +} + +#if 0 +/* this might become useful again at some point. not now -prumpf */ +int lasi_ps2_test(void *hpa) +{ + u8 control,c; + int i, ret = 0; + + control = read_control(hpa); + write_control(control | LASI_CTRL_LPBXR | LASI_CTRL_ENBL, hpa); + + for (i=0; i<256; i++) { + write_output(i, hpa); + + while (!(read_status(hpa) & LASI_STAT_RBNE)) + /* just wait */; + + c = read_input(hpa); + if (c != i) + ret--; + } + + write_control(control, hpa); + + return ret; +} +#endif + +static int __init lasi_ps2_reset(void *hpa, int id) +{ + u8 control; + int ret = 1; + + /* reset the interface */ + gsc_writeb(0xff, hpa+LASI_RESET); + gsc_writeb(0x0 , hpa+LASI_RESET); + + /* enable it */ + control = read_control(hpa); + write_control(control | LASI_CTRL_ENBL, hpa); + + /* initializes the leds at the default state */ + if (id==0) { + write_output(KBD_CMD_SET_LEDS, hpa); + write_output(0, hpa); + ret = write_output(KBD_CMD_ENABLE, hpa); + } + + return ret; +} + +static int inited; + +static void lasi_ps2_init_hw(void) +{ + ++inited; +} + + +/* Greatly inspired by pc_keyb.c */ + +/* + * Wait for keyboard controller input buffer to drain. + * + * Don't use 'jiffies' so that we don't depend on + * interrupts.. + * + * Quote from PS/2 System Reference Manual: + * + * "Address hex 0060 and address hex 0064 should be written only when + * the input-buffer-full bit and output-buffer-full bit in the + * Controller Status register are set 0." + */ +#ifdef CONFIG_PSMOUSE + +static struct aux_queue *queue; +static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; +static unsigned char mouse_reply_expected; +static int aux_count; + +static int fasync_aux(int fd, struct file *filp, int on) +{ + int retval; + + retval = fasync_helper(fd, filp, on, &queue->fasync); + if (retval < 0) + return retval; + + return 0; +} + + + +static inline void handle_mouse_scancode(unsigned char scancode) +{ + if (mouse_reply_expected) { + if (scancode == AUX_REPLY_ACK) { + mouse_reply_expected--; + return; + } + mouse_reply_expected = 0; + } + else if (scancode == AUX_RECONNECT) { + queue->head = queue->tail = 0; /* Flush input queue */ + return; + } + + add_mouse_randomness(scancode); + if (aux_count) { + int head = queue->head; + + queue->buf[head] = scancode; + head = (head + 1) & (AUX_BUF_SIZE-1); + + if (head != queue->tail) { + queue->head = head; + kill_fasync(&queue->fasync, SIGIO, POLL_IN); + wake_up_interruptible(&queue->proc_list); + } + } +} + +static inline int queue_empty(void) +{ + return queue->head == queue->tail; +} + +static unsigned char get_from_queue(void) +{ + unsigned char result; + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + result = queue->buf[queue->tail]; + queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1); + spin_unlock_irqrestore(&kbd_controller_lock, flags); + + return result; +} + + +/* + * Write to the aux device. + */ + +static ssize_t write_aux(struct file * file, const char * buffer, + size_t count, loff_t *ppos) +{ + ssize_t retval = 0; + + if (count) { + ssize_t written = 0; + + if (count > 32) + count = 32; /* Limit to 32 bytes. */ + do { + char c; + get_user(c, buffer++); + written++; + } while (--count); + retval = -EIO; + if (written) { + retval = written; + file->f_dentry->d_inode->i_mtime = CURRENT_TIME; + } + } + + return retval; +} + + + +static ssize_t read_aux(struct file * file, char * buffer, + size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + ssize_t i = count; + unsigned char c; + + if (queue_empty()) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + add_wait_queue(&queue->proc_list, &wait); +repeat: + set_current_state(TASK_INTERRUPTIBLE); + if (queue_empty() && !signal_pending(current)) { + schedule(); + goto repeat; + } + current->state = TASK_RUNNING; + remove_wait_queue(&queue->proc_list, &wait); + } + while (i > 0 && !queue_empty()) { + c = get_from_queue(); + put_user(c, buffer++); + i--; + } + if (count-i) { + file->f_dentry->d_inode->i_atime = CURRENT_TIME; + return count-i; + } + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + + +static int open_aux(struct inode * inode, struct file * file) +{ + if (aux_count++) + return 0; + + queue->head = queue->tail = 0; /* Flush input queue */ + aux_count = 1; + aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */ + + return 0; +} + + +/* No kernel lock held - fine */ +static unsigned int aux_poll(struct file *file, poll_table * wait) +{ + + poll_wait(file, &queue->proc_list, wait); + if (!queue_empty()) + return POLLIN | POLLRDNORM; + return 0; +} + + +static int release_aux(struct inode * inode, struct file * file) +{ + lock_kernel(); + fasync_aux(-1, file, 0); + if (--aux_count) { + unlock_kernel(); + return 0; + } + unlock_kernel(); + return 0; +} + +static struct file_operations psaux_fops = { + read: read_aux, + write: write_aux, + poll: aux_poll, + open: open_aux, + release: release_aux, + fasync: fasync_aux, +}; + +static struct miscdevice psaux_mouse = { + minor: PSMOUSE_MINOR, + name: "psaux", + fops: &psaux_fops, +}; + +#endif /* CONFIG_PSMOUSE */ + + +/* This function is looking at the PS2 controller and empty the two buffers */ + +static u8 handle_lasikbd_event(void *hpa) +{ + u8 status_keyb,status_mouse,scancode,id; + extern void handle_at_scancode(int); /* in drivers/char/keyb_at.c */ + + /* Mask to get the base address of the PS/2 controller */ + id = gsc_readb(hpa+LASI_ID) & 0x0f; + + if (id==1) + hpa -= LASI_PSAUX_OFFSET; + lasikbd_hpa = hpa; + + + status_keyb = read_status(hpa); + status_mouse = read_status(hpa+LASI_PSAUX_OFFSET); + + while ((status_keyb|status_mouse) & LASI_STAT_RBNE){ + + while (status_keyb & LASI_STAT_RBNE) { + + scancode = read_input(hpa); + + /* XXX don't know if this is a valid fix, but filtering + * 0xfa avoids 'unknown scancode' errors on, eg, capslock + * on some keyboards. + */ + if (inited && scancode != 0xfa) + handle_at_scancode(scancode); + + status_keyb =read_status(hpa); + } + +#ifdef CONFIG_PSMOUSE + while (status_mouse & LASI_STAT_RBNE) { + scancode = read_input(hpa+LASI_PSAUX_OFFSET); + handle_mouse_scancode(scancode); + status_mouse = read_status(hpa+LASI_PSAUX_OFFSET); + } + status_mouse = read_status(hpa+LASI_PSAUX_OFFSET); +#endif /* CONFIG_PSMOUSE */ + status_keyb = read_status(hpa); + } + + tasklet_schedule(&keyboard_tasklet); + return (status_keyb|status_mouse); +} + + + + +extern struct pt_regs *kbd_pt_regs; + +static void lasikbd_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + lasips2_hpa = dev; /* save "hpa" for lasikbd_leds() */ + kbd_pt_regs = regs; + handle_lasikbd_event(lasips2_hpa); +} + + +extern int pckbd_translate(unsigned char, unsigned char *, char); + +static struct kbd_ops gsc_ps2_kbd_ops = { + translate: pckbd_translate, + init_hw: lasi_ps2_init_hw, + leds: lasikbd_leds, +#ifdef CONFIG_MAGIC_SYSRQ + sysrq_key: 0x54, + sysrq_xlate: hp_ps2kbd_sysrq_xlate, +#endif +}; + +static int __init +lasi_ps2_register(struct hp_device *d, struct pa_iodc_driver *dri) +{ + void *hpa = (void *) d->hpa; + unsigned int irq; + char *name; + int device_found; + u8 id; + + id = gsc_readb(hpa+LASI_ID) & 0x0f; + + switch (id) { + case 0: + name = "keyboard"; + lasikbd_hpa = hpa; + break; + case 1: + name = "psaux"; + break; + default: + printk(KERN_WARNING "%s: Unknown PS/2 port (id=%d) - ignored.\n", + __FUNCTION__, id ); + return 0; + } + + /* reset the PS/2 port */ + device_found = lasi_ps2_reset(hpa,id); + + /* allocate the irq and memory region for that device */ + if (!(irq = busdevice_alloc_irq(d))) + return -ENODEV; + + if (request_irq(irq, lasikbd_interrupt, 0, name, hpa)) + return -ENODEV; + + if (!request_mem_region((unsigned long)hpa, LASI_STATUS + 4, name)) + return -ENODEV; + + switch (id) { + case 0: + register_kbd_ops(&gsc_ps2_kbd_ops); + break; + case 1: +#ifdef CONFIG_PSMOUSE + queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); + if (!queue) + return -ENOMEM; + + memset(queue, 0, sizeof(*queue)); + queue->head = queue->tail = 0; + init_waitqueue_head(&queue->proc_list); + + misc_register(&psaux_mouse); + + aux_write_ack(AUX_ENABLE_DEV); + /* try it a second time, this will give status if the device is + * available */ + device_found = aux_write_ack(AUX_ENABLE_DEV); + break; +#else + /* return without printing any unnecessary and misleading info */ + return 0; +#endif + } /* of case */ + + printk(KERN_INFO "PS/2 %s controller at 0x%08lx (irq %d) found, " + "%sdevice attached.\n", + name, (unsigned long)hpa, irq, + device_found ? "":"no "); + + return 0; +} + + +static struct pa_iodc_driver lasi_psaux_drivers_for[] __initdata = { + {HPHW_FIO, 0x0, 0,0x00084, 0, 0, + DRIVER_CHECK_HWTYPE + DRIVER_CHECK_SVERSION, + "Lasi psaux", "generic", (void *) lasi_ps2_register}, + { 0, } +}; + +static int __init gsc_ps2_init(void) +{ + return pdc_register_driver(lasi_psaux_drivers_for); +} + +module_init(gsc_ps2_init); + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/i810_rng.c linux.ac/drivers/char/i810_rng.c --- linux.vanilla/drivers/char/i810_rng.c Thu Apr 12 20:15:25 2001 +++ linux.ac/drivers/char/i810_rng.c Thu Oct 11 09:24:36 2001 @@ -342,6 +342,7 @@ MODULE_AUTHOR("Jeff Garzik, Philipp Rumpf, Matt Sottek"); MODULE_DESCRIPTION("Intel i8xx chipset Random Number Generator (RNG) driver"); +MODULE_LICENSE("GPL"); /* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/ib700wdt.c linux.ac/drivers/char/ib700wdt.c --- linux.vanilla/drivers/char/ib700wdt.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/ib700wdt.c Thu Oct 11 09:24:51 2001 @@ -0,0 +1,272 @@ +/* + * IB700 Single Board Computer WDT driver for Linux 2.4.x + * + * (c) Copyright 2001 Charles Howes + * + * Based on advantechwdt.c which is based on acquirewdt.c which + * is based on wdt.c. + * + * (c) Copyright 2000-2001 Marek Michalkiewicz + * + * Based on acquirewdt.c which is based on wdt.c. + * Original copyright messages: + * + * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * 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. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int ibwdt_is_open; +static spinlock_t ibwdt_lock; + +/* + * + * Watchdog Timer Configuration + * + * The function of the watchdog timer is to reset the system + * automatically and is defined at I/O port 0443H. To enable the + * watchdog timer and allow the system to reset, write I/O port 0443H. + * To disable the timer, write I/O port 0441H for the system to stop the + * watchdog function. The timer has a tolerance of 20% for its + * intervals. + * + * The following describes how the timer should be programmed. + * + * Enabling Watchdog: + * MOV AX,000FH (Choose the values from 0 to F) + * MOV DX,0443H + * OUT DX,AX + * + * Disabling Watchdog: + * MOV AX,000FH (Any value is fine.) + * MOV DX,0441H + * OUT DX,AX + * + * Watchdog timer control table: + * Level Value Time/sec | Level Value Time/sec + * 1 F 0 | 9 7 16 + * 2 E 2 | 10 6 18 + * 3 D 4 | 11 5 20 + * 4 C 6 | 12 4 22 + * 5 B 8 | 13 3 24 + * 6 A 10 | 14 2 26 + * 7 9 12 | 15 1 28 + * 8 8 14 | 16 0 30 + * + */ + +#define WDT_STOP 0x441 +#define WDT_START 0x443 + +#define WD_TIMO 0 /* 30 seconds +/- 20%, from table */ + +/* + * Kernel methods. + */ + +static void +ibwdt_ping(void) +{ + /* Write a watchdog value */ + outb_p(WD_TIMO, WDT_START); +} + +static ssize_t +ibwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + if (count) { + ibwdt_ping(); + return 1; + } + return 0; +} + +static ssize_t +ibwdt_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static int +ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + static struct watchdog_info ident = { + WDIOF_KEEPALIVEPING, 1, "IB700 WDT" + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) + return -EFAULT; + break; + + case WDIOC_GETSTATUS: + if (copy_to_user((int *)arg, &ibwdt_is_open, sizeof(int))) + return -EFAULT; + break; + + case WDIOC_KEEPALIVE: + ibwdt_ping(); + break; + + default: + return -ENOTTY; + } + return 0; +} + +static int +ibwdt_open(struct inode *inode, struct file *file) +{ + switch (MINOR(inode->i_rdev)) { + case WATCHDOG_MINOR: + spin_lock(&ibwdt_lock); + if (ibwdt_is_open) { + spin_unlock(&ibwdt_lock); + return -EBUSY; + } + /* + * Activate + */ + + ibwdt_is_open = 1; + ibwdt_ping(); + spin_unlock(&ibwdt_lock); + return 0; + default: + return -ENODEV; + } +} + +static int +ibwdt_close(struct inode *inode, struct file *file) +{ + lock_kernel(); + if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) { + spin_lock(&ibwdt_lock); +#ifndef CONFIG_WATCHDOG_NOWAYOUT + outb_p(WD_TIMO, WDT_STOP); +#endif + ibwdt_is_open = 0; + spin_unlock(&ibwdt_lock); + } + unlock_kernel(); + return 0; +} + +/* + * Notifier for system down + */ + +static int +ibwdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) { + /* Turn the WDT off */ + outb_p(WD_TIMO, WDT_STOP); + } + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + +static struct file_operations ibwdt_fops = { + owner: THIS_MODULE, + read: ibwdt_read, + write: ibwdt_write, + ioctl: ibwdt_ioctl, + open: ibwdt_open, + release: ibwdt_close, +}; + +static struct miscdevice ibwdt_miscdev = { + WATCHDOG_MINOR, + "watchdog", + &ibwdt_fops +}; + +/* + * The WDT needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block ibwdt_notifier = { + ibwdt_notify_sys, + NULL, + 0 +}; + +static int __init +ibwdt_init(void) +{ + printk("WDT driver for IB700 single board computer initialising.\n"); + + spin_lock_init(&ibwdt_lock); + misc_register(&ibwdt_miscdev); +#if WDT_START != WDT_STOP + request_region(WDT_STOP, 1, "IB700 WDT"); +#endif + request_region(WDT_START, 1, "IB700 WDT"); + register_reboot_notifier(&ibwdt_notifier); + return 0; +} + +static void __exit +ibwdt_exit(void) +{ + misc_deregister(&ibwdt_miscdev); + unregister_reboot_notifier(&ibwdt_notifier); +#if WDT_START != WDT_STOP + release_region(WDT_STOP,1); +#endif + release_region(WDT_START,1); +} + +module_init(ibwdt_init); +module_exit(ibwdt_exit); + +MODULE_AUTHOR("Charles Howes "); +MODULE_DESCRIPTION("IB700 SBC watchdog driver"); +MODULE_LICENSE("GPL"); + +/* end of ib700wdt.c */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/ip2main.c linux.ac/drivers/char/ip2main.c --- linux.vanilla/drivers/char/ip2main.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/char/ip2main.c Wed Oct 10 01:47:41 2001 @@ -3458,3 +3458,4 @@ } +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/istallion.c linux.ac/drivers/char/istallion.c --- linux.vanilla/drivers/char/istallion.c Mon Jul 16 00:15:44 2001 +++ linux.ac/drivers/char/istallion.c Wed Oct 10 01:47:41 2001 @@ -386,6 +386,8 @@ */ MODULE_AUTHOR("Greg Ungerer"); MODULE_DESCRIPTION("Stallion Intelligent Multiport Serial Driver"); +MODULE_LICENSE("GPL"); + MODULE_PARM(board0, "1-3s"); MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,memaddr]"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/joystick/analog.c linux.ac/drivers/char/joystick/analog.c --- linux.vanilla/drivers/char/joystick/analog.c Fri Sep 14 22:40:00 2001 +++ linux.ac/drivers/char/joystick/analog.c Wed Oct 10 01:47:41 2001 @@ -138,7 +138,7 @@ #ifdef __i386__ #define TSC_PRESENT (test_bit(X86_FEATURE_TSC, &boot_cpu_data.x86_capability)) -#define GET_TIME(x) do { if (TSC_PRESENT) rdtscl(x); else outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) +#define GET_TIME(x) do { if (TSC_PRESENT) rdtscl(x); else { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } } while (0) #define DELTA(x,y) (TSC_PRESENT?((y)-(x)):((x)-(y)+((x)<(y)?1193180L/HZ:0))) #define TIME_NAME (TSC_PRESENT?"TSC":"PIT") #elif __x86_64__ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/keyboard.c linux.ac/drivers/char/keyboard.c --- linux.vanilla/drivers/char/keyboard.c Tue Sep 18 21:39:51 2001 +++ linux.ac/drivers/char/keyboard.c Wed Oct 10 01:47:41 2001 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -42,6 +43,7 @@ #include #include #include +#include #define SIZE(x) (sizeof(x)/sizeof((x)[0])) @@ -319,7 +321,7 @@ compute_shiftstate(); kbd->slockstate = 0; /* play it safe */ #else - keysym = U(plain_map[keycode]); + keysym = U(key_maps[0][keycode]); type = KTYP(keysym); if (type == KT_SHIFT) (*key_handler[type])(keysym & 0xff, up_flag); @@ -532,6 +534,7 @@ !(SPECIALS_ALLOWED_IN_RAW_MODE & (1 << value))) return; spec_fn_table[value](); + speakup_control(fg_console, kbd, value); } static void do_lowercase(unsigned char value, char up_flag) @@ -545,7 +548,8 @@ return; /* no action, if this is a key release */ if (diacr) - value = handle_diacr(value); + if ((value = handle_diacr(value)) == 0xff) /* speakup goto pos trigger*/ + return; if (dead_key_next) { dead_key_next = 0; @@ -553,6 +557,7 @@ return; } + speakup_savekey(value); put_queue(value); } @@ -562,8 +567,10 @@ #define A_TILDE '~' #define A_DIAER '"' #define A_CEDIL ',' +#define SPEAKUP_DIACR '$' + static unsigned char ret_diacr[NR_DEAD] = - {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER, A_CEDIL }; + {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER, A_CEDIL, SPEAKUP_DIACR}; /* Obsolete - for backwards compatibility only */ static void do_dead(unsigned char value, char up_flag) @@ -582,6 +589,12 @@ if (up_flag) return; + if (value == SPEAKUP_DIACR) { /*beep to alert that speakup's waiting */ + kd_mksound(1250,10); + mdelay(100); + kd_mksound(1500,15); + } + diacr = (diacr ? handle_diacr(value) : value); } @@ -598,6 +611,12 @@ int d = diacr; int i; + if (d == '$') { + if (speakup_diacr(ch,fg_console)) + diacr = 0; + return 0xff; + } + diacr = 0; for (i = 0; i < accent_table_size; i++) { @@ -711,6 +730,10 @@ clr_vc_kbd_led(kbd, VC_CAPSLOCK); } + /* shift = 0, altgr = 1, ctrl=2, alt=3 */ + if (!up_flag) + speakup_control(fg_console, kbd, value); + if (up_flag) { /* handle the case that two shift or control keys are depressed simultaneously */ @@ -750,7 +773,7 @@ k = i*BITS_PER_LONG; for(j=0; j= KERNEL_VERSION(2,4,0) static int mwave_get_info(char *buf, char **start, off_t offset, int len); #else diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/nwflash.c linux.ac/drivers/char/nwflash.c --- linux.vanilla/drivers/char/nwflash.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/char/nwflash.c Wed Oct 10 01:47:41 2001 @@ -59,6 +59,7 @@ static int gbWriteBase64Enable; static volatile unsigned char *FLASH_BASE; static int gbFlashSize = KFLASH_SIZE; +static DECLARE_MUTEX(nwflash_sem); extern spinlock_t gpio_lock; @@ -132,7 +133,6 @@ static ssize_t flash_read(struct file *file, char *buf, size_t size, loff_t * ppos) { - struct inode *inode = file->f_dentry->d_inode; unsigned long p = *ppos; unsigned int count = size; int ret = 0; @@ -151,7 +151,7 @@ /* * We now lock against reads and writes. --rmk */ - if (down_interruptible(&inode->i_sem)) + if (down_interruptible(&nwflash_sem)) return -ERESTARTSYS; ret = copy_to_user(buf, (void *)(FLASH_BASE + p), count); @@ -159,14 +159,13 @@ ret = count; *ppos += count; } - up(&inode->i_sem); + up(&nwflash_sem); } return ret; } static ssize_t flash_write(struct file *file, const char *buf, size_t size, loff_t * ppos) { - struct inode *inode = file->f_dentry->d_inode; unsigned long p = *ppos; unsigned int count = size; int written; @@ -198,7 +197,7 @@ /* * We now lock against reads and writes. --rmk */ - if (down_interruptible(&inode->i_sem)) + if (down_interruptible(&nwflash_sem)) return -ERESTARTSYS; written = 0; @@ -286,7 +285,7 @@ */ leds_event(led_release); - up(&inode->i_sem); + up(&nwflash_sem); return written; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/pc_keyb.c linux.ac/drivers/char/pc_keyb.c --- linux.vanilla/drivers/char/pc_keyb.c Tue Sep 18 06:52:35 2001 +++ linux.ac/drivers/char/pc_keyb.c Wed Oct 10 01:47:41 2001 @@ -397,29 +397,32 @@ return 0200; } -void pckbd_pm_resume(void) +int pckbd_pm_resume(struct pm_dev *dev, pm_request_t rqst, void *data) { #if defined CONFIG_PSMOUSE unsigned long flags; - if (queue) { /* Aux port detected */ - if (aux_count == 0) { /* Mouse not in use */ - spin_lock_irqsave(&kbd_controller_lock, flags); - /* - * Dell Lat. C600 A06 enables mouse after resume. - * When user touches the pad, it posts IRQ 12 - * (which we do not process), thus holding keyboard. - */ - kbd_write_command(KBD_CCMD_MOUSE_DISABLE); - /* kbd_write_cmd(AUX_INTS_OFF); */ /* Config & lock */ - kb_wait(); - kbd_write_command(KBD_CCMD_WRITE_MODE); - kb_wait(); - kbd_write_output(AUX_INTS_OFF); - spin_unlock_irqrestore(&kbd_controller_lock, flags); - } + if (rqst == PM_RESUME) { + if (queue) { /* Aux port detected */ + if (aux_count == 0) { /* Mouse not in use */ + spin_lock_irqsave(&kbd_controller_lock, flags); + /* + * Dell Lat. C600 A06 enables mouse after resume. + * When user touches the pad, it posts IRQ 12 + * (which we do not process), thus holding keyboard. + */ + kbd_write_command(KBD_CCMD_MOUSE_DISABLE); + /* kbd_write_cmd(AUX_INTS_OFF); */ /* Config & lock */ + kb_wait(); + kbd_write_command(KBD_CCMD_WRITE_MODE); + kb_wait(); + kbd_write_output(AUX_INTS_OFF); + spin_unlock_irqrestore(&kbd_controller_lock, flags); + } + } } -#endif +#endif + return 0; } @@ -1211,3 +1214,26 @@ } #endif /* CONFIG_PSMOUSE */ + + +/* Tell the user who may be running in X and not see the console that we have + panic'ed. This is to distingush panics from "real" lockups. + Could in theory send the panic message as morse, but that is left as an + exercise for the reader. */ +void panic_blink(void) +{ + static unsigned long last_jiffie; + static char led; + /* Roughly 1/2s frequency. KDB uses about 1s. Make sure it is + different. */ + if (jiffies - last_jiffie > HZ/2) { + led ^= 0x01 | 0x04; + while (kbd_read_status() & KBD_STAT_IBF) mdelay(1); + kbd_write_output(KBD_CMD_SET_LEDS); + mdelay(1); + while (kbd_read_status() & KBD_STAT_IBF) mdelay(1); + mdelay(1); + kbd_write_output(led); + last_jiffie = jiffies; + } +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/random.c linux.ac/drivers/char/random.c --- linux.vanilla/drivers/char/random.c Tue Sep 18 06:52:35 2001 +++ linux.ac/drivers/char/random.c Wed Oct 10 01:47:41 2001 @@ -408,7 +408,7 @@ * to optimize a static rotate left of x bits, it doesn't know how to * deal with a variable rotate of x bits. So we use a bit of asm magic. */ -#if (!defined (__i386__)) +#if (!defined (__i386__)) && (!defined(__x86_64__)) extern inline __u32 rotate_left(int i, __u32 word) { return (word << i) | (word >> (32 - i)); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/raw.c linux.ac/drivers/char/raw.c --- linux.vanilla/drivers/char/raw.c Sun Sep 23 04:35:43 2001 +++ linux.ac/drivers/char/raw.c Wed Oct 10 01:47:41 2001 @@ -20,6 +20,8 @@ #define dprintk(x...) typedef struct raw_device_data_s { + struct kiobuf * iobuf; + long iobuf_lock; struct block_device *binding; int inuse, sector_size, sector_bits; struct semaphore mutex; @@ -73,6 +75,7 @@ int err; int sector_size; int sector_bits; + int nbhs = KIO_MAX_SECTORS; minor = MINOR(inode->i_rdev); @@ -85,12 +88,6 @@ return 0; } - if (!filp->f_iobuf) { - err = alloc_kiovec(1, &filp->f_iobuf); - if (err) - return err; - } - down(&raw_devices[minor].mutex); /* * No, it is a normal raw device. All we need to do on open is @@ -103,8 +100,8 @@ if (!bdev) goto out; - atomic_inc(&bdev->bd_count); rdev = to_kdev_t(bdev->bd_dev); + atomic_inc(&bdev->bd_count); err = blkdev_get(bdev, filp->f_mode, 0, BDEV_RAW); if (err) goto out; @@ -118,6 +115,18 @@ goto out; /* + * We'll just use one kiobuf + */ + err = alloc_kiovec_sz(1, &raw_devices[minor].iobuf, &nbhs); + if (err) { + raw_devices[minor].inuse--; + up(&raw_devices[minor].mutex); + blkdev_put(bdev, BDEV_RAW); + return err; + } + + + /* * Don't interfere with mounted devices: we cannot safely set * the blocksize on a device which is already mounted. */ @@ -148,11 +157,13 @@ { int minor; struct block_device *bdev; + int nbhs = KIO_MAX_SECTORS; minor = MINOR(inode->i_rdev); down(&raw_devices[minor].mutex); bdev = raw_devices[minor].binding; - raw_devices[minor].inuse--; + if (!--raw_devices[minor].inuse) + free_kiovec_sz(1, &raw_devices[minor].iobuf, &nbhs); up(&raw_devices[minor].mutex); blkdev_put(bdev, BDEV_RAW); return 0; @@ -283,7 +294,7 @@ int sector_size, sector_bits, sector_mask; int max_sectors; - + int nbhs; /* * First, a few checks on device size limits */ @@ -291,24 +302,26 @@ minor = MINOR(filp->f_dentry->d_inode->i_rdev); new_iobuf = 0; - iobuf = filp->f_iobuf; - if (test_and_set_bit(0, &filp->f_iobuf_lock)) { + iobuf = raw_devices[minor].iobuf; + dev = to_kdev_t(raw_devices[minor].binding->bd_dev); + sector_size = raw_devices[minor].sector_size; + sector_bits = raw_devices[minor].sector_bits; + sector_mask = sector_size- 1; + max_sectors = KIO_MAX_SECTORS >> (sector_bits - 9); + + if (test_and_set_bit(0, &raw_devices[minor].iobuf_lock)) { /* * A parallel read/write is using the preallocated iobuf * so just run slow and allocate a new one. */ - err = alloc_kiovec(1, &iobuf); + nbhs = (size >> sector_bits); + if (nbhs > KIO_MAX_SECTORS) + nbhs = KIO_MAX_SECTORS; + err = alloc_kiovec_sz(1, &iobuf, &nbhs); if (err) goto out; new_iobuf = 1; } - - dev = to_kdev_t(raw_devices[minor].binding->bd_dev); - sector_size = raw_devices[minor].sector_size; - sector_bits = raw_devices[minor].sector_bits; - sector_mask = sector_size- 1; - max_sectors = KIO_MAX_SECTORS >> (sector_bits - 9); - if (blk_size[MAJOR(dev)]) limit = (((loff_t) blk_size[MAJOR(dev)][MINOR(dev)]) << BLOCK_SIZE_BITS) >> sector_bits; else @@ -375,9 +388,9 @@ out_free: if (!new_iobuf) - clear_bit(0, &filp->f_iobuf_lock); + clear_bit(0, &raw_devices[minor].iobuf_lock); else - free_kiovec(1, &iobuf); + free_kiovec_sz(1, &iobuf, &nbhs); out: return err; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/serial.c linux.ac/drivers/char/serial.c --- linux.vanilla/drivers/char/serial.c Mon Sep 17 21:16:30 2001 +++ linux.ac/drivers/char/serial.c Wed Oct 10 01:47:41 2001 @@ -1766,13 +1766,11 @@ if (I_IGNPAR(info->tty)) info->ignore_status_mask |= UART_LSR_OE; } -#if 0 /* breaks serial console during boot stage */ /* * !!! ignore all characters if CREAD is not set */ if ((cflag & CREAD) == 0) info->ignore_status_mask |= UART_LSR_DR; -#endif save_flags(flags); cli(); if (uart_config[info->state->type].flags & UART_STARTECH) { serial_outp(info, UART_LCR, 0xBF); @@ -4136,7 +4134,7 @@ { unsigned long oldval; - if (!(pci_get_subdevice(dev) & 0x1000)) + if (!(pci_get_subvendor(dev) & 0x1000)) return(-1); if (!enable) /* is there something to deinit? */ @@ -4882,8 +4880,11 @@ MODULE_DEVICE_TABLE(pci, serial_pci_tbl); +/* serial_pci_driver_name[] gets truncated to "" if the pci probe fails */ +static char serial_pci_driver_name[] = "serial"; + static struct pci_driver serial_pci_driver = { - name: "serial", + name: serial_pci_driver_name, probe: serial_init_one, remove: serial_remove_one, id_table: serial_pci_tbl, @@ -4908,7 +4909,7 @@ * not to attempt to unregister the driver later */ if (pci_module_init (&serial_pci_driver) != 0) - serial_pci_driver.name = ""; + serial_pci_driver.name[0] = 0; #ifdef SERIAL_DEBUG_PCI printk(KERN_DEBUG "Leaving probe_serial_pci() (probe finished)\n"); @@ -5386,6 +5387,7 @@ #endif serial_driver.major = TTY_MAJOR; serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET; + serial_driver.name_base = SERIAL_DEV_OFFSET; serial_driver.num = NR_PORTS; serial_driver.type = TTY_DRIVER_TYPE_SERIAL; serial_driver.subtype = SERIAL_TYPE_NORMAL; @@ -5765,7 +5767,7 @@ * Print a string to the serial port trying not to disturb * any possible real use of the port... * - * The console must be locked when we get here. + * The console_lock must be held when we get here. */ static void serial_console_write(struct console *co, const char *s, unsigned count) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/serial_tx3912.c linux.ac/drivers/char/serial_tx3912.c --- linux.vanilla/drivers/char/serial_tx3912.c Fri Sep 7 17:28:38 2001 +++ linux.ac/drivers/char/serial_tx3912.c Wed Oct 10 01:47:42 2001 @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/sonypi.c linux.ac/drivers/char/sonypi.c --- linux.vanilla/drivers/char/sonypi.c Tue Sep 18 06:52:35 2001 +++ linux.ac/drivers/char/sonypi.c Wed Oct 10 01:47:42 2001 @@ -39,6 +39,7 @@ #include #include #include +#include #include "sonypi.h" #include @@ -48,7 +49,7 @@ static int verbose; /* = 0 */ static int fnkeyinit; /* = 0 */ static int camera; /* = 0 */ -extern int is_sony_vaio_laptop; /* set in DMI table parse routines */ +static int compat; /* = 0 */ /* Inits the queue */ static inline void sonypi_initq(void) { @@ -110,27 +111,27 @@ static void sonypi_ecrset(u16 addr, u16 value) { - wait_on_command(inw_p(SONYPI_CST_IOPORT) & 3); + wait_on_command(1, inw_p(SONYPI_CST_IOPORT) & 3); outw_p(0x81, SONYPI_CST_IOPORT); - wait_on_command(inw_p(SONYPI_CST_IOPORT) & 2); + wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2); outw_p(addr, SONYPI_DATA_IOPORT); - wait_on_command(inw_p(SONYPI_CST_IOPORT) & 2); + wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2); outw_p(value, SONYPI_DATA_IOPORT); - wait_on_command(inw_p(SONYPI_CST_IOPORT) & 2); + wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2); } static u16 sonypi_ecrget(u16 addr) { - wait_on_command(inw_p(SONYPI_CST_IOPORT) & 3); + wait_on_command(1, inw_p(SONYPI_CST_IOPORT) & 3); outw_p(0x80, SONYPI_CST_IOPORT); - wait_on_command(inw_p(SONYPI_CST_IOPORT) & 2); + wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2); outw_p(addr, SONYPI_DATA_IOPORT); - wait_on_command(inw_p(SONYPI_CST_IOPORT) & 2); + wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2); return inw_p(SONYPI_DATA_IOPORT); } /* Initializes the device - this comes from the AML code in the ACPI bios */ -static void __devinit sonypi_normal_srs(void) { +static void __devinit sonypi_type1_srs(void) { u32 v; pci_read_config_dword(sonypi_device.dev, SONYPI_G10A, &v); @@ -152,7 +153,7 @@ pci_write_config_dword(sonypi_device.dev, SONYPI_G10A, v); } -static void __devinit sonypi_r505_srs(void) { +static void __devinit sonypi_type2_srs(void) { sonypi_ecrset(SONYPI_SHIB, (sonypi_device.ioport1 & 0xFF00) >> 8); sonypi_ecrset(SONYPI_SLOB, sonypi_device.ioport1 & 0x00FF); sonypi_ecrset(SONYPI_SIRQ, sonypi_device.bits); @@ -160,7 +161,7 @@ } /* Disables the device - this comes from the AML code in the ACPI bios */ -static void __devexit sonypi_normal_dis(void) { +static void __devexit sonypi_type1_dis(void) { u32 v; pci_read_config_dword(sonypi_device.dev, SONYPI_G10A, &v); @@ -172,7 +173,7 @@ outl(v, SONYPI_IRQ_PORT); } -static void __devexit sonypi_r505_dis(void) { +static void __devexit sonypi_type2_dis(void) { sonypi_ecrset(SONYPI_SHIB, 0); sonypi_ecrset(SONYPI_SLOB, 0); sonypi_ecrset(SONYPI_SIRQ, 0); @@ -181,7 +182,7 @@ static u8 sonypi_call1(u8 dev) { u8 v1, v2; - wait_on_command(inb_p(sonypi_device.ioport2) & 2); + wait_on_command(0, inb_p(sonypi_device.ioport2) & 2); outb(dev, sonypi_device.ioport2); v1 = inb_p(sonypi_device.ioport2); v2 = inb_p(sonypi_device.ioport1); @@ -191,9 +192,9 @@ static u8 sonypi_call2(u8 dev, u8 fn) { u8 v1; - wait_on_command(inb_p(sonypi_device.ioport2) & 2); + wait_on_command(0, inb_p(sonypi_device.ioport2) & 2); outb(dev, sonypi_device.ioport2); - wait_on_command(inb_p(sonypi_device.ioport2) & 2); + wait_on_command(0, inb_p(sonypi_device.ioport2) & 2); outb(fn, sonypi_device.ioport1); v1 = inb_p(sonypi_device.ioport1); return v1; @@ -202,11 +203,11 @@ static u8 sonypi_call3(u8 dev, u8 fn, u8 v) { u8 v1; - wait_on_command(inb_p(sonypi_device.ioport2) & 2); + wait_on_command(0, inb_p(sonypi_device.ioport2) & 2); outb(dev, sonypi_device.ioport2); - wait_on_command(inb_p(sonypi_device.ioport2) & 2); + wait_on_command(0, inb_p(sonypi_device.ioport2) & 2); outb(fn, sonypi_device.ioport1); - wait_on_command(inb_p(sonypi_device.ioport2) & 2); + wait_on_command(0, inb_p(sonypi_device.ioport2) & 2); outb(v, sonypi_device.ioport1); v1 = inb_p(sonypi_device.ioport1); return v1; @@ -228,7 +229,7 @@ /* Set brightness, hue etc */ static void sonypi_set(u8 fn, u8 v) { - wait_on_command(sonypi_call3(0x90, fn, v)); + wait_on_command(0, sonypi_call3(0x90, fn, v)); } /* Tests if the camera is ready */ @@ -291,19 +292,19 @@ int i; u8 sonypi_jogger_ev, sonypi_fnkey_ev; - if (sonypi_device.model == SONYPI_DEVICE_MODEL_R505) { - sonypi_jogger_ev = SONYPI_R505_JOGGER_EV; - sonypi_fnkey_ev = SONYPI_R505_FNKEY_EV; + if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) { + sonypi_jogger_ev = SONYPI_TYPE2_JOGGER_EV; + sonypi_fnkey_ev = SONYPI_TYPE2_FNKEY_EV; } else { - sonypi_jogger_ev = SONYPI_NORMAL_JOGGER_EV; - sonypi_fnkey_ev = SONYPI_NORMAL_FNKEY_EV; + sonypi_jogger_ev = SONYPI_TYPE1_JOGGER_EV; + sonypi_fnkey_ev = SONYPI_TYPE1_FNKEY_EV; } v1 = inb_p(sonypi_device.ioport1); v2 = inb_p(sonypi_device.ioport2); - if ((v2 & SONYPI_NORMAL_PKEY_EV) == SONYPI_NORMAL_PKEY_EV) { + if ((v2 & SONYPI_TYPE1_PKEY_EV) == SONYPI_TYPE1_PKEY_EV) { for (i = 0; sonypi_pkeyev[i].event; i++) if (sonypi_pkeyev[i].data == v1) { event = sonypi_pkeyev[i].event; @@ -338,9 +339,23 @@ goto found; } } + if ((v2 & SONYPI_BACK_EV) == SONYPI_BACK_EV) { + for (i = 0; sonypi_backev[i].event; i++) + if (sonypi_backev[i].data == v1) { + event = sonypi_backev[i].event; + goto found; + } + } + if ((v2 & SONYPI_LID_EV) == SONYPI_LID_EV) { + for (i = 0; sonypi_lidev[i].event; i++) + if (sonypi_lidev[i].data == v1) { + event = sonypi_lidev[i].event; + goto found; + } + } if (verbose) printk(KERN_WARNING - "sonypi: unknown event port1=0x%x,port2=0x%x\n",v1,v2); + "sonypi: unknown event port1=0x%02x,port2=0x%02x\n",v1,v2); return; found: @@ -564,15 +579,15 @@ goto out1; } - if (sonypi_device.model == SONYPI_DEVICE_MODEL_R505) { - ioport_list = sonypi_r505_ioport_list; - sonypi_device.region_size = SONYPI_R505_REGION_SIZE; - irq_list = sonypi_r505_irq_list; + if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) { + ioport_list = sonypi_type2_ioport_list; + sonypi_device.region_size = SONYPI_TYPE2_REGION_SIZE; + irq_list = sonypi_type2_irq_list; } else { - ioport_list = sonypi_normal_ioport_list; - sonypi_device.region_size = SONYPI_NORMAL_REGION_SIZE; - irq_list = sonypi_normal_irq_list; + ioport_list = sonypi_type1_ioport_list; + sonypi_device.region_size = SONYPI_TYPE1_REGION_SIZE; + irq_list = sonypi_type1_irq_list; } for (i = 0; ioport_list[i].port1; i++) { @@ -608,22 +623,28 @@ if (fnkeyinit) outb(0xf0, 0xb2); - if (sonypi_device.model == SONYPI_DEVICE_MODEL_R505) - sonypi_r505_srs(); + if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) + sonypi_type2_srs(); else - sonypi_normal_srs(); + sonypi_type1_srs(); sonypi_call1(0x82); sonypi_call2(0x81, 0xff); - sonypi_call1(0x92); + if (compat) + sonypi_call1(0x92); + else + sonypi_call1(0x82); printk(KERN_INFO "sonypi: Sony Programmable I/O Controller Driver v%d.%d.\n", SONYPI_DRIVER_MAJORVERSION, SONYPI_DRIVER_MINORVERSION); - printk(KERN_INFO "sonypi: detected %s model, camera = %s\n", - (sonypi_device.model == SONYPI_DEVICE_MODEL_NORMAL) ? - "normal" : "R505", - camera ? "on" : "off"); + printk(KERN_INFO "sonypi: detected %s model (%04x:%04x), " + "camera = %s, compat = %s\n", + (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE1) ? + "type1" : "type2", + sonypi_device.dev->vendor, sonypi_device.dev->device, + camera ? "on" : "off", + compat ? "on" : "off"); printk(KERN_INFO "sonypi: enabled at irq=%d, port1=0x%x, port2=0x%x\n", sonypi_device.irq, sonypi_device.ioport1, sonypi_device.ioport2); @@ -645,10 +666,10 @@ sonypi_call2(0x81, 0); /* make sure we don't get any more events */ if (camera) sonypi_camera_off(); - if (sonypi_device.model == SONYPI_DEVICE_MODEL_R505) - sonypi_r505_dis(); + if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) + sonypi_type2_dis(); else - sonypi_normal_dis(); + sonypi_type1_dis(); free_irq(sonypi_device.irq, sonypi_irq); release_region(sonypi_device.ioport1, sonypi_device.region_size); misc_deregister(&sonypi_misc_device); @@ -658,10 +679,13 @@ static struct pci_device_id sonypi_id_tbl[] __devinitdata = { { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - (unsigned long) SONYPI_DEVICE_MODEL_NORMAL }, + (unsigned long) SONYPI_DEVICE_MODEL_TYPE1 }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - (unsigned long) SONYPI_DEVICE_MODEL_R505 }, + (unsigned long) SONYPI_DEVICE_MODEL_TYPE2 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + (unsigned long) SONYPI_DEVICE_MODEL_TYPE2 }, { } }; @@ -685,6 +709,33 @@ pci_unregister_driver(&sonypi_driver); } +#ifndef MODULE +static int __init sonypi_setup(char *str) { + int ints[6]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + if (ints[0] <= 0) + goto out; + minor = ints[1]; + if (ints[0] == 1) + goto out; + verbose = ints[2]; + if (ints[0] == 2) + goto out; + fnkeyinit = ints[3]; + if (ints[0] == 3) + goto out; + camera = ints[4]; + if (ints[0] == 4) + goto out; + compat = ints[5]; +out: + return 1; +} + +__setup("sonypi=", sonypi_setup); +#endif /* !MODULE */ + /* Module entry points */ module_init(sonypi_init_module); module_exit(sonypi_cleanup_module); @@ -702,5 +753,7 @@ MODULE_PARM_DESC(fnkeyinit, "set this if your Fn keys do not generate any event"); MODULE_PARM(camera,"i"); MODULE_PARM_DESC(camera, "set this if you have a MotionEye camera (PictureBook series)"); +MODULE_PARM(compat,"i"); +MODULE_PARM_DESC(compat, "set this if you want to enable backward compatibility mode"); EXPORT_SYMBOL(sonypi_camera_command); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/sonypi.h linux.ac/drivers/char/sonypi.h --- linux.vanilla/drivers/char/sonypi.h Tue Sep 18 06:52:35 2001 +++ linux.ac/drivers/char/sonypi.h Fri Oct 12 12:45:20 2001 @@ -35,26 +35,26 @@ #ifdef __KERNEL__ #define SONYPI_DRIVER_MAJORVERSION 1 -#define SONYPI_DRIVER_MINORVERSION 5 +#define SONYPI_DRIVER_MINORVERSION 6 #include #include #include "linux/sonypi.h" -/* Normal models use those */ +/* type1 models use those */ #define SONYPI_IRQ_PORT 0x8034 #define SONYPI_IRQ_SHIFT 22 #define SONYPI_BASE 0x50 #define SONYPI_G10A (SONYPI_BASE+0x14) -#define SONYPI_NORMAL_REGION_SIZE 0x08 +#define SONYPI_TYPE1_REGION_SIZE 0x08 -/* R505 series specifics */ -#define SONYPI_SIRQ 0x9b -#define SONYPI_SLOB 0x9c -#define SONYPI_SHIB 0x9d -#define SONYPI_R505_REGION_SIZE 0x20 +/* type2 series specifics */ +#define SONYPI_SIRQ 0x9b +#define SONYPI_SLOB 0x9c +#define SONYPI_SHIB 0x9d +#define SONYPI_TYPE2_REGION_SIZE 0x20 -/* ioports used for brightness and R505 events */ +/* ioports used for brightness and type2 events */ #define SONYPI_DATA_IOPORT 0x62 #define SONYPI_CST_IOPORT 0x66 @@ -64,7 +64,7 @@ u16 port2; }; -static struct sonypi_ioport_list sonypi_normal_ioport_list[] = { +static struct sonypi_ioport_list sonypi_type1_ioport_list[] = { { 0x10c0, 0x10c4 }, /* looks like the default on C1Vx */ { 0x1080, 0x1084 }, { 0x1090, 0x1094 }, @@ -73,7 +73,7 @@ { 0x0, 0x0 } }; -static struct sonypi_ioport_list sonypi_r505_ioport_list[] = { +static struct sonypi_ioport_list sonypi_type2_ioport_list[] = { { 0x1080, 0x1084 }, { 0x10a0, 0x10a4 }, { 0x10c0, 0x10c4 }, @@ -87,14 +87,14 @@ u16 bits; }; -static struct sonypi_irq_list sonypi_normal_irq_list[] = { +static struct sonypi_irq_list sonypi_type1_irq_list[] = { { 11, 0x2 }, /* IRQ 11, GO22=0,GO23=1 in AML */ { 10, 0x1 }, /* IRQ 10, GO22=1,GO23=0 in AML */ { 5, 0x0 }, /* IRQ 5, GO22=0,GO23=0 in AML */ { 0, 0x3 } /* no IRQ, GO22=1,GO23=1 in AML */ }; -static struct sonypi_irq_list sonypi_r505_irq_list[] = { +static struct sonypi_irq_list sonypi_type2_irq_list[] = { { 11, 0x80 }, /* IRQ 11, 0x80 in SIRQ in AML */ { 10, 0x40 }, /* IRQ 10, 0x40 in SIRQ in AML */ { 9, 0x20 }, /* IRQ 9, 0x20 in SIRQ in AML */ @@ -132,13 +132,15 @@ #define SONYPI_CAMERA_ROMVERSION 9 /* key press event data (ioport2) */ -#define SONYPI_NORMAL_JOGGER_EV 0x10 -#define SONYPI_R505_JOGGER_EV 0x08 +#define SONYPI_TYPE1_JOGGER_EV 0x10 +#define SONYPI_TYPE2_JOGGER_EV 0x08 #define SONYPI_CAPTURE_EV 0x60 -#define SONYPI_NORMAL_FNKEY_EV 0x20 -#define SONYPI_R505_FNKEY_EV 0x08 +#define SONYPI_TYPE1_FNKEY_EV 0x20 +#define SONYPI_TYPE2_FNKEY_EV 0x08 #define SONYPI_BLUETOOTH_EV 0x30 -#define SONYPI_NORMAL_PKEY_EV 0x40 +#define SONYPI_TYPE1_PKEY_EV 0x40 +#define SONYPI_BACK_EV 0x08 +#define SONYPI_LID_EV 0x38 struct sonypi_event { u8 data; @@ -204,6 +206,19 @@ { 0x00, 0x00 } }; +/* The set of possible back button events */ +static struct sonypi_event sonypi_backev[] = { + { 0x20, SONYPI_EVENT_BACK_PRESSED }, + { 0x00, 0x00 } +}; + +/* The set of possible lid events */ +static struct sonypi_event sonypi_lidev[] = { + { 0x51, SONYPI_EVENT_LID_CLOSED }, + { 0x50, SONYPI_EVENT_LID_OPENED }, + { 0x00, 0x00 } +}; + #define SONYPI_BUF_SIZE 128 struct sonypi_queue { unsigned long head; @@ -215,8 +230,8 @@ unsigned char buf[SONYPI_BUF_SIZE]; }; -#define SONYPI_DEVICE_MODEL_NORMAL 1 -#define SONYPI_DEVICE_MODEL_R505 2 +#define SONYPI_DEVICE_MODEL_TYPE1 1 +#define SONYPI_DEVICE_MODEL_TYPE2 2 struct sonypi_device { struct pci_dev *dev; @@ -232,11 +247,11 @@ int model; }; -#define wait_on_command(command) { \ +#define wait_on_command(quiet, command) { \ unsigned int n = 10000; \ while (--n && (command)) \ udelay(1); \ - if (!n) \ + if (!n && (verbose || !quiet)) \ printk(KERN_WARNING "sonypi command failed at " __FILE__ " : " __FUNCTION__ "(line %d)\n", __LINE__); \ } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/sysrq.c linux.ac/drivers/char/sysrq.c --- linux.vanilla/drivers/char/sysrq.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/char/sysrq.c Wed Oct 10 01:47:42 2001 @@ -32,6 +32,7 @@ #include +extern void wakeup_bdflush(int); extern void reset_vc(unsigned int); extern struct list_head super_blocks; @@ -220,7 +221,7 @@ static void sysrq_handle_sync(int key, struct pt_regs *pt_regs, struct kbd_struct *kbd, struct tty_struct *tty) { emergency_sync_scheduled = EMERG_SYNC; - wakeup_bdflush(); + wakeup_bdflush(0); } static struct sysrq_key_op sysrq_sync_op = { handler: sysrq_handle_sync, @@ -231,12 +232,12 @@ static void sysrq_handle_mountro(int key, struct pt_regs *pt_regs, struct kbd_struct *kbd, struct tty_struct *tty) { emergency_sync_scheduled = EMERG_REMOUNT; - wakeup_bdflush(); + wakeup_bdflush(0); } static struct sysrq_key_op sysrq_mountro_op = { handler: sysrq_handle_mountro, help_msg: "Unmount", - action_msg: "Emergency Remount R/O", + action_msg: "Emergency Remount R/0", }; /* END SYNC SYSRQ HANDLERS BLOCK */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/toshiba.c linux.ac/drivers/char/toshiba.c --- linux.vanilla/drivers/char/toshiba.c Thu Sep 13 23:21:32 2001 +++ linux.ac/drivers/char/toshiba.c Wed Oct 10 01:47:42 2001 @@ -17,7 +17,9 @@ * 0xfc15: Tom May * 0xfc17: Dave Konrad * 0xfc1a: George Betzos + * 0xfc1b: Munemasa Wada * 0xfc1d: Arthur Liu + * 0xfc5a: Jacques L'helgoualc'h * 0xfcd1: Mr. Dave Konrad * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING @@ -51,7 +53,7 @@ * */ -#define TOSH_VERSION "1.9 22/3/2001" +#define TOSH_VERSION "1.11 26/9/2001" #define TOSH_DEBUG 0 #include @@ -264,7 +266,7 @@ if (!arg) return -EINVAL; - if(copy_from_user(®s, (SMMRegisters *) arg, sizeof(SMMRegisters))) + if (copy_from_user(®s, (SMMRegisters *) arg, sizeof(SMMRegisters))) return -EFAULT; switch (cmd) { @@ -288,7 +290,7 @@ return -EINVAL; } - if(copy_to_user((SMMRegisters *) arg, ®s, sizeof(SMMRegisters))) + if (copy_to_user((SMMRegisters *) arg, ®s, sizeof(SMMRegisters))) return -EFAULT; return (err==0) ? 0:-EINVAL; @@ -335,7 +337,8 @@ { switch (tosh_id) { case 0xfc02: case 0xfc04: case 0xfc09: case 0xfc0a: case 0xfc10: - case 0xfc11: case 0xfc13: case 0xfc15: case 0xfc1a: + case 0xfc11: case 0xfc13: case 0xfc15: case 0xfc1a: case 0xfc1b: + case 0xfc5a: tosh_fn = 0x62; break; case 0xfc08: case 0xfc17: case 0xfc1d: case 0xfcd1: case 0xfce0: @@ -412,8 +415,19 @@ */ int tosh_probe(void) { - int major,minor,day,year,month,flag; + int i,major,minor,day,year,month,flag; + unsigned char signature[7] = { 0x54,0x4f,0x53,0x48,0x49,0x42,0x41 }; SMMRegisters regs; + + /* extra sanity check for the string "TOSHIBA" in the BIOS because + some machines that are not Toshiba's pass the next test */ + + for (i=0;i<7;i++) { + if (isa_readb(0xfe010+i)!=signature[i]) { + printk("toshiba: not a supported Toshiba laptop\n"); + return -ENODEV; + } + } /* call the Toshiba SCI support check routine */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/tty_io.c linux.ac/drivers/char/tty_io.c --- linux.vanilla/drivers/char/tty_io.c Sat Sep 22 19:51:43 2001 +++ linux.ac/drivers/char/tty_io.c Wed Oct 10 01:47:42 2001 @@ -270,8 +270,6 @@ return 0; } -EXPORT_SYMBOL(tty_register_ldisc); - /* Set the discipline of a tty line. */ static int tty_set_ldisc(struct tty_struct *tty, int ldisc) { @@ -2057,8 +2055,64 @@ #endif /* CONFIG_DEVFS_FS */ } +/* + * Register a tty device described by , with minor number , + * device name and in the /dev directory given by . + */ +void tty_register_devfs_name (struct tty_driver *driver, unsigned int flags, + unsigned minor, devfs_handle_t dir, + const char *name) +{ +#ifdef CONFIG_DEVFS_FS + umode_t mode = S_IFCHR | S_IRUSR | S_IWUSR; + kdev_t device = MKDEV (driver->major, minor); + + switch (device) { + case TTY_DEV: + case PTMX_DEV: + mode |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + break; + default: + if (driver->major == PTY_MASTER_MAJOR) + flags |= DEVFS_FL_AUTO_OWNER; + break; + } + if ( (minor < driver->minor_start) || + (minor >= driver->minor_start + driver->num) ) { + printk(KERN_ERR "Attempt to register invalid minor number " + "with devfs (%d:%d).\n", (int)driver->major,(int)minor); + return; + } +# ifdef CONFIG_UNIX98_PTYS + if ( (driver->major >= UNIX98_PTY_SLAVE_MAJOR) && + (driver->major < UNIX98_PTY_SLAVE_MAJOR + UNIX98_NR_MAJORS) ) + flags |= DEVFS_FL_CURRENT_OWNER; +# endif + devfs_register (dir, name, flags | DEVFS_FL_DEFAULT, + driver->major, minor, mode, &tty_fops, NULL); +#endif /* CONFIG_DEVFS_FS */ +} + +void tty_unregister_devfs_name (struct tty_driver *driver, unsigned minor, + devfs_handle_t dir, const char *name) +{ +#ifdef CONFIG_DEVFS_FS + void * handle; + + handle = devfs_find_handle (dir, name, driver->major, minor, + DEVFS_SPECIAL_CHR, 0); + devfs_unregister (handle); +#endif /* CONFIG_DEVFS_FS */ +} + +extern void tty_unregister_devfs_name (struct tty_driver *driver, + unsigned minor, devfs_handle_t dir, + const char *name); EXPORT_SYMBOL(tty_register_devfs); EXPORT_SYMBOL(tty_unregister_devfs); +EXPORT_SYMBOL(tty_register_devfs_name); +EXPORT_SYMBOL(tty_unregister_devfs_name); +EXPORT_SYMBOL(tty_register_ldisc); /* * Called by a tty driver to register itself. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/vt.c linux.ac/drivers/char/vt.c --- linux.vanilla/drivers/char/vt.c Tue Sep 18 06:52:35 2001 +++ linux.ac/drivers/char/vt.c Wed Oct 10 01:47:42 2001 @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/fc4/soc.c linux.ac/drivers/fc4/soc.c --- linux.vanilla/drivers/fc4/soc.c Thu Sep 20 22:11:58 2001 +++ linux.ac/drivers/fc4/soc.c Wed Oct 10 01:47:42 2001 @@ -764,3 +764,4 @@ module_init(soc_probe); module_exit(soc_cleanup); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/fc4/socal.c linux.ac/drivers/fc4/socal.c --- linux.vanilla/drivers/fc4/socal.c Fri Sep 14 22:04:08 2001 +++ linux.ac/drivers/fc4/socal.c Wed Oct 10 01:47:42 2001 @@ -904,3 +904,4 @@ module_init(socal_probe); module_exit(socal_cleanup); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2c/Config.in linux.ac/drivers/i2c/Config.in --- linux.vanilla/drivers/i2c/Config.in Fri Sep 14 22:04:07 2001 +++ linux.ac/drivers/i2c/Config.in Wed Oct 10 01:47:42 2001 @@ -26,11 +26,24 @@ dep_tristate ' ITE I2C Adapter' CONFIG_ITE_I2C_ADAP $CONFIG_ITE_I2C_ALGO fi fi + if [ "$CONFIG_8xx" = "y" ]; then + dep_tristate 'MPC8xx CPM I2C interface' CONFIG_I2C_ALGO8XX $CONFIG_I2C + if [ "$CONFIG_RPXLITE" = "y" -o "$CONFIG_RPXCLASSIC" = "y" ]; then + dep_tristate ' Embedded Planet RPX Lite/Classic suppoort' CONFIG_I2C_RPXLITE $CONFIG_I2C_ALGO8XX + fi + fi + if [ "$CONFIG_405" = "y" ]; then + dep_tristate 'PPC 405 I2C Algorithm' CONFIG_I2C_PPC405_ALGO $CONFIG_I2C + if [ "$CONFIG_I2C_PPC405_ALGO" != "n" ]; then + dep_tristate ' PPC 405 I2C Adapter' CONFIG_I2C_PPC405_ADAP $CONFIG_I2C_PPC405_ALGO + fi + fi # This is needed for automatic patch generation: sensors code starts here # This is needed for automatic patch generation: sensors code ends here dep_tristate 'I2C device interface' CONFIG_I2C_CHARDEV $CONFIG_I2C + dep_tristate 'I2C /proc interface (required for hardware sensors)' CONFIG_I2C_PROC $CONFIG_I2C fi endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2c/Makefile linux.ac/drivers/i2c/Makefile --- linux.vanilla/drivers/i2c/Makefile Fri Dec 29 22:07:21 2000 +++ linux.ac/drivers/i2c/Makefile Wed Oct 10 01:47:42 2001 @@ -4,7 +4,8 @@ O_TARGET := i2c.o -export-objs := i2c-core.o i2c-algo-bit.o i2c-algo-pcf.o +export-objs := i2c-core.o i2c-algo-bit.o i2c-algo-pcf.o \ + i2c-algo-ite.o i2c-proc.o obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o @@ -14,6 +15,9 @@ obj-$(CONFIG_I2C_VELLEMAN) += i2c-velleman.o obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o +obj-$(CONFIG_ITE_I2C_ALGO) += i2c-algo-ite.o +obj-$(CONFIG_ITE_I2C_ADAP) += i2c-adap-ite.o +obj-$(CONFIG_I2C_PROC) += i2c-proc.o # This is needed for automatic patch generation: sensors code starts here # This is needed for automatic patch generation: sensors code ends here diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2c/i2c-adap-ite.c linux.ac/drivers/i2c/i2c-adap-ite.c --- linux.vanilla/drivers/i2c/i2c-adap-ite.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/i2c/i2c-adap-ite.c Wed Oct 10 01:47:44 2001 @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2c/i2c-algo-bit.c linux.ac/drivers/i2c/i2c-algo-bit.c --- linux.vanilla/drivers/i2c/i2c-algo-bit.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/i2c/i2c-algo-bit.c Wed Oct 10 01:47:44 2001 @@ -21,7 +21,7 @@ /* With some changes from Kyösti Mälkki and even Frodo Looijaard */ -/* $Id: i2c-algo-bit.c,v 1.27 2000/07/09 15:16:16 frodo Exp $ */ +/* $Id: i2c-algo-bit.c,v 1.30 2001/07/29 02:44:25 mds Exp $ */ #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2c/i2c-algo-ite.c linux.ac/drivers/i2c/i2c-algo-ite.c --- linux.vanilla/drivers/i2c/i2c-algo-ite.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/i2c/i2c-algo-ite.c Wed Oct 10 01:47:44 2001 @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2c/i2c-algo-pcf.c linux.ac/drivers/i2c/i2c-algo-pcf.c --- linux.vanilla/drivers/i2c/i2c-algo-pcf.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/i2c/i2c-algo-pcf.c Wed Oct 10 01:47:44 2001 @@ -1,4 +1,3 @@ - /* ------------------------------------------------------------------------- */ /* i2c-algo-pcf.c i2c driver algorithms for PCF8584 adapters */ /* ------------------------------------------------------------------------- */ @@ -24,7 +23,9 @@ Frodo Looijaard ,and also from Martin Bailey */ -/* $Id: i2c-algo-pcf.c,v 1.25 2000/11/10 13:43:32 frodo Exp $ */ +/* Partially rewriten by Oleg I. Vdovikin to handle multiple + messages, proper stop/repstart signaling during receive, + added detect code */ #include #include @@ -49,26 +50,9 @@ /* debug the protocol by showing transferred bits */ #define DEF_TIMEOUT 16 -/* debugging - slow down transfer to have a look at the data .. */ -/* I use this with two leds&resistors, each one connected to sda,scl */ -/* respectively. This makes sure that the algorithm works. Some chips */ -/* might not like this, as they have an internal timeout of some mils */ -/* -#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ - if (need_resched) schedule(); -*/ - - -/* ----- global variables --------------------------------------------- */ - -#ifdef SLO_IO - int jif; -#endif - /* module parameters: */ -static int i2c_debug=1; -static int pcf_test=0; /* see if the line-setting functions work */ +static int i2c_debug=0; static int pcf_scan=0; /* have a look at what's hanging 'round */ /* --- setting states on the bus with the right timing: --------------- */ @@ -80,7 +64,6 @@ #define i2c_outb(adap, val) adap->setpcf(adap->data, 0, val) #define i2c_inb(adap) adap->getpcf(adap->data, 0) - /* --- other auxiliary functions -------------------------------------- */ static void i2c_start(struct i2c_algo_pcf_data *adap) @@ -111,16 +94,15 @@ status = get_pcf(adap, 1); #ifndef STUB_I2C while (timeout-- && !(status & I2C_PCF_BB)) { - udelay(1000); /* How much is this? */ + udelay(100); /* wait for 100 us */ status = get_pcf(adap, 1); } #endif - if (timeout<=0) + if (timeout <= 0) { printk("Timeout waiting for Bus Busy\n"); - /* - set_pcf(adap, 1, I2C_PCF_STOP); - */ - return(timeout<=0); + } + + return (timeout<=0); } @@ -147,7 +129,6 @@ return(0); } - /* * This should perform the 'PCF8584 initialization sequence' as described * in the Philips IC12 data book (1995, Aug 29). @@ -156,111 +137,64 @@ * There should be a delay at the end equal to the longest I2C message * to synchronize the BB-bit (in multimaster systems). How long is * this? I assume 1 second is always long enough. + * + * vdovikin: added detect code for PCF8584 */ static int pcf_init_8584 (struct i2c_algo_pcf_data *adap) { + unsigned char temp; + + DEB3(printk("i2c-algo-pcf.o: PCF state 0x%02x\n", get_pcf(adap, 1))); - /* S1=0x80: S0 selected, serial interface off */ + /* S1=0x80: S0 selected, serial interface off */ set_pcf(adap, 1, I2C_PCF_PIN); + /* check to see S1 now used as R/W ctrl - + PCF8584 does that when ESO is zero */ + /* PCF also resets PIN bit */ + if ((temp = get_pcf(adap, 1)) != (0)) { + DEB2(printk("i2c-algo-pcf.o: PCF detection failed -- can't select S0 (0x%02x).\n", temp)); + return -ENXIO; /* definetly not PCF8584 */ + } /* load own address in S0, effective address is (own << 1) */ i2c_outb(adap, get_own(adap)); + /* check it's realy writen */ + if ((temp = i2c_inb(adap)) != get_own(adap)) { + DEB2(printk("i2c-algo-pcf.o: PCF detection failed -- can't set S0 (0x%02x).\n", temp)); + return -ENXIO; + } /* S1=0xA0, next byte in S2 */ set_pcf(adap, 1, I2C_PCF_PIN | I2C_PCF_ES1); + /* check to see S2 now selected */ + if ((temp = get_pcf(adap, 1)) != I2C_PCF_ES1) { + DEB2(printk("i2c-algo-pcf.o: PCF detection failed -- can't select S2 (0x%02x).\n", temp)); + return -ENXIO; + } /* load clock register S2 */ i2c_outb(adap, get_clock(adap)); + /* check it's realy writen, the only 5 lowest bits does matter */ + if (((temp = i2c_inb(adap)) & 0x1f) != get_clock(adap)) { + DEB2(printk("i2c-algo-pcf.o: PCF detection failed -- can't set S2 (0x%02x).\n", temp)); + return -ENXIO; + } /* Enable serial interface, idle, S0 selected */ set_pcf(adap, 1, I2C_PCF_IDLE); - DEB2(printk("i2c-algo-pcf.o: irq: Initialized 8584.\n")); - return 0; -} - - -/* - * Sanity check for the adapter hardware - check the reaction of - * the bus lines only if it seems to be idle. - */ -static int test_bus(struct i2c_algo_pcf_data *adap, char *name) { -#if 0 - int scl,sda; - sda=getsda(adap); - if (adap->getscl==NULL) { - printk("i2c-algo-pcf.o: Warning: Adapter can't read from clock line - skipping test.\n"); - return 0; - } - scl=getscl(adap); - printk("i2c-algo-pcf.o: Adapter: %s scl: %d sda: %d -- testing...\n", - name,getscl(adap),getsda(adap)); - if (!scl || !sda ) { - printk("i2c-algo-pcf.o: %s seems to be busy.\n",adap->name); - goto bailout; - } - sdalo(adap); - printk("i2c-algo-pcf.o:1 scl: %d sda: %d \n",getscl(adap), - getsda(adap)); - if ( 0 != getsda(adap) ) { - printk("i2c-algo-pcf.o: %s SDA stuck high!\n",name); - sdahi(adap); - goto bailout; - } - if ( 0 == getscl(adap) ) { - printk("i2c-algo-pcf.o: %s SCL unexpected low while pulling SDA low!\n", - name); - goto bailout; - } - sdahi(adap); - printk("i2c-algo-pcf.o:2 scl: %d sda: %d \n",getscl(adap), - getsda(adap)); - if ( 0 == getsda(adap) ) { - printk("i2c-algo-pcf.o: %s SDA stuck low!\n",name); - sdahi(adap); - goto bailout; - } - if ( 0 == getscl(adap) ) { - printk("i2c-algo-pcf.o: %s SCL unexpected low while SDA high!\n", - adap->name); - goto bailout; + /* check to see PCF is realy idled and we can access status register */ + if ((temp = get_pcf(adap, 1)) != (I2C_PCF_PIN | I2C_PCF_BB)) { + DEB2(printk("i2c-algo-pcf.o: PCF detection failed -- can't select S1` (0x%02x).\n", temp)); + return -ENXIO; } - scllo(adap); - printk("i2c-algo-pcf.o:3 scl: %d sda: %d \n",getscl(adap), - getsda(adap)); - if ( 0 != getscl(adap) ) { - printk("i2c-algo-pcf.o: %s SCL stuck high!\n",name); - sclhi(adap); - goto bailout; - } - if ( 0 == getsda(adap) ) { - printk("i2c-algo-pcf.o: %s SDA unexpected low while pulling SCL low!\n", - name); - goto bailout; - } - sclhi(adap); - printk("i2c-algo-pcf.o:4 scl: %d sda: %d \n",getscl(adap), - getsda(adap)); - if ( 0 == getscl(adap) ) { - printk("i2c-algo-pcf.o: %s SCL stuck low!\n",name); - sclhi(adap); - goto bailout; - } - if ( 0 == getsda(adap) ) { - printk("i2c-algo-pcf.o: %s SDA unexpected low while SCL high!\n", - name); - goto bailout; - } - printk("i2c-algo-pcf.o: %s passed test.\n",name); + + printk("i2c-algo-pcf.o: deteted and initialized PCF8584.\n"); + return 0; -bailout: - sdahi(adap); - sclhi(adap); - return -ENODEV; -#endif - return (0); } + /* ----- Utility functions */ @@ -287,8 +221,8 @@ } -static int pcf_sendbytes(struct i2c_adapter *i2c_adap,const char *buf, - int count) +static int pcf_sendbytes(struct i2c_adapter *i2c_adap, const char *buf, + int count, int last) { struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; int wrcount, status, timeout; @@ -313,58 +247,59 @@ } #endif } - i2c_stop(adap); + if (last) { + i2c_stop(adap); + } + else { + i2c_repstart(adap); + } + return (wrcount); } -static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count) +static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf, + int count, int last) { - int rdcount=0, i, status, timeout, dummy=1; + int i, status; struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; - - for (i=0; ialgo_data; struct i2c_msg *pmsg; - int i = 0; - int ret, timeout, status; - - pmsg = &msgs[i]; - - /* Send address here if Read */ - if (pmsg->flags & I2C_M_RD) { - ret = pcf_doAddress(adap, pmsg, i2c_adap->retries); - } + int i; + int ret=0, timeout, status; + /* Check for bus busy */ timeout = wait_for_bb(adap); if (timeout) { @@ -435,60 +364,68 @@ "Timeout waiting for BB in pcf_xfer\n");) return -EIO; } + + for (i = 0;ret >= 0 && i < num; i++) { + pmsg = &msgs[i]; + + DEB2(printk("i2c-algo-pcf.o: Doing %s %d bytes to 0x%02x - %d of %d messages\n", + pmsg->flags & I2C_M_RD ? "read" : "write", + pmsg->len, pmsg->addr, i + 1, num);) - /* Send address here if Write */ - if (!(pmsg->flags & I2C_M_RD)) { ret = pcf_doAddress(adap, pmsg, i2c_adap->retries); - } - /* Send START */ - i2c_start(adap); + + /* Send START */ + if (i == 0) { + i2c_start(adap); + } - /* Wait for PIN (pending interrupt NOT) */ - timeout = wait_for_pin(adap, &status); - if (timeout) { - i2c_stop(adap); - DEB2(printk("i2c-algo-pcf.o: Timeout waiting " - "for PIN(1) in pcf_xfer\n");) - return (-EREMOTEIO); - } + /* Wait for PIN (pending interrupt NOT) */ + timeout = wait_for_pin(adap, &status); + if (timeout) { + i2c_stop(adap); + DEB2(printk("i2c-algo-pcf.o: Timeout waiting " + "for PIN(1) in pcf_xfer\n");) + return (-EREMOTEIO); + } #ifndef STUB_I2C - /* Check LRB (last rcvd bit - slave ack) */ - if (status & I2C_PCF_LRB) { - i2c_stop(adap); - DEB2(printk("i2c-algo-pcf.o: No LRB(1) in pcf_xfer\n");) - return (-EREMOTEIO); - } + /* Check LRB (last rcvd bit - slave ack) */ + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + DEB2(printk("i2c-algo-pcf.o: No LRB(1) in pcf_xfer\n");) + return (-EREMOTEIO); + } #endif - DEB3(printk("i2c-algo-pcf.o: Msg %d, addr=0x%x, flags=0x%x, len=%d\n", - i, msgs[i].addr, msgs[i].flags, msgs[i].len);) + DEB3(printk("i2c-algo-pcf.o: Msg %d, addr=0x%x, flags=0x%x, len=%d\n", + i, msgs[i].addr, msgs[i].flags, msgs[i].len);) - /* Read */ - if (pmsg->flags & I2C_M_RD) { + /* Read */ + if (pmsg->flags & I2C_M_RD) { + /* read bytes into buffer*/ + ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len, + (i + 1 == num)); - /* read bytes into buffer*/ - ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len); - - if (ret != pmsg->len) { - DEB2(printk("i2c-algo-pcf.o: fail: " - "only read %d bytes.\n",ret)); - } else { - DEB2(printk("i2c-algo-pcf.o: read %d bytes.\n",ret)); - } - } else { /* Write */ - - /* Write bytes from buffer */ - ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len); + if (ret != pmsg->len) { + DEB2(printk("i2c-algo-pcf.o: fail: " + "only read %d bytes.\n",ret)); + } else { + DEB2(printk("i2c-algo-pcf.o: read %d bytes.\n",ret)); + } + } else { /* Write */ + ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len, + (i + 1 == num)); - if (ret != pmsg->len) { - DEB2(printk("i2c-algo-pcf.o: fail: " - "only wrote %d bytes.\n",ret)); - } else { - DEB2(printk("i2c-algo-pcf.o: wrote %d bytes.\n",ret)); + if (ret != pmsg->len) { + DEB2(printk("i2c-algo-pcf.o: fail: " + "only wrote %d bytes.\n",ret)); + } else { + DEB2(printk("i2c-algo-pcf.o: wrote %d bytes.\n",ret)); + } } } - return (num); + + return (i); } static int algo_control(struct i2c_adapter *adapter, @@ -524,12 +461,6 @@ int i, status; struct i2c_algo_pcf_data *pcf_adap = adap->algo_data; - if (pcf_test) { - int ret = test_bus(pcf_adap, adap->name); - if (ret<0) - return -ENODEV; - } - DEB2(printk("i2c-algo-pcf.o: hw routines for %s registered.\n", adap->name)); @@ -538,21 +469,29 @@ adap->id |= pcf_algo.id; adap->algo = &pcf_algo; - adap->timeout = 100; /* default values, should */ + adap->timeout = 100; /* default values, should */ adap->retries = 3; /* be replaced by defines */ + if ((i = pcf_init_8584(pcf_adap))) { + return i; + } + #ifdef MODULE MOD_INC_USE_COUNT; #endif i2c_add_adapter(adap); - pcf_init_8584(pcf_adap); /* scan bus */ if (pcf_scan) { printk(KERN_INFO " i2c-algo-pcf.o: scanning bus %s.\n", adap->name); for (i = 0x00; i < 0xff; i+=2) { + if (wait_for_bb(pcf_adap)) { + printk(KERN_INFO " i2c-algo-pcf.o: scanning bus %s - TIMEOUTed.\n", + adap->name); + break; + } i2c_outb(pcf_adap, i); i2c_start(pcf_adap); if ((wait_for_pin(pcf_adap, &status) >= 0) && @@ -598,11 +537,9 @@ MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm"); MODULE_LICENSE("GPL"); -MODULE_PARM(pcf_test, "i"); MODULE_PARM(pcf_scan, "i"); MODULE_PARM(i2c_debug,"i"); -MODULE_PARM_DESC(pcf_test, "Test if the I2C bus is available"); MODULE_PARM_DESC(pcf_scan, "Scan for active chips on the bus"); MODULE_PARM_DESC(i2c_debug, "debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2c/i2c-core.c linux.ac/drivers/i2c/i2c-core.c --- linux.vanilla/drivers/i2c/i2c-core.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/i2c/i2c-core.c Wed Oct 10 01:47:44 2001 @@ -20,7 +20,7 @@ /* With some changes from Kyösti Mälkki . All SMBus-related things are written by Frodo Looijaard */ -/* $Id: i2c-core.c,v 1.58 2000/10/29 22:57:38 frodo Exp $ */ +/* $Id: i2c-core.c,v 1.64 2001/08/13 01:35:56 mds Exp $ */ #include #include @@ -248,7 +248,7 @@ */ if ((res=client->driver->detach_client(client))) { printk("i2c-core.o: adapter %s not " - "unregisted, because client at " + "unregistered, because client at " "address %02x can't be detached. ", adap->name, client->addr); goto ERROR0; @@ -563,6 +563,7 @@ if(adapters[j]->clients[i]->flags & I2C_CLIENT_ALLOW_USE) return adapters[j]->clients[i]; } + i = 0; } return 0; @@ -658,7 +659,7 @@ int i,j,k,order_nr,len=0,len_total; int order[I2C_CLIENT_MAX]; - if (count < 0) + if (count > 4000) return -EINVAL; len_total = file->f_pos + count; /* Too bad if this gets longer (unlikely) */ @@ -1277,14 +1278,41 @@ } #ifndef MODULE +#ifdef CONFIG_I2C_CHARDEV extern int i2c_dev_init(void); +#endif +#ifdef CONFIG_I2C_ALGOBIT extern int i2c_algo_bit_init(void); +#endif +#ifdef CONFIG_I2C_CONFIG_I2C_PHILIPSPAR extern int i2c_bitlp_init(void); +#endif +#ifdef CONFIG_I2C_ELV extern int i2c_bitelv_init(void); +#endif +#ifdef CONFIG_I2C_VELLEMAN extern int i2c_bitvelle_init(void); +#endif +#ifdef CONFIG_I2C_BITVIA extern int i2c_bitvia_init(void); +#endif + +#ifdef CONFIG_I2C_ALGOPCF extern int i2c_algo_pcf_init(void); +#endif +#ifdef CONFIG_I2C_ELEKTOR extern int i2c_pcfisa_init(void); +#endif + +#ifdef CONFIG_I2C_ALGO8XX + extern int i2c_algo_8xx_init(void); +#endif +#ifdef CONFIG_I2C_RPXLITE + extern int i2c_rpx_init(void); +#endif +#ifdef CONFIG_I2C_PROC + extern int sensors_init(void); +#endif /* This is needed for automatic patch generation: sensors code starts here */ /* This is needed for automatic patch generation: sensors code ends here */ @@ -1317,6 +1345,19 @@ #endif #ifdef CONFIG_I2C_ELEKTOR i2c_pcfisa_init(); +#endif + + /* --------------------- 8xx -------- */ +#ifdef CONFIG_I2C_ALGO8XX + i2c_algo_8xx_init(); +#endif +#ifdef CONFIG_I2C_RPXLITE + i2c_rpx_init(); +#endif + + /* -------------- proc interface ---- */ +#ifdef CONFIG_I2C_PROC + sensors_init(); #endif /* This is needed for automatic patch generation: sensors code starts here */ /* This is needed for automatic patch generation: sensors code ends here */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2c/i2c-dev.c linux.ac/drivers/i2c/i2c-dev.c --- linux.vanilla/drivers/i2c/i2c-dev.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/i2c/i2c-dev.c Wed Oct 10 01:47:45 2001 @@ -28,7 +28,7 @@ /* The devfs code is contributed by Philipp Matthias Hahn */ -/* $Id: i2c-dev.c,v 1.36 2000/09/22 02:19:35 mds Exp $ */ +/* $Id: i2c-dev.c,v 1.40 2001/08/25 01:28:01 mds Exp $ */ #include #include @@ -60,6 +60,9 @@ /* struct file_operations changed too often in the 2.1 series for nice code */ +#if LINUX_KERNEL_VERSION < KERNEL_VERSION(2,4,9) +static loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin); +#endif static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, loff_t *offset); static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, @@ -88,7 +91,11 @@ #if LINUX_KERNEL_VERSION >= KERNEL_VERSION(2,4,0) owner: THIS_MODULE, #endif /* LINUX_KERNEL_VERSION >= KERNEL_VERSION(2,4,0) */ +#if LINUX_KERNEL_VERSION < KERNEL_VERSION(2,4,9) + llseek: i2cdev_lseek, +#else llseek: no_llseek, +#endif read: i2cdev_read, write: i2cdev_write, ioctl: i2cdev_ioctl, @@ -126,6 +133,20 @@ static int i2cdev_initialized; +#if LINUX_KERNEL_VERSION < KERNEL_VERSION(2,4,9) +/* Note that the lseek function is called llseek in 2.1 kernels. But things + are complicated enough as is. */ +loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin) +{ +#ifdef DEBUG + struct inode *inode = file->f_dentry->d_inode; + printk("i2c-dev.o: i2c-%d lseek to %ld bytes relative to %d.\n", + MINOR(inode->i_rdev),(long) offset,origin); +#endif /* DEBUG */ + return -ESPIPE; +} +#endif + static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, loff_t *offset) { @@ -227,9 +248,6 @@ sizeof(rdwr_arg))) return -EFAULT; - if(rdwr_arg.nmsgs > 2048) - return -EINVAL; - rdwr_pa = (struct i2c_msg *) kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL); @@ -505,7 +523,7 @@ "module not removed.\n"); return res; } - i2cdev_initialized ++; + i2cdev_initialized --; } if (i2cdev_initialized >= 1) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2c/i2c-elektor.c linux.ac/drivers/i2c/i2c-elektor.c --- linux.vanilla/drivers/i2c/i2c-elektor.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/i2c/i2c-elektor.c Wed Oct 10 01:47:45 2001 @@ -22,7 +22,8 @@ /* With some changes from Kyösti Mälkki and even Frodo Looijaard */ -/* $Id: i2c-elektor.c,v 1.19 2000/07/25 23:52:17 frodo Exp $ */ +/* Partialy rewriten by Oleg I. Vdovikin for mmapped support of + for Alpha Processor Inc. UP-2000(+) boards */ #include #include @@ -31,6 +32,7 @@ #include #include #include +#include #include #include @@ -39,17 +41,20 @@ #include #include "i2c-pcf8584.h" -#define DEFAULT_BASE 0x300 -#define DEFAULT_IRQ 0 -#define DEFAULT_CLOCK 0x1c -#define DEFAULT_OWN 0x55 - -static int base = 0; -static int irq = 0; -static int clock = 0; -static int own = 0; -static int i2c_debug=0; -static struct i2c_pcf_isa gpi; +#define DEFAULT_BASE 0x330 + +static int base = 0; +static int irq = 0; +static int clock = 0x1c; +static int own = 0x55; +static int mmapped = 0; +static int i2c_debug = 0; + +/* vdovikin: removed static struct i2c_pcf_isa gpi; code - + this module in real supports only one device, due to missing arguments + in some functions, called from the algo-pcf module. Sometimes it's + need to be rewriten - but for now just remove this for simpler reading */ + #if (LINUX_VERSION_CODE < 0x020301) static struct wait_queue *pcf_wait = NULL; #else @@ -63,81 +68,63 @@ #define DEB3(x) if (i2c_debug>=3) x #define DEBE(x) x /* error messages */ - -/* --- Convenience defines for the i2c port: */ -#define BASE ((struct i2c_pcf_isa *)(data))->pi_base -#define DATA BASE /* Adapter data port */ -#define CTRL (BASE+1) /* Adapter control port */ - /* ----- local functions ---------------------------------------------- */ static void pcf_isa_setbyte(void *data, int ctl, int val) { - unsigned long j = jiffies + 10; + int address = ctl ? (base + 1) : base; - if (ctl) { - if (gpi.pi_irq > 0) { - DEB3(printk("i2c-elektor.o: Write Ctrl 0x%02X\n", - val|I2C_PCF_ENI)); - DEB3({while (jiffies < j) schedule();}) - outb(val | I2C_PCF_ENI, CTRL); - } else { - DEB3(printk("i2c-elektor.o: Write Ctrl 0x%02X\n", val|I2C_PCF_ENI)); - DEB3({while (jiffies < j) schedule();}) - outb(val|I2C_PCF_ENI, CTRL); - } - } else { - DEB3(printk("i2c-elektor.o: Write Data 0x%02X\n", val&0xff)); - DEB3({while (jiffies < j) schedule();}) - outb(val, DATA); + if (ctl && irq) { + val |= I2C_PCF_ENI; + } + + DEB3(printk("i2c-elektor.o: Write 0x%X 0x%02X\n", address, val & 255)); + + switch (mmapped) { + case 0: /* regular I/O */ + outb(val, address); + break; + case 2: /* double mapped I/O needed for UP2000 board, + I don't know why this... */ + writeb(val, address); + /* fall */ + case 1: /* memory mapped I/O */ + writeb(val, address); + break; } } static int pcf_isa_getbyte(void *data, int ctl) { - int val; + int address = ctl ? (base + 1) : base; + int val = mmapped ? readb(address) : inb(address); + + DEB3(printk("i2c-elektor.o: Read 0x%X 0x%02X\n", address, val)); - if (ctl) { - val = inb(CTRL); - DEB3(printk("i2c-elektor.o: Read Ctrl 0x%02X\n", val)); - } else { - val = inb(DATA); - DEB3(printk("i2c-elektor.o: Read Data 0x%02X\n", val)); - } return (val); } static int pcf_isa_getown(void *data) { - return (gpi.pi_own); + return (own); } static int pcf_isa_getclock(void *data) { - return (gpi.pi_clock); -} - - - -#if 0 -static void pcf_isa_sleep(unsigned long timeout) -{ - schedule_timeout( timeout * HZ); + return (clock); } -#endif - static void pcf_isa_waitforpin(void) { int timeout = 2; - if (gpi.pi_irq > 0) { + if (irq > 0) { cli(); - if (pcf_pending == 0) { - interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ ); - } else - pcf_pending = 0; + if (pcf_pending == 0) { + interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ ); + } else + pcf_pending = 0; sti(); } else { udelay(100); @@ -153,30 +140,34 @@ static int pcf_isa_init(void) { - if (check_region(gpi.pi_base, 2) < 0 ) { - return -ENODEV; - } else { - request_region(gpi.pi_base, 2, "i2c (isa bus adapter)"); + if (!mmapped) { + if (check_region(base, 2) < 0 ) { + printk("i2c-elektor.o: requested I/O region (0x%X:2) is in use.\n", base); + return -ENODEV; + } else { + request_region(base, 2, "i2c (isa bus adapter)"); + } } - if (gpi.pi_irq > 0) { - if (request_irq(gpi.pi_irq, pcf_isa_handler, 0, "PCF8584", 0) - < 0) { - printk("i2c-elektor.o: Request irq%d failed\n", gpi.pi_irq); - gpi.pi_irq = 0; - } else - enable_irq(gpi.pi_irq); + if (irq > 0) { + if (request_irq(irq, pcf_isa_handler, 0, "PCF8584", 0) < 0) { + printk("i2c-elektor.o: Request irq%d failed\n", irq); + irq = 0; + } else + enable_irq(irq); } return 0; } -static void pcf_isa_exit(void) +static void __exit pcf_isa_exit(void) { - if (gpi.pi_irq > 0) { - disable_irq(gpi.pi_irq); - free_irq(gpi.pi_irq, 0); + if (irq > 0) { + disable_irq(irq); + free_irq(irq, 0); + } + if (!mmapped) { + release_region(base , 2); } - release_region(gpi.pi_base , 2); } @@ -217,7 +208,7 @@ pcf_isa_getown, pcf_isa_getclock, pcf_isa_waitforpin, - 80, 80, 100, /* waits, timeout */ + 10, 10, 100, /* waits, timeout */ }; static struct i2c_adapter pcf_isa_ops = { @@ -233,31 +224,61 @@ int __init i2c_pcfisa_init(void) { +#ifdef __alpha__ + /* check to see we have memory mapped PCF8584 connected to the + Cypress cy82c693 PCI-ISA bridge as on UP2000 board */ + if ((base == 0) && pci_present()) { + + struct pci_dev *cy693_dev = + pci_find_device(PCI_VENDOR_ID_CONTAQ, + PCI_DEVICE_ID_CONTAQ_82C693, NULL); + + if (cy693_dev) { + char config; + /* yeap, we've found cypress, let's check config */ + if (!pci_read_config_byte(cy693_dev, 0x47, &config)) { + + DEB3(printk("i2c-elektor.o: found cy82c693, config register 0x47 = 0x%02x.\n", config)); + + /* UP2000 board has this register set to 0xe1, + but the most significant bit as seems can be + reset during the proper initialisation + sequence if guys from API decides to do that + (so, we can even enable Tsunami Pchip + window for the upper 1 Gb) */ + + /* so just check for ROMCS at 0xe0000, + ROMCS enabled for writes + and external XD Bus buffer in use. */ + if ((config & 0x7f) == 0x61) { + /* seems to be UP2000 like board */ + base = 0xe0000; + /* I don't know why we need to + write twice */ + mmapped = 2; + /* UP2000 drives ISA with + 8.25 MHz (PCI/4) clock + (this can be read from cypress) */ + clock = I2C_PCF_CLK | I2C_PCF_TRNS90; + printk("i2c-elektor.o: found API UP2000 like board, will probe PCF8584 later.\n"); + } + } + } + } +#endif - struct i2c_pcf_isa *pisa = &gpi; + /* sanity checks for mmapped I/O */ + if (mmapped && base < 0xc8000) { + printk("i2c-elektor.o: incorrect base address (0x%0X) specified for mmapped I/O.\n", base); + return -ENODEV; + } printk("i2c-elektor.o: i2c pcf8584-isa adapter module\n"); - if (base == 0) - pisa->pi_base = DEFAULT_BASE; - else - pisa->pi_base = base; - - if (irq == 0) - pisa->pi_irq = DEFAULT_IRQ; - else - pisa->pi_irq = irq; - - if (clock == 0) - pisa->pi_clock = DEFAULT_CLOCK; - else - pisa->pi_clock = clock; - - if (own == 0) - pisa->pi_own = DEFAULT_OWN; - else - pisa->pi_own = own; - pcf_isa_data.data = (void *)pisa; + if (base == 0) { + base = DEFAULT_BASE; + } + #if (LINUX_VERSION_CODE >= 0x020301) init_waitqueue_head(&pcf_wait); #endif @@ -267,7 +288,9 @@ } else { return -ENODEV; } - printk("i2c-elektor.o: found device at %#x.\n", pisa->pi_base); + + printk("i2c-elektor.o: found device at %#x.\n", base); + return 0; } @@ -283,7 +306,8 @@ MODULE_PARM(irq, "i"); MODULE_PARM(clock, "i"); MODULE_PARM(own, "i"); -MODULE_PARM(i2c_debug,"i"); +MODULE_PARM(mmapped, "i"); +MODULE_PARM(i2c_debug, "i"); int init_module(void) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2c/i2c-elv.c linux.ac/drivers/i2c/i2c-elv.c --- linux.vanilla/drivers/i2c/i2c-elv.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/i2c/i2c-elv.c Wed Oct 10 01:47:45 2001 @@ -21,7 +21,7 @@ /* With some changes from Kyösti Mälkki and even Frodo Looijaard */ -/* $Id: i2c-elv.c,v 1.16 2000/01/18 23:54:07 frodo Exp $ */ +/* $Id: i2c-elv.c,v 1.17 2001/07/29 02:44:25 mds Exp $ */ #include #include @@ -115,7 +115,7 @@ return 0; } -static void bit_elv_exit(void) +static void __exit bit_elv_exit(void) { release_region( base , (base == 0x3bc)? 3 : 8 ); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2c/i2c-proc.c linux.ac/drivers/i2c/i2c-proc.c --- linux.vanilla/drivers/i2c/i2c-proc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/i2c/i2c-proc.c Wed Oct 10 01:47:45 2001 @@ -0,0 +1,906 @@ +/* + i2c-proc.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2001 Frodo Looijaard and + Mark D. Studebaker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + This driver puts entries in /proc/sys/dev/sensors for each I2C device +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* FIXME need i2c versioning */ +#define LM_DATE "20010825" +#define LM_VERSION "2.6.1" + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +static int i2c_create_name(char **name, const char *prefix, + struct i2c_adapter *adapter, int addr); +static int i2c_parse_reals(int *nrels, void *buffer, int bufsize, + long *results, int magnitude); +static int i2c_write_reals(int nrels, void *buffer, int *bufsize, + long *results, int magnitude); +static int i2c_proc_chips(ctl_table * ctl, int write, + struct file *filp, void *buffer, + size_t * lenp); +static int i2c_sysctl_chips(ctl_table * table, int *name, int nlen, + void *oldval, size_t * oldlenp, + void *newval, size_t newlen, + void **context); + +int __init sensors_init(void); + +#define SENSORS_ENTRY_MAX 20 +static struct ctl_table_header *i2c_entries[SENSORS_ENTRY_MAX]; + +static struct i2c_client *i2c_clients[SENSORS_ENTRY_MAX]; +static unsigned short i2c_inodes[SENSORS_ENTRY_MAX]; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) +static void i2c_fill_inode(struct inode *inode, int fill); +static void i2c_dir_fill_inode(struct inode *inode, int fill); +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) */ + +static ctl_table sysctl_table[] = { + {CTL_DEV, "dev", NULL, 0, 0555}, + {0}, + {DEV_SENSORS, "sensors", NULL, 0, 0555}, + {0}, + {0, NULL, NULL, 0, 0555}, + {0} +}; + +static ctl_table i2c_proc_dev_sensors[] = { + {SENSORS_CHIPS, "chips", NULL, 0, 0644, NULL, &i2c_proc_chips, + &i2c_sysctl_chips}, + {0} +}; + +static ctl_table i2c_proc_dev[] = { + {DEV_SENSORS, "sensors", NULL, 0, 0555, i2c_proc_dev_sensors}, + {0}, +}; + + +static ctl_table i2c_proc[] = { + {CTL_DEV, "dev", NULL, 0, 0555, i2c_proc_dev}, + {0} +}; + + +static struct ctl_table_header *i2c_proc_header; +static int i2c_initialized; + +/* This returns a nice name for a new directory; for example lm78-isa-0310 + (for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for + a LM75 chip on the third i2c bus at address 0x4e). + name is allocated first. */ +int i2c_create_name(char **name, const char *prefix, + struct i2c_adapter *adapter, int addr) +{ + char name_buffer[50]; + int id; + if (i2c_is_isa_adapter(adapter)) + sprintf(name_buffer, "%s-isa-%04x", prefix, addr); + else { + if ((id = i2c_adapter_id(adapter)) < 0) + return -ENOENT; + sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr); + } + *name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL); + strcpy(*name, name_buffer); + return 0; +} + +/* This rather complex function must be called when you want to add an entry + to /proc/sys/dev/sensors/chips. It also creates a new directory within + /proc/sys/dev/sensors/. + ctl_template should be a template of the newly created directory. It is + copied in memory. The extra2 field of each file is set to point to client. + If any driver wants subdirectories within the newly created directory, + this function must be updated! + controlling_mod is the controlling module. It should usually be + THIS_MODULE when calling. Note that this symbol is not defined in + kernels before 2.3.13; define it to NULL in that case. We will not use it + for anything older than 2.3.27 anyway. */ +int i2c_register_entry(struct i2c_client *client, const char *prefix, + ctl_table * ctl_template, + struct module *controlling_mod) +{ + int i, res, len, id; + ctl_table *new_table; + char *name; + struct ctl_table_header *new_header; + + if ((res = i2c_create_name(&name, prefix, client->adapter, + client->addr))) return res; + + for (id = 0; id < SENSORS_ENTRY_MAX; id++) + if (!i2c_entries[id]) { + break; + } + if (id == SENSORS_ENTRY_MAX) { + kfree(name); + return -ENOMEM; + } + id += 256; + + len = 0; + while (ctl_template[len].procname) + len++; + len += 7; + if (!(new_table = kmalloc(sizeof(ctl_table) * len, GFP_KERNEL))) { + kfree(name); + return -ENOMEM; + } + + memcpy(new_table, sysctl_table, 6 * sizeof(ctl_table)); + new_table[0].child = &new_table[2]; + new_table[2].child = &new_table[4]; + new_table[4].child = &new_table[6]; + new_table[4].procname = name; + new_table[4].ctl_name = id; + memcpy(new_table + 6, ctl_template, (len - 6) * sizeof(ctl_table)); + for (i = 6; i < len; i++) + new_table[i].extra2 = client; + + if (!(new_header = register_sysctl_table(new_table, 0))) { + kfree(new_table); + kfree(name); + return -ENOMEM; + } + + i2c_entries[id - 256] = new_header; + + i2c_clients[id - 256] = client; +#ifdef DEBUG + if (!new_header || !new_header->ctl_table || + !new_header->ctl_table->child || + !new_header->ctl_table->child->child || + !new_header->ctl_table->child->child->de) { + printk + ("i2c-proc.o: NULL pointer when trying to install fill_inode fix!\n"); + return id; + } +#endif /* DEBUG */ + i2c_inodes[id - 256] = + new_header->ctl_table->child->child->de->low_ino; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) + new_header->ctl_table->child->child->de->owner = controlling_mod; +#else + new_header->ctl_table->child->child->de->fill_inode = + &i2c_dir_fill_inode; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) */ + + return id; +} + +void i2c_deregister_entry(int id) +{ + ctl_table *table; + char *temp; + id -= 256; + if (i2c_entries[id]) { + table = i2c_entries[id]->ctl_table; + unregister_sysctl_table(i2c_entries[id]); + /* 2-step kfree needed to keep gcc happy about const points */ + (const char *) temp = table[4].procname; + kfree(temp); + kfree(table); + i2c_entries[id] = NULL; + i2c_clients[id] = NULL; + } +} + +/* Monitor access for /proc/sys/dev/sensors; make unloading i2c-proc.o + impossible if some process still uses it or some file in it */ +void i2c_fill_inode(struct inode *inode, int fill) +{ + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +} + +/* Monitor access for /proc/sys/dev/sensors/ directories; make unloading + the corresponding module impossible if some process still uses it or + some file in it */ +void i2c_dir_fill_inode(struct inode *inode, int fill) +{ + int i; + struct i2c_client *client; + +#ifdef DEBUG + if (!inode) { + printk("i2c-proc.o: Warning: inode NULL in fill_inode()\n"); + return; + } +#endif /* def DEBUG */ + + for (i = 0; i < SENSORS_ENTRY_MAX; i++) + if (i2c_clients[i] + && (i2c_inodes[i] == inode->i_ino)) break; +#ifdef DEBUG + if (i == SENSORS_ENTRY_MAX) { + printk + ("i2c-proc.o: Warning: inode (%ld) not found in fill_inode()\n", + inode->i_ino); + return; + } +#endif /* def DEBUG */ + client = i2c_clients[i]; + if (fill) + client->driver->inc_use(client); + else + client->driver->dec_use(client); +} + +int i2c_proc_chips(ctl_table * ctl, int write, struct file *filp, + void *buffer, size_t * lenp) +{ + char BUF[SENSORS_PREFIX_MAX + 30]; + int buflen, curbufsize, i; + struct ctl_table *client_tbl; + + if (write) + return 0; + + /* If buffer is size 0, or we try to read when not at the start, we + return nothing. Note that I think writing when not at the start + does not work either, but anyway, this is straight from the kernel + sources. */ + if (!*lenp || (filp->f_pos && !write)) { + *lenp = 0; + return 0; + } + curbufsize = 0; + for (i = 0; i < SENSORS_ENTRY_MAX; i++) + if (i2c_entries[i]) { + client_tbl = + i2c_entries[i]->ctl_table->child->child; + buflen = + sprintf(BUF, "%d\t%s\n", client_tbl->ctl_name, + client_tbl->procname); + if (buflen + curbufsize > *lenp) + buflen = *lenp - curbufsize; + if(copy_to_user(buffer, BUF, buflen)) + return -EFAULT; + curbufsize += buflen; + (char *) buffer += buflen; + } + *lenp = curbufsize; + filp->f_pos += curbufsize; + return 0; +} + +int i2c_sysctl_chips(ctl_table * table, int *name, int nlen, + void *oldval, size_t * oldlenp, void *newval, + size_t newlen, void **context) +{ + struct i2c_chips_data data; + int i, oldlen, nrels, maxels,ret=0; + struct ctl_table *client_tbl; + + if (oldval && oldlenp && !((ret = get_user(oldlen, oldlenp))) && + oldlen) { + maxels = oldlen / sizeof(struct i2c_chips_data); + nrels = 0; + for (i = 0; (i < SENSORS_ENTRY_MAX) && (nrels < maxels); + i++) + if (i2c_entries[i]) { + client_tbl = + i2c_entries[i]->ctl_table->child-> + child; + data.sysctl_id = client_tbl->ctl_name; + strcpy(data.name, client_tbl->procname); + if(copy_to_user(oldval, &data, + sizeof(struct + i2c_chips_data))) + return -EFAULT; + (char *) oldval += + sizeof(struct i2c_chips_data); + nrels++; + } + oldlen = nrels * sizeof(struct i2c_chips_data); + if(put_user(oldlen, oldlenp)) + return -EFAULT; + } + return ret; +} + + +/* This funcion reads or writes a 'real' value (encoded by the combination + of an integer and a magnitude, the last is the power of ten the value + should be divided with) to a /proc/sys directory. To use this function, + you must (before registering the ctl_table) set the extra2 field to the + client, and the extra1 field to a function of the form: + void func(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) + This function can be called for three values of operation. If operation + equals SENSORS_PROC_REAL_INFO, the magnitude should be returned in + nrels_mag. If operation equals SENSORS_PROC_REAL_READ, values should + be read into results. nrels_mag should return the number of elements + read; the maximum number is put in it on entry. Finally, if operation + equals SENSORS_PROC_REAL_WRITE, the values in results should be + written to the chip. nrels_mag contains on entry the number of elements + found. + In all cases, client points to the client we wish to interact with, + and ctl_name is the SYSCTL id of the file we are accessing. */ +int i2c_proc_real(ctl_table * ctl, int write, struct file *filp, + void *buffer, size_t * lenp) +{ +#define MAX_RESULTS 32 + int mag, nrels = MAX_RESULTS; + long results[MAX_RESULTS]; + i2c_real_callback callback = ctl->extra1; + struct i2c_client *client = ctl->extra2; + int res; + + /* If buffer is size 0, or we try to read when not at the start, we + return nothing. Note that I think writing when not at the start + does not work either, but anyway, this is straight from the kernel + sources. */ + if (!*lenp || (filp->f_pos && !write)) { + *lenp = 0; + return 0; + } + + /* Get the magnitude */ + callback(client, SENSORS_PROC_REAL_INFO, ctl->ctl_name, &mag, + NULL); + + if (write) { + /* Read the complete input into results, converting to longs */ + res = i2c_parse_reals(&nrels, buffer, *lenp, results, mag); + if (res) + return res; + + if (!nrels) + return 0; + + /* Now feed this information back to the client */ + callback(client, SENSORS_PROC_REAL_WRITE, ctl->ctl_name, + &nrels, results); + + filp->f_pos += *lenp; + return 0; + } else { /* read */ + /* Get the information from the client into results */ + callback(client, SENSORS_PROC_REAL_READ, ctl->ctl_name, + &nrels, results); + + /* And write them to buffer, converting to reals */ + res = i2c_write_reals(nrels, buffer, lenp, results, mag); + if (res) + return res; + filp->f_pos += *lenp; + return 0; + } +} + +/* This function is equivalent to i2c_proc_real, only it interacts with + the sysctl(2) syscall, and returns no reals, but integers */ +int i2c_sysctl_real(ctl_table * table, int *name, int nlen, + void *oldval, size_t * oldlenp, void *newval, + size_t newlen, void **context) +{ + long results[MAX_RESULTS]; + int oldlen, nrels = MAX_RESULTS,ret=0; + i2c_real_callback callback = table->extra1; + struct i2c_client *client = table->extra2; + + /* Check if we need to output the old values */ + if (oldval && oldlenp && !((ret=get_user(oldlen, oldlenp))) && oldlen) { + callback(client, SENSORS_PROC_REAL_READ, table->ctl_name, + &nrels, results); + + /* Note the rounding factor! */ + if (nrels * sizeof(long) < oldlen) + oldlen = nrels * sizeof(long); + oldlen = (oldlen / sizeof(long)) * sizeof(long); + if(copy_to_user(oldval, results, oldlen)) + return -EFAULT; + if(put_user(oldlen, oldlenp)) + return -EFAULT; + } + + if (newval && newlen) { + /* Note the rounding factor! */ + newlen -= newlen % sizeof(long); + nrels = newlen / sizeof(long); + if(copy_from_user(results, newval, newlen)) + return -EFAULT; + + /* Get the new values back to the client */ + callback(client, SENSORS_PROC_REAL_WRITE, table->ctl_name, + &nrels, results); + } + return ret; +} + + +/* nrels contains initially the maximum number of elements which can be + put in results, and finally the number of elements actually put there. + A magnitude of 1 will multiply everything with 10; etc. + buffer, bufsize is the character buffer we read from and its length. + results will finally contain the parsed integers. + + Buffer should contain several reals, separated by whitespace. A real + has the following syntax: + [ Minus ] Digit* [ Dot Digit* ] + (everything between [] is optional; * means zero or more). + When the next character is unparsable, everything is skipped until the + next whitespace. + + WARNING! This is tricky code. I have tested it, but there may still be + hidden bugs in it, even leading to crashes and things! +*/ +int i2c_parse_reals(int *nrels, void *buffer, int bufsize, + long *results, int magnitude) +{ + int maxels, min, mag; + long res,ret=0; + char nextchar = 0; + + maxels = *nrels; + *nrels = 0; + + while (bufsize && (*nrels < maxels)) { + + /* Skip spaces at the start */ + while (bufsize && + !((ret=get_user(nextchar, (char *) buffer))) && + isspace((int) nextchar)) { + bufsize--; + ((char *) buffer)++; + } + + if (ret) + return -EFAULT; + /* Well, we may be done now */ + if (!bufsize) + return 0; + + /* New defaults for our result */ + min = 0; + res = 0; + mag = magnitude; + + /* Check for a minus */ + if (!((ret=get_user(nextchar, (char *) buffer))) + && (nextchar == '-')) { + min = 1; + bufsize--; + ((char *) buffer)++; + } + if (ret) + return -EFAULT; + + /* Digits before a decimal dot */ + while (bufsize && + !((ret=get_user(nextchar, (char *) buffer))) && + isdigit((int) nextchar)) { + res = res * 10 + nextchar - '0'; + bufsize--; + ((char *) buffer)++; + } + if (ret) + return -EFAULT; + + /* If mag < 0, we must actually divide here! */ + while (mag < 0) { + res = res / 10; + mag++; + } + + if (bufsize && (nextchar == '.')) { + /* Skip the dot */ + bufsize--; + ((char *) buffer)++; + + /* Read digits while they are significant */ + while (bufsize && (mag > 0) && + !((ret=get_user(nextchar, (char *) buffer))) && + isdigit((int) nextchar)) { + res = res * 10 + nextchar - '0'; + mag--; + bufsize--; + ((char *) buffer)++; + } + if (ret) + return -EFAULT; + } + /* If we are out of data, but mag > 0, we need to scale here */ + while (mag > 0) { + res = res * 10; + mag--; + } + + /* Skip everything until we hit whitespace */ + while (bufsize && + !((ret=get_user(nextchar, (char *) buffer))) && + isspace((int) nextchar)) { + bufsize--; + ((char *) buffer)++; + } + if (ret) + return -EFAULT; + + /* Put res in results */ + results[*nrels] = (min ? -1 : 1) * res; + (*nrels)++; + } + + /* Well, there may be more in the buffer, but we need no more data. + Ignore anything that is left. */ + return 0; +} + +int i2c_write_reals(int nrels, void *buffer, int *bufsize, + long *results, int magnitude) +{ +#define BUFLEN 20 + char BUF[BUFLEN + 1]; /* An individual representation should fit! */ + char printfstr[10]; + int nr = 0; + int buflen, mag, times; + int curbufsize = 0; + + while ((nr < nrels) && (curbufsize < *bufsize)) { + mag = magnitude; + + if (nr != 0) { + if(put_user(' ', (char *) buffer)) + return -EFAULT; + curbufsize++; + ((char *) buffer)++; + } + + /* Fill BUF with the representation of the next string */ + if (mag <= 0) { + buflen = sprintf(BUF, "%ld", results[nr]); + if (buflen < 0) { /* Oops, a sprintf error! */ + *bufsize = 0; + return -EINVAL; + } + while ((mag < 0) && (buflen < BUFLEN)) { + BUF[buflen++] = '0'; + mag++; + } + BUF[buflen] = 0; + } else { + times = 1; + for (times = 1; mag-- > 0; times *= 10); + if (results[nr] < 0) { + BUF[0] = '-'; + buflen = 1; + } else + buflen = 0; + strcpy(printfstr, "%ld.%0Xld"); + printfstr[6] = magnitude + '0'; + buflen += + sprintf(BUF + buflen, printfstr, + abs(results[nr]) / times, + abs(results[nr]) % times); + if (buflen < 0) { /* Oops, a sprintf error! */ + *bufsize = 0; + return -EINVAL; + } + } + + /* Now copy it to the user-space buffer */ + if (buflen + curbufsize > *bufsize) + buflen = *bufsize - curbufsize; + if(copy_to_user(buffer, BUF, buflen)) + return -EFAULT; + curbufsize += buflen; + (char *) buffer += buflen; + + nr++; + } + if (curbufsize < *bufsize) { + if(put_user('\n', (char *) buffer)) + return -EFAULT; + curbufsize++; + } + *bufsize = curbufsize; + return 0; +} + + +/* Very inefficient for ISA detects, and won't work for 10-bit addresses! */ +int i2c_detect(struct i2c_adapter *adapter, + struct i2c_address_data *address_data, + i2c_found_addr_proc * found_proc) +{ + int addr, i, found, j, err; + struct i2c_force_data *this_force; + int is_isa = i2c_is_isa_adapter(adapter); + int adapter_id = + is_isa ? SENSORS_ISA_BUS : i2c_adapter_id(adapter); + + /* Forget it if we can't probe using SMBUS_QUICK */ + if ((!is_isa) + && !i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_QUICK)) return -1; + + for (addr = 0x00; addr <= (is_isa ? 0xffff : 0x7f); addr++) { + if ((is_isa && check_region(addr, 1)) || + (!is_isa && i2c_check_addr(adapter, addr))) + continue; + + /* If it is in one of the force entries, we don't do any + detection at all */ + found = 0; + for (i = 0; + !found + && (this_force = + address_data->forces + i, this_force->force); i++) { + for (j = 0; + !found + && (this_force->force[j] != SENSORS_I2C_END); + j += 2) { + if ( + ((adapter_id == this_force->force[j]) + || + ((this_force-> + force[j] == SENSORS_ANY_I2C_BUS) + && !is_isa)) + && (addr == this_force->force[j + 1])) { +#ifdef DEBUG + printk + ("i2c-proc.o: found force parameter for adapter %d, addr %04x\n", + adapter_id, addr); +#endif + if ( + (err = + found_proc(adapter, addr, 0, + this_force-> + kind))) return err; + found = 1; + } + } + } + if (found) + continue; + + /* If this address is in one of the ignores, we can forget about it + right now */ + for (i = 0; + !found + && (address_data->ignore[i] != SENSORS_I2C_END); + i += 2) { + if ( + ((adapter_id == address_data->ignore[i]) + || + ((address_data-> + ignore[i] == SENSORS_ANY_I2C_BUS) + && !is_isa)) + && (addr == address_data->ignore[i + 1])) { +#ifdef DEBUG + printk + ("i2c-proc.o: found ignore parameter for adapter %d, " + "addr %04x\n", adapter_id, addr); +#endif + found = 1; + } + } + for (i = 0; + !found + && (address_data->ignore_range[i] != SENSORS_I2C_END); + i += 3) { + if ( + ((adapter_id == address_data->ignore_range[i]) + || + ((address_data-> + ignore_range[i] == + SENSORS_ANY_I2C_BUS) & !is_isa)) + && (addr >= address_data->ignore_range[i + 1]) + && (addr <= address_data->ignore_range[i + 2])) { +#ifdef DEBUG + printk + ("i2c-proc.o: found ignore_range parameter for adapter %d, " + "addr %04x\n", adapter_id, addr); +#endif + found = 1; + } + } + if (found) + continue; + + /* Now, we will do a detection, but only if it is in the normal or + probe entries */ + if (is_isa) { + for (i = 0; + !found + && (address_data->normal_isa[i] != + SENSORS_ISA_END); i += 1) { + if (addr == address_data->normal_isa[i]) { +#ifdef DEBUG + printk + ("i2c-proc.o: found normal isa entry for adapter %d, " + "addr %04x\n", adapter_id, + addr); +#endif + found = 1; + } + } + for (i = 0; + !found + && (address_data->normal_isa_range[i] != + SENSORS_ISA_END); i += 3) { + if ((addr >= + address_data->normal_isa_range[i]) + && (addr <= + address_data->normal_isa_range[i + 1]) + && + ((addr - + address_data->normal_isa_range[i]) % + address_data->normal_isa_range[i + 2] == + 0)) { +#ifdef DEBUG + printk + ("i2c-proc.o: found normal isa_range entry for adapter %d, " + "addr %04x", adapter_id, addr); +#endif + found = 1; + } + } + } else { + for (i = 0; + !found && (address_data->normal_i2c[i] != + SENSORS_I2C_END); i += 1) { + if (addr == address_data->normal_i2c[i]) { + found = 1; +#ifdef DEBUG + printk + ("i2c-proc.o: found normal i2c entry for adapter %d, " + "addr %02x", adapter_id, addr); +#endif + } + } + for (i = 0; + !found + && (address_data->normal_i2c_range[i] != + SENSORS_I2C_END); i += 2) { + if ((addr >= + address_data->normal_i2c_range[i]) + && (addr <= + address_data->normal_i2c_range[i + 1])) + { +#ifdef DEBUG + printk + ("i2c-proc.o: found normal i2c_range entry for adapter %d, " + "addr %04x\n", adapter_id, addr); +#endif + found = 1; + } + } + } + + for (i = 0; + !found && (address_data->probe[i] != SENSORS_I2C_END); + i += 2) { + if (((adapter_id == address_data->probe[i]) || + ((address_data-> + probe[i] == SENSORS_ANY_I2C_BUS) & !is_isa)) + && (addr == address_data->probe[i + 1])) { +#ifdef DEBUG + printk + ("i2c-proc.o: found probe parameter for adapter %d, " + "addr %04x\n", adapter_id, addr); +#endif + found = 1; + } + } + for (i = 0; !found && + (address_data->probe_range[i] != SENSORS_I2C_END); + i += 3) { + if ( + ((adapter_id == address_data->probe_range[i]) + || + ((address_data->probe_range[i] == + SENSORS_ANY_I2C_BUS) & !is_isa)) + && (addr >= address_data->probe_range[i + 1]) + && (addr <= address_data->probe_range[i + 2])) { + found = 1; +#ifdef DEBUG + printk + ("i2c-proc.o: found probe_range parameter for adapter %d, " + "addr %04x\n", adapter_id, addr); +#endif + } + } + if (!found) + continue; + + /* OK, so we really should examine this address. First check + whether there is some client here at all! */ + if (is_isa || + (i2c_smbus_xfer + (adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) >= 0)) + if ((err = found_proc(adapter, addr, 0, -1))) + return err; + } + return 0; +} + +int __init sensors_init(void) +{ + printk("i2c-proc.o version %s (%s)\n", LM_VERSION, LM_DATE); + i2c_initialized = 0; + if (! + (i2c_proc_header = + register_sysctl_table(i2c_proc, 0))) return -ENOMEM; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1)) + i2c_proc_header->ctl_table->child->de->owner = THIS_MODULE; +#else + i2c_proc_header->ctl_table->child->de->fill_inode = + &i2c_fill_inode; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1)) */ + i2c_initialized++; + return 0; +} + +EXPORT_SYMBOL(i2c_deregister_entry); +EXPORT_SYMBOL(i2c_detect); +EXPORT_SYMBOL(i2c_proc_real); +EXPORT_SYMBOL(i2c_register_entry); +EXPORT_SYMBOL(i2c_sysctl_real); + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("i2c-proc driver"); +MODULE_LICENSE("GPL"); + +int i2c_cleanup(void) +{ + if (i2c_initialized >= 1) { + unregister_sysctl_table(i2c_proc_header); + i2c_initialized--; + } + return 0; +} + +int init_module(void) +{ + return sensors_init(); +} + +int cleanup_module(void) +{ + return i2c_cleanup(); +} +#endif /* MODULE */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2c/i2c-velleman.c linux.ac/drivers/i2c/i2c-velleman.c --- linux.vanilla/drivers/i2c/i2c-velleman.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/i2c/i2c-velleman.c Wed Oct 10 01:47:45 2001 @@ -103,7 +103,7 @@ return 0; } -static void bit_velle_exit(void) +static void __exit bit_velle_exit(void) { release_region( base , (base == 0x3bc)? 3 : 8 ); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/Config.in linux.ac/drivers/i2o/Config.in --- linux.vanilla/drivers/i2o/Config.in Wed Apr 12 17:38:53 2000 +++ linux.ac/drivers/i2o/Config.in Thu Jan 1 01:00:00 1970 @@ -1,16 +0,0 @@ -mainmenu_option next_comment -comment 'I2O device support' - -tristate 'I2O support' CONFIG_I2O - -if [ "$CONFIG_PCI" = "y" ]; then - dep_tristate ' I2O PCI support' CONFIG_I2O_PCI $CONFIG_I2O -fi -dep_tristate ' I2O Block OSM' CONFIG_I2O_BLOCK $CONFIG_I2O -if [ "$CONFIG_NET" = "y" ]; then - dep_tristate ' I2O LAN OSM' CONFIG_I2O_LAN $CONFIG_I2O -fi -dep_tristate ' I2O SCSI OSM' CONFIG_I2O_SCSI $CONFIG_I2O $CONFIG_SCSI -dep_tristate ' I2O /proc support' CONFIG_I2O_PROC $CONFIG_I2O - -endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/Makefile linux.ac/drivers/i2o/Makefile --- linux.vanilla/drivers/i2o/Makefile Fri Dec 29 22:07:21 2000 +++ linux.ac/drivers/i2o/Makefile Thu Jan 1 01:00:00 1970 @@ -1,20 +0,0 @@ -# -# Makefile for the kernel I2O OSM. -# -# Note : at this point, these files are compiled on all systems. -# In the future, some of these should be built conditionally. -# - -O_TARGET := i2o.o - -export-objs := i2o_pci.o i2o_core.o i2o_config.o i2o_block.o i2o_lan.o i2o_scsi.o i2o_proc.o - -obj-$(CONFIG_I2O_PCI) += i2o_pci.o -obj-$(CONFIG_I2O) += i2o_core.o i2o_config.o -obj-$(CONFIG_I2O_BLOCK) += i2o_block.o -obj-$(CONFIG_I2O_LAN) += i2o_lan.o -obj-$(CONFIG_I2O_SCSI) += i2o_scsi.o -obj-$(CONFIG_I2O_PROC) += i2o_proc.o - -include $(TOPDIR)/Rules.make - diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/README linux.ac/drivers/i2o/README --- linux.vanilla/drivers/i2o/README Mon Jun 19 21:30:55 2000 +++ linux.ac/drivers/i2o/README Thu Jan 1 01:00:00 1970 @@ -1,98 +0,0 @@ - - Linux I2O Support (c) Copyright 1999 Red Hat Software - and others. - - 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. - -AUTHORS (so far) - -Alan Cox, Building Number Three Ltd. - Core code, SCSI and Block OSMs - -Steve Ralston, LSI Logic Corp. - Debugging SCSI and Block OSM - -Deepak Saxena, Intel Corp. - Various core/block extensions - /proc interface, bug fixes - Ioctl interfaces for control - Debugging LAN OSM - -Philip Rumpf - Fixed assorted dumb SMP locking bugs - -Juha Sievanen, University of Helsinki Finland - LAN OSM code - /proc interface to LAN class - Bug fixes - Core code extensions - -Auvo Häkkinen, University of Helsinki Finland - LAN OSM code - /Proc interface to LAN class - Bug fixes - Core code extensions - -Taneli Vähäkangas, University of Helsinki Finland - Fixes to i2o_config - -CREDITS - - This work was made possible by - -Red Hat Software - Funding for the Building #3 part of the project - -Symbios Logic (Now LSI) - Host adapters, hints, known to work platforms when I hit - compatibility problems - -BoxHill Corporation - Loan of initial FibreChannel disk array used for development work. - -European Comission - Funding the work done by the University of Helsinki - -SysKonnect - Loan of FDDI and Gigabit Ethernet cards - -ASUSTeK - Loan of I2O motherboard - -STATUS: - -o The core setup works within limits. -o The scsi layer seems to almost work. - I'm still chasing down the hang bug. -o The block OSM is mostly functional -o LAN OSM works with FDDI and Ethernet cards. - -TO DO: - -General: -o Provide hidden address space if asked -o Long term message flow control -o PCI IOP's without interrupts are not supported yet -o Push FAIL handling into the core -o DDM control interfaces for module load etc -o Add I2O 2.0 support (Deffered to 2.5 kernel) - -Block: -o Multiple major numbers -o Read ahead and cache handling stuff. Talk to Ingo and people -o Power management -o Finish Media changers - -SCSI: -o Find the right way to associate drives/luns/busses - -Lan: -o Performance tuning -o Test Fibre Channel code - -Tape: -o Anyone seen anything implementing this ? - (D.S: Will attempt to do so if spare cycles permit) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/README.ioctl linux.ac/drivers/i2o/README.ioctl --- linux.vanilla/drivers/i2o/README.ioctl Mon Jun 19 21:30:55 2000 +++ linux.ac/drivers/i2o/README.ioctl Thu Jan 1 01:00:00 1970 @@ -1,394 +0,0 @@ - -Linux I2O User Space Interface -rev 0.3 - 04/20/99 - -============================================================================= -Originally written by Deepak Saxena(deepak@plexity.net) -Currently maintained by Deepak Saxena(deepak@plexity.net) -============================================================================= - -I. Introduction - -The Linux I2O subsystem provides a set of ioctl() commands that can be -utilized by user space applications to communicate with IOPs and devices -on individual IOPs. This document defines the specific ioctl() commands -that are available to the user and provides examples of their uses. - -This document assumes the reader is familiar with or has access to the -I2O specification as no I2O message parameters are outlined. For information -on the specification, see http://www.i2osig.org - -This document and the I2O user space interface are currently maintained -by Deepak Saxena. Please send all comments, errata, and bug fixes to -deepak@csociety.purdue.edu - -II. IOP Access - -Access to the I2O subsystem is provided through the device file named -/dev/i2o/ctl. This file is a character file with major number 10 and minor -number 166. It can be created through the following command: - - mknod /dev/i2o/ctl c 10 166 - -III. Determining the IOP Count - - SYNOPSIS - - ioctl(fd, I2OGETIOPS, int *count); - - u8 count[MAX_I2O_CONTROLLERS]; - - DESCRIPTION - - This function returns the system's active IOP table. count should - point to a buffer containing MAX_I2O_CONTROLLERS entries. Upon - returning, each entry will contain a non-zero value if the given - IOP unit is active, and NULL if it is inactive or non-existent. - - RETURN VALUE. - - Returns 0 if no errors occur, and -1 otherwise. If an error occurs, - errno is set appropriately: - - EFAULT Invalid user space pointer was passed - -IV. Getting Hardware Resource Table - - SYNOPSIS - - ioctl(fd, I2OHRTGET, struct i2o_cmd_hrt *hrt); - - struct i2o_cmd_hrtlct - { - u32 iop; /* IOP unit number */ - void *resbuf; /* Buffer for result */ - u32 *reslen; /* Buffer length in bytes */ - }; - - DESCRIPTION - - This function returns the Hardware Resource Table of the IOP specified - by hrt->iop in the buffer pointed to by hrt->resbuf. The actual size of - the data is written into *(hrt->reslen). - - RETURNS - - This function returns 0 if no errors occur. If an error occurs, -1 - is returned and errno is set appropriately: - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ENOBUFS Buffer not large enough. If this occurs, the required - buffer length is written into *(hrt->reslen) - -V. Getting Logical Configuration Table - - SYNOPSIS - - ioctl(fd, I2OLCTGET, struct i2o_cmd_lct *lct); - - struct i2o_cmd_hrtlct - { - u32 iop; /* IOP unit number */ - void *resbuf; /* Buffer for result */ - u32 *reslen; /* Buffer length in bytes */ - }; - - DESCRIPTION - - This function returns the Logical Configuration Table of the IOP specified - by lct->iop in the buffer pointed to by lct->resbuf. The actual size of - the data is written into *(lct->reslen). - - RETURNS - - This function returns 0 if no errors occur. If an error occurs, -1 - is returned and errno is set appropriately: - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ENOBUFS Buffer not large enough. If this occurs, the required - buffer length is written into *(lct->reslen) - -VI. Settting Parameters - - SYNOPSIS - - ioctl(fd, I2OPARMSET, struct i2o_parm_setget *ops); - - struct i2o_cmd_psetget - { - u32 iop; /* IOP unit number */ - u32 tid; /* Target device TID */ - void *opbuf; /* Operation List buffer */ - u32 oplen; /* Operation List buffer length in bytes */ - void *resbuf; /* Result List buffer */ - u32 *reslen; /* Result List buffer length in bytes */ - }; - - DESCRIPTION - - This function posts a UtilParamsSet message to the device identified - by ops->iop and ops->tid. The operation list for the message is - sent through the ops->opbuf buffer, and the result list is written - into the buffer pointed to by ops->resbuf. The number of bytes - written is placed into *(ops->reslen). - - RETURNS - - The return value is the size in bytes of the data written into - ops->resbuf if no errors occur. If an error occurs, -1 is returned - and errno is set appropriatly: - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ENOBUFS Buffer not large enough. If this occurs, the required - buffer length is written into *(ops->reslen) - ETIMEDOUT Timeout waiting for reply message - ENOMEM Kernel memory allocation error - - A return value of 0 does not mean that the value was actually - changed properly on the IOP. The user should check the result - list to determine the specific status of the transaction. - -VII. Getting Parameters - - SYNOPSIS - - ioctl(fd, I2OPARMGET, struct i2o_parm_setget *ops); - - struct i2o_parm_setget - { - u32 iop; /* IOP unit number */ - u32 tid; /* Target device TID */ - void *opbuf; /* Operation List buffer */ - u32 oplen; /* Operation List buffer length in bytes */ - void *resbuf; /* Result List buffer */ - u32 *reslen; /* Result List buffer length in bytes */ - }; - - DESCRIPTION - - This function posts a UtilParamsGet message to the device identified - by ops->iop and ops->tid. The operation list for the message is - sent through the ops->opbuf buffer, and the result list is written - into the buffer pointed to by ops->resbuf. The actual size of data - written is placed into *(ops->reslen). - - RETURNS - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ENOBUFS Buffer not large enough. If this occurs, the required - buffer length is written into *(ops->reslen) - ETIMEDOUT Timeout waiting for reply message - ENOMEM Kernel memory allocation error - - A return value of 0 does not mean that the value was actually - properly retreived. The user should check the result list - to determine the specific status of the transaction. - -VIII. Downloading Software - - SYNOPSIS - - ioctl(fd, I2OSWDL, struct i2o_sw_xfer *sw); - - struct i2o_sw_xfer - { - u32 iop; /* IOP unit number */ - u8 flags; /* DownloadFlags field */ - u8 sw_type; /* Software type */ - u32 sw_id; /* Software ID */ - void *buf; /* Pointer to software buffer */ - u32 *swlen; /* Length of software buffer */ - u32 *maxfrag; /* Number of fragments */ - u32 *curfrag; /* Current fragment number */ - }; - - DESCRIPTION - - This function downloads a software fragment pointed by sw->buf - to the iop identified by sw->iop. The DownloadFlags, SwID, SwType - and SwSize fields of the ExecSwDownload message are filled in with - the values of sw->flags, sw->sw_id, sw->sw_type and *(sw->swlen). - - The fragments _must_ be sent in order and be 8K in size. The last - fragment _may_ be shorter, however. The kernel will compute its - size based on information in the sw->swlen field. - - Please note that SW transfers can take a long time. - - RETURNS - - This function returns 0 no errors occur. If an error occurs, -1 - is returned and errno is set appropriatly: - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ETIMEDOUT Timeout waiting for reply message - ENOMEM Kernel memory allocation error - -IX. Uploading Software - - SYNOPSIS - - ioctl(fd, I2OSWUL, struct i2o_sw_xfer *sw); - - struct i2o_sw_xfer - { - u32 iop; /* IOP unit number */ - u8 flags; /* UploadFlags */ - u8 sw_type; /* Software type */ - u32 sw_id; /* Software ID */ - void *buf; /* Pointer to software buffer */ - u32 *swlen; /* Length of software buffer */ - u32 *maxfrag; /* Number of fragments */ - u32 *curfrag; /* Current fragment number */ - }; - - DESCRIPTION - - This function uploads a software fragment from the IOP identified - by sw->iop, sw->sw_type, sw->sw_id and optionally sw->swlen fields. - The UploadFlags, SwID, SwType and SwSize fields of the ExecSwUpload - message are filled in with the values of sw->flags, sw->sw_id, - sw->sw_type and *(sw->swlen). - - The fragments _must_ be requested in order and be 8K in size. The - user is responsible for allocating memory pointed by sw->buf. The - last fragment _may_ be shorter. - - Please note that SW transfers can take a long time. - - RETURNS - - This function returns 0 if no errors occur. If an error occurs, -1 - is returned and errno is set appropriatly: - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ETIMEDOUT Timeout waiting for reply message - ENOMEM Kernel memory allocation error - -X. Removing Software - - SYNOPSIS - - ioctl(fd, I2OSWDEL, struct i2o_sw_xfer *sw); - - struct i2o_sw_xfer - { - u32 iop; /* IOP unit number */ - u8 flags; /* RemoveFlags */ - u8 sw_type; /* Software type */ - u32 sw_id; /* Software ID */ - void *buf; /* Unused */ - u32 *swlen; /* Length of the software data */ - u32 *maxfrag; /* Unused */ - u32 *curfrag; /* Unused */ - }; - - DESCRIPTION - - This function removes software from the IOP identified by sw->iop. - The RemoveFlags, SwID, SwType and SwSize fields of the ExecSwRemove message - are filled in with the values of sw->flags, sw->sw_id, sw->sw_type and - *(sw->swlen). Give zero in *(sw->len) if the value is unknown. IOP uses - *(sw->swlen) value to verify correct identication of the module to remove. - The actual size of the module is written into *(sw->swlen). - - RETURNS - - This function returns 0 if no errors occur. If an error occurs, -1 - is returned and errno is set appropriatly: - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ETIMEDOUT Timeout waiting for reply message - ENOMEM Kernel memory allocation error - -X. Validating Configuration - - SYNOPSIS - - ioctl(fd, I2OVALIDATE, int *iop); - u32 iop; - - DESCRIPTION - - This function posts an ExecConfigValidate message to the controller - identified by iop. This message indicates that the the current - configuration is accepted. The iop changes the status of suspect drivers - to valid and may delete old drivers from its store. - - RETURNS - - This function returns 0 if no erro occur. If an error occurs, -1 is - returned and errno is set appropriatly: - - ETIMEDOUT Timeout waiting for reply message - ENXIO Invalid IOP number - -XI. Configuration Dialog - - SYNOPSIS - - ioctl(fd, I2OHTML, struct i2o_html *htquery); - struct i2o_html - { - u32 iop; /* IOP unit number */ - u32 tid; /* Target device ID */ - u32 page; /* HTML page */ - void *resbuf; /* Buffer for reply HTML page */ - u32 *reslen; /* Length in bytes of reply buffer */ - void *qbuf; /* Pointer to HTTP query string */ - u32 qlen; /* Length in bytes of query string buffer */ - }; - - DESCRIPTION - - This function posts an UtilConfigDialog message to the device identified - by htquery->iop and htquery->tid. The requested HTML page number is - provided by the htquery->page field, and the resultant data is stored - in the buffer pointed to by htquery->resbuf. If there is an HTTP query - string that is to be sent to the device, it should be sent in the buffer - pointed to by htquery->qbuf. If there is no query string, this field - should be set to NULL. The actual size of the reply received is written - into *(htquery->reslen). - - RETURNS - - This function returns 0 if no error occur. If an error occurs, -1 - is returned and errno is set appropriatly: - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ENOBUFS Buffer not large enough. If this occurs, the required - buffer length is written into *(ops->reslen) - ETIMEDOUT Timeout waiting for reply message - ENOMEM Kernel memory allocation error - -XII. Events - - In the process of determining this. Current idea is to have use - the select() interface to allow user apps to periodically poll - the /dev/i2o/ctl device for events. When select() notifies the user - that an event is available, the user would call read() to retrieve - a list of all the events that are pending for the specific device. - -============================================================================= -Revision History -============================================================================= - -Rev 0.1 - 04/01/99 -- Initial revision - -Rev 0.2 - 04/06/99 -- Changed return values to match UNIX ioctl() standard. Only return values - are 0 and -1. All errors are reported through errno. -- Added summary of proposed possible event interfaces - -Rev 0.3 - 04/20/99 -- Changed all ioctls() to use pointers to user data instead of actual data -- Updated error values to match the code diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_block.c linux.ac/drivers/i2o/i2o_block.c --- linux.vanilla/drivers/i2o/i2o_block.c Mon Sep 10 20:42:31 2001 +++ linux.ac/drivers/i2o/i2o_block.c Thu Jan 1 01:00:00 1970 @@ -1,2051 +0,0 @@ -/* - * I2O Random Block Storage Class OSM - * - * (C) Copyright 1999 Red Hat Software - * - * Written by Alan Cox, Building Number Three Ltd - * - * 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 is a beta test release. Most of the good code was taken - * from the nbd driver by Pavel Machek, who in turn took some of it - * from loop.c. Isn't free software great for reusability 8) - * - * Fixes/additions: - * Steve Ralston: - * Multiple device handling error fixes, - * Added a queue depth. - * Alan Cox: - * FC920 has an rmw bug. Dont or in the end marker. - * Removed queue walk, fixed for 64bitness. - * Deepak Saxena: - * Independent queues per IOP - * Support for dynamic device creation/deletion - * Code cleanup - * Support for larger I/Os through merge* functions - * (taken from DAC960 driver) - * Boji T Kannanthanam: - * Set the I2O Block devices to be detected in increasing - * order of TIDs during boot. - * Search and set the I2O block device that we boot off from as - * the first device to be claimed (as /dev/i2o/hda) - * Properly attach/detach I2O gendisk structure from the system - * gendisk list. The I2O block devices now appear in - * /proc/partitions. - * - * To do: - * Serial number scanning to find duplicates for FC multipathing - */ - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define MAJOR_NR I2O_MAJOR - -#include - -#define MAX_I2OB 16 - -#define MAX_I2OB_DEPTH 128 -#define MAX_I2OB_RETRIES 4 - -//#define DRIVERDEBUG -#ifdef DRIVERDEBUG -#define DEBUG( s ) -#else -#define DEBUG( s ) printk( s ) -#endif - -/* - * Events that this OSM is interested in - */ -#define I2OB_EVENT_MASK (I2O_EVT_IND_BSA_VOLUME_LOAD | \ - I2O_EVT_IND_BSA_VOLUME_UNLOAD | \ - I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ | \ - I2O_EVT_IND_BSA_CAPACITY_CHANGE | \ - I2O_EVT_IND_BSA_SCSI_SMART ) - - -/* - * I2O Block Error Codes - should be in a header file really... - */ -#define I2O_BSA_DSC_SUCCESS 0x0000 -#define I2O_BSA_DSC_MEDIA_ERROR 0x0001 -#define I2O_BSA_DSC_ACCESS_ERROR 0x0002 -#define I2O_BSA_DSC_DEVICE_FAILURE 0x0003 -#define I2O_BSA_DSC_DEVICE_NOT_READY 0x0004 -#define I2O_BSA_DSC_MEDIA_NOT_PRESENT 0x0005 -#define I2O_BSA_DSC_MEDIA_LOCKED 0x0006 -#define I2O_BSA_DSC_MEDIA_FAILURE 0x0007 -#define I2O_BSA_DSC_PROTOCOL_FAILURE 0x0008 -#define I2O_BSA_DSC_BUS_FAILURE 0x0009 -#define I2O_BSA_DSC_ACCESS_VIOLATION 0x000A -#define I2O_BSA_DSC_WRITE_PROTECTED 0x000B -#define I2O_BSA_DSC_DEVICE_RESET 0x000C -#define I2O_BSA_DSC_VOLUME_CHANGED 0x000D -#define I2O_BSA_DSC_TIMEOUT 0x000E - -/* - * Some of these can be made smaller later - */ - -static int i2ob_blksizes[MAX_I2OB<<4]; -static int i2ob_hardsizes[MAX_I2OB<<4]; -static int i2ob_sizes[MAX_I2OB<<4]; -static int i2ob_media_change_flag[MAX_I2OB]; -static u32 i2ob_max_sectors[MAX_I2OB<<4]; - -static int i2ob_context; - -/* - * I2O Block device descriptor - */ -struct i2ob_device -{ - struct i2o_controller *controller; - struct i2o_device *i2odev; - int unit; - int tid; - int flags; - int refcnt; - struct request *head, *tail; - request_queue_t *req_queue; - int max_segments; - int done_flag; - int constipated; - int depth; -}; - -/* - * FIXME: - * We should cache align these to avoid ping-ponging lines on SMP - * boxes under heavy I/O load... - */ -struct i2ob_request -{ - struct i2ob_request *next; - struct request *req; - int num; -}; - -/* - * Per IOP requst queue information - * - * We have a separate requeust_queue_t per IOP so that a heavilly - * loaded I2O block device on an IOP does not starve block devices - * across all I2O controllers. - * - */ -struct i2ob_iop_queue -{ - atomic_t queue_depth; - struct i2ob_request request_queue[MAX_I2OB_DEPTH]; - struct i2ob_request *i2ob_qhead; - request_queue_t req_queue; -}; -static struct i2ob_iop_queue *i2ob_queues[MAX_I2O_CONTROLLERS]; -static struct i2ob_request *i2ob_backlog[MAX_I2O_CONTROLLERS]; -static struct i2ob_request *i2ob_backlog_tail[MAX_I2O_CONTROLLERS]; - -/* - * Each I2O disk is one of these. - */ - -static struct i2ob_device i2ob_dev[MAX_I2OB<<4]; -static int i2ob_dev_count = 0; -static struct hd_struct i2ob[MAX_I2OB<<4]; -static struct gendisk i2ob_gendisk; /* Declared later */ - -/* - * Mutex and spin lock for event handling synchronization - * evt_msg contains the last event. - */ -static DECLARE_MUTEX_LOCKED(i2ob_evt_sem); -static DECLARE_COMPLETION(i2ob_thread_dead); -static spinlock_t i2ob_evt_lock = SPIN_LOCK_UNLOCKED; -static u32 evt_msg[MSG_FRAME_SIZE>>2]; - -static struct timer_list i2ob_timer; -static int i2ob_timer_started = 0; - -static void i2o_block_reply(struct i2o_handler *, struct i2o_controller *, - struct i2o_message *); -static void i2ob_new_device(struct i2o_controller *, struct i2o_device *); -static void i2ob_del_device(struct i2o_controller *, struct i2o_device *); -static void i2ob_reboot_event(void); -static int i2ob_install_device(struct i2o_controller *, struct i2o_device *, int); -static void i2ob_end_request(struct request *); -static void i2ob_request(request_queue_t *); -static int i2ob_backlog_request(struct i2o_controller *, struct i2ob_device *); -static int i2ob_init_iop(unsigned int); -static request_queue_t* i2ob_get_queue(kdev_t); -static int i2ob_query_device(struct i2ob_device *, int, int, void*, int); -static int do_i2ob_revalidate(kdev_t, int); -static int i2ob_evt(void *); - -static int evt_pid = 0; -static int evt_running = 0; -static int scan_unit = 0; - -/* - * I2O OSM registration structure...keeps getting bigger and bigger :) - */ -static struct i2o_handler i2o_block_handler = -{ - i2o_block_reply, - i2ob_new_device, - i2ob_del_device, - i2ob_reboot_event, - "I2O Block OSM", - 0, - I2O_CLASS_RANDOM_BLOCK_STORAGE -}; - -/* - * Get a message - */ - -static u32 i2ob_get(struct i2ob_device *dev) -{ - struct i2o_controller *c=dev->controller; - return I2O_POST_READ32(c); -} - -/* - * Turn a Linux block request into an I2O block read/write. - */ - -static int i2ob_send(u32 m, struct i2ob_device *dev, struct i2ob_request *ireq, u32 base, int unit) -{ - struct i2o_controller *c = dev->controller; - int tid = dev->tid; - unsigned long msg; - unsigned long mptr; - u64 offset; - struct request *req = ireq->req; - struct buffer_head *bh = req->bh; - int count = req->nr_sectors<<9; - char *last = NULL; - unsigned short size = 0; - - // printk(KERN_INFO "i2ob_send called\n"); - /* Map the message to a virtual address */ - msg = c->mem_offset + m; - - /* - * Build the message based on the request. - */ - __raw_writel(i2ob_context|(unit<<8), msg+8); - __raw_writel(ireq->num, msg+12); - __raw_writel(req->nr_sectors << 9, msg+20); - - /* - * Mask out partitions from now on - */ - unit &= 0xF0; - - /* This can be optimised later - just want to be sure its right for - starters */ - offset = ((u64)(req->sector+base)) << 9; - __raw_writel( offset & 0xFFFFFFFF, msg+24); - __raw_writel(offset>>32, msg+28); - mptr=msg+32; - - if(req->cmd == READ) - { - __raw_writel(I2O_CMD_BLOCK_READ<<24|HOST_TID<<12|tid, msg+4); - while(bh!=NULL) - { - if(bh->b_data == last) { - size += bh->b_size; - last += bh->b_size; - if(bh->b_reqnext) - __raw_writel(0x14000000|(size), mptr-8); - else - __raw_writel(0xD4000000|(size), mptr-8); - } - else - { - if(bh->b_reqnext) - __raw_writel(0x10000000|(bh->b_size), mptr); - else - __raw_writel(0xD0000000|(bh->b_size), mptr); - __raw_writel(virt_to_bus(bh->b_data), mptr+4); - mptr += 8; - size = bh->b_size; - last = bh->b_data + size; - } - - count -= bh->b_size; - bh = bh->b_reqnext; - } - /* - * Heuristic for now since the block layer doesnt give - * us enough info. If its a big write assume sequential - * readahead on controller. If its small then don't read - * ahead but do use the controller cache. - */ - if(size >= 8192) - __raw_writel((8<<24)|(1<<16)|8, msg+16); - else - __raw_writel((8<<24)|(1<<16)|4, msg+16); - } - else if(req->cmd == WRITE) - { - __raw_writel(I2O_CMD_BLOCK_WRITE<<24|HOST_TID<<12|tid, msg+4); - while(bh!=NULL) - { - if(bh->b_data == last) { - size += bh->b_size; - last += bh->b_size; - if(bh->b_reqnext) - __raw_writel(0x14000000|(size), mptr-8); - else - __raw_writel(0xD4000000|(size), mptr-8); - } - else - { - if(bh->b_reqnext) - __raw_writel(0x14000000|(bh->b_size), mptr); - else - __raw_writel(0xD4000000|(bh->b_size), mptr); - __raw_writel(virt_to_bus(bh->b_data), mptr+4); - mptr += 8; - size = bh->b_size; - last = bh->b_data + size; - } - - count -= bh->b_size; - bh = bh->b_reqnext; - } - - if(c->battery) - { - - if(size>16384) - __raw_writel(4, msg+16); - else - /* - * Allow replies to come back once data is cached in the controller - * This allows us to handle writes quickly thus giving more of the - * queue to reads. - */ - __raw_writel(16, msg+16); - } - else - { - /* Large write, don't cache */ - if(size>8192) - __raw_writel(4, msg+16); - else - /* write through */ - __raw_writel(8, msg+16); - } - } - __raw_writel(I2O_MESSAGE_SIZE(mptr-msg)>>2 | SGL_OFFSET_8, msg); - - if(count != 0) - { - printk(KERN_ERR "Request count botched by %d.\n", count); - } - - i2o_post_message(c,m); - atomic_inc(&i2ob_queues[c->unit]->queue_depth); - - return 0; -} - -/* - * Remove a request from the _locked_ request list. We update both the - * list chain and if this is the last item the tail pointer. Caller - * must hold the lock. - */ - -static inline void i2ob_unhook_request(struct i2ob_request *ireq, - unsigned int iop) -{ - ireq->next = i2ob_queues[iop]->i2ob_qhead; - i2ob_queues[iop]->i2ob_qhead = ireq; -} - -/* - * Request completion handler - */ - -static inline void i2ob_end_request(struct request *req) -{ - /* - * Loop until all of the buffers that are linked - * to this request have been marked updated and - * unlocked. - */ - - while (end_that_request_first( req, !req->errors, "i2o block" )); - - /* - * It is now ok to complete the request. - */ - end_that_request_last( req ); -} - -/* - * Request merging functions - */ -static inline int i2ob_new_segment(request_queue_t *q, struct request *req, - int __max_segments) -{ - int max_segments = i2ob_dev[MINOR(req->rq_dev)].max_segments; - - if (__max_segments < max_segments) - max_segments = __max_segments; - - if (req->nr_segments < max_segments) { - req->nr_segments++; - return 1; - } - return 0; -} - -static int i2ob_back_merge(request_queue_t *q, struct request *req, - struct buffer_head *bh, int __max_segments) -{ - if (req->bhtail->b_data + req->bhtail->b_size == bh->b_data) - return 1; - return i2ob_new_segment(q, req, __max_segments); -} - -static int i2ob_front_merge(request_queue_t *q, struct request *req, - struct buffer_head *bh, int __max_segments) -{ - if (bh->b_data + bh->b_size == req->bh->b_data) - return 1; - return i2ob_new_segment(q, req, __max_segments); -} - -static int i2ob_merge_requests(request_queue_t *q, - struct request *req, - struct request *next, - int __max_segments) -{ - int max_segments = i2ob_dev[MINOR(req->rq_dev)].max_segments; - int total_segments = req->nr_segments + next->nr_segments; - - if (__max_segments < max_segments) - max_segments = __max_segments; - - if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) - total_segments--; - - if (total_segments > max_segments) - return 0; - - req->nr_segments = total_segments; - return 1; -} - -static int i2ob_flush(struct i2o_controller *c, struct i2ob_device *d, int unit) -{ - unsigned long msg; - u32 m = i2ob_get(d); - - if(m == 0xFFFFFFFF) - return -1; - - msg = c->mem_offset + m; - - /* - * Ask the controller to write the cache back. This sorts out - * the supertrak firmware flaw and also does roughly the right - * thing for other cases too. - */ - - __raw_writel(FIVE_WORD_MSG_SIZE|SGL_OFFSET_0, msg); - __raw_writel(I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|d->tid, msg+4); - __raw_writel(i2ob_context|(unit<<8), msg+8); - __raw_writel(0, msg+12); - __raw_writel(60<<16, msg+16); - - i2o_post_message(c,m); - return 0; -} - -/* - * OSM reply handler. This gets all the message replies - */ - -static void i2o_block_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *msg) -{ - unsigned long flags; - struct i2ob_request *ireq = NULL; - u8 st; - u32 *m = (u32 *)msg; - u8 unit = (m[2]>>8)&0xF0; /* low 4 bits are partition */ - struct i2ob_device *dev = &i2ob_dev[(unit&0xF0)]; - - /* - * FAILed message - */ - if(m[0] & (1<<13)) - { - /* - * FAILed message from controller - * We increment the error count and abort it - * - * In theory this will never happen. The I2O block class - * speficiation states that block devices never return - * FAILs but instead use the REQ status field...but - * better be on the safe side since no one really follows - * the spec to the book :) - */ - ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; - ireq->req->errors++; - - spin_lock_irqsave(&io_request_lock, flags); - i2ob_unhook_request(ireq, c->unit); - i2ob_end_request(ireq->req); - spin_unlock_irqrestore(&io_request_lock, flags); - - /* Now flush the message by making it a NOP */ - m[0]&=0x00FFFFFF; - m[0]|=(I2O_CMD_UTIL_NOP)<<24; - i2o_post_message(c,virt_to_bus(m)); - - return; - } - - if(msg->function == I2O_CMD_UTIL_EVT_REGISTER) - { - spin_lock(&i2ob_evt_lock); - memcpy(evt_msg, msg, (m[0]>>16)<<2); - spin_unlock(&i2ob_evt_lock); - up(&i2ob_evt_sem); - return; - } - - if(msg->function == I2O_CMD_BLOCK_CFLUSH) - { - spin_lock_irqsave(&io_request_lock, flags); - dev->constipated=0; - DEBUG(("unconstipated\n")); - if(i2ob_backlog_request(c, dev)==0) - i2ob_request(dev->req_queue); - spin_unlock_irqrestore(&io_request_lock, flags); - return; - } - - if(!dev->i2odev) - { - /* - * This is HACK, but Intel Integrated RAID allows user - * to delete a volume that is claimed, locked, and in use - * by the OS. We have to check for a reply from a - * non-existent device and flag it as an error or the system - * goes kaput... - */ - ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; - ireq->req->errors++; - printk(KERN_WARNING "I2O Block: Data transfer to deleted device!\n"); - spin_lock_irqsave(&io_request_lock, flags); - i2ob_unhook_request(ireq, c->unit); - i2ob_end_request(ireq->req); - spin_unlock_irqrestore(&io_request_lock, flags); - return; - } - - /* - * Lets see what is cooking. We stuffed the - * request in the context. - */ - - ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; - st=m[4]>>24; - - if(st!=0) - { - int err; - char *bsa_errors[] = - { - "Success", - "Media Error", - "Failure communicating to device", - "Device Failure", - "Device is not ready", - "Media not present", - "Media is locked by another user", - "Media has failed", - "Failure communicating to device", - "Device bus failure", - "Device is locked by another user", - "Device is write protected", - "Device has reset", - "Volume has changed, waiting for acknowledgement" - }; - - err = m[4]&0xFFFF; - - /* - * Device not ready means two things. One is that the - * the thing went offline (but not a removal media) - * - * The second is that you have a SuperTrak 100 and the - * firmware got constipated. Unlike standard i2o card - * setups the supertrak returns an error rather than - * blocking for the timeout in these cases. - */ - - - spin_lock_irqsave(&io_request_lock, flags); - if(err==4) - { - /* - * Time to uncork stuff - */ - - if(!dev->constipated) - { - dev->constipated = 1; - DEBUG(("constipated\n")); - /* Now pull the chain */ - if(i2ob_flush(c, dev, unit)<0) - { - DEBUG(("i2ob: Unable to queue flush. Retrying I/O immediately.\n")); - dev->constipated=0; - } - DEBUG(("flushing\n")); - } - - /* - * Recycle the request - */ - -// i2ob_unhook_request(ireq, c->unit); - - /* - * Place it on the recycle queue - */ - - ireq->next = NULL; - if(i2ob_backlog_tail[c->unit]!=NULL) - i2ob_backlog_tail[c->unit]->next = ireq; - else - i2ob_backlog[c->unit] = ireq; - i2ob_backlog_tail[c->unit] = ireq; - - atomic_dec(&i2ob_queues[c->unit]->queue_depth); - - /* - * If the constipator flush failed we want to - * poke the queue again. - */ - - i2ob_request(dev->req_queue); - spin_unlock_irqrestore(&io_request_lock, flags); - - /* - * and out - */ - - return; - } - spin_unlock_irqrestore(&io_request_lock, flags); - printk(KERN_ERR "\n/dev/%s error: %s", dev->i2odev->dev_name, - bsa_errors[m[4]&0XFFFF]); - if(m[4]&0x00FF0000) - printk(" - DDM attempted %d retries", (m[4]>>16)&0x00FF ); - printk(".\n"); - ireq->req->errors++; - } - else - ireq->req->errors = 0; - - /* - * Dequeue the request. We use irqsave locks as one day we - * may be running polled controllers from a BH... - */ - - spin_lock_irqsave(&io_request_lock, flags); - i2ob_unhook_request(ireq, c->unit); - i2ob_end_request(ireq->req); - atomic_dec(&i2ob_queues[c->unit]->queue_depth); - - /* - * We may be able to do more I/O - */ - - if(i2ob_backlog_request(c, dev)==0) - i2ob_request(dev->req_queue); - - spin_unlock_irqrestore(&io_request_lock, flags); -} - -/* - * Event handler. Needs to be a separate thread b/c we may have - * to do things like scan a partition table, or query parameters - * which cannot be done from an interrupt or from a bottom half. - */ -static int i2ob_evt(void *dummy) -{ - unsigned int evt; - unsigned long flags; - int unit; - int i; - //The only event that has data is the SCSI_SMART event. - struct i2o_reply { - u32 header[4]; - u32 evt_indicator; - u8 ASC; - u8 ASCQ; - u8 data[16]; - } *evt_local; - - lock_kernel(); - daemonize(); - unlock_kernel(); - - strcpy(current->comm, "i2oblock"); - evt_running = 1; - - while(1) - { - if(down_interruptible(&i2ob_evt_sem)) - { - evt_running = 0; - printk("exiting..."); - break; - } - - /* - * Keep another CPU/interrupt from overwriting the - * message while we're reading it - * - * We stuffed the unit in the TxContext and grab the event mask - * None of the BSA we care about events have EventData - */ - spin_lock_irqsave(&i2ob_evt_lock, flags); - evt_local = (struct i2o_reply *)evt_msg; - spin_unlock_irqrestore(&i2ob_evt_lock, flags); - - unit = evt_local->header[3]; - evt = evt_local->evt_indicator; - - switch(evt) - { - /* - * New volume loaded on same TID, so we just re-install. - * The TID/controller don't change as it is the same - * I2O device. It's just new media that we have to - * rescan. - */ - case I2O_EVT_IND_BSA_VOLUME_LOAD: - { - i2ob_install_device(i2ob_dev[unit].i2odev->controller, - i2ob_dev[unit].i2odev, unit); - break; - } - - /* - * No media, so set all parameters to 0 and set the media - * change flag. The I2O device is still valid, just doesn't - * have media, so we don't want to clear the controller or - * device pointer. - */ - case I2O_EVT_IND_BSA_VOLUME_UNLOAD: - { - for(i = unit; i <= unit+15; i++) - { - i2ob_sizes[i] = 0; - i2ob_hardsizes[i] = 0; - i2ob_max_sectors[i] = 0; - i2ob[i].nr_sects = 0; - i2ob_gendisk.part[i].nr_sects = 0; - } - i2ob_media_change_flag[unit] = 1; - break; - } - - case I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ: - printk(KERN_WARNING "%s: Attempt to eject locked media\n", - i2ob_dev[unit].i2odev->dev_name); - break; - - /* - * The capacity has changed and we are going to be - * updating the max_sectors and other information - * about this disk. We try a revalidate first. If - * the block device is in use, we don't want to - * do that as there may be I/Os bound for the disk - * at the moment. In that case we read the size - * from the device and update the information ourselves - * and the user can later force a partition table - * update through an ioctl. - */ - case I2O_EVT_IND_BSA_CAPACITY_CHANGE: - { - u64 size; - - if(do_i2ob_revalidate(MKDEV(MAJOR_NR, unit),0) != -EBUSY) - continue; - - if(i2ob_query_device(&i2ob_dev[unit], 0x0004, 0, &size, 8) !=0 ) - i2ob_query_device(&i2ob_dev[unit], 0x0000, 4, &size, 8); - - spin_lock_irqsave(&io_request_lock, flags); - i2ob_sizes[unit] = (int)(size>>10); - i2ob_gendisk.part[unit].nr_sects = size>>9; - i2ob[unit].nr_sects = (int)(size>>9); - spin_unlock_irqrestore(&io_request_lock, flags); - break; - } - - /* - * We got a SCSI SMART event, we just log the relevant - * information and let the user decide what they want - * to do with the information. - */ - case I2O_EVT_IND_BSA_SCSI_SMART: - { - char buf[16]; - printk(KERN_INFO "I2O Block: %s received a SCSI SMART Event\n",i2ob_dev[unit].i2odev->dev_name); - evt_local->data[16]='\0'; - sprintf(buf,"%s",&evt_local->data[0]); - printk(KERN_INFO " Disk Serial#:%s\n",buf); - printk(KERN_INFO " ASC 0x%02x \n",evt_local->ASC); - printk(KERN_INFO " ASCQ 0x%02x \n",evt_local->ASCQ); - break; - } - - /* - * Non event - */ - - case 0: - break; - - /* - * An event we didn't ask for. Call the card manufacturer - * and tell them to fix their firmware :) - */ - default: - printk(KERN_INFO "%s: Received event %d we didn't register for\n" - KERN_INFO " Blame the I2O card manufacturer 8)\n", - i2ob_dev[unit].i2odev->dev_name, evt); - break; - } - }; - - complete_and_exit(&i2ob_thread_dead,0); - return 0; -} - -/* - * The timer handler will attempt to restart requests - * that are queued to the driver. This handler - * currently only gets called if the controller - * had no more room in its inbound fifo. - */ - -static void i2ob_timer_handler(unsigned long q) -{ - unsigned long flags; - - /* - * We cannot touch the request queue or the timer - * flag without holding the io_request_lock. - */ - spin_lock_irqsave(&io_request_lock,flags); - - /* - * Clear the timer started flag so that - * the timer can be queued again. - */ - i2ob_timer_started = 0; - - /* - * Restart any requests. - */ - i2ob_request((request_queue_t*)q); - - /* - * Free the lock. - */ - spin_unlock_irqrestore(&io_request_lock,flags); -} - -static int i2ob_backlog_request(struct i2o_controller *c, struct i2ob_device *dev) -{ - u32 m; - struct i2ob_request *ireq; - - while((ireq=i2ob_backlog[c->unit])!=NULL) - { - int unit; - - if(atomic_read(&i2ob_queues[c->unit]->queue_depth) > dev->depth/4) - break; - - m = i2ob_get(dev); - if(m == 0xFFFFFFFF) - break; - - i2ob_backlog[c->unit] = ireq->next; - if(i2ob_backlog[c->unit] == NULL) - i2ob_backlog_tail[c->unit] = NULL; - - unit = MINOR(ireq->req->rq_dev); - i2ob_send(m, dev, ireq, i2ob[unit].start_sect, unit); - } - if(i2ob_backlog[c->unit]) - return 1; - return 0; -} - -/* - * The I2O block driver is listed as one of those that pulls the - * front entry off the queue before processing it. This is important - * to remember here. If we drop the io lock then CURRENT will change - * on us. We must unlink CURRENT in this routine before we return, if - * we use it. - */ - -static void i2ob_request(request_queue_t *q) -{ - struct request *req; - struct i2ob_request *ireq; - int unit; - struct i2ob_device *dev; - u32 m; - - - while (!list_empty(&q->queue_head)) { - /* - * On an IRQ completion if there is an inactive - * request on the queue head it means it isnt yet - * ready to dispatch. - */ - req = blkdev_entry_next_request(&q->queue_head); - - if(req->rq_status == RQ_INACTIVE) - return; - - unit = MINOR(req->rq_dev); - dev = &i2ob_dev[(unit&0xF0)]; - - /* - * Queue depths probably belong with some kind of - * generic IOP commit control. Certainly its not right - * its global! - */ - if(atomic_read(&i2ob_queues[dev->unit]->queue_depth) >= dev->depth) - break; - - /* - * Is the channel constipated ? - */ - - if(i2ob_backlog[dev->unit]!=NULL) - break; - - /* Get a message */ - m = i2ob_get(dev); - - if(m==0xFFFFFFFF) - { - /* - * See if the timer has already been queued. - */ - if (!i2ob_timer_started) - { - DEBUG((KERN_ERR "i2ob: starting timer\n")); - - /* - * Set the timer_started flag to insure - * that the timer is only queued once. - * Queing it more than once will corrupt - * the timer queue. - */ - i2ob_timer_started = 1; - - /* - * Set up the timer to expire in - * 500ms. - */ - i2ob_timer.expires = jiffies + (HZ >> 1); - i2ob_timer.data = (unsigned int)q; - - /* - * Start it. - */ - - add_timer(&i2ob_timer); - return; - } - } - - /* - * Everything ok, so pull from kernel queue onto our queue - */ - req->errors = 0; - blkdev_dequeue_request(req); - req->waiting = NULL; - - ireq = i2ob_queues[dev->unit]->i2ob_qhead; - i2ob_queues[dev->unit]->i2ob_qhead = ireq->next; - ireq->req = req; - - i2ob_send(m, dev, ireq, i2ob[unit].start_sect, (unit&0xF0)); - } -} - - -/* - * SCSI-CAM for ioctl geometry mapping - * Duplicated with SCSI - this should be moved into somewhere common - * perhaps genhd ? - * - * LBA -> CHS mapping table taken from: - * - * "Incorporating the I2O Architecture into BIOS for Intel Architecture - * Platforms" - * - * This is an I2O document that is only available to I2O members, - * not developers. - * - * From my understanding, this is how all the I2O cards do this - * - * Disk Size | Sectors | Heads | Cylinders - * ---------------+---------+-------+------------------- - * 1 < X <= 528M | 63 | 16 | X/(63 * 16 * 512) - * 528M < X <= 1G | 63 | 32 | X/(63 * 32 * 512) - * 1 < X <528M | 63 | 16 | X/(63 * 16 * 512) - * 1 < X <528M | 63 | 16 | X/(63 * 16 * 512) - * - */ -#define BLOCK_SIZE_528M 1081344 -#define BLOCK_SIZE_1G 2097152 -#define BLOCK_SIZE_21G 4403200 -#define BLOCK_SIZE_42G 8806400 -#define BLOCK_SIZE_84G 17612800 - -static void i2o_block_biosparam( - unsigned long capacity, - unsigned short *cyls, - unsigned char *hds, - unsigned char *secs) -{ - unsigned long heads, sectors, cylinders; - - sectors = 63L; /* Maximize sectors per track */ - if(capacity <= BLOCK_SIZE_528M) - heads = 16; - else if(capacity <= BLOCK_SIZE_1G) - heads = 32; - else if(capacity <= BLOCK_SIZE_21G) - heads = 64; - else if(capacity <= BLOCK_SIZE_42G) - heads = 128; - else - heads = 255; - - cylinders = capacity / (heads * sectors); - - *cyls = (unsigned short) cylinders; /* Stuff return values */ - *secs = (unsigned char) sectors; - *hds = (unsigned char) heads; -} - - -/* - * Rescan the partition tables - */ - -static int do_i2ob_revalidate(kdev_t dev, int maxu) -{ - int minor=MINOR(dev); - int i; - - minor&=0xF0; - - i2ob_dev[minor].refcnt++; - if(i2ob_dev[minor].refcnt>maxu+1) - { - i2ob_dev[minor].refcnt--; - return -EBUSY; - } - - for( i = 15; i>=0 ; i--) - { - int m = minor+i; - invalidate_device(MKDEV(MAJOR_NR, m), 1); - i2ob_gendisk.part[m].start_sect = 0; - i2ob_gendisk.part[m].nr_sects = 0; - } - - /* - * Do a physical check and then reconfigure - */ - - i2ob_install_device(i2ob_dev[minor].controller, i2ob_dev[minor].i2odev, - minor); - i2ob_dev[minor].refcnt--; - return 0; -} - -/* - * Issue device specific ioctl calls. - */ - -static int i2ob_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct i2ob_device *dev; - int minor; - - /* Anyone capable of this syscall can do *real bad* things */ - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (!inode) - return -EINVAL; - minor = MINOR(inode->i_rdev); - if (minor >= (MAX_I2OB<<4)) - return -ENODEV; - - dev = &i2ob_dev[minor]; - switch (cmd) { - case BLKGETSIZE: - return put_user(i2ob[minor].nr_sects, (long *) arg); - case BLKGETSIZE64: - return put_user((u64)i2ob[minor].nr_sects << 9, (u64 *)arg); - - case HDIO_GETGEO: - { - struct hd_geometry g; - int u=minor&0xF0; - i2o_block_biosparam(i2ob_sizes[u]<<1, - &g.cylinders, &g.heads, &g.sectors); - g.start = i2ob[minor].start_sect; - return copy_to_user((void *)arg,&g, sizeof(g))?-EFAULT:0; - } - - case BLKRRPART: - if(!capable(CAP_SYS_ADMIN)) - return -EACCES; - return do_i2ob_revalidate(inode->i_rdev,1); - - case BLKFLSBUF: - case BLKROSET: - case BLKROGET: - case BLKRASET: - case BLKRAGET: - case BLKPG: - return blk_ioctl(inode->i_rdev, cmd, arg); - - default: - return -EINVAL; - } -} - -/* - * Close the block device down - */ - -static int i2ob_release(struct inode *inode, struct file *file) -{ - struct i2ob_device *dev; - int minor; - - minor = MINOR(inode->i_rdev); - if (minor >= (MAX_I2OB<<4)) - return -ENODEV; - dev = &i2ob_dev[(minor&0xF0)]; - - /* - * This is to deail with the case of an application - * opening a device and then the device dissapears while - * it's in use, and then the application tries to release - * it. ex: Unmounting a deleted RAID volume at reboot. - * If we send messages, it will just cause FAILs since - * the TID no longer exists. - */ - if(!dev->i2odev) - return 0; - - if (dev->refcnt <= 0) - printk(KERN_ALERT "i2ob_release: refcount(%d) <= 0\n", dev->refcnt); - dev->refcnt--; - if(dev->refcnt==0) - { - /* - * Flush the onboard cache on unmount - */ - u32 msg[5]; - int *query_done = &dev->done_flag; - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid; - msg[2] = i2ob_context|0x40000000; - msg[3] = (u32)query_done; - msg[4] = 60<<16; - DEBUG("Flushing..."); - i2o_post_wait(dev->controller, msg, 20, 60); - - /* - * Unlock the media - */ - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid; - msg[2] = i2ob_context|0x40000000; - msg[3] = (u32)query_done; - msg[4] = -1; - DEBUG("Unlocking..."); - i2o_post_wait(dev->controller, msg, 20, 2); - DEBUG("Unlocked.\n"); - - /* - * Now unclaim the device. - */ - - if (i2o_release_device(dev->i2odev, &i2o_block_handler)) - printk(KERN_ERR "i2ob_release: controller rejected unclaim.\n"); - - DEBUG("Unclaim\n"); - } - MOD_DEC_USE_COUNT; - return 0; -} - -/* - * Open the block device. - */ - -static int i2ob_open(struct inode *inode, struct file *file) -{ - int minor; - struct i2ob_device *dev; - - if (!inode) - return -EINVAL; - minor = MINOR(inode->i_rdev); - if (minor >= MAX_I2OB<<4) - return -ENODEV; - dev=&i2ob_dev[(minor&0xF0)]; - - if(!dev->i2odev) - return -ENODEV; - - if(dev->refcnt++==0) - { - u32 msg[6]; - - DEBUG("Claim "); - if(i2o_claim_device(dev->i2odev, &i2o_block_handler)) - { - dev->refcnt--; - printk(KERN_INFO "I2O Block: Could not open device\n"); - return -EBUSY; - } - DEBUG("Claimed "); - - /* - * Mount the media if needed. Note that we don't use - * the lock bit. Since we have to issue a lock if it - * refuses a mount (quite possible) then we might as - * well just send two messages out. - */ - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_MMOUNT<<24|HOST_TID<<12|dev->tid; - msg[4] = -1; - msg[5] = 0; - DEBUG("Mount "); - i2o_post_wait(dev->controller, msg, 24, 2); - - /* - * Lock the media - */ - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_MLOCK<<24|HOST_TID<<12|dev->tid; - msg[4] = -1; - DEBUG("Lock "); - i2o_post_wait(dev->controller, msg, 20, 2); - DEBUG("Ready.\n"); - } - MOD_INC_USE_COUNT; - return 0; -} - -/* - * Issue a device query - */ - -static int i2ob_query_device(struct i2ob_device *dev, int table, - int field, void *buf, int buflen) -{ - return i2o_query_scalar(dev->controller, dev->tid, - table, field, buf, buflen); -} - - -/* - * Install the I2O block device we found. - */ - -static int i2ob_install_device(struct i2o_controller *c, struct i2o_device *d, int unit) -{ - u64 size; - u32 blocksize; - u32 limit; - u8 type; - u32 flags, status; - struct i2ob_device *dev=&i2ob_dev[unit]; - int i; - - /* - * For logging purposes... - */ - printk(KERN_INFO "i2ob: Installing tid %d device at unit %d\n", - d->lct_data.tid, unit); - - /* - * Ask for the current media data. If that isn't supported - * then we ask for the device capacity data - */ - if(i2ob_query_device(dev, 0x0004, 1, &blocksize, 4) != 0 - || i2ob_query_device(dev, 0x0004, 0, &size, 8) !=0 ) - { - i2ob_query_device(dev, 0x0000, 3, &blocksize, 4); - i2ob_query_device(dev, 0x0000, 4, &size, 8); - } - - i2ob_query_device(dev, 0x0000, 5, &flags, 4); - i2ob_query_device(dev, 0x0000, 6, &status, 4); - i2ob_sizes[unit] = (int)(size>>10); - for(i=unit; i <= unit+15 ; i++) - i2ob_hardsizes[i] = blocksize; - i2ob_gendisk.part[unit].nr_sects = size>>9; - i2ob[unit].nr_sects = (int)(size>>9); - - /* Set limit based on inbound frame size */ - limit = (d->controller->status_block->inbound_frame_size - 8)/2; - limit = limit<<9; - - /* - * Max number of Scatter-Gather Elements - */ - - for(i=unit;i<=unit+15;i++) - { - if(d->controller->type == I2O_TYPE_PCI && d->controller->bus.pci.queue_buggy) - { - i2ob_max_sectors[i] = 32; - i2ob_dev[i].max_segments = 8; - i2ob_dev[i].depth = 4; - } - else if(d->controller->type == I2O_TYPE_PCI && d->controller->bus.pci.short_req) - { - i2ob_max_sectors[i] = 8; - i2ob_dev[i].max_segments = 8; - } - else - { - /* MAX_SECTORS was used but 255 is a dumb number for - striped RAID */ - i2ob_max_sectors[i]=256; - i2ob_dev[i].max_segments = (d->controller->status_block->inbound_frame_size - 8)/2; - } - } - - printk(KERN_INFO "Max segments set to %d\n", - i2ob_dev[unit].max_segments); - printk(KERN_INFO "Byte limit is %d.\n", limit); - - i2ob_query_device(dev, 0x0000, 0, &type, 1); - - sprintf(d->dev_name, "%s%c", i2ob_gendisk.major_name, 'a' + (unit>>4)); - - printk(KERN_INFO "%s: ", d->dev_name); - switch(type) - { - case 0: printk("Disk Storage");break; - case 4: printk("WORM");break; - case 5: printk("CD-ROM");break; - case 7: printk("Optical device");break; - default: - printk("Type %d", type); - } - if(status&(1<<10)) - printk("(RAID)"); - if(((flags & (1<<3)) && !(status & (1<<3))) || - ((flags & (1<<4)) && !(status & (1<<4)))) - { - printk(KERN_INFO " Not loaded.\n"); - return 1; - } - printk("- %dMb, %d byte sectors", - (int)(size>>20), blocksize); - if(status&(1<<0)) - { - u32 cachesize; - i2ob_query_device(dev, 0x0003, 0, &cachesize, 4); - cachesize>>=10; - if(cachesize>4095) - printk(", %dMb cache", cachesize>>10); - else - printk(", %dKb cache", cachesize); - - } - printk(".\n"); - printk(KERN_INFO "%s: Maximum sectors/read set to %d.\n", - d->dev_name, i2ob_max_sectors[unit]); - - /* - * If this is the first I2O block device found on this IOP, - * we need to initialize all the queue data structures - * before any I/O can be performed. If it fails, this - * device is useless. - */ - if(!i2ob_queues[c->unit]) { - if(i2ob_init_iop(c->unit)) - return 1; - } - - /* - * This will save one level of lookup/indirection in critical - * code so that we can directly get the queue ptr from the - * device instead of having to go the IOP data structure. - */ - dev->req_queue = &i2ob_queues[c->unit]->req_queue; - - grok_partitions(&i2ob_gendisk, unit>>4, 1<<4, (long)(size>>9)); - - /* - * Register for the events we're interested in and that the - * device actually supports. - */ - i2o_event_register(c, d->lct_data.tid, i2ob_context, unit, - (I2OB_EVENT_MASK & d->lct_data.event_capabilities)); - - return 0; -} - -/* - * Initialize IOP specific queue structures. This is called - * once for each IOP that has a block device sitting behind it. - */ -static int i2ob_init_iop(unsigned int unit) -{ - int i; - - i2ob_queues[unit] = (struct i2ob_iop_queue*) - kmalloc(sizeof(struct i2ob_iop_queue), GFP_ATOMIC); - if(!i2ob_queues[unit]) - { - printk(KERN_WARNING - "Could not allocate request queue for I2O block device!\n"); - return -1; - } - - for(i = 0; i< MAX_I2OB_DEPTH; i++) - { - i2ob_queues[unit]->request_queue[i].next = - &i2ob_queues[unit]->request_queue[i+1]; - i2ob_queues[unit]->request_queue[i].num = i; - } - - /* Queue is MAX_I2OB + 1... */ - i2ob_queues[unit]->request_queue[i].next = NULL; - i2ob_queues[unit]->i2ob_qhead = &i2ob_queues[unit]->request_queue[0]; - atomic_set(&i2ob_queues[unit]->queue_depth, 0); - - blk_init_queue(&i2ob_queues[unit]->req_queue, i2ob_request); - blk_queue_headactive(&i2ob_queues[unit]->req_queue, 0); - i2ob_queues[unit]->req_queue.back_merge_fn = i2ob_back_merge; - i2ob_queues[unit]->req_queue.front_merge_fn = i2ob_front_merge; - i2ob_queues[unit]->req_queue.merge_requests_fn = i2ob_merge_requests; - i2ob_queues[unit]->req_queue.queuedata = &i2ob_queues[unit]; - - return 0; -} - -/* - * Get the request queue for the given device. - */ -static request_queue_t* i2ob_get_queue(kdev_t dev) -{ - int unit = MINOR(dev)&0xF0; - - return i2ob_dev[unit].req_queue; -} - -/* - * Probe the I2O subsytem for block class devices - */ -static void i2ob_scan(int bios) -{ - int i; - int warned = 0; - - struct i2o_device *d, *b=NULL; - struct i2o_controller *c; - struct i2ob_device *dev; - - for(i=0; i< MAX_I2O_CONTROLLERS; i++) - { - c=i2o_find_controller(i); - - if(c==NULL) - continue; - - /* - * The device list connected to the I2O Controller is doubly linked - * Here we traverse the end of the list , and start claiming devices - * from that end. This assures that within an I2O controller atleast - * the newly created volumes get claimed after the older ones, thus - * mapping to same major/minor (and hence device file name) after - * every reboot. - * The exception being: - * 1. If there was a TID reuse. - * 2. There was more than one I2O controller. - */ - - if(!bios) - { - for (d=c->devices;d!=NULL;d=d->next) - if(d->next == NULL) - b = d; - } - else - b = c->devices; - - while(b != NULL) - { - d=b; - if(bios) - b = b->next; - else - b = b->prev; - - if(d->lct_data.class_id!=I2O_CLASS_RANDOM_BLOCK_STORAGE) - continue; - - if(d->lct_data.user_tid != 0xFFF) - continue; - - if(bios) - { - if(d->lct_data.bios_info != 0x80) - continue; - printk(KERN_INFO "Claiming as Boot device: Controller %d, TID %d\n", c->unit, d->lct_data.tid); - } - else - { - if(d->lct_data.bios_info == 0x80) - continue; /*Already claimed on pass 1 */ - } - - if(i2o_claim_device(d, &i2o_block_handler)) - { - printk(KERN_WARNING "i2o_block: Controller %d, TID %d\n", c->unit, - d->lct_data.tid); - printk(KERN_WARNING "\t%sevice refused claim! Skipping installation\n", bios?"Boot d":"D"); - continue; - } - - if(scan_uniti2odev = d; - dev->controller = c; - dev->unit = c->unit; - dev->tid = d->lct_data.tid; - - if(i2ob_install_device(c,d,scan_unit)) - printk(KERN_WARNING "Could not install I2O block device\n"); - else - { - scan_unit+=16; - i2ob_dev_count++; - - /* We want to know when device goes away */ - i2o_device_notify_on(d, &i2o_block_handler); - } - } - else - { - if(!warned++) - printk(KERN_WARNING "i2o_block: too many device, registering only %d.\n", scan_unit>>4); - } - i2o_release_device(d, &i2o_block_handler); - } - i2o_unlock_controller(c); - } -} - -static void i2ob_probe(void) -{ - /* - * Some overhead/redundancy involved here, while trying to - * claim the first boot volume encountered as /dev/i2o/hda - * everytime. All the i2o_controllers are searched and the - * first i2o block device marked as bootable is claimed - * If an I2O block device was booted off , the bios sets - * its bios_info field to 0x80, this what we search for. - * Assuming that the bootable volume is /dev/i2o/hda - * everytime will prevent any kernel panic while mounting - * root partition - */ - - printk(KERN_INFO "i2o_block: Checking for Boot device...\n"); - i2ob_scan(1); - - /* - * Now the remainder. - */ - printk(KERN_INFO "i2o_block: Checking for I2O Block devices...\n"); - i2ob_scan(0); -} - - -/* - * New device notification handler. Called whenever a new - * I2O block storage device is added to the system. - * - * Should we spin lock around this to keep multiple devs from - * getting updated at the same time? - * - */ -void i2ob_new_device(struct i2o_controller *c, struct i2o_device *d) -{ - struct i2ob_device *dev; - int unit = 0; - - printk(KERN_INFO "i2o_block: New device detected\n"); - printk(KERN_INFO " Controller %d Tid %d\n",c->unit, d->lct_data.tid); - - /* Check for available space */ - if(i2ob_dev_count>=MAX_I2OB<<4) - { - printk(KERN_ERR "i2o_block: No more devices allowed!\n"); - return; - } - for(unit = 0; unit < (MAX_I2OB<<4); unit += 16) - { - if(!i2ob_dev[unit].i2odev) - break; - } - - if(i2o_claim_device(d, &i2o_block_handler)) - { - printk(KERN_INFO - "i2o_block: Unable to claim device. Installation aborted\n"); - return; - } - - dev = &i2ob_dev[unit]; - dev->i2odev = d; - dev->controller = c; - dev->tid = d->lct_data.tid; - - if(i2ob_install_device(c,d,unit)) - printk(KERN_ERR "i2o_block: Could not install new device\n"); - else - { - i2ob_dev_count++; - i2o_device_notify_on(d, &i2o_block_handler); - } - - i2o_release_device(d, &i2o_block_handler); - - return; -} - -/* - * Deleted device notification handler. Called when a device we - * are talking to has been deleted by the user or some other - * mysterious fource outside the kernel. - */ -void i2ob_del_device(struct i2o_controller *c, struct i2o_device *d) -{ - int unit = 0; - int i = 0; - unsigned long flags; - - spin_lock_irqsave(&io_request_lock, flags); - - /* - * Need to do this...we somtimes get two events from the IRTOS - * in a row and that causes lots of problems. - */ - i2o_device_notify_off(d, &i2o_block_handler); - - printk(KERN_INFO "I2O Block Device Deleted\n"); - - for(unit = 0; unit < MAX_I2OB<<4; unit += 16) - { - if(i2ob_dev[unit].i2odev == d) - { - printk(KERN_INFO " /dev/%s: Controller %d Tid %d\n", - d->dev_name, c->unit, d->lct_data.tid); - break; - } - } - if(unit >= MAX_I2OB<<4) - { - printk(KERN_ERR "i2ob_del_device called, but not in dev table!\n"); - spin_unlock_irqrestore(&io_request_lock, flags); - return; - } - - /* - * This will force errors when i2ob_get_queue() is called - * by the kenrel. - */ - i2ob_dev[unit].req_queue = NULL; - for(i = unit; i <= unit+15; i++) - { - i2ob_dev[i].i2odev = NULL; - i2ob_sizes[i] = 0; - i2ob_hardsizes[i] = 0; - i2ob_max_sectors[i] = 0; - i2ob[i].nr_sects = 0; - i2ob_gendisk.part[i].nr_sects = 0; - } - spin_unlock_irqrestore(&io_request_lock, flags); - - /* - * Sync the device...this will force all outstanding I/Os - * to attempt to complete, thus causing error messages. - * We have to do this as the user could immediatelly create - * a new volume that gets assigned the same minor number. - * If there are still outstanding writes to the device, - * that could cause data corruption on the new volume! - * - * The truth is that deleting a volume that you are currently - * accessing will do _bad things_ to your system. This - * handler will keep it from crashing, but must probably - * you'll have to do a 'reboot' to get the system running - * properly. Deleting disks you are using is dumb. - * Umount them first and all will be good! - * - * It's not this driver's job to protect the system from - * dumb user mistakes :) - */ - if(i2ob_dev[unit].refcnt) - fsync_dev(MKDEV(MAJOR_NR,unit)); - - /* - * Decrease usage count for module - */ - while(i2ob_dev[unit].refcnt--) - MOD_DEC_USE_COUNT; - - i2ob_dev[unit].refcnt = 0; - - i2ob_dev[i].tid = 0; - - /* - * Do we need this? - * The media didn't really change...the device is just gone - */ - i2ob_media_change_flag[unit] = 1; - - i2ob_dev_count--; -} - -/* - * Have we seen a media change ? - */ -static int i2ob_media_change(kdev_t dev) -{ - int i=MINOR(dev); - i>>=4; - if(i2ob_media_change_flag[i]) - { - i2ob_media_change_flag[i]=0; - return 1; - } - return 0; -} - -static int i2ob_revalidate(kdev_t dev) -{ - return do_i2ob_revalidate(dev, 0); -} - -/* - * Reboot notifier. This is called by i2o_core when the system - * shuts down. - */ -static void i2ob_reboot_event(void) -{ - int i; - - for(i=0;irefcnt!=0) - { - /* - * Flush the onboard cache - */ - u32 msg[5]; - int *query_done = &dev->done_flag; - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid; - msg[2] = i2ob_context|0x40000000; - msg[3] = (u32)query_done; - msg[4] = 60<<16; - - DEBUG("Flushing..."); - i2o_post_wait(dev->controller, msg, 20, 60); - - DEBUG("Unlocking..."); - /* - * Unlock the media - */ - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid; - msg[2] = i2ob_context|0x40000000; - msg[3] = (u32)query_done; - msg[4] = -1; - i2o_post_wait(dev->controller, msg, 20, 2); - - DEBUG("Unlocked.\n"); - } - } -} - -static struct block_device_operations i2ob_fops = -{ - open: i2ob_open, - release: i2ob_release, - ioctl: i2ob_ioctl, - check_media_change: i2ob_media_change, - revalidate: i2ob_revalidate, -}; - -static struct gendisk i2ob_gendisk = -{ - major: MAJOR_NR, - major_name: "i2o/hd", - minor_shift: 4, - max_p: 1<<4, - part: i2ob, - sizes: i2ob_sizes, - nr_real: MAX_I2OB, - fops: &i2ob_fops, -}; - - -/* - * And here should be modules and kernel interface - * (Just smiley confuses emacs :-) - */ - -#ifdef MODULE -#define i2o_block_init init_module -#endif - -int i2o_block_init(void) -{ - int i; - - printk(KERN_INFO "I2O Block Storage OSM v0.9\n"); - printk(KERN_INFO " (c) Copyright 1999-2001 Red Hat Software.\n"); - - /* - * Register the block device interfaces - */ - - if (register_blkdev(MAJOR_NR, "i2o_block", &i2ob_fops)) { - printk(KERN_ERR "Unable to get major number %d for i2o_block\n", - MAJOR_NR); - return -EIO; - } -#ifdef MODULE - printk(KERN_INFO "i2o_block: registered device at major %d\n", MAJOR_NR); -#endif - - /* - * Now fill in the boiler plate - */ - - blksize_size[MAJOR_NR] = i2ob_blksizes; - hardsect_size[MAJOR_NR] = i2ob_hardsizes; - blk_size[MAJOR_NR] = i2ob_sizes; - max_sectors[MAJOR_NR] = i2ob_max_sectors; - blk_dev[MAJOR_NR].queue = i2ob_get_queue; - - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), i2ob_request); - blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR), 0); - - for (i = 0; i < MAX_I2OB << 4; i++) { - i2ob_dev[i].refcnt = 0; - i2ob_dev[i].flags = 0; - i2ob_dev[i].controller = NULL; - i2ob_dev[i].i2odev = NULL; - i2ob_dev[i].tid = 0; - i2ob_dev[i].head = NULL; - i2ob_dev[i].tail = NULL; - i2ob_dev[i].depth = MAX_I2OB_DEPTH; - i2ob_blksizes[i] = 1024; - i2ob_max_sectors[i] = 2; - } - - /* - * Set up the queue - */ - for(i = 0; i < MAX_I2O_CONTROLLERS; i++) - { - i2ob_queues[i] = NULL; - } - - /* - * Timers - */ - - init_timer(&i2ob_timer); - i2ob_timer.function = i2ob_timer_handler; - i2ob_timer.data = 0; - - /* - * Register the OSM handler as we will need this to probe for - * drives, geometry and other goodies. - */ - - if(i2o_install_handler(&i2o_block_handler)<0) - { - unregister_blkdev(MAJOR_NR, "i2o_block"); - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); - printk(KERN_ERR "i2o_block: unable to register OSM.\n"); - return -EINVAL; - } - i2ob_context = i2o_block_handler.context; - - /* - * Initialize event handling thread - */ - init_MUTEX_LOCKED(&i2ob_evt_sem); - evt_pid = kernel_thread(i2ob_evt, NULL, CLONE_SIGHAND); - if(evt_pid < 0) - { - printk(KERN_ERR - "i2o_block: Could not initialize event thread. Aborting\n"); - i2o_remove_handler(&i2o_block_handler); - return 0; - } - - /* - * Finally see what is actually plugged in to our controllers - */ - for (i = 0; i < MAX_I2OB; i++) - register_disk(&i2ob_gendisk, MKDEV(MAJOR_NR,i<<4), 1<<4, - &i2ob_fops, 0); - i2ob_probe(); - - /* - * Adding i2ob_gendisk into the gendisk list. - */ - add_gendisk(&i2ob_gendisk); - - return 0; -} - -#ifdef MODULE - -EXPORT_NO_SYMBOLS; -MODULE_AUTHOR("Red Hat Software"); -MODULE_DESCRIPTION("I2O Block Device OSM"); - -void cleanup_module(void) -{ - struct gendisk *gdp; - int i; - - if(evt_running) { - printk(KERN_INFO "Killing I2O block threads..."); - i = kill_proc(evt_pid, SIGTERM, 1); - if(!i) { - printk("waiting..."); - } - /* Be sure it died */ - wait_for_completion(&i2ob_thread_dead); - printk("done.\n"); - } - - /* - * Unregister for updates from any devices..otherwise we still - * get them and the core jumps to random memory :O - */ - if(i2ob_dev_count) { - struct i2o_device *d; - for(i = 0; i < MAX_I2OB; i++) - if((d=i2ob_dev[i<<4].i2odev)) { - i2o_device_notify_off(d, &i2o_block_handler); - i2o_event_register(d->controller, d->lct_data.tid, - i2ob_context, i<<4, 0); - } - } - - /* - * We may get further callbacks for ourself. The i2o_core - * code handles this case reasonably sanely. The problem here - * is we shouldn't get them .. but a couple of cards feel - * obliged to tell us stuff we dont care about. - * - * This isnt ideal at all but will do for now. - */ - - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ); - - /* - * Flush the OSM - */ - - i2o_remove_handler(&i2o_block_handler); - - /* - * Return the block device - */ - if (unregister_blkdev(MAJOR_NR, "i2o_block") != 0) - printk("i2o_block: cleanup_module failed\n"); - - /* - * free request queue - */ - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); - - del_gendisk(&i2ob_gendisk); -} -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_config.c linux.ac/drivers/i2o/i2o_config.c --- linux.vanilla/drivers/i2o/i2o_config.c Thu Aug 16 17:50:15 2001 +++ linux.ac/drivers/i2o/i2o_config.c Thu Jan 1 01:00:00 1970 @@ -1,965 +0,0 @@ -/* - * I2O Configuration Interface Driver - * - * (C) Copyright 1999 Red Hat Software - * - * Written by Alan Cox, Building Number Three Ltd - * - * Modified 04/20/1999 by Deepak Saxena - * - Added basic ioctl() support - * Modified 06/07/1999 by Deepak Saxena - * - Added software download ioctl (still testing) - * Modified 09/10/1999 by Auvo Häkkinen - * - Changes to i2o_cfg_reply(), ioctl_parms() - * - Added ioct_validate() - * Modified 09/30/1999 by Taneli Vähäkangas - * - Fixed ioctl_swdl() - * Modified 10/04/1999 by Taneli Vähäkangas - * - Changed ioctl_swdl(), implemented ioctl_swul() and ioctl_swdel() - * Modified 11/18/199 by Deepak Saxena - * - Added event managmenet support - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static int i2o_cfg_context = -1; -static void *page_buf; -static spinlock_t i2o_config_lock = SPIN_LOCK_UNLOCKED; -struct wait_queue *i2o_wait_queue; - -#define MODINC(x,y) (x = x++ % y) - -struct i2o_cfg_info -{ - struct file* fp; - struct fasync_struct *fasync; - struct i2o_evt_info event_q[I2O_EVT_Q_LEN]; - u16 q_in; // Queue head index - u16 q_out; // Queue tail index - u16 q_len; // Queue length - u16 q_lost; // Number of lost events - u32 q_id; // Event queue ID...used as tx_context - struct i2o_cfg_info *next; -}; -static struct i2o_cfg_info *open_files = NULL; -static int i2o_cfg_info_id = 0; - -static int ioctl_getiops(unsigned long); -static int ioctl_gethrt(unsigned long); -static int ioctl_getlct(unsigned long); -static int ioctl_parms(unsigned long, unsigned int); -static int ioctl_html(unsigned long); -static int ioctl_swdl(unsigned long); -static int ioctl_swul(unsigned long); -static int ioctl_swdel(unsigned long); -static int ioctl_validate(unsigned long); -static int ioctl_evt_reg(unsigned long, struct file *); -static int ioctl_evt_get(unsigned long, struct file *); -static int cfg_fasync(int, struct file*, int); - -/* - * This is the callback for any message we have posted. The message itself - * will be returned to the message pool when we return from the IRQ - * - * This runs in irq context so be short and sweet. - */ -static void i2o_cfg_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *m) -{ - u32 *msg = (u32 *)m; - - if (msg[0] & MSG_FAIL) { - u32 *preserved_msg = (u32*)(c->mem_offset + msg[7]); - - printk(KERN_ERR "i2o_config: IOP failed to process the msg.\n"); - - /* Release the preserved msg frame by resubmitting it as a NOP */ - - preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; - preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0; - preserved_msg[2] = 0; - i2o_post_message(c, msg[7]); - } - - if (msg[4] >> 24) // ReqStatus != SUCCESS - i2o_report_status(KERN_INFO,"i2o_config", msg); - - if(m->function == I2O_CMD_UTIL_EVT_REGISTER) - { - struct i2o_cfg_info *inf; - - for(inf = open_files; inf; inf = inf->next) - if(inf->q_id == msg[3]) - break; - - // - // If this is the case, it means that we're getting - // events for a file descriptor that's been close()'d - // w/o the user unregistering for events first. - // The code currently assumes that the user will - // take care of unregistering for events before closing - // a file. - // - // TODO: - // Should we track event registartion and deregister - // for events when a file is close()'d so this doesn't - // happen? That would get rid of the search through - // the linked list since file->private_data could point - // directly to the i2o_config_info data structure...but - // it would mean having all sorts of tables to track - // what each file is registered for...I think the - // current method is simpler. - DS - // - if(!inf) - return; - - inf->event_q[inf->q_in].id.iop = c->unit; - inf->event_q[inf->q_in].id.tid = m->target_tid; - inf->event_q[inf->q_in].id.evt_mask = msg[4]; - - // - // Data size = msg size - reply header - // - inf->event_q[inf->q_in].data_size = (m->size - 5) * 4; - if(inf->event_q[inf->q_in].data_size) - memcpy(inf->event_q[inf->q_in].evt_data, - (unsigned char *)(msg + 5), - inf->event_q[inf->q_in].data_size); - - spin_lock(&i2o_config_lock); - MODINC(inf->q_in, I2O_EVT_Q_LEN); - if(inf->q_len == I2O_EVT_Q_LEN) - { - MODINC(inf->q_out, I2O_EVT_Q_LEN); - inf->q_lost++; - } - else - { - // Keep I2OEVTGET on another CPU from touching this - inf->q_len++; - } - spin_unlock(&i2o_config_lock); - - -// printk(KERN_INFO "File %p w/id %d has %d events\n", -// inf->fp, inf->q_id, inf->q_len); - - kill_fasync(&inf->fasync, SIGIO, POLL_IN); - } - - return; -} - -/* - * Each of these describes an i2o message handler. They are - * multiplexed by the i2o_core code - */ - -struct i2o_handler cfg_handler= -{ - i2o_cfg_reply, - NULL, - NULL, - NULL, - "Configuration", - 0, - 0xffffffff // All classes -}; - -static ssize_t cfg_write(struct file *file, const char *buf, size_t count, loff_t *ppos) -{ - printk(KERN_INFO "i2o_config write not yet supported\n"); - - return 0; -} - - -static ssize_t cfg_read(struct file *file, char *buf, size_t count, loff_t *ptr) -{ - return 0; -} - -/* - * IOCTL Handler - */ -static int cfg_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, - unsigned long arg) -{ - int ret; - - switch(cmd) - { - case I2OGETIOPS: - ret = ioctl_getiops(arg); - break; - - case I2OHRTGET: - ret = ioctl_gethrt(arg); - break; - - case I2OLCTGET: - ret = ioctl_getlct(arg); - break; - - case I2OPARMSET: - ret = ioctl_parms(arg, I2OPARMSET); - break; - - case I2OPARMGET: - ret = ioctl_parms(arg, I2OPARMGET); - break; - - case I2OSWDL: - ret = ioctl_swdl(arg); - break; - - case I2OSWUL: - ret = ioctl_swul(arg); - break; - - case I2OSWDEL: - ret = ioctl_swdel(arg); - break; - - case I2OVALIDATE: - ret = ioctl_validate(arg); - break; - - case I2OHTML: - ret = ioctl_html(arg); - break; - - case I2OEVTREG: - ret = ioctl_evt_reg(arg, fp); - break; - - case I2OEVTGET: - ret = ioctl_evt_get(arg, fp); - break; - - default: - ret = -EINVAL; - } - - return ret; -} - -int ioctl_getiops(unsigned long arg) -{ - u8 *user_iop_table = (u8*)arg; - struct i2o_controller *c = NULL; - int i; - u8 foo[MAX_I2O_CONTROLLERS]; - - if(!access_ok(VERIFY_WRITE, user_iop_table, MAX_I2O_CONTROLLERS)) - return -EFAULT; - - for(i = 0; i < MAX_I2O_CONTROLLERS; i++) - { - c = i2o_find_controller(i); - if(c) - { - foo[i] = 1; - i2o_unlock_controller(c); - } - else - { - foo[i] = 0; - } - } - - __copy_to_user(user_iop_table, foo, MAX_I2O_CONTROLLERS); - return 0; -} - -int ioctl_gethrt(unsigned long arg) -{ - struct i2o_controller *c; - struct i2o_cmd_hrtlct *cmd = (struct i2o_cmd_hrtlct*)arg; - struct i2o_cmd_hrtlct kcmd; - i2o_hrt *hrt; - int len; - u32 reslen; - int ret = 0; - - if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct))) - return -EFAULT; - - if(get_user(reslen, kcmd.reslen) < 0) - return -EFAULT; - - if(kcmd.resbuf == NULL) - return -EFAULT; - - c = i2o_find_controller(kcmd.iop); - if(!c) - return -ENXIO; - - hrt = (i2o_hrt *)c->hrt; - - i2o_unlock_controller(c); - - len = 8 + ((hrt->entry_len * hrt->num_entries) << 2); - - /* We did a get user...so assuming mem is ok...is this bad? */ - put_user(len, kcmd.reslen); - if(len > reslen) - ret = -ENOBUFS; - if(copy_to_user(kcmd.resbuf, (void*)hrt, len)) - ret = -EFAULT; - - return ret; -} - -int ioctl_getlct(unsigned long arg) -{ - struct i2o_controller *c; - struct i2o_cmd_hrtlct *cmd = (struct i2o_cmd_hrtlct*)arg; - struct i2o_cmd_hrtlct kcmd; - i2o_lct *lct; - int len; - int ret = 0; - u32 reslen; - - if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct))) - return -EFAULT; - - if(get_user(reslen, kcmd.reslen) < 0) - return -EFAULT; - - if(kcmd.resbuf == NULL) - return -EFAULT; - - c = i2o_find_controller(kcmd.iop); - if(!c) - return -ENXIO; - - lct = (i2o_lct *)c->lct; - i2o_unlock_controller(c); - - len = (unsigned int)lct->table_size << 2; - put_user(len, kcmd.reslen); - if(len > reslen) - ret = -ENOBUFS; - else if(copy_to_user(kcmd.resbuf, (void*)lct, len)) - ret = -EFAULT; - - return ret; -} - -static int ioctl_parms(unsigned long arg, unsigned int type) -{ - int ret = 0; - struct i2o_controller *c; - struct i2o_cmd_psetget *cmd = (struct i2o_cmd_psetget*)arg; - struct i2o_cmd_psetget kcmd; - u32 reslen; - u8 *ops; - u8 *res; - int len; - - u32 i2o_cmd = (type == I2OPARMGET ? - I2O_CMD_UTIL_PARAMS_GET : - I2O_CMD_UTIL_PARAMS_SET); - - if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_psetget))) - return -EFAULT; - - if(get_user(reslen, kcmd.reslen)) - return -EFAULT; - - c = i2o_find_controller(kcmd.iop); - if(!c) - return -ENXIO; - - ops = (u8*)kmalloc(kcmd.oplen, GFP_KERNEL); - if(!ops) - { - i2o_unlock_controller(c); - return -ENOMEM; - } - - if(copy_from_user(ops, kcmd.opbuf, kcmd.oplen)) - { - i2o_unlock_controller(c); - kfree(ops); - return -EFAULT; - } - - /* - * It's possible to have a _very_ large table - * and that the user asks for all of it at once... - */ - res = (u8*)kmalloc(65536, GFP_KERNEL); - if(!res) - { - i2o_unlock_controller(c); - kfree(ops); - return -ENOMEM; - } - - len = i2o_issue_params(i2o_cmd, c, kcmd.tid, - ops, kcmd.oplen, res, 65536); - i2o_unlock_controller(c); - kfree(ops); - - if (len < 0) { - kfree(res); - return -EAGAIN; - } - - put_user(len, kcmd.reslen); - if(len > reslen) - ret = -ENOBUFS; - else if(copy_to_user(cmd->resbuf, res, len)) - ret = -EFAULT; - - kfree(res); - - return ret; -} - -int ioctl_html(unsigned long arg) -{ - struct i2o_html *cmd = (struct i2o_html*)arg; - struct i2o_html kcmd; - struct i2o_controller *c; - u8 *res = NULL; - void *query = NULL; - int ret = 0; - int token; - u32 len; - u32 reslen; - u32 msg[MSG_FRAME_SIZE/4]; - - if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_html))) - { - printk(KERN_INFO "i2o_config: can't copy html cmd\n"); - return -EFAULT; - } - - if(get_user(reslen, kcmd.reslen) < 0) - { - printk(KERN_INFO "i2o_config: can't copy html reslen\n"); - return -EFAULT; - } - - if(!kcmd.resbuf) - { - printk(KERN_INFO "i2o_config: NULL html buffer\n"); - return -EFAULT; - } - - c = i2o_find_controller(kcmd.iop); - if(!c) - return -ENXIO; - - if(kcmd.qlen) /* Check for post data */ - { - query = kmalloc(kcmd.qlen, GFP_KERNEL); - if(!query) - { - i2o_unlock_controller(c); - return -ENOMEM; - } - if(copy_from_user(query, kcmd.qbuf, kcmd.qlen)) - { - i2o_unlock_controller(c); - printk(KERN_INFO "i2o_config: could not get query\n"); - kfree(query); - return -EFAULT; - } - } - - res = kmalloc(65536, GFP_KERNEL); - if(!res) - { - i2o_unlock_controller(c); - kfree(query); - return -ENOMEM; - } - - msg[1] = (I2O_CMD_UTIL_CONFIG_DIALOG << 24)|HOST_TID<<12|kcmd.tid; - msg[2] = i2o_cfg_context; - msg[3] = 0; - msg[4] = kcmd.page; - msg[5] = 0xD0000000|65536; - msg[6] = virt_to_bus(res); - if(!kcmd.qlen) /* Check for post data */ - msg[0] = SEVEN_WORD_MSG_SIZE|SGL_OFFSET_5; - else - { - msg[0] = NINE_WORD_MSG_SIZE|SGL_OFFSET_5; - msg[5] = 0x50000000|65536; - msg[7] = 0xD4000000|(kcmd.qlen); - msg[8] = virt_to_bus(query); - } - /* - Wait for a considerable time till the Controller - does its job before timing out. The controller might - take more time to process this request if there are - many devices connected to it. - */ - token = i2o_post_wait_mem(c, msg, 9*4, 400, query, res); - if(token < 0) - { - printk(KERN_DEBUG "token = %#10x\n", token); - i2o_unlock_controller(c); - - if(token != -ETIMEDOUT) - { - kfree(res); - if(kcmd.qlen) kfree(query); - } - - return token; - } - i2o_unlock_controller(c); - - len = strnlen(res, 65536); - put_user(len, kcmd.reslen); - if(len > reslen) - ret = -ENOMEM; - if(copy_to_user(kcmd.resbuf, res, len)) - ret = -EFAULT; - - kfree(res); - if(kcmd.qlen) - kfree(query); - - return ret; -} - -int ioctl_swdl(unsigned long arg) -{ - struct i2o_sw_xfer kxfer; - struct i2o_sw_xfer *pxfer = (struct i2o_sw_xfer *)arg; - unsigned char maxfrag = 0, curfrag = 1; - unsigned char *buffer; - u32 msg[9]; - unsigned int status = 0, swlen = 0, fragsize = 8192; - struct i2o_controller *c; - - if(copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) - return -EFAULT; - - if(get_user(swlen, kxfer.swlen) < 0) - return -EFAULT; - - if(get_user(maxfrag, kxfer.maxfrag) < 0) - return -EFAULT; - - if(get_user(curfrag, kxfer.curfrag) < 0) - return -EFAULT; - - if(curfrag==maxfrag) fragsize = swlen-(maxfrag-1)*8192; - - if(!kxfer.buf || !access_ok(VERIFY_READ, kxfer.buf, fragsize)) - return -EFAULT; - - c = i2o_find_controller(kxfer.iop); - if(!c) - return -ENXIO; - - buffer=kmalloc(fragsize, GFP_KERNEL); - if (buffer==NULL) - { - i2o_unlock_controller(c); - return -ENOMEM; - } - __copy_from_user(buffer, kxfer.buf, fragsize); - - msg[0]= NINE_WORD_MSG_SIZE | SGL_OFFSET_7; - msg[1]= I2O_CMD_SW_DOWNLOAD<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2]= (u32)cfg_handler.context; - msg[3]= 0; - msg[4]= (((u32)kxfer.flags)<<24) | (((u32)kxfer.sw_type)<<16) | - (((u32)maxfrag)<<8) | (((u32)curfrag)); - msg[5]= swlen; - msg[6]= kxfer.sw_id; - msg[7]= (0xD0000000 | fragsize); - msg[8]= virt_to_bus(buffer); - -// printk("i2o_config: swdl frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); - status = i2o_post_wait_mem(c, msg, sizeof(msg), 60, buffer, NULL); - - i2o_unlock_controller(c); - if(status != -ETIMEDOUT) - kfree(buffer); - - if (status != I2O_POST_WAIT_OK) - { - // it fails if you try and send frags out of order - // and for some yet unknown reasons too - printk(KERN_INFO "i2o_config: swdl failed, DetailedStatus = %d\n", status); - return status; - } - - return 0; -} - -int ioctl_swul(unsigned long arg) -{ - struct i2o_sw_xfer kxfer; - struct i2o_sw_xfer *pxfer = (struct i2o_sw_xfer *)arg; - unsigned char maxfrag = 0, curfrag = 1; - unsigned char *buffer; - u32 msg[9]; - unsigned int status = 0, swlen = 0, fragsize = 8192; - struct i2o_controller *c; - - if(copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) - return -EFAULT; - - if(get_user(swlen, kxfer.swlen) < 0) - return -EFAULT; - - if(get_user(maxfrag, kxfer.maxfrag) < 0) - return -EFAULT; - - if(get_user(curfrag, kxfer.curfrag) < 0) - return -EFAULT; - - if(curfrag==maxfrag) fragsize = swlen-(maxfrag-1)*8192; - - if(!kxfer.buf || !access_ok(VERIFY_WRITE, kxfer.buf, fragsize)) - return -EFAULT; - - c = i2o_find_controller(kxfer.iop); - if(!c) - return -ENXIO; - - buffer=kmalloc(fragsize, GFP_KERNEL); - if (buffer==NULL) - { - i2o_unlock_controller(c); - return -ENOMEM; - } - - msg[0]= NINE_WORD_MSG_SIZE | SGL_OFFSET_7; - msg[1]= I2O_CMD_SW_UPLOAD<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2]= (u32)cfg_handler.context; - msg[3]= 0; - msg[4]= (u32)kxfer.flags<<24|(u32)kxfer.sw_type<<16|(u32)maxfrag<<8|(u32)curfrag; - msg[5]= swlen; - msg[6]= kxfer.sw_id; - msg[7]= (0xD0000000 | fragsize); - msg[8]= virt_to_bus(buffer); - -// printk("i2o_config: swul frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); - status = i2o_post_wait_mem(c, msg, sizeof(msg), 60, buffer, NULL); - i2o_unlock_controller(c); - - if (status != I2O_POST_WAIT_OK) - { - if(status != -ETIMEDOUT) - kfree(buffer); - printk(KERN_INFO "i2o_config: swul failed, DetailedStatus = %d\n", status); - return status; - } - - __copy_to_user(kxfer.buf, buffer, fragsize); - kfree(buffer); - - return 0; -} - -int ioctl_swdel(unsigned long arg) -{ - struct i2o_controller *c; - struct i2o_sw_xfer kxfer, *pxfer = (struct i2o_sw_xfer *)arg; - u32 msg[7]; - unsigned int swlen; - int token; - - if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) - return -EFAULT; - - if (get_user(swlen, kxfer.swlen) < 0) - return -EFAULT; - - c = i2o_find_controller(kxfer.iop); - if (!c) - return -ENXIO; - - msg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0; - msg[1] = I2O_CMD_SW_REMOVE<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2] = (u32)i2o_cfg_context; - msg[3] = 0; - msg[4] = (u32)kxfer.flags<<24 | (u32)kxfer.sw_type<<16; - msg[5] = swlen; - msg[6] = kxfer.sw_id; - - token = i2o_post_wait(c, msg, sizeof(msg), 10); - i2o_unlock_controller(c); - - if (token != I2O_POST_WAIT_OK) - { - printk(KERN_INFO "i2o_config: swdel failed, DetailedStatus = %d\n", token); - return -ETIMEDOUT; - } - - return 0; -} - -int ioctl_validate(unsigned long arg) -{ - int token; - int iop = (int)arg; - u32 msg[4]; - struct i2o_controller *c; - - c=i2o_find_controller(iop); - if (!c) - return -ENXIO; - - msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_CONFIG_VALIDATE<<24 | HOST_TID<<12 | iop; - msg[2] = (u32)i2o_cfg_context; - msg[3] = 0; - - token = i2o_post_wait(c, msg, sizeof(msg), 10); - i2o_unlock_controller(c); - - if (token != I2O_POST_WAIT_OK) - { - printk(KERN_INFO "Can't validate configuration, ErrorStatus = %d\n", - token); - return -ETIMEDOUT; - } - - return 0; -} - -static int ioctl_evt_reg(unsigned long arg, struct file *fp) -{ - u32 msg[5]; - struct i2o_evt_id *pdesc = (struct i2o_evt_id *)arg; - struct i2o_evt_id kdesc; - struct i2o_controller *iop; - struct i2o_device *d; - - if (copy_from_user(&kdesc, pdesc, sizeof(struct i2o_evt_id))) - return -EFAULT; - - /* IOP exists? */ - iop = i2o_find_controller(kdesc.iop); - if(!iop) - return -ENXIO; - i2o_unlock_controller(iop); - - /* Device exists? */ - for(d = iop->devices; d; d = d->next) - if(d->lct_data.tid == kdesc.tid) - break; - - if(!d) - return -ENODEV; - - msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_UTIL_EVT_REGISTER<<24 | HOST_TID<<12 | kdesc.tid; - msg[2] = (u32)i2o_cfg_context; - msg[3] = (u32)fp->private_data; - msg[4] = kdesc.evt_mask; - - i2o_post_this(iop, msg, 20); - - return 0; -} - -static int ioctl_evt_get(unsigned long arg, struct file *fp) -{ - u32 id = (u32)fp->private_data; - struct i2o_cfg_info *p = NULL; - struct i2o_evt_get *uget = (struct i2o_evt_get*)arg; - struct i2o_evt_get kget; - unsigned long flags; - - for(p = open_files; p; p = p->next) - if(p->q_id == id) - break; - - if(!p->q_len) - { - return -ENOENT; - return 0; - } - - memcpy(&kget.info, &p->event_q[p->q_out], sizeof(struct i2o_evt_info)); - MODINC(p->q_out, I2O_EVT_Q_LEN); - spin_lock_irqsave(&i2o_config_lock, flags); - p->q_len--; - kget.pending = p->q_len; - kget.lost = p->q_lost; - spin_unlock_irqrestore(&i2o_config_lock, flags); - - if(copy_to_user(uget, &kget, sizeof(struct i2o_evt_get))) - return -EFAULT; - return 0; -} - -static int cfg_open(struct inode *inode, struct file *file) -{ - struct i2o_cfg_info *tmp = - (struct i2o_cfg_info *)kmalloc(sizeof(struct i2o_cfg_info), GFP_KERNEL); - unsigned long flags; - - if(!tmp) - return -ENOMEM; - - file->private_data = (void*)(i2o_cfg_info_id++); - tmp->fp = file; - tmp->fasync = NULL; - tmp->q_id = (u32)file->private_data; - tmp->q_len = 0; - tmp->q_in = 0; - tmp->q_out = 0; - tmp->q_lost = 0; - tmp->next = open_files; - - spin_lock_irqsave(&i2o_config_lock, flags); - open_files = tmp; - spin_unlock_irqrestore(&i2o_config_lock, flags); - - return 0; -} - -static int cfg_release(struct inode *inode, struct file *file) -{ - u32 id = (u32)file->private_data; - struct i2o_cfg_info *p1, *p2; - unsigned long flags; - - lock_kernel(); - p1 = p2 = NULL; - - spin_lock_irqsave(&i2o_config_lock, flags); - for(p1 = open_files; p1; ) - { - if(p1->q_id == id) - { - - if(p1->fasync) - cfg_fasync(-1, file, 0); - if(p2) - p2->next = p1->next; - else - open_files = p1->next; - - kfree(p1); - break; - } - p2 = p1; - p1 = p1->next; - } - spin_unlock_irqrestore(&i2o_config_lock, flags); - unlock_kernel(); - - return 0; -} - -static int cfg_fasync(int fd, struct file *fp, int on) -{ - u32 id = (u32)fp->private_data; - struct i2o_cfg_info *p; - - for(p = open_files; p; p = p->next) - if(p->q_id == id) - break; - - if(!p) - return -EBADF; - - return fasync_helper(fd, fp, on, &p->fasync); -} - -static struct file_operations config_fops = -{ - owner: THIS_MODULE, - llseek: no_llseek, - read: cfg_read, - write: cfg_write, - ioctl: cfg_ioctl, - open: cfg_open, - release: cfg_release, - fasync: cfg_fasync, -}; - -static struct miscdevice i2o_miscdev = { - I2O_MINOR, - "i2octl", - &config_fops -}; - -#ifdef MODULE -int init_module(void) -#else -int __init i2o_config_init(void) -#endif -{ - printk(KERN_INFO "I2O configuration manager v 0.04.\n"); - printk(KERN_INFO " (C) Copyright 1999 Red Hat Software\n"); - - if((page_buf = kmalloc(4096, GFP_KERNEL))==NULL) - { - printk(KERN_ERR "i2o_config: no memory for page buffer.\n"); - return -ENOBUFS; - } - if(misc_register(&i2o_miscdev)==-1) - { - printk(KERN_ERR "i2o_config: can't register device.\n"); - kfree(page_buf); - return -EBUSY; - } - /* - * Install our handler - */ - if(i2o_install_handler(&cfg_handler)<0) - { - kfree(page_buf); - printk(KERN_ERR "i2o_config: handler register failed.\n"); - misc_deregister(&i2o_miscdev); - return -EBUSY; - } - /* - * The low 16bits of the transaction context must match this - * for everything we post. Otherwise someone else gets our mail - */ - i2o_cfg_context = cfg_handler.context; - return 0; -} - -#ifdef MODULE - -void cleanup_module(void) -{ - misc_deregister(&i2o_miscdev); - - if(page_buf) - kfree(page_buf); - if(i2o_cfg_context != -1) - i2o_remove_handler(&cfg_handler); -} - -EXPORT_NO_SYMBOLS; -MODULE_AUTHOR("Red Hat Software"); -MODULE_DESCRIPTION("I2O Configuration"); - -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_core.c linux.ac/drivers/i2o/i2o_core.c --- linux.vanilla/drivers/i2o/i2o_core.c Thu Aug 16 17:50:24 2001 +++ linux.ac/drivers/i2o/i2o_core.c Thu Jan 1 01:00:00 1970 @@ -1,3564 +0,0 @@ -/* - * Core I2O structure management - * - * (C) Copyright 1999 Red Hat Software - * - * Written by Alan Cox, Building Number Three Ltd - * - * 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. - * - * A lot of the I2O message side code from this is taken from the - * Red Creek RCPCI45 adapter driver by Red Creek Communications - * - * Fixes by: - * Philipp Rumpf - * Juha Sievänen - * Auvo Häkkinen - * Deepak Saxena - * Boji T Kannanthanam - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "i2o_lan.h" - -//#define DRIVERDEBUG - -#ifdef DRIVERDEBUG -#define dprintk(s, args...) printk(s, ## args) -#else -#define dprintk(s, args...) -#endif - -/* OSM table */ -static struct i2o_handler *i2o_handlers[MAX_I2O_MODULES]; - -/* Controller list */ -static struct i2o_controller *i2o_controllers[MAX_I2O_CONTROLLERS]; -struct i2o_controller *i2o_controller_chain; -int i2o_num_controllers; - -/* Initiator Context for Core message */ -static int core_context; - -/* Initialization && shutdown functions */ -static void i2o_sys_init(void); -static void i2o_sys_shutdown(void); -static int i2o_reset_controller(struct i2o_controller *); -static int i2o_reboot_event(struct notifier_block *, unsigned long , void *); -static int i2o_online_controller(struct i2o_controller *); -static int i2o_init_outbound_q(struct i2o_controller *); -static int i2o_post_outbound_messages(struct i2o_controller *); - -/* Reply handler */ -static void i2o_core_reply(struct i2o_handler *, struct i2o_controller *, - struct i2o_message *); - -/* Various helper functions */ -static int i2o_lct_get(struct i2o_controller *); -static int i2o_lct_notify(struct i2o_controller *); -static int i2o_hrt_get(struct i2o_controller *); - -static int i2o_build_sys_table(void); -static int i2o_systab_send(struct i2o_controller *c); - -/* I2O core event handler */ -static int i2o_core_evt(void *); -static int evt_pid; -static int evt_running; - -/* Dynamic LCT update handler */ -static int i2o_dyn_lct(void *); - -void i2o_report_controller_unit(struct i2o_controller *, struct i2o_device *); - -/* - * I2O System Table. Contains information about - * all the IOPs in the system. Used to inform IOPs - * about each other's existence. - * - * sys_tbl_ver is the CurrentChangeIndicator that is - * used by IOPs to track changes. - */ -static struct i2o_sys_tbl *sys_tbl; -static int sys_tbl_ind; -static int sys_tbl_len; - -/* - * This spin lock is used to keep a device from being - * added and deleted concurrently across CPUs or interrupts. - * This can occur when a user creates a device and immediatelly - * deletes it before the new_dev_notify() handler is called. - */ -static spinlock_t i2o_dev_lock = SPIN_LOCK_UNLOCKED; - -#ifdef MODULE -/* - * Function table to send to bus specific layers - * See for explanation of this - */ -static struct i2o_core_func_table i2o_core_functions = -{ - i2o_install_controller, - i2o_activate_controller, - i2o_find_controller, - i2o_unlock_controller, - i2o_run_queue, - i2o_delete_controller -}; - -#ifdef CONFIG_I2O_PCI_MODULE -extern int i2o_pci_core_attach(struct i2o_core_func_table *); -extern void i2o_pci_core_detach(void); -#endif /* CONFIG_I2O_PCI_MODULE */ - -#endif /* MODULE */ - -/* - * Structures and definitions for synchronous message posting. - * See i2o_post_wait() for description. - */ -struct i2o_post_wait_data -{ - int *status; /* Pointer to status block on caller stack */ - int *complete; /* Pointer to completion flag on caller stack */ - u32 id; /* Unique identifier */ - wait_queue_head_t *wq; /* Wake up for caller (NULL for dead) */ - struct i2o_post_wait_data *next; /* Chain */ - void *mem[2]; /* Memory blocks to recover on failure path */ -}; -static struct i2o_post_wait_data *post_wait_queue; -static u32 post_wait_id; // Unique ID for each post_wait -static spinlock_t post_wait_lock = SPIN_LOCK_UNLOCKED; -static void i2o_post_wait_complete(u32, int); - -/* OSM descriptor handler */ -static struct i2o_handler i2o_core_handler = -{ - (void *)i2o_core_reply, - NULL, - NULL, - NULL, - "I2O core layer", - 0, - I2O_CLASS_EXECUTIVE -}; - -/* - * Used when queueing a reply to be handled later - */ - -struct reply_info -{ - struct i2o_controller *iop; - u32 msg[MSG_FRAME_SIZE]; -}; -static struct reply_info evt_reply; -static struct reply_info events[I2O_EVT_Q_LEN]; -static int evt_in; -static int evt_out; -static int evt_q_len; -#define MODINC(x,y) ((x) = ((x) + 1) % (y)) - -/* - * I2O configuration spinlock. This isnt a big deal for contention - * so we have one only - */ - -static DECLARE_MUTEX(i2o_configuration_lock); - -/* - * Event spinlock. Used to keep event queue sane and from - * handling multiple events simultaneously. - */ -static spinlock_t i2o_evt_lock = SPIN_LOCK_UNLOCKED; - -/* - * Semaphore used to synchronize event handling thread with - * interrupt handler. - */ - -static DECLARE_MUTEX(evt_sem); -static DECLARE_COMPLETION(evt_dead); -DECLARE_WAIT_QUEUE_HEAD(evt_wait); - -static struct notifier_block i2o_reboot_notifier = -{ - i2o_reboot_event, - NULL, - 0 -}; - -/* - * Config options - */ - -static int verbose; -MODULE_PARM(verbose, "i"); - -/* - * I2O Core reply handler - */ -static void i2o_core_reply(struct i2o_handler *h, struct i2o_controller *c, - struct i2o_message *m) -{ - u32 *msg=(u32 *)m; - u32 status; - u32 context = msg[2]; - - if (msg[0] & MSG_FAIL) // Fail bit is set - { - u32 *preserved_msg = (u32*)(c->mem_offset + msg[7]); - - i2o_report_status(KERN_INFO, "i2o_core", msg); - i2o_dump_message(preserved_msg); - - /* If the failed request needs special treatment, - * it should be done here. */ - - /* Release the preserved msg by resubmitting it as a NOP */ - - preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; - preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0; - preserved_msg[2] = 0; - i2o_post_message(c, msg[7]); - - /* If reply to i2o_post_wait failed, return causes a timeout */ - - return; - } - -#ifdef DRIVERDEBUG - i2o_report_status(KERN_INFO, "i2o_core", msg); -#endif - - if(msg[2]&0x80000000) // Post wait message - { - if (msg[4] >> 24) - status = (msg[4] & 0xFFFF); - else - status = I2O_POST_WAIT_OK; - - i2o_post_wait_complete(context, status); - return; - } - - if(m->function == I2O_CMD_UTIL_EVT_REGISTER) - { - memcpy(events[evt_in].msg, msg, (msg[0]>>16)<<2); - events[evt_in].iop = c; - - spin_lock(&i2o_evt_lock); - MODINC(evt_in, I2O_EVT_Q_LEN); - if(evt_q_len == I2O_EVT_Q_LEN) - MODINC(evt_out, I2O_EVT_Q_LEN); - else - evt_q_len++; - spin_unlock(&i2o_evt_lock); - - up(&evt_sem); - wake_up_interruptible(&evt_wait); - return; - } - - if(m->function == I2O_CMD_LCT_NOTIFY) - { - up(&c->lct_sem); - return; - } - - /* - * If this happens, we want to dump the message to the syslog so - * it can be sent back to the card manufacturer by the end user - * to aid in debugging. - * - */ - printk(KERN_WARNING "%s: Unsolicited message reply sent to core!" - "Message dumped to syslog\n", - c->name); - i2o_dump_message(msg); - - return; -} - -/** - * i2o_install_handler - install a message handler - * @h: Handler structure - * - * Install an I2O handler - these handle the asynchronous messaging - * from the card once it has initialised. If the table of handlers is - * full then -ENOSPC is returned. On a success 0 is returned and the - * context field is set by the function. The structure is part of the - * system from this time onwards. It must not be freed until it has - * been uninstalled - */ - -int i2o_install_handler(struct i2o_handler *h) -{ - int i; - down(&i2o_configuration_lock); - for(i=0;icontext = i; - i2o_handlers[i]=h; - up(&i2o_configuration_lock); - return 0; - } - } - up(&i2o_configuration_lock); - return -ENOSPC; -} - -/** - * i2o_remove_handler - remove an i2o message handler - * @h: handler - * - * Remove a message handler previously installed with i2o_install_handler. - * After this function returns the handler object can be freed or re-used - */ - -int i2o_remove_handler(struct i2o_handler *h) -{ - i2o_handlers[h->context]=NULL; - return 0; -} - - -/* - * Each I2O controller has a chain of devices on it. - * Each device has a pointer to it's LCT entry to be used - * for fun purposes. - */ - -/** - * i2o_install_device - attach a device to a controller - * @c: controller - * @d: device - * - * Add a new device to an i2o controller. This can be called from - * non interrupt contexts only. It adds the device and marks it as - * unclaimed. The device memory becomes part of the kernel and must - * be uninstalled before being freed or reused. Zero is returned - * on success. - */ - -int i2o_install_device(struct i2o_controller *c, struct i2o_device *d) -{ - int i; - - down(&i2o_configuration_lock); - d->controller=c; - d->owner=NULL; - d->next=c->devices; - d->prev=NULL; - if (c->devices != NULL) - c->devices->prev=d; - c->devices=d; - *d->dev_name = 0; - - for(i = 0; i < I2O_MAX_MANAGERS; i++) - d->managers[i] = NULL; - - up(&i2o_configuration_lock); - return 0; -} - -/* we need this version to call out of i2o_delete_controller */ - -int __i2o_delete_device(struct i2o_device *d) -{ - struct i2o_device **p; - int i; - - p=&(d->controller->devices); - - /* - * Hey we have a driver! - * Check to see if the driver wants us to notify it of - * device deletion. If it doesn't we assume that it - * is unsafe to delete a device with an owner and - * fail. - */ - if(d->owner) - { - if(d->owner->dev_del_notify) - { - dprintk(KERN_INFO "Device has owner, notifying\n"); - d->owner->dev_del_notify(d->controller, d); - if(d->owner) - { - printk(KERN_WARNING - "Driver \"%s\" did not release device!\n", d->owner->name); - return -EBUSY; - } - } - else - return -EBUSY; - } - - /* - * Tell any other users who are talking to this device - * that it's going away. We assume that everything works. - */ - for(i=0; i < I2O_MAX_MANAGERS; i++) - { - if(d->managers[i] && d->managers[i]->dev_del_notify) - d->managers[i]->dev_del_notify(d->controller, d); - } - - while(*p!=NULL) - { - if(*p==d) - { - /* - * Destroy - */ - *p=d->next; - kfree(d); - return 0; - } - p=&((*p)->next); - } - printk(KERN_ERR "i2o_delete_device: passed invalid device.\n"); - return -EINVAL; -} - -/** - * i2o_delete_device - remove an i2o device - * @d: device to remove - * - * This function unhooks a device from a controller. The device - * will not be unhooked if it has an owner who does not wish to free - * it, or if the owner lacks a dev_del_notify function. In that case - * -EBUSY is returned. On success 0 is returned. Other errors cause - * negative errno values to be returned - */ - -int i2o_delete_device(struct i2o_device *d) -{ - int ret; - - down(&i2o_configuration_lock); - - /* - * Seek, locate - */ - - ret = __i2o_delete_device(d); - - up(&i2o_configuration_lock); - - return ret; -} - -/** - * i2o_install_controller - attach a controller - * @c: controller - * - * Add a new controller to the i2o layer. This can be called from - * non interrupt contexts only. It adds the controller and marks it as - * unused with no devices. If the tables are full or memory allocations - * fail then a negative errno code is returned. On success zero is - * returned and the controller is bound to the system. The structure - * must not be freed or reused until being uninstalled. - */ - -int i2o_install_controller(struct i2o_controller *c) -{ - int i; - down(&i2o_configuration_lock); - for(i=0;idlct = (i2o_lct*)kmalloc(8192, GFP_KERNEL); - if(c->dlct==NULL) - { - up(&i2o_configuration_lock); - return -ENOMEM; - } - i2o_controllers[i]=c; - c->devices = NULL; - c->next=i2o_controller_chain; - i2o_controller_chain=c; - c->unit = i; - c->page_frame = NULL; - c->hrt = NULL; - c->lct = NULL; - c->status_block = NULL; - sprintf(c->name, "i2o/iop%d", i); - i2o_num_controllers++; - init_MUTEX_LOCKED(&c->lct_sem); - up(&i2o_configuration_lock); - return 0; - } - } - printk(KERN_ERR "No free i2o controller slots.\n"); - up(&i2o_configuration_lock); - return -EBUSY; -} - -/** - * i2o_delete_controller - delete a controller - * @c: controller - * - * Remove an i2o controller from the system. If the controller or its - * devices are busy then -EBUSY is returned. On a failure a negative - * errno code is returned. On success zero is returned. - */ - -int i2o_delete_controller(struct i2o_controller *c) -{ - struct i2o_controller **p; - int users; - char name[16]; - int stat; - - dprintk(KERN_INFO "Deleting controller %s\n", c->name); - - /* - * Clear event registration as this can cause weird behavior - */ - if(c->status_block->iop_state == ADAPTER_STATE_OPERATIONAL) - i2o_event_register(c, core_context, 0, 0, 0); - - down(&i2o_configuration_lock); - if((users=atomic_read(&c->users))) - { - dprintk(KERN_INFO "I2O: %d users for controller %s\n", users, - c->name); - up(&i2o_configuration_lock); - return -EBUSY; - } - while(c->devices) - { - if(__i2o_delete_device(c->devices)<0) - { - /* Shouldnt happen */ - c->bus_disable(c); - up(&i2o_configuration_lock); - return -EBUSY; - } - } - - /* - * If this is shutdown time, the thread's already been killed - */ - if(c->lct_running) { - stat = kill_proc(c->lct_pid, SIGTERM, 1); - if(!stat) { - int count = 10 * 100; - while(c->lct_running && --count) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); - } - - if(!count) - printk(KERN_ERR - "%s: LCT thread still running!\n", - c->name); - } - } - - p=&i2o_controller_chain; - - while(*p) - { - if(*p==c) - { - /* Ask the IOP to switch to RESET state */ - i2o_reset_controller(c); - - /* Release IRQ */ - c->destructor(c); - - *p=c->next; - up(&i2o_configuration_lock); - - if(c->page_frame) - kfree(c->page_frame); - if(c->hrt) - kfree(c->hrt); - if(c->lct) - kfree(c->lct); - if(c->status_block) - kfree(c->status_block); - if(c->dlct) - kfree(c->dlct); - - i2o_controllers[c->unit]=NULL; - memcpy(name, c->name, strlen(c->name)+1); - kfree(c); - dprintk(KERN_INFO "%s: Deleted from controller chain.\n", name); - - i2o_num_controllers--; - return 0; - } - p=&((*p)->next); - } - up(&i2o_configuration_lock); - printk(KERN_ERR "i2o_delete_controller: bad pointer!\n"); - return -ENOENT; -} - -/** - * i2o_unlock_controller - unlock a controller - * @c: controller to unlock - * - * Take a lock on an i2o controller. This prevents it being deleted. - * i2o controllers are not refcounted so a deletion of an in use device - * will fail, not take affect on the last dereference. - */ - -void i2o_unlock_controller(struct i2o_controller *c) -{ - atomic_dec(&c->users); -} - -/** - * i2o_find_controller - return a locked controller - * @n: controller number - * - * Returns a pointer to the controller object. The controller is locked - * on return. NULL is returned if the controller is not found. - */ - -struct i2o_controller *i2o_find_controller(int n) -{ - struct i2o_controller *c; - - if(n<0 || n>=MAX_I2O_CONTROLLERS) - return NULL; - - down(&i2o_configuration_lock); - c=i2o_controllers[n]; - if(c!=NULL) - atomic_inc(&c->users); - up(&i2o_configuration_lock); - return c; -} - -/** - * i2o_issue_claim - claim or release a device - * @cmd: command - * @c: controller to claim for - * @tid: i2o task id - * @type: type of claim - * - * Issue I2O UTIL_CLAIM and UTIL_RELEASE messages. The message to be sent - * is set by cmd. The tid is the task id of the object to claim and the - * type is the claim type (see the i2o standard) - * - * Zero is returned on success. - */ - -static int i2o_issue_claim(u32 cmd, struct i2o_controller *c, int tid, u32 type) -{ - u32 msg[5]; - - msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; - msg[1] = cmd << 24 | HOST_TID<<12 | tid; - msg[3] = 0; - msg[4] = type; - - return i2o_post_wait(c, msg, sizeof(msg), 60); -} - -/* - * i2o_claim_device - claim a device for use by an OSM - * @d: device to claim - * @h: handler for this device - * - * Do the leg work to assign a device to a given OSM on Linux. The - * kernel updates the internal handler data for the device and then - * performs an I2O claim for the device, attempting to claim the - * device as primary. If the attempt fails a negative errno code - * is returned. On success zero is returned. - */ - -int i2o_claim_device(struct i2o_device *d, struct i2o_handler *h) -{ - down(&i2o_configuration_lock); - if (d->owner) { - printk(KERN_INFO "Device claim called, but dev already owned by %s!", - h->name); - up(&i2o_configuration_lock); - return -EBUSY; - } - d->owner=h; - - if(i2o_issue_claim(I2O_CMD_UTIL_CLAIM ,d->controller,d->lct_data.tid, - I2O_CLAIM_PRIMARY)) - { - d->owner = NULL; - return -EBUSY; - } - up(&i2o_configuration_lock); - return 0; -} - -/** - * i2o_release_device - release a device that the OSM is using - * @d: device to claim - * @h: handler for this device - * - * Drop a claim by an OSM on a given I2O device. The handler is cleared - * and 0 is returned on success. - * - * AC - some devices seem to want to refuse an unclaim until they have - * finished internal processing. It makes sense since you don't want a - * new device to go reconfiguring the entire system until you are done. - * Thus we are prepared to wait briefly. - */ - -int i2o_release_device(struct i2o_device *d, struct i2o_handler *h) -{ - int err = 0; - int tries; - - down(&i2o_configuration_lock); - if (d->owner != h) { - printk(KERN_INFO "Claim release called, but not owned by %s!\n", - h->name); - up(&i2o_configuration_lock); - return -ENOENT; - } - - for(tries=0;tries<10;tries++) - { - d->owner = NULL; - - /* - * If the controller takes a nonblocking approach to - * releases we have to sleep/poll for a few times. - */ - - if((err=i2o_issue_claim(I2O_CMD_UTIL_RELEASE, d->controller, d->lct_data.tid, I2O_CLAIM_PRIMARY)) ) - { - err = -ENXIO; - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(HZ); - } - else - { - err=0; - break; - } - } - up(&i2o_configuration_lock); - return err; -} - -/** - * i2o_device_notify_on - Enable deletion notifiers - * @d: device for notification - * @h: handler to install - * - * Called by OSMs to let the core know that they want to be - * notified if the given device is deleted from the system. - */ - -int i2o_device_notify_on(struct i2o_device *d, struct i2o_handler *h) -{ - int i; - - if(d->num_managers == I2O_MAX_MANAGERS) - return -ENOSPC; - - for(i = 0; i < I2O_MAX_MANAGERS; i++) - { - if(!d->managers[i]) - { - d->managers[i] = h; - break; - } - } - - d->num_managers++; - - return 0; -} - -/** - * i2o_device_notify_off - Remove deletion notifiers - * @d: device for notification - * @h: handler to remove - * - * Called by OSMs to let the core know that they no longer - * are interested in the fate of the given device. - */ -int i2o_device_notify_off(struct i2o_device *d, struct i2o_handler *h) -{ - int i; - - for(i=0; i < I2O_MAX_MANAGERS; i++) - { - if(d->managers[i] == h) - { - d->managers[i] = NULL; - d->num_managers--; - return 0; - } - } - - return -ENOENT; -} - -/** - * i2o_event_register - register interest in an event - * @c: Controller to register interest with - * @tid: I2O task id - * @init_context: initiator context to use with this notifier - * @tr_context: transaction context to use with this notifier - * @evt_mask: mask of events - * - * Create and posts an event registration message to the task. No reply - * is waited for, or expected. Errors in posting will be reported. - */ - -int i2o_event_register(struct i2o_controller *c, u32 tid, - u32 init_context, u32 tr_context, u32 evt_mask) -{ - u32 msg[5]; // Not performance critical, so we just - // i2o_post_this it instead of building it - // in IOP memory - - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_UTIL_EVT_REGISTER<<24 | HOST_TID<<12 | tid; - msg[2] = init_context; - msg[3] = tr_context; - msg[4] = evt_mask; - - return i2o_post_this(c, msg, sizeof(msg)); -} - -/* - * i2o_event_ack - acknowledge an event - * @c: controller - * @msg: pointer to the UTIL_EVENT_REGISTER reply we received - * - * We just take a pointer to the original UTIL_EVENT_REGISTER reply - * message and change the function code since that's what spec - * describes an EventAck message looking like. - */ - -int i2o_event_ack(struct i2o_controller *c, u32 *msg) -{ - struct i2o_message *m = (struct i2o_message *)msg; - - m->function = I2O_CMD_UTIL_EVT_ACK; - - return i2o_post_wait(c, msg, m->size * 4, 2); -} - -/* - * Core event handler. Runs as a separate thread and is woken - * up whenever there is an Executive class event. - */ -static int i2o_core_evt(void *reply_data) -{ - struct reply_info *reply = (struct reply_info *) reply_data; - u32 *msg = reply->msg; - struct i2o_controller *c = NULL; - unsigned long flags; - - lock_kernel(); - daemonize(); - unlock_kernel(); - - strcpy(current->comm, "i2oevtd"); - evt_running = 1; - - while(1) - { - if(down_interruptible(&evt_sem)) - { - dprintk(KERN_INFO "I2O event thread dead\n"); - printk("exiting..."); - evt_running = 0; - complete_and_exit(&evt_dead, 0); - } - - /* - * Copy the data out of the queue so that we don't have to lock - * around the whole function and just around the qlen update - */ - spin_lock_irqsave(&i2o_evt_lock, flags); - memcpy(reply, &events[evt_out], sizeof(struct reply_info)); - MODINC(evt_out, I2O_EVT_Q_LEN); - evt_q_len--; - spin_unlock_irqrestore(&i2o_evt_lock, flags); - - c = reply->iop; - dprintk(KERN_INFO "I2O IRTOS EVENT: iop%d, event %#10x\n", c->unit, msg[4]); - - /* - * We do not attempt to delete/quiesce/etc. the controller if - * some sort of error indidication occurs. We may want to do - * so in the future, but for now we just let the user deal with - * it. One reason for this is that what to do with an error - * or when to send what ćrror is not really agreed on, so - * we get errors that may not be fatal but just look like they - * are...so let the user deal with it. - */ - switch(msg[4]) - { - case I2O_EVT_IND_EXEC_RESOURCE_LIMITS: - printk(KERN_ERR "%s: Out of resources\n", c->name); - break; - - case I2O_EVT_IND_EXEC_POWER_FAIL: - printk(KERN_ERR "%s: Power failure\n", c->name); - break; - - case I2O_EVT_IND_EXEC_HW_FAIL: - { - char *fail[] = - { - "Unknown Error", - "Power Lost", - "Code Violation", - "Parity Error", - "Code Execution Exception", - "Watchdog Timer Expired" - }; - - if(msg[5] <= 6) - printk(KERN_ERR "%s: Hardware Failure: %s\n", - c->name, fail[msg[5]]); - else - printk(KERN_ERR "%s: Unknown Hardware Failure\n", c->name); - - break; - } - - /* - * New device created - * - Create a new i2o_device entry - * - Inform all interested drivers about this device's existence - */ - case I2O_EVT_IND_EXEC_NEW_LCT_ENTRY: - { - struct i2o_device *d = (struct i2o_device *) - kmalloc(sizeof(struct i2o_device), GFP_KERNEL); - int i; - - if (d == NULL) { - printk(KERN_EMERG "i2oevtd: out of memory\n"); - break; - } - memcpy(&d->lct_data, &msg[5], sizeof(i2o_lct_entry)); - - d->next = NULL; - d->controller = c; - d->flags = 0; - - i2o_report_controller_unit(c, d); - i2o_install_device(c,d); - - for(i = 0; i < MAX_I2O_MODULES; i++) - { - if(i2o_handlers[i] && - i2o_handlers[i]->new_dev_notify && - (i2o_handlers[i]->class&d->lct_data.class_id)) - { - spin_lock(&i2o_dev_lock); - i2o_handlers[i]->new_dev_notify(c,d); - spin_unlock(&i2o_dev_lock); - } - } - - break; - } - - /* - * LCT entry for a device has been modified, so update it - * internally. - */ - case I2O_EVT_IND_EXEC_MODIFIED_LCT: - { - struct i2o_device *d; - i2o_lct_entry *new_lct = (i2o_lct_entry *)&msg[5]; - - for(d = c->devices; d; d = d->next) - { - if(d->lct_data.tid == new_lct->tid) - { - memcpy(&d->lct_data, new_lct, sizeof(i2o_lct_entry)); - break; - } - } - break; - } - - case I2O_EVT_IND_CONFIGURATION_FLAG: - printk(KERN_WARNING "%s requires user configuration\n", c->name); - break; - - case I2O_EVT_IND_GENERAL_WARNING: - printk(KERN_WARNING "%s: Warning notification received!" - "Check configuration for errors!\n", c->name); - break; - - case I2O_EVT_IND_EVT_MASK_MODIFIED: - /* Well I guess that was us hey .. */ - break; - - default: - printk(KERN_WARNING "%s: No handler for event (0x%08x)\n", c->name, msg[4]); - break; - } - } - - return 0; -} - -/* - * Dynamic LCT update. This compares the LCT with the currently - * installed devices to check for device deletions..this needed b/c there - * is no DELETED_LCT_ENTRY EventIndicator for the Executive class so - * we can't just have the event handler do this...annoying - * - * This is a hole in the spec that will hopefully be fixed someday. - */ -static int i2o_dyn_lct(void *foo) -{ - struct i2o_controller *c = (struct i2o_controller *)foo; - struct i2o_device *d = NULL; - struct i2o_device *d1 = NULL; - int i = 0; - int found = 0; - int entries; - void *tmp; - char name[16]; - - lock_kernel(); - daemonize(); - unlock_kernel(); - - sprintf(name, "iop%d_lctd", c->unit); - strcpy(current->comm, name); - - c->lct_running = 1; - - while(1) - { - down_interruptible(&c->lct_sem); - if(signal_pending(current)) - { - dprintk(KERN_ERR "%s: LCT thread dead\n", c->name); - c->lct_running = 0; - return 0; - } - - entries = c->dlct->table_size; - entries -= 3; - entries /= 9; - - dprintk(KERN_INFO "%s: Dynamic LCT Update\n",c->name); - dprintk(KERN_INFO "%s: Dynamic LCT contains %d entries\n", c->name, entries); - - if(!entries) - { - printk(KERN_INFO "%s: Empty LCT???\n", c->name); - continue; - } - - /* - * Loop through all the devices on the IOP looking for their - * LCT data in the LCT. We assume that TIDs are not repeated. - * as that is the only way to really tell. It's been confirmed - * by the IRTOS vendor(s?) that TIDs are not reused until they - * wrap arround(4096), and I doubt a system will up long enough - * to create/delete that many devices. - */ - for(d = c->devices; d; ) - { - found = 0; - d1 = d->next; - - for(i = 0; i < entries; i++) - { - if(d->lct_data.tid == c->dlct->lct_entry[i].tid) - { - found = 1; - break; - } - } - if(!found) - { - dprintk(KERN_INFO "i2o_core: Deleted device!\n"); - spin_lock(&i2o_dev_lock); - i2o_delete_device(d); - spin_unlock(&i2o_dev_lock); - } - d = d1; - } - - /* - * Tell LCT to renotify us next time there is a change - */ - i2o_lct_notify(c); - - /* - * Copy new LCT into public LCT - * - * Possible race if someone is reading LCT while we are copying - * over it. If this happens, we'll fix it then. but I doubt that - * the LCT will get updated often enough or will get read by - * a user often enough to worry. - */ - if(c->lct->table_size < c->dlct->table_size) - { - tmp = c->lct; - c->lct = kmalloc(c->dlct->table_size<<2, GFP_KERNEL); - if(!c->lct) - { - printk(KERN_ERR "%s: No memory for LCT!\n", c->name); - c->lct = tmp; - continue; - } - kfree(tmp); - } - memcpy(c->lct, c->dlct, c->dlct->table_size<<2); - } - - return 0; -} - -/** - * i2o_run_queue - process pending events on a controller - * @c: controller to process - * - * This is called by the bus specific driver layer when an interrupt - * or poll of this card interface is desired. - */ - -void i2o_run_queue(struct i2o_controller *c) -{ - struct i2o_message *m; - u32 mv; - u32 *msg; - - /* - * Old 960 steppings had a bug in the I2O unit that caused - * the queue to appear empty when it wasn't. - */ - if((mv=I2O_REPLY_READ32(c))==0xFFFFFFFF) - mv=I2O_REPLY_READ32(c); - - while(mv!=0xFFFFFFFF) - { - struct i2o_handler *i; - m=(struct i2o_message *)bus_to_virt(mv); - msg=(u32*)m; - - i=i2o_handlers[m->initiator_context&(MAX_I2O_MODULES-1)]; - if(i && i->reply) - i->reply(i,c,m); - else - { - printk(KERN_WARNING "I2O: Spurious reply to handler %d\n", - m->initiator_context&(MAX_I2O_MODULES-1)); - } - i2o_flush_reply(c,mv); - mb(); - - /* That 960 bug again... */ - if((mv=I2O_REPLY_READ32(c))==0xFFFFFFFF) - mv=I2O_REPLY_READ32(c); - } -} - - -/** - * i2o_get_class_name - do i2o class name lookup - * @class: class number - * - * Return a descriptive string for an i2o class - */ - -const char *i2o_get_class_name(int class) -{ - int idx = 16; - static char *i2o_class_name[] = { - "Executive", - "Device Driver Module", - "Block Device", - "Tape Device", - "LAN Interface", - "WAN Interface", - "Fibre Channel Port", - "Fibre Channel Device", - "SCSI Device", - "ATE Port", - "ATE Device", - "Floppy Controller", - "Floppy Device", - "Secondary Bus Port", - "Peer Transport Agent", - "Peer Transport", - "Unknown" - }; - - switch(class&0xFFF) - { - case I2O_CLASS_EXECUTIVE: - idx = 0; break; - case I2O_CLASS_DDM: - idx = 1; break; - case I2O_CLASS_RANDOM_BLOCK_STORAGE: - idx = 2; break; - case I2O_CLASS_SEQUENTIAL_STORAGE: - idx = 3; break; - case I2O_CLASS_LAN: - idx = 4; break; - case I2O_CLASS_WAN: - idx = 5; break; - case I2O_CLASS_FIBRE_CHANNEL_PORT: - idx = 6; break; - case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL: - idx = 7; break; - case I2O_CLASS_SCSI_PERIPHERAL: - idx = 8; break; - case I2O_CLASS_ATE_PORT: - idx = 9; break; - case I2O_CLASS_ATE_PERIPHERAL: - idx = 10; break; - case I2O_CLASS_FLOPPY_CONTROLLER: - idx = 11; break; - case I2O_CLASS_FLOPPY_DEVICE: - idx = 12; break; - case I2O_CLASS_BUS_ADAPTER_PORT: - idx = 13; break; - case I2O_CLASS_PEER_TRANSPORT_AGENT: - idx = 14; break; - case I2O_CLASS_PEER_TRANSPORT: - idx = 15; break; - } - - return i2o_class_name[idx]; -} - - -/** - * i2o_wait_message - obtain an i2o message from the IOP - * @c: controller - * @why: explanation - * - * This function waits up to 5 seconds for a message slot to be - * available. If no message is available it prints an error message - * that is expected to be what the message will be used for (eg - * "get_status"). 0xFFFFFFFF is returned on a failure. - * - * On a success the message is returned. This is the physical page - * frame offset address from the read port. (See the i2o spec) - */ - -u32 i2o_wait_message(struct i2o_controller *c, char *why) -{ - long time=jiffies; - u32 m; - while((m=I2O_POST_READ32(c))==0xFFFFFFFF) - { - if((jiffies-time)>=5*HZ) - { - dprintk(KERN_ERR "%s: Timeout waiting for message frame to send %s.\n", - c->name, why); - return 0xFFFFFFFF; - } - schedule(); - barrier(); - } - return m; -} - -/** - * i2o_report_controller_unit - print information about a tid - * @c: controller - * @d: device - * - * Dump an information block associated with a given unit (TID). The - * tables are read and a block of text is output to printk that is - * formatted intended for the user. - */ - -void i2o_report_controller_unit(struct i2o_controller *c, struct i2o_device *d) -{ - char buf[64]; - char str[22]; - int ret; - int unit = d->lct_data.tid; - - if(verbose==0) - return; - - printk(KERN_INFO "Target ID %d.\n", unit); - if((ret=i2o_query_scalar(c, unit, 0xF100, 3, buf, 16))>=0) - { - buf[16]=0; - printk(KERN_INFO " Vendor: %s\n", buf); - } - if((ret=i2o_query_scalar(c, unit, 0xF100, 4, buf, 16))>=0) - { - buf[16]=0; - printk(KERN_INFO " Device: %s\n", buf); - } - if(i2o_query_scalar(c, unit, 0xF100, 5, buf, 16)>=0) - { - buf[16]=0; - printk(KERN_INFO " Description: %s\n", buf); - } - if((ret=i2o_query_scalar(c, unit, 0xF100, 6, buf, 8))>=0) - { - buf[8]=0; - printk(KERN_INFO " Rev: %s\n", buf); - } - - printk(KERN_INFO " Class: "); - sprintf(str, "%-21s", i2o_get_class_name(d->lct_data.class_id)); - printk("%s\n", str); - - printk(KERN_INFO " Subclass: 0x%04X\n", d->lct_data.sub_class); - printk(KERN_INFO " Flags: "); - - if(d->lct_data.device_flags&(1<<0)) - printk("C"); // ConfigDialog requested - if(d->lct_data.device_flags&(1<<1)) - printk("U"); // Multi-user capable - if(!(d->lct_data.device_flags&(1<<4))) - printk("P"); // Peer service enabled! - if(!(d->lct_data.device_flags&(1<<5))) - printk("M"); // Mgmt service enabled! - printk("\n"); - -} - - -/* - * Parse the hardware resource table. Right now we print it out - * and don't do a lot with it. We should collate these and then - * interact with the Linux resource allocation block. - * - * Lets prove we can read it first eh ? - * - * This is full of endianisms! - */ - -static int i2o_parse_hrt(struct i2o_controller *c) -{ -#ifdef DRIVERDEBUG - u32 *rows=(u32*)c->hrt; - u8 *p=(u8 *)c->hrt; - u8 *d; - int count; - int length; - int i; - int state; - - if(p[3]!=0) - { - printk(KERN_ERR "%s: HRT table for controller is too new a version.\n", - c->name); - return -1; - } - - count=p[0]|(p[1]<<8); - length = p[2]; - - printk(KERN_INFO "%s: HRT has %d entries of %d bytes each.\n", - c->name, count, length<<2); - - rows+=2; - - for(i=0;i>=12; - if(state&(1<<0)) - printk("H"); /* Hidden */ - if(state&(1<<2)) - { - printk("P"); /* Present */ - if(state&(1<<1)) - printk("C"); /* Controlled */ - } - if(state>9) - printk("*"); /* Hard */ - - printk("]:"); - - switch(p[3]&0xFFFF) - { - case 0: - /* Adapter private bus - easy */ - printk("Local bus %d: I/O at 0x%04X Mem 0x%08X", - p[2], d[1]<<8|d[0], *(u32 *)(d+4)); - break; - case 1: - /* ISA bus */ - printk("ISA %d: CSN %d I/O at 0x%04X Mem 0x%08X", - p[2], d[2], d[1]<<8|d[0], *(u32 *)(d+4)); - break; - - case 2: /* EISA bus */ - printk("EISA %d: Slot %d I/O at 0x%04X Mem 0x%08X", - p[2], d[3], d[1]<<8|d[0], *(u32 *)(d+4)); - break; - - case 3: /* MCA bus */ - printk("MCA %d: Slot %d I/O at 0x%04X Mem 0x%08X", - p[2], d[3], d[1]<<8|d[0], *(u32 *)(d+4)); - break; - - case 4: /* PCI bus */ - printk("PCI %d: Bus %d Device %d Function %d", - p[2], d[2], d[1], d[0]); - break; - - case 0x80: /* Other */ - default: - printk("Unsupported bus type."); - break; - } - printk("\n"); - rows+=length; - } -#endif - return 0; -} - -/* - * The logical configuration table tells us what we can talk to - * on the board. Most of the stuff isn't interesting to us. - */ - -static int i2o_parse_lct(struct i2o_controller *c) -{ - int i; - int max; - int tid; - struct i2o_device *d; - i2o_lct *lct = c->lct; - - if (lct == NULL) { - printk(KERN_ERR "%s: LCT is empty???\n", c->name); - return -1; - } - - max = lct->table_size; - max -= 3; - max /= 9; - - printk(KERN_INFO "%s: LCT has %d entries.\n", c->name, max); - - if(lct->iop_flags&(1<<0)) - printk(KERN_WARNING "%s: Configuration dialog desired.\n", c->name); - - for(i=0;icontroller = c; - d->next = NULL; - - memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry)); - - d->flags = 0; - tid = d->lct_data.tid; - - i2o_report_controller_unit(c, d); - - i2o_install_device(c, d); - } - return 0; -} - - -/** - * i2o_quiesce_controller - quiesce controller - * @c: controller - * - * Quiesce an IOP. Causes IOP to make external operation quiescent - * (i2o 'READY' state). Internal operation of the IOP continues normally. - */ - -int i2o_quiesce_controller(struct i2o_controller *c) -{ - u32 msg[4]; - int ret; - - i2o_status_get(c); - - /* SysQuiesce discarded if IOP not in READY or OPERATIONAL state */ - - if ((c->status_block->iop_state != ADAPTER_STATE_READY) && - (c->status_block->iop_state != ADAPTER_STATE_OPERATIONAL)) - { - return 0; - } - - msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID; - msg[3] = 0; - - /* Long timeout needed for quiesce if lots of devices */ - - if ((ret = i2o_post_wait(c, msg, sizeof(msg), 240))) - printk(KERN_INFO "%s: Unable to quiesce (status=%#x).\n", - c->name, -ret); - else - dprintk(KERN_INFO "%s: Quiesced.\n", c->name); - - i2o_status_get(c); // Entered READY state - return ret; -} - -/** - * i2o_enable_controller - move controller from ready to operational - * @c: controller - * - * Enable IOP. This allows the IOP to resume external operations and - * reverses the effect of a quiesce. In the event of an error a negative - * errno code is returned. - */ - -int i2o_enable_controller(struct i2o_controller *c) -{ - u32 msg[4]; - int ret; - - i2o_status_get(c); - - /* Enable only allowed on READY state */ - if(c->status_block->iop_state != ADAPTER_STATE_READY) - return -EINVAL; - - msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1]=I2O_CMD_SYS_ENABLE<<24|HOST_TID<<12|ADAPTER_TID; - - /* How long of a timeout do we need? */ - - if ((ret = i2o_post_wait(c, msg, sizeof(msg), 240))) - printk(KERN_ERR "%s: Could not enable (status=%#x).\n", - c->name, -ret); - else - dprintk(KERN_INFO "%s: Enabled.\n", c->name); - - i2o_status_get(c); // entered OPERATIONAL state - - return ret; -} - -/** - * i2o_clear_controller - clear a controller - * @c: controller - * - * Clear an IOP to HOLD state, ie. terminate external operations, clear all - * input queues and prepare for a system restart. IOP's internal operation - * continues normally and the outbound queue is alive. - * The IOP is not expected to rebuild its LCT. - */ - -int i2o_clear_controller(struct i2o_controller *c) -{ - struct i2o_controller *iop; - u32 msg[4]; - int ret; - - /* Quiesce all IOPs first */ - - for (iop = i2o_controller_chain; iop; iop = iop->next) - i2o_quiesce_controller(iop); - - msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1]=I2O_CMD_ADAPTER_CLEAR<<24|HOST_TID<<12|ADAPTER_TID; - msg[3]=0; - - if ((ret=i2o_post_wait(c, msg, sizeof(msg), 30))) - printk(KERN_INFO "%s: Unable to clear (status=%#x).\n", - c->name, -ret); - else - dprintk(KERN_INFO "%s: Cleared.\n",c->name); - - i2o_status_get(c); - - /* Enable other IOPs */ - - for (iop = i2o_controller_chain; iop; iop = iop->next) - if (iop != c) - i2o_enable_controller(iop); - - return ret; -} - - -/** - * i2o_reset_controller - reset an IOP - * @c: controller to reset - * - * Reset the IOP into INIT state and wait until IOP gets into RESET state. - * Terminate all external operations, clear IOP's inbound and outbound - * queues, terminate all DDMs, and reload the IOP's operating environment - * and all local DDMs. The IOP rebuilds its LCT. - */ - -static int i2o_reset_controller(struct i2o_controller *c) -{ - struct i2o_controller *iop; - u32 m; - u8 *status; - u32 *msg; - long time; - - /* Quiesce all IOPs first */ - - for (iop = i2o_controller_chain; iop; iop = iop->next) - { - if(iop->type != I2O_TYPE_PCI || !iop->bus.pci.dpt) - i2o_quiesce_controller(iop); - } - - m=i2o_wait_message(c, "AdapterReset"); - if(m==0xFFFFFFFF) - return -ETIMEDOUT; - msg=(u32 *)(c->mem_offset+m); - - status=(void *)kmalloc(4, GFP_KERNEL); - if(status==NULL) { - printk(KERN_ERR "IOP reset failed - no free memory.\n"); - return -ENOMEM; - } - memset(status, 0, 4); - - msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID; - msg[2]=core_context; - msg[3]=0; - msg[4]=0; - msg[5]=0; - msg[6]=virt_to_bus(status); - msg[7]=0; /* 64bit host FIXME */ - - i2o_post_message(c,m); - - /* Wait for a reply */ - time=jiffies; - while(*status==0) - { - if((jiffies-time)>=20*HZ) - { - printk(KERN_ERR "IOP reset timeout.\n"); - // Better to leak this for safety: kfree(status); - return -ETIMEDOUT; - } - schedule(); - barrier(); - } - - if (*status==I2O_CMD_IN_PROGRESS) - { - /* - * Once the reset is sent, the IOP goes into the INIT state - * which is indeterminate. We need to wait until the IOP - * has rebooted before we can let the system talk to - * it. We read the inbound Free_List until a message is - * available. If we can't read one in the given ammount of - * time, we assume the IOP could not reboot properly. - */ - - dprintk(KERN_INFO "%s: Reset in progress, waiting for reboot...\n", - c->name); - - time = jiffies; - m = I2O_POST_READ32(c); - while(m == 0XFFFFFFFF) - { - if((jiffies-time) >= 30*HZ) - { - printk(KERN_ERR "%s: Timeout waiting for IOP reset.\n", - c->name); - return -ETIMEDOUT; - } - schedule(); - barrier(); - m = I2O_POST_READ32(c); - } - i2o_flush_reply(c,m); - } - - /* If IopReset was rejected or didn't perform reset, try IopClear */ - - i2o_status_get(c); - if (status[0] == I2O_CMD_REJECTED || - c->status_block->iop_state != ADAPTER_STATE_RESET) - { - printk(KERN_WARNING "%s: Reset rejected, trying to clear\n",c->name); - i2o_clear_controller(c); - } - else - dprintk(KERN_INFO "%s: Reset completed.\n", c->name); - - /* Enable other IOPs */ - - for (iop = i2o_controller_chain; iop; iop = iop->next) - if (iop != c) - i2o_enable_controller(iop); - - kfree(status); - return 0; -} - - -/** - * i2o_status_get - get the status block for the IOP - * @c: controller - * - * Issue a status query on the controller. This updates the - * attached status_block. If the controller fails to reply or an - * error occurs then a negative errno code is returned. On success - * zero is returned and the status_blok is updated. - */ - -int i2o_status_get(struct i2o_controller *c) -{ - long time; - u32 m; - u32 *msg; - u8 *status_block; - - if (c->status_block == NULL) - { - c->status_block = (i2o_status_block *) - kmalloc(sizeof(i2o_status_block),GFP_KERNEL); - if (c->status_block == NULL) - { - printk(KERN_CRIT "%s: Get Status Block failed; Out of memory.\n", - c->name); - return -ENOMEM; - } - } - - status_block = (u8*)c->status_block; - memset(c->status_block,0,sizeof(i2o_status_block)); - - m=i2o_wait_message(c, "StatusGet"); - if(m==0xFFFFFFFF) - return -ETIMEDOUT; - msg=(u32 *)(c->mem_offset+m); - - msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1]=I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID; - msg[2]=core_context; - msg[3]=0; - msg[4]=0; - msg[5]=0; - msg[6]=virt_to_bus(c->status_block); - msg[7]=0; /* 64bit host FIXME */ - msg[8]=sizeof(i2o_status_block); /* always 88 bytes */ - - i2o_post_message(c,m); - - /* Wait for a reply */ - - time=jiffies; - while(status_block[87]!=0xFF) - { - if((jiffies-time)>=5*HZ) - { - printk(KERN_ERR "%s: Get status timeout.\n",c->name); - return -ETIMEDOUT; - } - schedule(); - barrier(); - } - -#ifdef DRIVERDEBUG - printk(KERN_INFO "%s: State = ", c->name); - switch (c->status_block->iop_state) { - case 0x01: - printk("INIT\n"); - break; - case 0x02: - printk("RESET\n"); - break; - case 0x04: - printk("HOLD\n"); - break; - case 0x05: - printk("READY\n"); - break; - case 0x08: - printk("OPERATIONAL\n"); - break; - case 0x10: - printk("FAILED\n"); - break; - case 0x11: - printk("FAULTED\n"); - break; - default: - printk("%x (unknown !!)\n",c->status_block->iop_state); -} -#endif - - return 0; -} - -/* - * Get the Hardware Resource Table for the device. - * The HRT contains information about possible hidden devices - * but is mostly useless to us - */ -int i2o_hrt_get(struct i2o_controller *c) -{ - u32 msg[6]; - int ret, size = sizeof(i2o_hrt); - - /* First read just the header to figure out the real size */ - - do { - if (c->hrt == NULL) { - c->hrt=kmalloc(size, GFP_KERNEL); - if (c->hrt == NULL) { - printk(KERN_CRIT "%s: Hrt Get failed; Out of memory.\n", c->name); - return -ENOMEM; - } - } - - msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4; - msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[3]= 0; - msg[4]= (0xD0000000 | size); /* Simple transaction */ - msg[5]= virt_to_bus(c->hrt); /* Dump it here */ - - ret = i2o_post_wait_mem(c, msg, sizeof(msg), 20, c->hrt, NULL); - - if(ret == -ETIMEDOUT) - { - /* The HRT block we used is in limbo somewhere. When the iop wakes up - we will recover it */ - c->hrt = NULL; - return ret; - } - - if(ret<0) - { - printk(KERN_ERR "%s: Unable to get HRT (status=%#x)\n", - c->name, -ret); - return ret; - } - - if (c->hrt->num_entries * c->hrt->entry_len << 2 > size) { - size = c->hrt->num_entries * c->hrt->entry_len << 2; - kfree(c->hrt); - c->hrt = NULL; - } - } while (c->hrt == NULL); - - i2o_parse_hrt(c); // just for debugging - - return 0; -} - -/* - * Send the I2O System Table to the specified IOP - * - * The system table contains information about all the IOPs in the - * system. It is build and then sent to each IOP so that IOPs can - * establish connections between each other. - * - */ -static int i2o_systab_send(struct i2o_controller *iop) -{ - u32 msg[12]; - int ret; - u32 *privbuf = kmalloc(16, GFP_KERNEL); - if(privbuf == NULL) - return -ENOMEM; - - if(iop->type == I2O_TYPE_PCI) - { - struct resource *root; - - if(iop->status_block->current_mem_size < iop->status_block->desired_mem_size) - { - struct resource *res = &iop->mem_resource; - res->name = iop->bus.pci.pdev->bus->name; - res->flags = IORESOURCE_MEM; - res->start = 0; - res->end = 0; - printk("%s: requires private memory resources.\n", iop->name); - root = pci_find_parent_resource(iop->bus.pci.pdev, res); - if(root==NULL) - printk("Can't find parent resource!\n"); - if(root && allocate_resource(root, res, - iop->status_block->desired_mem_size, - iop->status_block->desired_mem_size, - iop->status_block->desired_mem_size, - 1<<20, /* Unspecified, so use 1Mb and play safe */ - NULL, - NULL)>=0) - { - iop->mem_alloc = 1; - iop->status_block->current_mem_size = 1 + res->end - res->start; - iop->status_block->current_mem_base = res->start; - printk(KERN_INFO "%s: allocated %ld bytes of PCI memory at 0x%08lX.\n", - iop->name, 1+res->end-res->start, res->start); - } - } - if(iop->status_block->current_io_size < iop->status_block->desired_io_size) - { - struct resource *res = &iop->io_resource; - res->name = iop->bus.pci.pdev->bus->name; - res->flags = IORESOURCE_IO; - res->start = 0; - res->end = 0; - printk("%s: requires private memory resources.\n", iop->name); - root = pci_find_parent_resource(iop->bus.pci.pdev, res); - if(root==NULL) - printk("Can't find parent resource!\n"); - if(root && allocate_resource(root, res, - iop->status_block->desired_io_size, - iop->status_block->desired_io_size, - iop->status_block->desired_io_size, - 1<<20, /* Unspecified, so use 1Mb and play safe */ - NULL, - NULL)>=0) - { - iop->io_alloc = 1; - iop->status_block->current_io_size = 1 + res->end - res->start; - iop->status_block->current_mem_base = res->start; - printk(KERN_INFO "%s: allocated %ld bytes of PCI I/O at 0x%08lX.\n", - iop->name, 1+res->end-res->start, res->start); - } - } - } - else - { - privbuf[0] = iop->status_block->current_mem_base; - privbuf[1] = iop->status_block->current_mem_size; - privbuf[2] = iop->status_block->current_io_base; - privbuf[3] = iop->status_block->current_io_size; - } - - msg[0] = I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6; - msg[1] = I2O_CMD_SYS_TAB_SET<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[3] = 0; - msg[4] = (0<<16) | ((iop->unit+2) << 12); /* Host 0 IOP ID (unit + 2) */ - msg[5] = 0; /* Segment 0 */ - - /* - * Provide three SGL-elements: - * System table (SysTab), Private memory space declaration and - * Private i/o space declaration - * - * FIXME: provide these for controllers needing them - */ - msg[6] = 0x54000000 | sys_tbl_len; - msg[7] = virt_to_bus(sys_tbl); - msg[8] = 0x54000000 | 8; - msg[9] = virt_to_bus(privbuf); - msg[10] = 0xD4000000 | 8; - msg[11] = virt_to_bus(privbuf+2); - - ret=i2o_post_wait_mem(iop, msg, sizeof(msg), 120, privbuf, NULL); - - if(ret==-ETIMEDOUT) - { - printk(KERN_ERR "%s: SysTab setup timed out.\n", iop->name); - } - else if(ret<0) - { - printk(KERN_ERR "%s: Unable to set SysTab (status=%#x).\n", - iop->name, -ret); - kfree(privbuf); - } - else - { - dprintk(KERN_INFO "%s: SysTab set.\n", iop->name); - kfree(privbuf); - } - i2o_status_get(iop); // Entered READY state - - return ret; - - } - -/* - * Initialize I2O subsystem. - */ -static void __init i2o_sys_init(void) -{ - struct i2o_controller *iop, *niop = NULL; - - printk(KERN_INFO "Activating I2O controllers...\n"); - printk(KERN_INFO "This may take a few minutes if there are many devices\n"); - - /* In INIT state, Activate IOPs */ - for (iop = i2o_controller_chain; iop; iop = niop) { - dprintk(KERN_INFO "Calling i2o_activate_controller for %s...\n", - iop->name); - niop = iop->next; - if (i2o_activate_controller(iop) < 0) - i2o_delete_controller(iop); - } - - /* Active IOPs in HOLD state */ - -rebuild_sys_tab: - if (i2o_controller_chain == NULL) - return; - - /* - * If build_sys_table fails, we kill everything and bail - * as we can't init the IOPs w/o a system table - */ - dprintk(KERN_INFO "i2o_core: Calling i2o_build_sys_table...\n"); - if (i2o_build_sys_table() < 0) { - i2o_sys_shutdown(); - return; - } - - /* If IOP don't get online, we need to rebuild the System table */ - for (iop = i2o_controller_chain; iop; iop = niop) { - niop = iop->next; - dprintk(KERN_INFO "Calling i2o_online_controller for %s...\n", iop->name); - if (i2o_online_controller(iop) < 0) { - i2o_delete_controller(iop); - goto rebuild_sys_tab; - } - } - - /* Active IOPs now in OPERATIONAL state */ - - /* - * Register for status updates from all IOPs - */ - for(iop = i2o_controller_chain; iop; iop=iop->next) { - - /* Create a kernel thread to deal with dynamic LCT updates */ - iop->lct_pid = kernel_thread(i2o_dyn_lct, iop, CLONE_SIGHAND); - - /* Update change ind on DLCT */ - iop->dlct->change_ind = iop->lct->change_ind; - - /* Start dynamic LCT updates */ - i2o_lct_notify(iop); - - /* Register for all events from IRTOS */ - i2o_event_register(iop, core_context, 0, 0, 0xFFFFFFFF); - } -} - -/** - * i2o_sys_shutdown - shutdown I2O system - * - * Bring down each i2o controller and then return. Each controller - * is taken through an orderly shutdown - */ - -static void i2o_sys_shutdown(void) -{ - struct i2o_controller *iop, *niop; - - /* Delete all IOPs from the controller chain */ - /* that will reset all IOPs too */ - - for (iop = i2o_controller_chain; iop; iop = niop) { - niop = iop->next; - i2o_delete_controller(iop); - } -} - -/** - * i2o_activate_controller - bring controller up to HOLD - * @iop: controller - * - * This function brings an I2O controller into HOLD state. The adapter - * is reset if neccessary and then the queues and resource table - * are read. -1 is returned on a failure, 0 on success. - * - */ - -int i2o_activate_controller(struct i2o_controller *iop) -{ - /* In INIT state, Wait Inbound Q to initialize (in i2o_status_get) */ - /* In READY state, Get status */ - - if (i2o_status_get(iop) < 0) { - printk(KERN_INFO "Unable to obtain status of %s, " - "attempting a reset.\n", iop->name); - if (i2o_reset_controller(iop) < 0) - return -1; - } - - if(iop->status_block->iop_state == ADAPTER_STATE_FAULTED) { - printk(KERN_CRIT "%s: hardware fault\n", iop->name); - return -1; - } - - if (iop->status_block->i2o_version > I2OVER15) { - printk(KERN_ERR "%s: Not running vrs. 1.5. of the I2O Specification.\n", - iop->name); - return -1; - } - - if (iop->status_block->iop_state == ADAPTER_STATE_READY || - iop->status_block->iop_state == ADAPTER_STATE_OPERATIONAL || - iop->status_block->iop_state == ADAPTER_STATE_HOLD || - iop->status_block->iop_state == ADAPTER_STATE_FAILED) - { - dprintk(KERN_INFO "%s: Already running, trying to reset...\n", - iop->name); - if (i2o_reset_controller(iop) < 0) - return -1; - } - - if (i2o_init_outbound_q(iop) < 0) - return -1; - - if (i2o_post_outbound_messages(iop)) - return -1; - - /* In HOLD state */ - - if (i2o_hrt_get(iop) < 0) - return -1; - - return 0; -} - - -/** - * i2o_init_outbound_queue - setup the outbound queue - * @c: controller - * - * Clear and (re)initialize IOP's outbound queue. Returns 0 on - * success or a negative errno code on a failure. - */ - -int i2o_init_outbound_q(struct i2o_controller *c) -{ - u8 *status; - u32 m; - u32 *msg; - u32 time; - - dprintk(KERN_INFO "%s: Initializing Outbound Queue...\n", c->name); - m=i2o_wait_message(c, "OutboundInit"); - if(m==0xFFFFFFFF) - return -ETIMEDOUT; - msg=(u32 *)(c->mem_offset+m); - - status = kmalloc(4,GFP_KERNEL); - if (status==NULL) { - printk(KERN_ERR "%s: Outbound Queue initialization failed - no free memory.\n", - c->name); - return -ENOMEM; - } - memset(status, 0, 4); - - msg[0]= EIGHT_WORD_MSG_SIZE| TRL_OFFSET_6; - msg[1]= I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2]= core_context; - msg[3]= 0x0106; /* Transaction context */ - msg[4]= 4096; /* Host page frame size */ - /* Frame size is in words. Pick 128, its what everyone elses uses and - other sizes break some adapters. */ - msg[5]= MSG_FRAME_SIZE<<16|0x80; /* Outbound msg frame size and Initcode */ - msg[6]= 0xD0000004; /* Simple SG LE, EOB */ - msg[7]= virt_to_bus(status); - - i2o_post_message(c,m); - - barrier(); - time=jiffies; - while(status[0] < I2O_CMD_REJECTED) - { - if((jiffies-time)>=30*HZ) - { - if(status[0]==0x00) - printk(KERN_ERR "%s: Ignored queue initialize request.\n", - c->name); - else - printk(KERN_ERR "%s: Outbound queue initialize timeout.\n", - c->name); - kfree(status); - return -ETIMEDOUT; - } - schedule(); - barrier(); - } - - if(status[0] != I2O_CMD_COMPLETED) - { - printk(KERN_ERR "%s: IOP outbound initialise failed.\n", c->name); - kfree(status); - return -ETIMEDOUT; - } - - return 0; -} - -/** - * i2o_post_outbound_messages - fill message queue - * @c: controller - * - * Allocate a message frame and load the messages into the IOP. The - * function returns zero on success or a negative errno code on - * failure. - */ - -int i2o_post_outbound_messages(struct i2o_controller *c) -{ - int i; - u32 m; - /* Alloc space for IOP's outbound queue message frames */ - - c->page_frame = kmalloc(MSG_POOL_SIZE, GFP_KERNEL); - if(c->page_frame==NULL) { - printk(KERN_CRIT "%s: Outbound Q initialize failed; out of memory.\n", - c->name); - return -ENOMEM; - } - m=virt_to_bus(c->page_frame); - - /* Post frames */ - - for(i=0; i< NMBR_MSG_FRAMES; i++) { - I2O_REPLY_WRITE32(c,m); - mb(); - m += MSG_FRAME_SIZE; - } - - return 0; -} - -/* - * Get the IOP's Logical Configuration Table - */ -int i2o_lct_get(struct i2o_controller *c) -{ - u32 msg[8]; - int ret, size = c->status_block->expected_lct_size; - - do { - if (c->lct == NULL) { - c->lct = kmalloc(size, GFP_KERNEL); - if(c->lct == NULL) { - printk(KERN_CRIT "%s: Lct Get failed. Out of memory.\n", - c->name); - return -ENOMEM; - } - } - memset(c->lct, 0, size); - - msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6; - msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID; - /* msg[2] filled in i2o_post_wait */ - msg[3] = 0; - msg[4] = 0xFFFFFFFF; /* All devices */ - msg[5] = 0x00000000; /* Report now */ - msg[6] = 0xD0000000|size; - msg[7] = virt_to_bus(c->lct); - - ret=i2o_post_wait_mem(c, msg, sizeof(msg), 120, c->lct, NULL); - - if(ret == -ETIMEDOUT) - { - c->lct = NULL; - return ret; - } - - if(ret<0) - { - printk(KERN_ERR "%s: LCT Get failed (status=%#x.\n", - c->name, -ret); - return ret; - } - - if (c->lct->table_size << 2 > size) { - size = c->lct->table_size << 2; - kfree(c->lct); - c->lct = NULL; - } - } while (c->lct == NULL); - - if ((ret=i2o_parse_lct(c)) < 0) - return ret; - - return 0; -} - -/* - * Like above, but used for async notification. The main - * difference is that we keep track of the CurrentChangeIndiicator - * so that we only get updates when it actually changes. - * - */ -int i2o_lct_notify(struct i2o_controller *c) -{ - u32 msg[8]; - - msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6; - msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2] = core_context; - msg[3] = 0xDEADBEEF; - msg[4] = 0xFFFFFFFF; /* All devices */ - msg[5] = c->dlct->change_ind+1; /* Next change */ - msg[6] = 0xD0000000|8192; - msg[7] = virt_to_bus(c->dlct); - - return i2o_post_this(c, msg, sizeof(msg)); -} - -/* - * Bring a controller online into OPERATIONAL state. - */ - -int i2o_online_controller(struct i2o_controller *iop) -{ - u32 v; - - if (i2o_systab_send(iop) < 0) - return -1; - - /* In READY state */ - - dprintk(KERN_INFO "%s: Attempting to enable...\n", iop->name); - if (i2o_enable_controller(iop) < 0) - return -1; - - /* In OPERATIONAL state */ - - dprintk(KERN_INFO "%s: Attempting to get/parse lct...\n", iop->name); - if (i2o_lct_get(iop) < 0) - return -1; - - /* Check battery status */ - - iop->battery = 0; - if(i2o_query_scalar(iop, ADAPTER_TID, 0x0000, 4, &v, 4)>=0) - { - if(v&16) - iop->battery = 1; - } - - return 0; -} - -/* - * Build system table - * - * The system table contains information about all the IOPs in the - * system (duh) and is used by the Executives on the IOPs to establish - * peer2peer connections. We're not supporting peer2peer at the moment, - * but this will be needed down the road for things like lan2lan forwarding. - */ -static int i2o_build_sys_table(void) -{ - struct i2o_controller *iop = NULL; - struct i2o_controller *niop = NULL; - int count = 0; - - sys_tbl_len = sizeof(struct i2o_sys_tbl) + // Header + IOPs - (i2o_num_controllers) * - sizeof(struct i2o_sys_tbl_entry); - - if(sys_tbl) - kfree(sys_tbl); - - sys_tbl = kmalloc(sys_tbl_len, GFP_KERNEL); - if(!sys_tbl) { - printk(KERN_CRIT "SysTab Set failed. Out of memory.\n"); - return -ENOMEM; - } - memset((void*)sys_tbl, 0, sys_tbl_len); - - sys_tbl->num_entries = i2o_num_controllers; - sys_tbl->version = I2OVERSION; /* TODO: Version 2.0 */ - sys_tbl->change_ind = sys_tbl_ind++; - - for(iop = i2o_controller_chain; iop; iop = niop) - { - niop = iop->next; - - /* - * Get updated IOP state so we have the latest information - * - * We should delete the controller at this point if it - * doesn't respond since if it's not on the system table - * it is techninically not part of the I2O subsyßtem... - */ - if(i2o_status_get(iop)) { - printk(KERN_ERR "%s: Deleting b/c could not get status while" - "attempting to build system table\n", iop->name); - i2o_delete_controller(iop); - sys_tbl->num_entries--; - continue; // try the next one - } - - sys_tbl->iops[count].org_id = iop->status_block->org_id; - sys_tbl->iops[count].iop_id = iop->unit + 2; - sys_tbl->iops[count].seg_num = 0; - sys_tbl->iops[count].i2o_version = - iop->status_block->i2o_version; - sys_tbl->iops[count].iop_state = - iop->status_block->iop_state; - sys_tbl->iops[count].msg_type = - iop->status_block->msg_type; - sys_tbl->iops[count].frame_size = - iop->status_block->inbound_frame_size; - sys_tbl->iops[count].last_changed = sys_tbl_ind - 1; // ?? - sys_tbl->iops[count].iop_capabilities = - iop->status_block->iop_capabilities; - sys_tbl->iops[count].inbound_low = - (u32)virt_to_bus(iop->post_port); - sys_tbl->iops[count].inbound_high = 0; // TODO: 64-bit support - - count++; - } - -#ifdef DRIVERDEBUG -{ - u32 *table; - table = (u32*)sys_tbl; - for(count = 0; count < (sys_tbl_len >>2); count++) - printk(KERN_INFO "sys_tbl[%d] = %0#10x\n", count, table[count]); -} -#endif - - return 0; -} - - -/* - * Run time support routines - */ - -/* - * Generic "post and forget" helpers. This is less efficient - we do - * a memcpy for example that isnt strictly needed, but for most uses - * this is simply not worth optimising - */ - -int i2o_post_this(struct i2o_controller *c, u32 *data, int len) -{ - u32 m; - u32 *msg; - unsigned long t=jiffies; - - do - { - mb(); - m = I2O_POST_READ32(c); - } - while(m==0xFFFFFFFF && (jiffies-t)name); - return -ETIMEDOUT; - } - msg = (u32 *)(c->mem_offset + m); - memcpy_toio(msg, data, len); - i2o_post_message(c,m); - return 0; -} - -/** - * i2o_post_wait_mem - I2O query/reply with DMA buffers - * @c: controller - * @msg: message to send - * @len: length of message - * @timeout: time in seconds to wait - * @mem1: attached memory buffer 1 - * @mem2: attached memory buffer 2 - * - * This core API allows an OSM to post a message and then be told whether - * or not the system received a successful reply. - * - * If the message times out then the value '-ETIMEDOUT' is returned. This - * is a special case. In this situation the message may (should) complete - * at an indefinite time in the future. When it completes it will use the - * memory buffers attached to the request. If -ETIMEDOUT is returned then - * the memory buffers must not be freed. Instead the event completion will - * free them for you. In all other cases the buffers are your problem. - * - * Pass NULL for unneeded buffers. - */ - -int i2o_post_wait_mem(struct i2o_controller *c, u32 *msg, int len, int timeout, void *mem1, void *mem2) -{ - DECLARE_WAIT_QUEUE_HEAD(wq_i2o_post); - int complete = 0; - int status; - unsigned long flags = 0; - struct i2o_post_wait_data *wait_data = - kmalloc(sizeof(struct i2o_post_wait_data), GFP_KERNEL); - - if(!wait_data) - return -ENOMEM; - - /* - * Create a new notification object - */ - wait_data->status = &status; - wait_data->complete = &complete; - wait_data->mem[0] = mem1; - wait_data->mem[1] = mem2; - /* - * Queue the event with its unique id - */ - spin_lock_irqsave(&post_wait_lock, flags); - - wait_data->next = post_wait_queue; - post_wait_queue = wait_data; - wait_data->id = (++post_wait_id) & 0x7fff; - wait_data->wq = &wq_i2o_post; - - spin_unlock_irqrestore(&post_wait_lock, flags); - - /* - * Fill in the message id - */ - - msg[2] = 0x80000000|(u32)core_context|((u32)wait_data->id<<16); - - /* - * Post the message to the controller. At some point later it - * will return. If we time out before it returns then - * complete will be zero. From the point post_this returns - * the wait_data may have been deleted. - */ - if ((status = i2o_post_this(c, msg, len))==0) { - sleep_on_timeout(&wq_i2o_post, HZ * timeout); - } - else - return -EIO; - - if(signal_pending(current)) - status = -EINTR; - - spin_lock_irqsave(&post_wait_lock, flags); - barrier(); /* Be sure we see complete as it is locked */ - if(!complete) - { - /* - * Mark the entry dead. We cannot remove it. This is important. - * When it does terminate (which it must do if the controller hasnt - * died..) then it will otherwise scribble on stuff. - * !complete lets us safely check if the entry is still - * allocated and thus we can write into it - */ - wait_data->wq = NULL; - status = -ETIMEDOUT; - } - else - { - /* Debugging check - remove me soon */ - if(status == -ETIMEDOUT) - { - printk("TIMEDOUT BUG!\n"); - status = -EIO; - } - } - /* And the wait_data is not leaked either! */ - spin_unlock_irqrestore(&post_wait_lock, flags); - return status; -} - -/** - * i2o_post_wait - I2O query/reply - * @c: controller - * @msg: message to send - * @len: length of message - * @timeout: time in seconds to wait - * - * This core API allows an OSM to post a message and then be told whether - * or not the system received a successful reply. - */ - -int i2o_post_wait(struct i2o_controller *c, u32 *msg, int len, int timeout) -{ - return i2o_post_wait_mem(c, msg, len, timeout, NULL, NULL); -} - -/* - * i2o_post_wait is completed and we want to wake up the - * sleeping proccess. Called by core's reply handler. - */ - -static void i2o_post_wait_complete(u32 context, int status) -{ - struct i2o_post_wait_data **p1, *q; - unsigned long flags; - - /* - * We need to search through the post_wait - * queue to see if the given message is still - * outstanding. If not, it means that the IOP - * took longer to respond to the message than we - * had allowed and timer has already expired. - * Not much we can do about that except log - * it for debug purposes, increase timeout, and recompile - * - * Lock needed to keep anyone from moving queue pointers - * around while we're looking through them. - */ - - spin_lock_irqsave(&post_wait_lock, flags); - - for(p1 = &post_wait_queue; *p1!=NULL; p1 = &((*p1)->next)) - { - q = (*p1); - if(q->id == ((context >> 16) & 0x7fff)) { - /* - * Delete it - */ - - *p1 = q->next; - - /* - * Live or dead ? - */ - - if(q->wq) - { - /* Live entry - wakeup and set status */ - *q->status = status; - *q->complete = 1; - wake_up(q->wq); - } - else - { - /* - * Free resources. Caller is dead - */ - if(q->mem[0]) - kfree(q->mem[0]); - if(q->mem[1]) - kfree(q->mem[1]); - printk(KERN_WARNING "i2o_post_wait event completed after timeout.\n"); - } - kfree(q); - spin_unlock(&post_wait_lock); - return; - } - } - spin_unlock(&post_wait_lock); - - printk(KERN_DEBUG "i2o_post_wait: Bogus reply!\n"); -} - -/* Issue UTIL_PARAMS_GET or UTIL_PARAMS_SET - * - * This function can be used for all UtilParamsGet/Set operations. - * The OperationList is given in oplist-buffer, - * and results are returned in reslist-buffer. - * Note that the minimum sized reslist is 8 bytes and contains - * ResultCount, ErrorInfoSize, BlockStatus and BlockSize. - */ -int i2o_issue_params(int cmd, struct i2o_controller *iop, int tid, - void *oplist, int oplen, void *reslist, int reslen) -{ - u32 msg[9]; - u32 *res32 = (u32*)reslist; - u32 *restmp = (u32*)reslist; - int len = 0; - int i = 0; - int wait_status; - u32 *opmem, *resmem; - - /* Get DMAable memory */ - opmem = kmalloc(oplen, GFP_KERNEL); - if(opmem == NULL) - return -ENOMEM; - memcpy(opmem, oplist, oplen); - - resmem = kmalloc(reslen, GFP_KERNEL); - if(resmem == NULL) - { - kfree(opmem); - return -ENOMEM; - } - - msg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_5; - msg[1] = cmd << 24 | HOST_TID << 12 | tid; - msg[3] = 0; - msg[4] = 0; - msg[5] = 0x54000000 | oplen; /* OperationList */ - msg[6] = virt_to_bus(opmem); - msg[7] = 0xD0000000 | reslen; /* ResultList */ - msg[8] = virt_to_bus(resmem); - - wait_status = i2o_post_wait_mem(iop, msg, sizeof(msg), 10, opmem, resmem); - - /* - * This only looks like a memory leak - don't "fix" it. - */ - if(wait_status == -ETIMEDOUT) - return wait_status; - - /* Query failed */ - if(wait_status != 0) - { - kfree(resmem); - kfree(opmem); - return wait_status; - } - - memcpy(reslist, resmem, reslen); - /* - * Calculate number of bytes of Result LIST - * We need to loop through each Result BLOCK and grab the length - */ - restmp = res32 + 1; - len = 1; - for(i = 0; i < (res32[0]&0X0000FFFF); i++) - { - if(restmp[0]&0x00FF0000) /* BlockStatus != SUCCESS */ - { - printk(KERN_WARNING "%s - Error:\n ErrorInfoSize = 0x%02x, " - "BlockStatus = 0x%02x, BlockSize = 0x%04x\n", - (cmd == I2O_CMD_UTIL_PARAMS_SET) ? "PARAMS_SET" - : "PARAMS_GET", - res32[1]>>24, (res32[1]>>16)&0xFF, res32[1]&0xFFFF); - - /* - * If this is the only request,than we return an error - */ - if((res32[0]&0x0000FFFF) == 1) - { - return -((res32[1] >> 16) & 0xFF); /* -BlockStatus */ - } - } - len += restmp[0] & 0x0000FFFF; /* Length of res BLOCK */ - restmp += restmp[0] & 0x0000FFFF; /* Skip to next BLOCK */ - } - return (len << 2); /* bytes used by result list */ -} - -/* - * Query one scalar group value or a whole scalar group. - */ -int i2o_query_scalar(struct i2o_controller *iop, int tid, - int group, int field, void *buf, int buflen) -{ - u16 opblk[] = { 1, 0, I2O_PARAMS_FIELD_GET, group, 1, field }; - u8 resblk[8+buflen]; /* 8 bytes for header */ - int size; - - if (field == -1) /* whole group */ - opblk[4] = -1; - - size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_GET, iop, tid, - opblk, sizeof(opblk), resblk, sizeof(resblk)); - - memcpy(buf, resblk+8, buflen); /* cut off header */ - - if(size>buflen) - return buflen; - return size; -} - -/* - * Set a scalar group value or a whole group. - */ -int i2o_set_scalar(struct i2o_controller *iop, int tid, - int group, int field, void *buf, int buflen) -{ - u16 *opblk; - u8 resblk[8+buflen]; /* 8 bytes for header */ - int size; - - opblk = kmalloc(buflen+64, GFP_KERNEL); - if (opblk == NULL) - { - printk(KERN_ERR "i2o: no memory for operation buffer.\n"); - return -ENOMEM; - } - - opblk[0] = 1; /* operation count */ - opblk[1] = 0; /* pad */ - opblk[2] = I2O_PARAMS_FIELD_SET; - opblk[3] = group; - - if(field == -1) { /* whole group */ - opblk[4] = -1; - memcpy(opblk+5, buf, buflen); - } - else /* single field */ - { - opblk[4] = 1; - opblk[5] = field; - memcpy(opblk+6, buf, buflen); - } - - size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_SET, iop, tid, - opblk, 12+buflen, resblk, sizeof(resblk)); - - kfree(opblk); - if(size>buflen) - return buflen; - return size; -} - -/* - * if oper == I2O_PARAMS_TABLE_GET, get from all rows - * if fieldcount == -1 return all fields - * ibuf and ibuflen are unused (use NULL, 0) - * else return specific fields - * ibuf contains fieldindexes - * - * if oper == I2O_PARAMS_LIST_GET, get from specific rows - * if fieldcount == -1 return all fields - * ibuf contains rowcount, keyvalues - * else return specific fields - * fieldcount is # of fieldindexes - * ibuf contains fieldindexes, rowcount, keyvalues - * - * You could also use directly function i2o_issue_params(). - */ -int i2o_query_table(int oper, struct i2o_controller *iop, int tid, int group, - int fieldcount, void *ibuf, int ibuflen, - void *resblk, int reslen) -{ - u16 *opblk; - int size; - - opblk = kmalloc(10 + ibuflen, GFP_KERNEL); - if (opblk == NULL) - { - printk(KERN_ERR "i2o: no memory for query buffer.\n"); - return -ENOMEM; - } - - opblk[0] = 1; /* operation count */ - opblk[1] = 0; /* pad */ - opblk[2] = oper; - opblk[3] = group; - opblk[4] = fieldcount; - memcpy(opblk+5, ibuf, ibuflen); /* other params */ - - size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_GET,iop, tid, - opblk, 10+ibuflen, resblk, reslen); - - kfree(opblk); - if(size>reslen) - return reslen; - return size; -} - -/* - * Clear table group, i.e. delete all rows. - */ -int i2o_clear_table(struct i2o_controller *iop, int tid, int group) -{ - u16 opblk[] = { 1, 0, I2O_PARAMS_TABLE_CLEAR, group }; - u8 resblk[32]; /* min 8 bytes for result header */ - - return i2o_issue_params(I2O_CMD_UTIL_PARAMS_SET, iop, tid, - opblk, sizeof(opblk), resblk, sizeof(resblk)); -} - -/* - * Add a new row into a table group. - * - * if fieldcount==-1 then we add whole rows - * buf contains rowcount, keyvalues - * else just specific fields are given, rest use defaults - * buf contains fieldindexes, rowcount, keyvalues - */ -int i2o_row_add_table(struct i2o_controller *iop, int tid, - int group, int fieldcount, void *buf, int buflen) -{ - u16 *opblk; - u8 resblk[32]; /* min 8 bytes for header */ - int size; - - opblk = kmalloc(buflen+64, GFP_KERNEL); - if (opblk == NULL) - { - printk(KERN_ERR "i2o: no memory for operation buffer.\n"); - return -ENOMEM; - } - - opblk[0] = 1; /* operation count */ - opblk[1] = 0; /* pad */ - opblk[2] = I2O_PARAMS_ROW_ADD; - opblk[3] = group; - opblk[4] = fieldcount; - memcpy(opblk+5, buf, buflen); - - size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_SET, iop, tid, - opblk, 10+buflen, resblk, sizeof(resblk)); - - kfree(opblk); - if(size>buflen) - return buflen; - return size; -} - - -/* - * Used for error reporting/debugging purposes. - * Following fail status are common to all classes. - * The preserved message must be handled in the reply handler. - */ -void i2o_report_fail_status(u8 req_status, u32* msg) -{ - static char *FAIL_STATUS[] = { - "0x80", /* not used */ - "SERVICE_SUSPENDED", /* 0x81 */ - "SERVICE_TERMINATED", /* 0x82 */ - "CONGESTION", - "FAILURE", - "STATE_ERROR", - "TIME_OUT", - "ROUTING_FAILURE", - "INVALID_VERSION", - "INVALID_OFFSET", - "INVALID_MSG_FLAGS", - "FRAME_TOO_SMALL", - "FRAME_TOO_LARGE", - "INVALID_TARGET_ID", - "INVALID_INITIATOR_ID", - "INVALID_INITIATOR_CONTEX", /* 0x8F */ - "UNKNOWN_FAILURE" /* 0xFF */ - }; - - if (req_status == I2O_FSC_TRANSPORT_UNKNOWN_FAILURE) - printk("TRANSPORT_UNKNOWN_FAILURE (%0#2x)\n.", req_status); - else - printk("TRANSPORT_%s.\n", FAIL_STATUS[req_status & 0x0F]); - - /* Dump some details */ - - printk(KERN_ERR " InitiatorId = %d, TargetId = %d\n", - (msg[1] >> 12) & 0xFFF, msg[1] & 0xFFF); - printk(KERN_ERR " LowestVersion = 0x%02X, HighestVersion = 0x%02X\n", - (msg[4] >> 8) & 0xFF, msg[4] & 0xFF); - printk(KERN_ERR " FailingHostUnit = 0x%04X, FailingIOP = 0x%03X\n", - msg[5] >> 16, msg[5] & 0xFFF); - - printk(KERN_ERR " Severity: 0x%02X ", (msg[4] >> 16) & 0xFF); - if (msg[4] & (1<<16)) - printk("(FormatError), " - "this msg can never be delivered/processed.\n"); - if (msg[4] & (1<<17)) - printk("(PathError), " - "this msg can no longer be delivered/processed.\n"); - if (msg[4] & (1<<18)) - printk("(PathState), " - "the system state does not allow delivery.\n"); - if (msg[4] & (1<<19)) - printk("(Congestion), resources temporarily not available;" - "do not retry immediately.\n"); -} - -/* - * Used for error reporting/debugging purposes. - * Following reply status are common to all classes. - */ -void i2o_report_common_status(u8 req_status) -{ - static char *REPLY_STATUS[] = { - "SUCCESS", - "ABORT_DIRTY", - "ABORT_NO_DATA_TRANSFER", - "ABORT_PARTIAL_TRANSFER", - "ERROR_DIRTY", - "ERROR_NO_DATA_TRANSFER", - "ERROR_PARTIAL_TRANSFER", - "PROCESS_ABORT_DIRTY", - "PROCESS_ABORT_NO_DATA_TRANSFER", - "PROCESS_ABORT_PARTIAL_TRANSFER", - "TRANSACTION_ERROR", - "PROGRESS_REPORT" - }; - - if (req_status > I2O_REPLY_STATUS_PROGRESS_REPORT) - printk("RequestStatus = %0#2x", req_status); - else - printk("%s", REPLY_STATUS[req_status]); -} - -/* - * Used for error reporting/debugging purposes. - * Following detailed status are valid for executive class, - * utility class, DDM class and for transaction error replies. - */ -static void i2o_report_common_dsc(u16 detailed_status) -{ - static char *COMMON_DSC[] = { - "SUCCESS", - "0x01", // not used - "BAD_KEY", - "TCL_ERROR", - "REPLY_BUFFER_FULL", - "NO_SUCH_PAGE", - "INSUFFICIENT_RESOURCE_SOFT", - "INSUFFICIENT_RESOURCE_HARD", - "0x08", // not used - "CHAIN_BUFFER_TOO_LARGE", - "UNSUPPORTED_FUNCTION", - "DEVICE_LOCKED", - "DEVICE_RESET", - "INAPPROPRIATE_FUNCTION", - "INVALID_INITIATOR_ADDRESS", - "INVALID_MESSAGE_FLAGS", - "INVALID_OFFSET", - "INVALID_PARAMETER", - "INVALID_REQUEST", - "INVALID_TARGET_ADDRESS", - "MESSAGE_TOO_LARGE", - "MESSAGE_TOO_SMALL", - "MISSING_PARAMETER", - "TIMEOUT", - "UNKNOWN_ERROR", - "UNKNOWN_FUNCTION", - "UNSUPPORTED_VERSION", - "DEVICE_BUSY", - "DEVICE_NOT_AVAILABLE" - }; - - if (detailed_status > I2O_DSC_DEVICE_NOT_AVAILABLE) - printk(" / DetailedStatus = %0#4x.\n", detailed_status); - else - printk(" / %s.\n", COMMON_DSC[detailed_status]); -} - -/* - * Used for error reporting/debugging purposes - */ -static void i2o_report_lan_dsc(u16 detailed_status) -{ - static char *LAN_DSC[] = { // Lan detailed status code strings - "SUCCESS", - "DEVICE_FAILURE", - "DESTINATION_NOT_FOUND", - "TRANSMIT_ERROR", - "TRANSMIT_ABORTED", - "RECEIVE_ERROR", - "RECEIVE_ABORTED", - "DMA_ERROR", - "BAD_PACKET_DETECTED", - "OUT_OF_MEMORY", - "BUCKET_OVERRUN", - "IOP_INTERNAL_ERROR", - "CANCELED", - "INVALID_TRANSACTION_CONTEXT", - "DEST_ADDRESS_DETECTED", - "DEST_ADDRESS_OMITTED", - "PARTIAL_PACKET_RETURNED", - "TEMP_SUSPENDED_STATE", // last Lan detailed status code - "INVALID_REQUEST" // general detailed status code - }; - - if (detailed_status > I2O_DSC_INVALID_REQUEST) - printk(" / %0#4x.\n", detailed_status); - else - printk(" / %s.\n", LAN_DSC[detailed_status]); -} - -/* - * Used for error reporting/debugging purposes - */ -static void i2o_report_util_cmd(u8 cmd) -{ - switch (cmd) { - case I2O_CMD_UTIL_NOP: - printk("UTIL_NOP, "); - break; - case I2O_CMD_UTIL_ABORT: - printk("UTIL_ABORT, "); - break; - case I2O_CMD_UTIL_CLAIM: - printk("UTIL_CLAIM, "); - break; - case I2O_CMD_UTIL_RELEASE: - printk("UTIL_CLAIM_RELEASE, "); - break; - case I2O_CMD_UTIL_CONFIG_DIALOG: - printk("UTIL_CONFIG_DIALOG, "); - break; - case I2O_CMD_UTIL_DEVICE_RESERVE: - printk("UTIL_DEVICE_RESERVE, "); - break; - case I2O_CMD_UTIL_DEVICE_RELEASE: - printk("UTIL_DEVICE_RELEASE, "); - break; - case I2O_CMD_UTIL_EVT_ACK: - printk("UTIL_EVENT_ACKNOWLEDGE, "); - break; - case I2O_CMD_UTIL_EVT_REGISTER: - printk("UTIL_EVENT_REGISTER, "); - break; - case I2O_CMD_UTIL_LOCK: - printk("UTIL_LOCK, "); - break; - case I2O_CMD_UTIL_LOCK_RELEASE: - printk("UTIL_LOCK_RELEASE, "); - break; - case I2O_CMD_UTIL_PARAMS_GET: - printk("UTIL_PARAMS_GET, "); - break; - case I2O_CMD_UTIL_PARAMS_SET: - printk("UTIL_PARAMS_SET, "); - break; - case I2O_CMD_UTIL_REPLY_FAULT_NOTIFY: - printk("UTIL_REPLY_FAULT_NOTIFY, "); - break; - default: - printk("Cmd = %0#2x, ",cmd); - } -} - -/* - * Used for error reporting/debugging purposes - */ -static void i2o_report_exec_cmd(u8 cmd) -{ - switch (cmd) { - case I2O_CMD_ADAPTER_ASSIGN: - printk("EXEC_ADAPTER_ASSIGN, "); - break; - case I2O_CMD_ADAPTER_READ: - printk("EXEC_ADAPTER_READ, "); - break; - case I2O_CMD_ADAPTER_RELEASE: - printk("EXEC_ADAPTER_RELEASE, "); - break; - case I2O_CMD_BIOS_INFO_SET: - printk("EXEC_BIOS_INFO_SET, "); - break; - case I2O_CMD_BOOT_DEVICE_SET: - printk("EXEC_BOOT_DEVICE_SET, "); - break; - case I2O_CMD_CONFIG_VALIDATE: - printk("EXEC_CONFIG_VALIDATE, "); - break; - case I2O_CMD_CONN_SETUP: - printk("EXEC_CONN_SETUP, "); - break; - case I2O_CMD_DDM_DESTROY: - printk("EXEC_DDM_DESTROY, "); - break; - case I2O_CMD_DDM_ENABLE: - printk("EXEC_DDM_ENABLE, "); - break; - case I2O_CMD_DDM_QUIESCE: - printk("EXEC_DDM_QUIESCE, "); - break; - case I2O_CMD_DDM_RESET: - printk("EXEC_DDM_RESET, "); - break; - case I2O_CMD_DDM_SUSPEND: - printk("EXEC_DDM_SUSPEND, "); - break; - case I2O_CMD_DEVICE_ASSIGN: - printk("EXEC_DEVICE_ASSIGN, "); - break; - case I2O_CMD_DEVICE_RELEASE: - printk("EXEC_DEVICE_RELEASE, "); - break; - case I2O_CMD_HRT_GET: - printk("EXEC_HRT_GET, "); - break; - case I2O_CMD_ADAPTER_CLEAR: - printk("EXEC_IOP_CLEAR, "); - break; - case I2O_CMD_ADAPTER_CONNECT: - printk("EXEC_IOP_CONNECT, "); - break; - case I2O_CMD_ADAPTER_RESET: - printk("EXEC_IOP_RESET, "); - break; - case I2O_CMD_LCT_NOTIFY: - printk("EXEC_LCT_NOTIFY, "); - break; - case I2O_CMD_OUTBOUND_INIT: - printk("EXEC_OUTBOUND_INIT, "); - break; - case I2O_CMD_PATH_ENABLE: - printk("EXEC_PATH_ENABLE, "); - break; - case I2O_CMD_PATH_QUIESCE: - printk("EXEC_PATH_QUIESCE, "); - break; - case I2O_CMD_PATH_RESET: - printk("EXEC_PATH_RESET, "); - break; - case I2O_CMD_STATIC_MF_CREATE: - printk("EXEC_STATIC_MF_CREATE, "); - break; - case I2O_CMD_STATIC_MF_RELEASE: - printk("EXEC_STATIC_MF_RELEASE, "); - break; - case I2O_CMD_STATUS_GET: - printk("EXEC_STATUS_GET, "); - break; - case I2O_CMD_SW_DOWNLOAD: - printk("EXEC_SW_DOWNLOAD, "); - break; - case I2O_CMD_SW_UPLOAD: - printk("EXEC_SW_UPLOAD, "); - break; - case I2O_CMD_SW_REMOVE: - printk("EXEC_SW_REMOVE, "); - break; - case I2O_CMD_SYS_ENABLE: - printk("EXEC_SYS_ENABLE, "); - break; - case I2O_CMD_SYS_MODIFY: - printk("EXEC_SYS_MODIFY, "); - break; - case I2O_CMD_SYS_QUIESCE: - printk("EXEC_SYS_QUIESCE, "); - break; - case I2O_CMD_SYS_TAB_SET: - printk("EXEC_SYS_TAB_SET, "); - break; - default: - printk("Cmd = %#02x, ",cmd); - } -} - -/* - * Used for error reporting/debugging purposes - */ -static void i2o_report_lan_cmd(u8 cmd) -{ - switch (cmd) { - case LAN_PACKET_SEND: - printk("LAN_PACKET_SEND, "); - break; - case LAN_SDU_SEND: - printk("LAN_SDU_SEND, "); - break; - case LAN_RECEIVE_POST: - printk("LAN_RECEIVE_POST, "); - break; - case LAN_RESET: - printk("LAN_RESET, "); - break; - case LAN_SUSPEND: - printk("LAN_SUSPEND, "); - break; - default: - printk("Cmd = %0#2x, ",cmd); - } -} - -/* - * Used for error reporting/debugging purposes. - * Report Cmd name, Request status, Detailed Status. - */ -void i2o_report_status(const char *severity, const char *str, u32 *msg) -{ - u8 cmd = (msg[1]>>24)&0xFF; - u8 req_status = (msg[4]>>24)&0xFF; - u16 detailed_status = msg[4]&0xFFFF; - struct i2o_handler *h = i2o_handlers[msg[2] & (MAX_I2O_MODULES-1)]; - - printk("%s%s: ", severity, str); - - if (cmd < 0x1F) // Utility cmd - i2o_report_util_cmd(cmd); - - else if (cmd >= 0xA0 && cmd <= 0xEF) // Executive cmd - i2o_report_exec_cmd(cmd); - - else if (h->class == I2O_CLASS_LAN && cmd >= 0x30 && cmd <= 0x3F) - i2o_report_lan_cmd(cmd); // LAN cmd - else - printk("Cmd = %0#2x, ", cmd); // Other cmds - - if (msg[0] & MSG_FAIL) { - i2o_report_fail_status(req_status, msg); - return; - } - - i2o_report_common_status(req_status); - - if (cmd < 0x1F || (cmd >= 0xA0 && cmd <= 0xEF)) - i2o_report_common_dsc(detailed_status); - else if (h->class == I2O_CLASS_LAN && cmd >= 0x30 && cmd <= 0x3F) - i2o_report_lan_dsc(detailed_status); - else - printk(" / DetailedStatus = %0#4x.\n", detailed_status); -} - -/* Used to dump a message to syslog during debugging */ -void i2o_dump_message(u32 *msg) -{ -#ifdef DRIVERDEBUG - int i; - printk(KERN_INFO "Dumping I2O message size %d @ %p\n", - msg[0]>>16&0xffff, msg); - for(i = 0; i < ((msg[0]>>16)&0xffff); i++) - printk(KERN_INFO " msg[%d] = %0#10x\n", i, msg[i]); -#endif -} - -/* - * I2O reboot/shutdown notification. - * - * - Call each OSM's reboot notifier (if one exists) - * - Quiesce each IOP in the system - * - * Each IOP has to be quiesced before we can ensure that the system - * can be properly shutdown as a transaction that has already been - * acknowledged still needs to be placed in permanent store on the IOP. - * The SysQuiesce causes the IOP to force all HDMs to complete their - * transactions before returning, so only at that point is it safe - * - */ -static int i2o_reboot_event(struct notifier_block *n, unsigned long code, void -*p) -{ - int i = 0; - struct i2o_controller *c = NULL; - - if(code != SYS_RESTART && code != SYS_HALT && code != SYS_POWER_OFF) - return NOTIFY_DONE; - - printk(KERN_INFO "Shutting down I2O system.\n"); - printk(KERN_INFO - " This could take a few minutes if there are many devices attached\n"); - - for(i = 0; i < MAX_I2O_MODULES; i++) - { - if(i2o_handlers[i] && i2o_handlers[i]->reboot_notify) - i2o_handlers[i]->reboot_notify(); - } - - for(c = i2o_controller_chain; c; c = c->next) - { - if(i2o_quiesce_controller(c)) - { - printk(KERN_WARNING "i2o: Could not quiesce %s." " - Verify setup on next system power up.\n", c->name); - } - } - - printk(KERN_INFO "I2O system down.\n"); - return NOTIFY_DONE; -} - - -EXPORT_SYMBOL(i2o_controller_chain); -EXPORT_SYMBOL(i2o_num_controllers); -EXPORT_SYMBOL(i2o_find_controller); -EXPORT_SYMBOL(i2o_unlock_controller); -EXPORT_SYMBOL(i2o_status_get); - -EXPORT_SYMBOL(i2o_install_handler); -EXPORT_SYMBOL(i2o_remove_handler); - -EXPORT_SYMBOL(i2o_claim_device); -EXPORT_SYMBOL(i2o_release_device); -EXPORT_SYMBOL(i2o_device_notify_on); -EXPORT_SYMBOL(i2o_device_notify_off); - -EXPORT_SYMBOL(i2o_post_this); -EXPORT_SYMBOL(i2o_post_wait); -EXPORT_SYMBOL(i2o_post_wait_mem); - -EXPORT_SYMBOL(i2o_query_scalar); -EXPORT_SYMBOL(i2o_set_scalar); -EXPORT_SYMBOL(i2o_query_table); -EXPORT_SYMBOL(i2o_clear_table); -EXPORT_SYMBOL(i2o_row_add_table); -EXPORT_SYMBOL(i2o_issue_params); - -EXPORT_SYMBOL(i2o_event_register); -EXPORT_SYMBOL(i2o_event_ack); - -EXPORT_SYMBOL(i2o_report_status); -EXPORT_SYMBOL(i2o_dump_message); - -EXPORT_SYMBOL(i2o_get_class_name); - -#ifdef MODULE - -MODULE_AUTHOR("Red Hat Software"); -MODULE_DESCRIPTION("I2O Core"); - - -int init_module(void) -{ - printk(KERN_INFO "I2O Core - (C) Copyright 1999 Red Hat Software\n"); - if (i2o_install_handler(&i2o_core_handler) < 0) - { - printk(KERN_ERR - "i2o_core: Unable to install core handler.\nI2O stack not loaded!"); - return 0; - } - - core_context = i2o_core_handler.context; - - /* - * Attach core to I2O PCI transport (and others as they are developed) - */ -#ifdef CONFIG_I2O_PCI_MODULE - if(i2o_pci_core_attach(&i2o_core_functions) < 0) - printk(KERN_INFO "i2o: No PCI I2O controllers found\n"); -#endif - - /* - * Initialize event handling thread - */ - init_MUTEX_LOCKED(&evt_sem); - evt_pid = kernel_thread(i2o_core_evt, &evt_reply, CLONE_SIGHAND); - if(evt_pid < 0) - { - printk(KERN_ERR "I2O: Could not create event handler kernel thread\n"); - i2o_remove_handler(&i2o_core_handler); - return 0; - } - else - printk(KERN_INFO "I2O: Event thread created as pid %d\n", evt_pid); - - if(i2o_num_controllers) - i2o_sys_init(); - - register_reboot_notifier(&i2o_reboot_notifier); - - return 0; -} - -void cleanup_module(void) -{ - int stat; - - unregister_reboot_notifier(&i2o_reboot_notifier); - - if(i2o_num_controllers) - i2o_sys_shutdown(); - - /* - * If this is shutdown time, the thread has already been killed - */ - if(evt_running) { - printk("Terminating i2o threads..."); - stat = kill_proc(evt_pid, SIGTERM, 1); - if(!stat) { - printk("waiting..."); - wait_for_completion(&evt_dead); - } - printk("done.\n"); - } - -#ifdef CONFIG_I2O_PCI_MODULE - i2o_pci_core_detach(); -#endif - - i2o_remove_handler(&i2o_core_handler); - - unregister_reboot_notifier(&i2o_reboot_notifier); -} - -#else - -extern int i2o_block_init(void); -extern int i2o_config_init(void); -extern int i2o_lan_init(void); -extern int i2o_pci_init(void); -extern int i2o_proc_init(void); -extern int i2o_scsi_init(void); - -int __init i2o_init(void) -{ - printk(KERN_INFO "Loading I2O Core - (c) Copyright 1999 Red Hat Software\n"); - - if (i2o_install_handler(&i2o_core_handler) < 0) - { - printk(KERN_ERR - "i2o_core: Unable to install core handler.\nI2O stack not loaded!"); - return 0; - } - - core_context = i2o_core_handler.context; - - /* - * Initialize event handling thread - * We may not find any controllers, but still want this as - * down the road we may have hot pluggable controllers that - * need to be dealt with. - */ - init_MUTEX_LOCKED(&evt_sem); - if((evt_pid = kernel_thread(i2o_core_evt, &evt_reply, CLONE_SIGHAND)) < 0) - { - printk(KERN_ERR "I2O: Could not create event handler kernel thread\n"); - i2o_remove_handler(&i2o_core_handler); - return 0; - } - - -#ifdef CONFIG_I2O_PCI - i2o_pci_init(); -#endif - - if(i2o_num_controllers) - i2o_sys_init(); - - register_reboot_notifier(&i2o_reboot_notifier); - - i2o_config_init(); -#ifdef CONFIG_I2O_BLOCK - i2o_block_init(); -#endif -#ifdef CONFIG_I2O_LAN - i2o_lan_init(); -#endif -#ifdef CONFIG_I2O_PROC - i2o_proc_init(); -#endif - return 0; -} - -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_lan.c linux.ac/drivers/i2o/i2o_lan.c --- linux.vanilla/drivers/i2o/i2o_lan.c Sun Aug 12 19:16:18 2001 +++ linux.ac/drivers/i2o/i2o_lan.c Thu Jan 1 01:00:00 1970 @@ -1,1577 +0,0 @@ -/* - * drivers/i2o/i2o_lan.c - * - * I2O LAN CLASS OSM May 26th 2000 - * - * (C) Copyright 1999, 2000 University of Helsinki, - * Department of Computer Science - * - * This code is still under development / test. - * - * 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. - * - * Authors: Auvo Häkkinen - * Fixes: Juha Sievänen - * Taneli Vähäkangas - * Deepak Saxena - * - * Tested: in FDDI environment (using SysKonnect's DDM) - * in Gigabit Eth environment (using SysKonnect's DDM) - * in Fast Ethernet environment (using Intel 82558 DDM) - * - * TODO: tests for other LAN classes (Token Ring, Fibre Channel) - */ - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include "i2o_lan.h" - -//#define DRIVERDEBUG -#ifdef DRIVERDEBUG -#define dprintk(s, args...) printk(s, ## args) -#else -#define dprintk(s, args...) -#endif - -/* The following module parameters are used as default values - * for per interface values located in the net_device private area. - * Private values are changed via /proc filesystem. - */ -static u32 max_buckets_out = I2O_LAN_MAX_BUCKETS_OUT; -static u32 bucket_thresh = I2O_LAN_BUCKET_THRESH; -static u32 rx_copybreak = I2O_LAN_RX_COPYBREAK; -static u8 tx_batch_mode = I2O_LAN_TX_BATCH_MODE; -static u32 i2o_event_mask = I2O_LAN_EVENT_MASK; - -#define MAX_LAN_CARDS 16 -static struct net_device *i2o_landevs[MAX_LAN_CARDS+1]; -static int unit = -1; /* device unit number */ - -static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); -static void i2o_lan_send_post_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); -static int i2o_lan_receive_post(struct net_device *dev); -static void i2o_lan_receive_post_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); -static void i2o_lan_release_buckets(struct net_device *dev, u32 *msg); - -static int i2o_lan_reset(struct net_device *dev); -static void i2o_lan_handle_event(struct net_device *dev, u32 *msg); - -/* Structures to register handlers for the incoming replies. */ - -static struct i2o_handler i2o_lan_send_handler = { - i2o_lan_send_post_reply, // For send replies - NULL, - NULL, - NULL, - "I2O LAN OSM send", - -1, - I2O_CLASS_LAN -}; -static int lan_send_context; - -static struct i2o_handler i2o_lan_receive_handler = { - i2o_lan_receive_post_reply, // For receive replies - NULL, - NULL, - NULL, - "I2O LAN OSM receive", - -1, - I2O_CLASS_LAN -}; -static int lan_receive_context; - -static struct i2o_handler i2o_lan_handler = { - i2o_lan_reply, // For other replies - NULL, - NULL, - NULL, - "I2O LAN OSM", - -1, - I2O_CLASS_LAN -}; -static int lan_context; - -DECLARE_TASK_QUEUE(i2o_post_buckets_task); -struct tq_struct run_i2o_post_buckets_task = { - routine: (void (*)(void *)) run_task_queue, - data: (void *) 0 -}; - -/* Functions to handle message failures and transaction errors: -==============================================================*/ - -/* - * i2o_lan_handle_failure(): Fail bit has been set since IOP's message - * layer cannot deliver the request to the target, or the target cannot - * process the request. - */ -static void i2o_lan_handle_failure(struct net_device *dev, u32 *msg) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - - u32 *preserved_msg = (u32*)(iop->mem_offset + msg[7]); - u32 *sgl_elem = &preserved_msg[4]; - struct sk_buff *skb = NULL; - u8 le_flag; - - i2o_report_status(KERN_INFO, dev->name, msg); - - /* If PacketSend failed, free sk_buffs reserved by upper layers */ - - if (msg[1] >> 24 == LAN_PACKET_SEND) { - do { - skb = (struct sk_buff *)(sgl_elem[1]); - dev_kfree_skb_irq(skb); - - atomic_dec(&priv->tx_out); - - le_flag = *sgl_elem >> 31; - sgl_elem +=3; - } while (le_flag == 0); /* Last element flag not set */ - - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); - } - - /* If ReceivePost failed, free sk_buffs we have reserved */ - - if (msg[1] >> 24 == LAN_RECEIVE_POST) { - do { - skb = (struct sk_buff *)(sgl_elem[1]); - dev_kfree_skb_irq(skb); - - atomic_dec(&priv->buckets_out); - - le_flag = *sgl_elem >> 31; - sgl_elem +=3; - } while (le_flag == 0); /* Last element flag not set */ - } - - /* Release the preserved msg frame by resubmitting it as a NOP */ - - preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; - preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0; - preserved_msg[2] = 0; - i2o_post_message(iop, msg[7]); -} -/* - * i2o_lan_handle_transaction_error(): IOP or DDM has rejected the request - * for general cause (format error, bad function code, insufficient resources, - * etc.). We get one transaction_error for each failed transaction. - */ -static void i2o_lan_handle_transaction_error(struct net_device *dev, u32 *msg) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct sk_buff *skb; - - i2o_report_status(KERN_INFO, dev->name, msg); - - /* If PacketSend was rejected, free sk_buff reserved by upper layers */ - - if (msg[1] >> 24 == LAN_PACKET_SEND) { - skb = (struct sk_buff *)(msg[3]); // TransactionContext - dev_kfree_skb_irq(skb); - atomic_dec(&priv->tx_out); - - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); - } - - /* If ReceivePost was rejected, free sk_buff we have reserved */ - - if (msg[1] >> 24 == LAN_RECEIVE_POST) { - skb = (struct sk_buff *)(msg[3]); - dev_kfree_skb_irq(skb); - atomic_dec(&priv->buckets_out); - } -} - -/* - * i2o_lan_handle_status(): Common parts of handling a not succeeded request - * (status != SUCCESS). - */ -static int i2o_lan_handle_status(struct net_device *dev, u32 *msg) -{ - /* Fail bit set? */ - - if (msg[0] & MSG_FAIL) { - i2o_lan_handle_failure(dev, msg); - return -1; - } - - /* Message rejected for general cause? */ - - if ((msg[4]>>24) == I2O_REPLY_STATUS_TRANSACTION_ERROR) { - i2o_lan_handle_transaction_error(dev, msg); - return -1; - } - - /* Else have to handle it in the callback function */ - - return 0; -} - -/* Callback functions called from the interrupt routine: -=======================================================*/ - -/* - * i2o_lan_send_post_reply(): Callback function to handle PostSend replies. - */ -static void i2o_lan_send_post_reply(struct i2o_handler *h, - struct i2o_controller *iop, struct i2o_message *m) -{ - u32 *msg = (u32 *)m; - u8 unit = (u8)(msg[2]>>16); // InitiatorContext - struct net_device *dev = i2o_landevs[unit]; - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - u8 trl_count = msg[3] & 0x000000FF; - - if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { - if (i2o_lan_handle_status(dev, msg)) - return; - } - -#ifdef DRIVERDEBUG - i2o_report_status(KERN_INFO, dev->name, msg); -#endif - - /* DDM has handled transmit request(s), free sk_buffs. - * We get similar single transaction reply also in error cases - * (except if msg failure or transaction error). - */ - while (trl_count) { - dev_kfree_skb_irq((struct sk_buff *)msg[4 + trl_count]); - dprintk(KERN_INFO "%s: tx skb freed (trl_count=%d).\n", - dev->name, trl_count); - atomic_dec(&priv->tx_out); - trl_count--; - } - - /* If priv->tx_out had reached tx_max_out, the queue was stopped */ - - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); -} - -/* - * i2o_lan_receive_post_reply(): Callback function to process incoming packets. - */ -static void i2o_lan_receive_post_reply(struct i2o_handler *h, - struct i2o_controller *iop, struct i2o_message *m) -{ - u32 *msg = (u32 *)m; - u8 unit = (u8)(msg[2]>>16); // InitiatorContext - struct net_device *dev = i2o_landevs[unit]; - - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_bucket_descriptor *bucket = (struct i2o_bucket_descriptor *)&msg[6]; - struct i2o_packet_info *packet; - u8 trl_count = msg[3] & 0x000000FF; - struct sk_buff *skb, *old_skb; - unsigned long flags = 0; - - if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { - if (i2o_lan_handle_status(dev, msg)) - return; - - i2o_lan_release_buckets(dev, msg); - return; - } - -#ifdef DRIVERDEBUG - i2o_report_status(KERN_INFO, dev->name, msg); -#endif - - /* Else we are receiving incoming post. */ - - while (trl_count--) { - skb = (struct sk_buff *)bucket->context; - packet = (struct i2o_packet_info *)bucket->packet_info; - atomic_dec(&priv->buckets_out); - - /* Sanity checks: Any weird characteristics in bucket? */ - - if (packet->flags & 0x0f || ! packet->flags & 0x40) { - if (packet->flags & 0x01) - printk(KERN_WARNING "%s: packet with errors, error code=0x%02x.\n", - dev->name, packet->status & 0xff); - - /* The following shouldn't happen, unless parameters in - * LAN_OPERATION group are changed during the run time. - */ - if (packet->flags & 0x0c) - printk(KERN_DEBUG "%s: multi-bucket packets not supported!\n", - dev->name); - - if (! packet->flags & 0x40) - printk(KERN_DEBUG "%s: multiple packets in a bucket not supported!\n", - dev->name); - - dev_kfree_skb_irq(skb); - - bucket++; - continue; - } - - /* Copy short packet to a new skb */ - - if (packet->len < priv->rx_copybreak) { - old_skb = skb; - skb = (struct sk_buff *)dev_alloc_skb(packet->len+2); - if (skb == NULL) { - printk(KERN_ERR "%s: Can't allocate skb.\n", dev->name); - return; - } - skb_reserve(skb, 2); - memcpy(skb_put(skb, packet->len), old_skb->data, packet->len); - - spin_lock_irqsave(&priv->fbl_lock, flags); - if (priv->i2o_fbl_tail < I2O_LAN_MAX_BUCKETS_OUT) - priv->i2o_fbl[++priv->i2o_fbl_tail] = old_skb; - else - dev_kfree_skb_irq(old_skb); - - spin_unlock_irqrestore(&priv->fbl_lock, flags); - } else - skb_put(skb, packet->len); - - /* Deliver to upper layers */ - - skb->dev = dev; - skb->protocol = priv->type_trans(skb, dev); - netif_rx(skb); - - dev->last_rx = jiffies; - - dprintk(KERN_INFO "%s: Incoming packet (%d bytes) delivered " - "to upper level.\n", dev->name, packet->len); - - bucket++; // to next Packet Descriptor Block - } - -#ifdef DRIVERDEBUG - if (msg[5] == 0) - printk(KERN_INFO "%s: DDM out of buckets (priv->count = %d)!\n", - dev->name, atomic_read(&priv->buckets_out)); -#endif - - /* If DDM has already consumed bucket_thresh buckets, post new ones */ - - if (atomic_read(&priv->buckets_out) <= priv->max_buckets_out - priv->bucket_thresh) { - run_i2o_post_buckets_task.data = (void *)dev; - queue_task(&run_i2o_post_buckets_task, &tq_immediate); - mark_bh(IMMEDIATE_BH); - } - - return; -} - -/* - * i2o_lan_reply(): Callback function to handle other incoming messages - * except SendPost and ReceivePost. - */ -static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, - struct i2o_message *m) -{ - u32 *msg = (u32 *)m; - u8 unit = (u8)(msg[2]>>16); // InitiatorContext - struct net_device *dev = i2o_landevs[unit]; - - if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { - if (i2o_lan_handle_status(dev, msg)) - return; - - /* In other error cases just report and continue */ - - i2o_report_status(KERN_INFO, dev->name, msg); - } - -#ifdef DRIVERDEBUG - i2o_report_status(KERN_INFO, dev->name, msg); -#endif - switch (msg[1] >> 24) { - case LAN_RESET: - case LAN_SUSPEND: - /* default reply without payload */ - break; - - case I2O_CMD_UTIL_EVT_REGISTER: - case I2O_CMD_UTIL_EVT_ACK: - i2o_lan_handle_event(dev, msg); - break; - - case I2O_CMD_UTIL_PARAMS_SET: - /* default reply, results in ReplyPayload (not examined) */ - switch (msg[3] >> 16) { - case 1: dprintk(KERN_INFO "%s: Reply to set MAC filter mask.\n", - dev->name); - break; - case 2: dprintk(KERN_INFO "%s: Reply to set MAC table.\n", - dev->name); - break; - default: printk(KERN_WARNING "%s: Bad group 0x%04X\n", - dev->name,msg[3] >> 16); - } - break; - - default: - printk(KERN_ERR "%s: No handler for the reply.\n", - dev->name); - i2o_report_status(KERN_INFO, dev->name, msg); - } -} - -/* Functions used by the above callback functions: -=================================================*/ -/* - * i2o_lan_release_buckets(): Free unused buckets (sk_buffs). - */ -static void i2o_lan_release_buckets(struct net_device *dev, u32 *msg) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - u8 trl_elem_size = (u8)(msg[3]>>8 & 0x000000FF); - u8 trl_count = (u8)(msg[3] & 0x000000FF); - u32 *pskb = &msg[6]; - - while (trl_count--) { - dprintk(KERN_DEBUG "%s: Releasing unused rx skb %p (trl_count=%d).\n", - dev->name, (struct sk_buff*)(*pskb),trl_count+1); - dev_kfree_skb_irq((struct sk_buff *)(*pskb)); - pskb += 1 + trl_elem_size; - atomic_dec(&priv->buckets_out); - } -} - -/* - * i2o_lan_event_reply(): Handle events. - */ -static void i2o_lan_handle_event(struct net_device *dev, u32 *msg) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u32 max_evt_data_size =iop->status_block->inbound_frame_size-5; - struct i2o_reply { - u32 header[4]; - u32 evt_indicator; - u32 data[max_evt_data_size]; - } *evt = (struct i2o_reply *)msg; - int evt_data_len = ((msg[0]>>16) - 5) * 4; /* real size*/ - - printk(KERN_INFO "%s: I2O event - ", dev->name); - - if (msg[1]>>24 == I2O_CMD_UTIL_EVT_ACK) { - printk("Event acknowledgement reply.\n"); - return; - } - - /* Else evt->function == I2O_CMD_UTIL_EVT_REGISTER) */ - - switch (evt->evt_indicator) { - case I2O_EVT_IND_STATE_CHANGE: { - struct state_data { - u16 status; - u8 state; - u8 data; - } *evt_data = (struct state_data *)(evt->data[0]); - - printk("State chance 0x%08x.\n", evt->data[0]); - - /* If the DDM is in error state, recovery may be - * possible if status = Transmit or Receive Control - * Unit Inoperable. - */ - if (evt_data->state==0x05 && evt_data->status==0x0003) - i2o_lan_reset(dev); - break; - } - - case I2O_EVT_IND_FIELD_MODIFIED: { - u16 *work16 = (u16 *)evt->data; - printk("Group 0x%04x, field %d changed.\n", work16[0], work16[1]); - break; - } - - case I2O_EVT_IND_VENDOR_EVT: { - int i; - printk("Vendor event:\n"); - for (i = 0; i < evt_data_len / 4; i++) - printk(" 0x%08x\n", evt->data[i]); - break; - } - - case I2O_EVT_IND_DEVICE_RESET: - /* Spec 2.0 p. 6-121: - * The event of _DEVICE_RESET should also be responded - */ - printk("Device reset.\n"); - if (i2o_event_ack(iop, msg) < 0) - printk("%s: Event Acknowledge timeout.\n", dev->name); - break; - -#if 0 - case I2O_EVT_IND_EVT_MASK_MODIFIED: - printk("Event mask modified, 0x%08x.\n", evt->data[0]); - break; - - case I2O_EVT_IND_GENERAL_WARNING: - printk("General warning 0x%04x.\n", evt->data[0]); - break; - - case I2O_EVT_IND_CONFIGURATION_FLAG: - printk("Configuration requested.\n"); - break; - - case I2O_EVT_IND_CAPABILITY_CHANGE: - printk("Capability change 0x%04x.\n", evt->data[0]); - break; - - case I2O_EVT_IND_DEVICE_STATE: - printk("Device state changed 0x%08x.\n", evt->data[0]); - break; -#endif - case I2O_LAN_EVT_LINK_DOWN: - netif_carrier_off(dev); - printk("Link to the physical device is lost.\n"); - break; - - case I2O_LAN_EVT_LINK_UP: - netif_carrier_on(dev); - printk("Link to the physical device is (re)established.\n"); - break; - - case I2O_LAN_EVT_MEDIA_CHANGE: - printk("Media change.\n"); - break; - default: - printk("0x%08x. No handler.\n", evt->evt_indicator); - } -} - -/* - * i2o_lan_receive_post(): Post buckets to receive packets. - */ -static int i2o_lan_receive_post(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - struct sk_buff *skb; - u32 m, *msg; - u32 bucket_len = (dev->mtu + dev->hard_header_len); - u32 total = priv->max_buckets_out - atomic_read(&priv->buckets_out); - u32 bucket_count; - u32 *sgl_elem; - unsigned long flags; - - /* Send (total/bucket_count) separate I2O requests */ - - while (total) { - m = I2O_POST_READ32(iop); - if (m == 0xFFFFFFFF) - return -ETIMEDOUT; - msg = (u32 *)(iop->mem_offset + m); - - bucket_count = (total >= priv->sgl_max) ? priv->sgl_max : total; - total -= bucket_count; - atomic_add(bucket_count, &priv->buckets_out); - - dprintk(KERN_INFO "%s: Sending %d buckets (size %d) to LAN DDM.\n", - dev->name, bucket_count, bucket_len); - - /* Fill in the header */ - - __raw_writel(I2O_MESSAGE_SIZE(4 + 3 * bucket_count) | SGL_OFFSET_4, msg); - __raw_writel(LAN_RECEIVE_POST<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); - __raw_writel(priv->unit << 16 | lan_receive_context, msg+2); - __raw_writel(bucket_count, msg+3); - sgl_elem = &msg[4]; - - /* Fill in the payload - contains bucket_count SGL elements */ - - while (bucket_count--) { - spin_lock_irqsave(&priv->fbl_lock, flags); - if (priv->i2o_fbl_tail >= 0) - skb = priv->i2o_fbl[priv->i2o_fbl_tail--]; - else { - skb = dev_alloc_skb(bucket_len + 2); - if (skb == NULL) { - spin_unlock_irqrestore(&priv->fbl_lock, flags); - return -ENOMEM; - } - skb_reserve(skb, 2); - } - spin_unlock_irqrestore(&priv->fbl_lock, flags); - - __raw_writel(0x51000000 | bucket_len, sgl_elem); - __raw_writel((u32)skb, sgl_elem+1); - __raw_writel(virt_to_bus(skb->data), sgl_elem+2); - sgl_elem += 3; - } - - /* set LE flag and post */ - __raw_writel(__raw_readl(sgl_elem-3) | 0x80000000, (sgl_elem-3)); - i2o_post_message(iop, m); - } - - return 0; -} - -/* Functions called from the network stack, and functions called by them: -========================================================================*/ - -/* - * i2o_lan_reset(): Reset the LAN adapter into the operational state and - * restore it to full operation. - */ -static int i2o_lan_reset(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u32 msg[5]; - - dprintk(KERN_INFO "%s: LAN RESET MESSAGE.\n", dev->name); - msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; - msg[1] = LAN_RESET<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid; - msg[2] = priv->unit << 16 | lan_context; // InitiatorContext - msg[3] = 0; // TransactionContext - msg[4] = 0; // Keep posted buckets - - if (i2o_post_this(iop, msg, sizeof(msg)) < 0) - return -ETIMEDOUT; - - return 0; -} - -/* - * i2o_lan_suspend(): Put LAN adapter into a safe, non-active state. - * IOP replies to any LAN class message with status error_no_data_transfer - * / suspended. - */ -static int i2o_lan_suspend(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u32 msg[5]; - - dprintk(KERN_INFO "%s: LAN SUSPEND MESSAGE.\n", dev->name); - msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; - msg[1] = LAN_SUSPEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid; - msg[2] = priv->unit << 16 | lan_context; // InitiatorContext - msg[3] = 0; // TransactionContext - msg[4] = 1 << 16; // return posted buckets - - if (i2o_post_this(iop, msg, sizeof(msg)) < 0) - return -ETIMEDOUT; - - return 0; -} - -/* - * i2o_set_ddm_parameters: - * These settings are done to ensure proper initial values for DDM. - * They can be changed via proc file system or vai configuration utility. - */ -static void i2o_set_ddm_parameters(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u32 val; - - /* - * When PacketOrphanlimit is set to the maximum packet length, - * the packets will never be split into two separate buckets - */ - val = dev->mtu + dev->hard_header_len; - if (i2o_set_scalar(iop, i2o_dev->lct_data.tid, 0x0004, 2, &val, sizeof(val)) < 0) - printk(KERN_WARNING "%s: Unable to set PacketOrphanLimit.\n", - dev->name); - else - dprintk(KERN_INFO "%s: PacketOrphanLimit set to %d.\n", - dev->name, val); - - /* When RxMaxPacketsBucket = 1, DDM puts only one packet into bucket */ - - val = 1; - if (i2o_set_scalar(iop, i2o_dev->lct_data.tid, 0x0008, 4, &val, sizeof(val)) <0) - printk(KERN_WARNING "%s: Unable to set RxMaxPacketsBucket.\n", - dev->name); - else - dprintk(KERN_INFO "%s: RxMaxPacketsBucket set to %d.\n", - dev->name, val); - return; -} - -/* Functions called from the network stack: -==========================================*/ - -/* - * i2o_lan_open(): Open the device to send/receive packets via - * the network device. - */ -static int i2o_lan_open(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u32 mc_addr_group[64]; - - MOD_INC_USE_COUNT; - - if (i2o_claim_device(i2o_dev, &i2o_lan_handler)) { - printk(KERN_WARNING "%s: Unable to claim the I2O LAN device.\n", dev->name); - MOD_DEC_USE_COUNT; - return -EAGAIN; - } - dprintk(KERN_INFO "%s: I2O LAN device (tid=%d) claimed by LAN OSM.\n", - dev->name, i2o_dev->lct_data.tid); - - if (i2o_event_register(iop, i2o_dev->lct_data.tid, - priv->unit << 16 | lan_context, 0, priv->i2o_event_mask) < 0) - printk(KERN_WARNING "%s: Unable to set the event mask.\n", dev->name); - - i2o_lan_reset(dev); - - /* Get the max number of multicast addresses */ - - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0001, -1, - &mc_addr_group, sizeof(mc_addr_group)) < 0 ) { - printk(KERN_WARNING "%s: Unable to query LAN_MAC_ADDRESS group.\n", dev->name); - MOD_DEC_USE_COUNT; - return -EAGAIN; - } - priv->max_size_mc_table = mc_addr_group[8]; - - /* Malloc space for free bucket list to resuse reveive post buckets */ - - priv->i2o_fbl = kmalloc(priv->max_buckets_out * sizeof(struct sk_buff *), - GFP_KERNEL); - if (priv->i2o_fbl == NULL) { - MOD_DEC_USE_COUNT; - return -ENOMEM; - } - priv->i2o_fbl_tail = -1; - priv->send_active = 0; - - i2o_set_ddm_parameters(dev); - i2o_lan_receive_post(dev); - - netif_start_queue(dev); - - return 0; -} - -/* - * i2o_lan_close(): End the transfering. - */ -static int i2o_lan_close(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - int ret = 0; - - netif_stop_queue(dev); - i2o_lan_suspend(dev); - - if (i2o_event_register(iop, i2o_dev->lct_data.tid, - priv->unit << 16 | lan_context, 0, 0) < 0) - printk(KERN_WARNING "%s: Unable to clear the event mask.\n", - dev->name); - - while (priv->i2o_fbl_tail >= 0) - dev_kfree_skb(priv->i2o_fbl[priv->i2o_fbl_tail--]); - - kfree(priv->i2o_fbl); - - if (i2o_release_device(i2o_dev, &i2o_lan_handler)) { - printk(KERN_WARNING "%s: Unable to unclaim I2O LAN device " - "(tid=%d).\n", dev->name, i2o_dev->lct_data.tid); - ret = -EBUSY; - } - - MOD_DEC_USE_COUNT; - - return ret; -} - -/* - * i2o_lan_tx_timeout(): Tx timeout handler. - */ -static void i2o_lan_tx_timeout(struct net_device *dev) -{ - if (!netif_queue_stopped(dev)) - netif_start_queue(dev); -} - -/* - * i2o_lan_batch_send(): Send packets in batch. - * Both i2o_lan_sdu_send and i2o_lan_packet_send use this. - */ -static void i2o_lan_batch_send(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_controller *iop = priv->i2o_dev->controller; - - spin_lock_irq(&priv->tx_lock); - if (priv->tx_count != 0) { - dev->trans_start = jiffies; - i2o_post_message(iop, priv->m); - dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count); - priv->tx_count = 0; - } - priv->send_active = 0; - spin_unlock_irq(&priv->tx_lock); - MOD_DEC_USE_COUNT; -} - -#ifdef CONFIG_NET_FC -/* - * i2o_lan_sdu_send(): Send a packet, MAC header added by the DDM. - * Must be supported by Fibre Channel, optional for Ethernet/802.3, - * Token Ring, FDDI - */ -static int i2o_lan_sdu_send(struct sk_buff *skb, struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - int tickssofar = jiffies - dev->trans_start; - u32 m, *msg; - u32 *sgl_elem; - - spin_lock_irq(&priv->tx_lock); - - priv->tx_count++; - atomic_inc(&priv->tx_out); - - /* - * If tx_batch_mode = 0x00 forced to immediate mode - * If tx_batch_mode = 0x01 forced to batch mode - * If tx_batch_mode = 0x10 switch automatically, current mode immediate - * If tx_batch_mode = 0x11 switch automatically, current mode batch - * If gap between two packets is > 0 ticks, switch to immediate - */ - if (priv->tx_batch_mode >> 1) // switch automatically - priv->tx_batch_mode = tickssofar ? 0x02 : 0x03; - - if (priv->tx_count == 1) { - m = I2O_POST_READ32(iop); - if (m == 0xFFFFFFFF) { - spin_unlock_irq(&priv->tx_lock); - return 1; - } - msg = (u32 *)(iop->mem_offset + m); - priv->m = m; - - __raw_writel(NINE_WORD_MSG_SIZE | 1<<12 | SGL_OFFSET_4, msg); - __raw_writel(LAN_PACKET_SEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); - __raw_writel(priv->unit << 16 | lan_send_context, msg+2); // InitiatorContext - __raw_writel(1 << 30 | 1 << 3, msg+3); // TransmitControlWord - - __raw_writel(0xD7000000 | skb->len, msg+4); // MAC hdr included - __raw_writel((u32)skb, msg+5); // TransactionContext - __raw_writel(virt_to_bus(skb->data), msg+6); - __raw_writel((u32)skb->mac.raw, msg+7); - __raw_writel((u32)skb->mac.raw+4, msg+8); - - if ((priv->tx_batch_mode & 0x01) && !priv->send_active) { - priv->send_active = 1; - MOD_INC_USE_COUNT; - if (schedule_task(&priv->i2o_batch_send_task) == 0) - MOD_DEC_USE_COUNT; - } - } else { /* Add new SGL element to the previous message frame */ - - msg = (u32 *)(iop->mem_offset + priv->m); - sgl_elem = &msg[priv->tx_count * 5 + 1]; - - __raw_writel(I2O_MESSAGE_SIZE((__raw_readl(msg)>>16) + 5) | 1<<12 | SGL_OFFSET_4, msg); - __raw_writel(__raw_readl(sgl_elem-5) & 0x7FFFFFFF, sgl_elem-5); /* clear LE flag */ - __raw_writel(0xD5000000 | skb->len, sgl_elem); - __raw_writel((u32)skb, sgl_elem+1); - __raw_writel(virt_to_bus(skb->data), sgl_elem+2); - __raw_writel((u32)(skb->mac.raw), sgl_elem+3); - __raw_writel((u32)(skb->mac.raw)+1, sgl_elem+4); - } - - /* If tx not in batch mode or frame is full, send immediatelly */ - - if (!(priv->tx_batch_mode & 0x01) || priv->tx_count == priv->sgl_max) { - dev->trans_start = jiffies; - i2o_post_message(iop, priv->m); - dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count); - priv->tx_count = 0; - } - - /* If DDMs TxMaxPktOut reached, stop queueing layer to send more */ - - if (atomic_read(&priv->tx_out) >= priv->tx_max_out) - netif_stop_queue(dev); - - spin_unlock_irq(&priv->tx_lock); - return 0; -} -#endif /* CONFIG_NET_FC */ - -/* - * i2o_lan_packet_send(): Send a packet as is, including the MAC header. - * - * Must be supported by Ethernet/802.3, Token Ring, FDDI, optional for - * Fibre Channel - */ -static int i2o_lan_packet_send(struct sk_buff *skb, struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - int tickssofar = jiffies - dev->trans_start; - u32 m, *msg; - u32 *sgl_elem; - - spin_lock_irq(&priv->tx_lock); - - priv->tx_count++; - atomic_inc(&priv->tx_out); - - /* - * If tx_batch_mode = 0x00 forced to immediate mode - * If tx_batch_mode = 0x01 forced to batch mode - * If tx_batch_mode = 0x10 switch automatically, current mode immediate - * If tx_batch_mode = 0x11 switch automatically, current mode batch - * If gap between two packets is > 0 ticks, switch to immediate - */ - if (priv->tx_batch_mode >> 1) // switch automatically - priv->tx_batch_mode = tickssofar ? 0x02 : 0x03; - - if (priv->tx_count == 1) { - m = I2O_POST_READ32(iop); - if (m == 0xFFFFFFFF) { - spin_unlock_irq(&priv->tx_lock); - return 1; - } - msg = (u32 *)(iop->mem_offset + m); - priv->m = m; - - __raw_writel(SEVEN_WORD_MSG_SIZE | 1<<12 | SGL_OFFSET_4, msg); - __raw_writel(LAN_PACKET_SEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); - __raw_writel(priv->unit << 16 | lan_send_context, msg+2); // InitiatorContext - __raw_writel(1 << 30 | 1 << 3, msg+3); // TransmitControlWord - // bit 30: reply as soon as transmission attempt is complete - // bit 3: Suppress CRC generation - __raw_writel(0xD5000000 | skb->len, msg+4); // MAC hdr included - __raw_writel((u32)skb, msg+5); // TransactionContext - __raw_writel(virt_to_bus(skb->data), msg+6); - - if ((priv->tx_batch_mode & 0x01) && !priv->send_active) { - priv->send_active = 1; - MOD_INC_USE_COUNT; - if (schedule_task(&priv->i2o_batch_send_task) == 0) - MOD_DEC_USE_COUNT; - } - } else { /* Add new SGL element to the previous message frame */ - - msg = (u32 *)(iop->mem_offset + priv->m); - sgl_elem = &msg[priv->tx_count * 3 + 1]; - - __raw_writel(I2O_MESSAGE_SIZE((__raw_readl(msg)>>16) + 3) | 1<<12 | SGL_OFFSET_4, msg); - __raw_writel(__raw_readl(sgl_elem-3) & 0x7FFFFFFF, sgl_elem-3); /* clear LE flag */ - __raw_writel(0xD5000000 | skb->len, sgl_elem); - __raw_writel((u32)skb, sgl_elem+1); - __raw_writel(virt_to_bus(skb->data), sgl_elem+2); - } - - /* If tx is in immediate mode or frame is full, send now */ - - if (!(priv->tx_batch_mode & 0x01) || priv->tx_count == priv->sgl_max) { - dev->trans_start = jiffies; - i2o_post_message(iop, priv->m); - dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count); - priv->tx_count = 0; - } - - /* If DDMs TxMaxPktOut reached, stop queueing layer to send more */ - - if (atomic_read(&priv->tx_out) >= priv->tx_max_out) - netif_stop_queue(dev); - - spin_unlock_irq(&priv->tx_lock); - return 0; -} - -/* - * i2o_lan_get_stats(): Fill in the statistics. - */ -static struct net_device_stats *i2o_lan_get_stats(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u64 val64[16]; - u64 supported_group[4] = { 0, 0, 0, 0 }; - - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0100, -1, val64, - sizeof(val64)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_HISTORICAL_STATS.\n", dev->name); - else { - dprintk(KERN_DEBUG "%s: LAN_HISTORICAL_STATS queried.\n", dev->name); - priv->stats.tx_packets = val64[0]; - priv->stats.tx_bytes = val64[1]; - priv->stats.rx_packets = val64[2]; - priv->stats.rx_bytes = val64[3]; - priv->stats.tx_errors = val64[4]; - priv->stats.rx_errors = val64[5]; - priv->stats.rx_dropped = val64[6]; - } - - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0180, -1, - &supported_group, sizeof(supported_group)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_SUPPORTED_OPTIONAL_HISTORICAL_STATS.\n", dev->name); - - if (supported_group[2]) { - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0183, -1, - val64, sizeof(val64)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_OPTIONAL_RX_HISTORICAL_STATS.\n", dev->name); - else { - dprintk(KERN_DEBUG "%s: LAN_OPTIONAL_RX_HISTORICAL_STATS queried.\n", dev->name); - priv->stats.multicast = val64[4]; - priv->stats.rx_length_errors = val64[10]; - priv->stats.rx_crc_errors = val64[0]; - } - } - - if (i2o_dev->lct_data.sub_class == I2O_LAN_ETHERNET) { - u64 supported_stats = 0; - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0200, -1, - val64, sizeof(val64)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_802_3_HISTORICAL_STATS.\n", dev->name); - else { - dprintk(KERN_DEBUG "%s: LAN_802_3_HISTORICAL_STATS queried.\n", dev->name); - priv->stats.transmit_collision = val64[1] + val64[2]; - priv->stats.rx_frame_errors = val64[0]; - priv->stats.tx_carrier_errors = val64[6]; - } - - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0280, -1, - &supported_stats, sizeof(supported_stats)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_SUPPORTED_802_3_HISTORICAL_STATS.\n", dev->name); - - if (supported_stats != 0) { - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0281, -1, - val64, sizeof(val64)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_OPTIONAL_802_3_HISTORICAL_STATS.\n", dev->name); - else { - dprintk(KERN_DEBUG "%s: LAN_OPTIONAL_802_3_HISTORICAL_STATS queried.\n", dev->name); - if (supported_stats & 0x1) - priv->stats.rx_over_errors = val64[0]; - if (supported_stats & 0x4) - priv->stats.tx_heartbeat_errors = val64[2]; - } - } - } - -#ifdef CONFIG_TR - if (i2o_dev->lct_data.sub_class == I2O_LAN_TR) { - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0300, -1, - val64, sizeof(val64)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_802_5_HISTORICAL_STATS.\n", dev->name); - else { - struct tr_statistics *stats = - (struct tr_statistics *)&priv->stats; - dprintk(KERN_DEBUG "%s: LAN_802_5_HISTORICAL_STATS queried.\n", dev->name); - - stats->line_errors = val64[0]; - stats->internal_errors = val64[7]; - stats->burst_errors = val64[4]; - stats->A_C_errors = val64[2]; - stats->abort_delimiters = val64[3]; - stats->lost_frames = val64[1]; - /* stats->recv_congest_count = ?; FIXME ??*/ - stats->frame_copied_errors = val64[5]; - stats->frequency_errors = val64[6]; - stats->token_errors = val64[9]; - } - /* Token Ring optional stats not yet defined */ - } -#endif - -#ifdef CONFIG_FDDI - if (i2o_dev->lct_data.sub_class == I2O_LAN_FDDI) { - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0400, -1, - val64, sizeof(val64)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_FDDI_HISTORICAL_STATS.\n", dev->name); - else { - dprintk(KERN_DEBUG "%s: LAN_FDDI_HISTORICAL_STATS queried.\n", dev->name); - priv->stats.smt_cf_state = val64[0]; - memcpy(priv->stats.mac_upstream_nbr, &val64[1], FDDI_K_ALEN); - memcpy(priv->stats.mac_downstream_nbr, &val64[2], FDDI_K_ALEN); - priv->stats.mac_error_cts = val64[3]; - priv->stats.mac_lost_cts = val64[4]; - priv->stats.mac_rmt_state = val64[5]; - memcpy(priv->stats.port_lct_fail_cts, &val64[6], 8); - memcpy(priv->stats.port_lem_reject_cts, &val64[7], 8); - memcpy(priv->stats.port_lem_cts, &val64[8], 8); - memcpy(priv->stats.port_pcm_state, &val64[9], 8); - } - /* FDDI optional stats not yet defined */ - } -#endif - -#ifdef CONFIG_NET_FC - /* Fibre Channel Statistics not yet defined in 1.53 nor 2.0 */ -#endif - - return (struct net_device_stats *)&priv->stats; -} - -/* - * i2o_lan_set_mc_filter(): Post a request to set multicast filter. - */ -int i2o_lan_set_mc_filter(struct net_device *dev, u32 filter_mask) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u32 msg[10]; - - msg[0] = TEN_WORD_MSG_SIZE | SGL_OFFSET_5; - msg[1] = I2O_CMD_UTIL_PARAMS_SET << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid; - msg[2] = priv->unit << 16 | lan_context; - msg[3] = 0x0001 << 16 | 3 ; // TransactionContext: group&field - msg[4] = 0; - msg[5] = 0xCC000000 | 16; // Immediate data SGL - msg[6] = 1; // OperationCount - msg[7] = 0x0001<<16 | I2O_PARAMS_FIELD_SET; // Group, Operation - msg[8] = 3 << 16 | 1; // FieldIndex, FieldCount - msg[9] = filter_mask; // Value - - return i2o_post_this(iop, msg, sizeof(msg)); -} - -/* - * i2o_lan_set_mc_table(): Post a request to set LAN_MULTICAST_MAC_ADDRESS table. - */ -int i2o_lan_set_mc_table(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - struct dev_mc_list *mc; - u32 msg[10 + 2 * dev->mc_count]; - u8 *work8 = (u8 *)(msg + 10); - - msg[0] = I2O_MESSAGE_SIZE(10 + 2 * dev->mc_count) | SGL_OFFSET_5; - msg[1] = I2O_CMD_UTIL_PARAMS_SET << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid; - msg[2] = priv->unit << 16 | lan_context; // InitiatorContext - msg[3] = 0x0002 << 16 | (u16)-1; // TransactionContext - msg[4] = 0; // OperationFlags - msg[5] = 0xCC000000 | (16 + 8 * dev->mc_count); // Immediate data SGL - msg[6] = 2; // OperationCount - msg[7] = 0x0002 << 16 | I2O_PARAMS_TABLE_CLEAR; // Group, Operation - msg[8] = 0x0002 << 16 | I2O_PARAMS_ROW_ADD; // Group, Operation - msg[9] = dev->mc_count << 16 | (u16)-1; // RowCount, FieldCount - - for (mc = dev->mc_list; mc ; mc = mc->next, work8 += 8) { - memset(work8, 0, 8); - memcpy(work8, mc->dmi_addr, mc->dmi_addrlen); // Values - } - - return i2o_post_this(iop, msg, sizeof(msg)); -} - -/* - * i2o_lan_set_multicast_list(): Enable a network device to receive packets - * not send to the protocol address. - */ -static void i2o_lan_set_multicast_list(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - u32 filter_mask; - - if (dev->flags & IFF_PROMISC) { - filter_mask = 0x00000002; - dprintk(KERN_INFO "%s: Enabling promiscuous mode...\n", dev->name); - } else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > priv->max_size_mc_table) { - filter_mask = 0x00000004; - dprintk(KERN_INFO "%s: Enabling all multicast mode...\n", dev->name); - } else if (dev->mc_count) { - filter_mask = 0x00000000; - dprintk(KERN_INFO "%s: Enabling multicast mode...\n", dev->name); - if (i2o_lan_set_mc_table(dev) < 0) - printk(KERN_WARNING "%s: Unable to send MAC table.\n", dev->name); - } else { - filter_mask = 0x00000300; // Broadcast, Multicast disabled - dprintk(KERN_INFO "%s: Enabling unicast mode...\n", dev->name); - } - - /* Finally copy new FilterMask to DDM */ - - if (i2o_lan_set_mc_filter(dev, filter_mask) < 0) - printk(KERN_WARNING "%s: Unable to send MAC FilterMask.\n", dev->name); -} - -/* - * i2o_lan_change_mtu(): Change maximum transfer unit size. - */ -static int i2o_lan_change_mtu(struct net_device *dev, int new_mtu) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - u32 max_pkt_size; - - if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, - 0x0000, 6, &max_pkt_size, 4) < 0) - return -EFAULT; - - if (new_mtu < 68 || new_mtu > 9000 || new_mtu > max_pkt_size) - return -EINVAL; - - dev->mtu = new_mtu; - - i2o_lan_suspend(dev); // to SUSPENDED state, return buckets - - while (priv->i2o_fbl_tail >= 0) // free buffered buckets - dev_kfree_skb(priv->i2o_fbl[priv->i2o_fbl_tail--]); - - i2o_lan_reset(dev); // to OPERATIONAL state - i2o_set_ddm_parameters(dev); // reset some parameters - i2o_lan_receive_post(dev); // post new buckets (new size) - - return 0; -} - -/* Functions to initialize I2O LAN OSM: -======================================*/ - -/* - * i2o_lan_register_device(): Register LAN class device to kernel. - */ -struct net_device *i2o_lan_register_device(struct i2o_device *i2o_dev) -{ - struct net_device *dev = NULL; - struct i2o_lan_local *priv = NULL; - u8 hw_addr[8]; - u32 tx_max_out = 0; - unsigned short (*type_trans)(struct sk_buff *, struct net_device *); - void (*unregister_dev)(struct net_device *dev); - - switch (i2o_dev->lct_data.sub_class) { - case I2O_LAN_ETHERNET: - dev = init_etherdev(NULL, sizeof(struct i2o_lan_local)); - if (dev == NULL) - return NULL; - type_trans = eth_type_trans; - unregister_dev = unregister_netdev; - break; - -#ifdef CONFIG_ANYLAN - case I2O_LAN_100VG: - printk(KERN_ERR "i2o_lan: 100base VG not yet supported.\n"); - return NULL; - break; -#endif - -#ifdef CONFIG_TR - case I2O_LAN_TR: - dev = init_trdev(NULL, sizeof(struct i2o_lan_local)); - if (dev==NULL) - return NULL; - type_trans = tr_type_trans; - unregister_dev = unregister_trdev; - break; -#endif - -#ifdef CONFIG_FDDI - case I2O_LAN_FDDI: - { - int size = sizeof(struct net_device) + sizeof(struct i2o_lan_local); - - dev = (struct net_device *) kmalloc(size, GFP_KERNEL); - if (dev == NULL) - return NULL; - memset((char *)dev, 0, size); - dev->priv = (void *)(dev + 1); - - if (dev_alloc_name(dev, "fddi%d") < 0) { - printk(KERN_WARNING "i2o_lan: Too many FDDI devices.\n"); - kfree(dev); - return NULL; - } - type_trans = fddi_type_trans; - unregister_dev = (void *)unregister_netdevice; - - fddi_setup(dev); - register_netdev(dev); - } - break; -#endif - -#ifdef CONFIG_NET_FC - case I2O_LAN_FIBRE_CHANNEL: - dev = init_fcdev(NULL, sizeof(struct i2o_lan_local)); - if (dev == NULL) - return NULL; - type_trans = NULL; -/* FIXME: Move fc_type_trans() from drivers/net/fc/iph5526.c to net/802/fc.c - * and export it in include/linux/fcdevice.h - * type_trans = fc_type_trans; - */ - unregister_dev = (void *)unregister_fcdev; - break; -#endif - - case I2O_LAN_UNKNOWN: - default: - printk(KERN_ERR "i2o_lan: LAN type 0x%04x not supported.\n", - i2o_dev->lct_data.sub_class); - return NULL; - } - - priv = (struct i2o_lan_local *)dev->priv; - priv->i2o_dev = i2o_dev; - priv->type_trans = type_trans; - priv->sgl_max = (i2o_dev->controller->status_block->inbound_frame_size - 4) / 3; - atomic_set(&priv->buckets_out, 0); - - /* Set default values for user configurable parameters */ - /* Private values are changed via /proc file system */ - - priv->max_buckets_out = max_buckets_out; - priv->bucket_thresh = bucket_thresh; - priv->rx_copybreak = rx_copybreak; - priv->tx_batch_mode = tx_batch_mode & 0x03; - priv->i2o_event_mask = i2o_event_mask; - - priv->tx_lock = SPIN_LOCK_UNLOCKED; - priv->fbl_lock = SPIN_LOCK_UNLOCKED; - - unit++; - i2o_landevs[unit] = dev; - priv->unit = unit; - - if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, - 0x0001, 0, &hw_addr, sizeof(hw_addr)) < 0) { - printk(KERN_ERR "%s: Unable to query hardware address.\n", dev->name); - unit--; - unregister_dev(dev); - kfree(dev); - return NULL; - } - dprintk(KERN_DEBUG "%s: hwaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", - dev->name, hw_addr[0], hw_addr[1], hw_addr[2], hw_addr[3], - hw_addr[4], hw_addr[5]); - - dev->addr_len = 6; - memcpy(dev->dev_addr, hw_addr, 6); - - if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, - 0x0007, 2, &tx_max_out, sizeof(tx_max_out)) < 0) { - printk(KERN_ERR "%s: Unable to query max TX queue.\n", dev->name); - unit--; - unregister_dev(dev); - kfree(dev); - return NULL; - } - dprintk(KERN_INFO "%s: Max TX Outstanding = %d.\n", dev->name, tx_max_out); - priv->tx_max_out = tx_max_out; - atomic_set(&priv->tx_out, 0); - priv->tx_count = 0; - - INIT_LIST_HEAD(&priv->i2o_batch_send_task.list); - priv->i2o_batch_send_task.sync = 0; - priv->i2o_batch_send_task.routine = (void *)i2o_lan_batch_send; - priv->i2o_batch_send_task.data = (void *)dev; - - dev->open = i2o_lan_open; - dev->stop = i2o_lan_close; - dev->get_stats = i2o_lan_get_stats; - dev->set_multicast_list = i2o_lan_set_multicast_list; - dev->tx_timeout = i2o_lan_tx_timeout; - dev->watchdog_timeo = I2O_LAN_TX_TIMEOUT; - -#ifdef CONFIG_NET_FC - if (i2o_dev->lct_data.sub_class == I2O_LAN_FIBRE_CHANNEL) - dev->hard_start_xmit = i2o_lan_sdu_send; - else -#endif - dev->hard_start_xmit = i2o_lan_packet_send; - - if (i2o_dev->lct_data.sub_class == I2O_LAN_ETHERNET) - dev->change_mtu = i2o_lan_change_mtu; - - return dev; -} - -#ifdef MODULE -#define i2o_lan_init init_module -#endif - -int __init i2o_lan_init(void) -{ - struct net_device *dev; - int i; - - printk(KERN_INFO "I2O LAN OSM (C) 1999 University of Helsinki.\n"); - - /* Module params are used as global defaults for private values */ - - if (max_buckets_out > I2O_LAN_MAX_BUCKETS_OUT) - max_buckets_out = I2O_LAN_MAX_BUCKETS_OUT; - if (bucket_thresh > max_buckets_out) - bucket_thresh = max_buckets_out; - - /* Install handlers for incoming replies */ - - if (i2o_install_handler(&i2o_lan_send_handler) < 0) { - printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); - return -EINVAL; - } - lan_send_context = i2o_lan_send_handler.context; - - if (i2o_install_handler(&i2o_lan_receive_handler) < 0) { - printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); - return -EINVAL; - } - lan_receive_context = i2o_lan_receive_handler.context; - - if (i2o_install_handler(&i2o_lan_handler) < 0) { - printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); - return -EINVAL; - } - lan_context = i2o_lan_handler.context; - - for(i=0; i <= MAX_LAN_CARDS; i++) - i2o_landevs[i] = NULL; - - for (i=0; i < MAX_I2O_CONTROLLERS; i++) { - struct i2o_controller *iop = i2o_find_controller(i); - struct i2o_device *i2o_dev; - - if (iop==NULL) - continue; - - for (i2o_dev=iop->devices;i2o_dev != NULL;i2o_dev=i2o_dev->next) { - - if (i2o_dev->lct_data.class_id != I2O_CLASS_LAN) - continue; - - /* Make sure device not already claimed by an ISM */ - if (i2o_dev->lct_data.user_tid != 0xFFF) - continue; - - if (unit == MAX_LAN_CARDS) { - i2o_unlock_controller(iop); - printk(KERN_WARNING "i2o_lan: Too many I2O LAN devices.\n"); - return -EINVAL; - } - - dev = i2o_lan_register_device(i2o_dev); - if (dev == NULL) { - printk(KERN_ERR "i2o_lan: Unable to register I2O LAN device 0x%04x.\n", - i2o_dev->lct_data.sub_class); - continue; - } - - printk(KERN_INFO "%s: I2O LAN device registered, " - "subclass = 0x%04x, unit = %d, tid = %d.\n", - dev->name, i2o_dev->lct_data.sub_class, - ((struct i2o_lan_local *)dev->priv)->unit, - i2o_dev->lct_data.tid); - } - - i2o_unlock_controller(iop); - } - - dprintk(KERN_INFO "%d I2O LAN devices found and registered.\n", unit+1); - - return 0; -} - -#ifdef MODULE - -void cleanup_module(void) -{ - int i; - - for (i = 0; i <= unit; i++) { - struct net_device *dev = i2o_landevs[i]; - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - - switch (i2o_dev->lct_data.sub_class) { - case I2O_LAN_ETHERNET: - unregister_netdev(dev); - break; -#ifdef CONFIG_FDDI - case I2O_LAN_FDDI: - unregister_netdevice(dev); - break; -#endif -#ifdef CONFIG_TR - case I2O_LAN_TR: - unregister_trdev(dev); - break; -#endif -#ifdef CONFIG_NET_FC - case I2O_LAN_FIBRE_CHANNEL: - unregister_fcdev(dev); - break; -#endif - default: - printk(KERN_WARNING "%s: Spurious I2O LAN subclass 0x%08x.\n", - dev->name, i2o_dev->lct_data.sub_class); - } - - dprintk(KERN_INFO "%s: I2O LAN device unregistered.\n", - dev->name); - kfree(dev); - } - - i2o_remove_handler(&i2o_lan_handler); - i2o_remove_handler(&i2o_lan_send_handler); - i2o_remove_handler(&i2o_lan_receive_handler); -} - -EXPORT_NO_SYMBOLS; - -MODULE_AUTHOR("University of Helsinki, Department of Computer Science"); -MODULE_DESCRIPTION("I2O Lan OSM"); - -MODULE_PARM(max_buckets_out, "1-" __MODULE_STRING(I2O_LAN_MAX_BUCKETS_OUT) "i"); -MODULE_PARM_DESC(max_buckets_out, "Total number of buckets to post (1-)"); -MODULE_PARM(bucket_thresh, "1-" __MODULE_STRING(I2O_LAN_MAX_BUCKETS_OUT) "i"); -MODULE_PARM_DESC(bucket_thresh, "Bucket post threshold (1-)"); -MODULE_PARM(rx_copybreak, "1-" "i"); -MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy only small frames (1-)"); -MODULE_PARM(tx_batch_mode, "0-2" "i"); -MODULE_PARM_DESC(tx_batch_mode, "0=Send immediatelly, 1=Send in batches, 2=Switch automatically"); - -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_lan.h linux.ac/drivers/i2o/i2o_lan.h --- linux.vanilla/drivers/i2o/i2o_lan.h Mon Dec 11 21:20:00 2000 +++ linux.ac/drivers/i2o/i2o_lan.h Thu Jan 1 01:00:00 1970 @@ -1,159 +0,0 @@ -/* - * i2o_lan.h I2O LAN Class definitions - * - * I2O LAN CLASS OSM May 26th 2000 - * - * (C) Copyright 1999, 2000 University of Helsinki, - * Department of Computer Science - * - * This code is still under development / test. - * - * Author: Auvo Häkkinen - * Juha Sievänen - * Taneli Vähäkangas - */ - -#ifndef _I2O_LAN_H -#define _I2O_LAN_H - -/* Default values for tunable parameters first */ - -#define I2O_LAN_MAX_BUCKETS_OUT 96 -#define I2O_LAN_BUCKET_THRESH 18 /* 9 buckets in one message */ -#define I2O_LAN_RX_COPYBREAK 200 -#define I2O_LAN_TX_TIMEOUT (1*HZ) -#define I2O_LAN_TX_BATCH_MODE 2 /* 2=automatic, 1=on, 0=off */ -#define I2O_LAN_EVENT_MASK 0 /* 0=None, 0xFFC00002=All */ - -/* LAN types */ -#define I2O_LAN_ETHERNET 0x0030 -#define I2O_LAN_100VG 0x0040 -#define I2O_LAN_TR 0x0050 -#define I2O_LAN_FDDI 0x0060 -#define I2O_LAN_FIBRE_CHANNEL 0x0070 -#define I2O_LAN_UNKNOWN 0x00000000 - -/* Connector types */ - -/* Ethernet */ -#define I2O_LAN_AUI (I2O_LAN_ETHERNET << 4) + 0x00000001 -#define I2O_LAN_10BASE5 (I2O_LAN_ETHERNET << 4) + 0x00000002 -#define I2O_LAN_FIORL (I2O_LAN_ETHERNET << 4) + 0x00000003 -#define I2O_LAN_10BASE2 (I2O_LAN_ETHERNET << 4) + 0x00000004 -#define I2O_LAN_10BROAD36 (I2O_LAN_ETHERNET << 4) + 0x00000005 -#define I2O_LAN_10BASE_T (I2O_LAN_ETHERNET << 4) + 0x00000006 -#define I2O_LAN_10BASE_FP (I2O_LAN_ETHERNET << 4) + 0x00000007 -#define I2O_LAN_10BASE_FB (I2O_LAN_ETHERNET << 4) + 0x00000008 -#define I2O_LAN_10BASE_FL (I2O_LAN_ETHERNET << 4) + 0x00000009 -#define I2O_LAN_100BASE_TX (I2O_LAN_ETHERNET << 4) + 0x0000000A -#define I2O_LAN_100BASE_FX (I2O_LAN_ETHERNET << 4) + 0x0000000B -#define I2O_LAN_100BASE_T4 (I2O_LAN_ETHERNET << 4) + 0x0000000C -#define I2O_LAN_1000BASE_SX (I2O_LAN_ETHERNET << 4) + 0x0000000D -#define I2O_LAN_1000BASE_LX (I2O_LAN_ETHERNET << 4) + 0x0000000E -#define I2O_LAN_1000BASE_CX (I2O_LAN_ETHERNET << 4) + 0x0000000F -#define I2O_LAN_1000BASE_T (I2O_LAN_ETHERNET << 4) + 0x00000010 - -/* AnyLAN */ -#define I2O_LAN_100VG_ETHERNET (I2O_LAN_100VG << 4) + 0x00000001 -#define I2O_LAN_100VG_TR (I2O_LAN_100VG << 4) + 0x00000002 - -/* Token Ring */ -#define I2O_LAN_4MBIT (I2O_LAN_TR << 4) + 0x00000001 -#define I2O_LAN_16MBIT (I2O_LAN_TR << 4) + 0x00000002 - -/* FDDI */ -#define I2O_LAN_125MBAUD (I2O_LAN_FDDI << 4) + 0x00000001 - -/* Fibre Channel */ -#define I2O_LAN_POINT_POINT (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000001 -#define I2O_LAN_ARB_LOOP (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000002 -#define I2O_LAN_PUBLIC_LOOP (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000003 -#define I2O_LAN_FABRIC (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000004 - -#define I2O_LAN_EMULATION 0x00000F00 -#define I2O_LAN_OTHER 0x00000F01 -#define I2O_LAN_DEFAULT 0xFFFFFFFF - -/* LAN class functions */ - -#define LAN_PACKET_SEND 0x3B -#define LAN_SDU_SEND 0x3D -#define LAN_RECEIVE_POST 0x3E -#define LAN_RESET 0x35 -#define LAN_SUSPEND 0x37 - -/* LAN DetailedStatusCode defines */ -#define I2O_LAN_DSC_SUCCESS 0x00 -#define I2O_LAN_DSC_DEVICE_FAILURE 0x01 -#define I2O_LAN_DSC_DESTINATION_NOT_FOUND 0x02 -#define I2O_LAN_DSC_TRANSMIT_ERROR 0x03 -#define I2O_LAN_DSC_TRANSMIT_ABORTED 0x04 -#define I2O_LAN_DSC_RECEIVE_ERROR 0x05 -#define I2O_LAN_DSC_RECEIVE_ABORTED 0x06 -#define I2O_LAN_DSC_DMA_ERROR 0x07 -#define I2O_LAN_DSC_BAD_PACKET_DETECTED 0x08 -#define I2O_LAN_DSC_OUT_OF_MEMORY 0x09 -#define I2O_LAN_DSC_BUCKET_OVERRUN 0x0A -#define I2O_LAN_DSC_IOP_INTERNAL_ERROR 0x0B -#define I2O_LAN_DSC_CANCELED 0x0C -#define I2O_LAN_DSC_INVALID_TRANSACTION_CONTEXT 0x0D -#define I2O_LAN_DSC_DEST_ADDRESS_DETECTED 0x0E -#define I2O_LAN_DSC_DEST_ADDRESS_OMITTED 0x0F -#define I2O_LAN_DSC_PARTIAL_PACKET_RETURNED 0x10 -#define I2O_LAN_DSC_SUSPENDED 0x11 - -struct i2o_packet_info { - u32 offset : 24; - u32 flags : 8; - u32 len : 24; - u32 status : 8; -}; - -struct i2o_bucket_descriptor { - u32 context; /* FIXME: 64bit support */ - struct i2o_packet_info packet_info[1]; -}; - -/* Event Indicator Mask Flags for LAN OSM */ - -#define I2O_LAN_EVT_LINK_DOWN 0x01 -#define I2O_LAN_EVT_LINK_UP 0x02 -#define I2O_LAN_EVT_MEDIA_CHANGE 0x04 - -#include -#include - -struct i2o_lan_local { - u8 unit; - struct i2o_device *i2o_dev; - - struct fddi_statistics stats; /* see also struct net_device_stats */ - unsigned short (*type_trans)(struct sk_buff *, struct net_device *); - atomic_t buckets_out; /* nbr of unused buckets on DDM */ - atomic_t tx_out; /* outstanding TXes */ - u8 tx_count; /* packets in one TX message frame */ - u16 tx_max_out; /* DDM's Tx queue len */ - u8 sgl_max; /* max SGLs in one message frame */ - u32 m; /* IOP address of the batch msg frame */ - - struct tq_struct i2o_batch_send_task; - int send_active; - struct sk_buff **i2o_fbl; /* Free bucket list (to reuse skbs) */ - int i2o_fbl_tail; - spinlock_t fbl_lock; - - spinlock_t tx_lock; - - u32 max_size_mc_table; /* max number of multicast addresses */ - - /* LAN OSM configurable parameters are here: */ - - u16 max_buckets_out; /* max nbr of buckets to send to DDM */ - u16 bucket_thresh; /* send more when this many used */ - u16 rx_copybreak; - - u8 tx_batch_mode; /* Set when using batch mode sends */ - u32 i2o_event_mask; /* To turn on interesting event flags */ -}; - -#endif /* _I2O_LAN_H */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_pci.c linux.ac/drivers/i2o/i2o_pci.c --- linux.vanilla/drivers/i2o/i2o_pci.c Sun Aug 12 19:16:18 2001 +++ linux.ac/drivers/i2o/i2o_pci.c Thu Jan 1 01:00:00 1970 @@ -1,377 +0,0 @@ -/* - * Find I2O capable controllers on the PCI bus, and register/install - * them with the I2O layer - * - * (C) Copyright 1999 Red Hat Software - * - * Written by Alan Cox, Building Number Three Ltd - * Modified by Deepak Saxena - * Modified by Boji T Kannanthanam - * - * 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. - * - * TODO: - * Support polled I2O PCI controllers. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_MTRR -#include -#endif // CONFIG_MTRR - -#ifdef MODULE -/* - * Core function table - * See for an explanation - */ -static struct i2o_core_func_table *core; - -/* Core attach function */ -extern int i2o_pci_core_attach(struct i2o_core_func_table *); -extern void i2o_pci_core_detach(void); -#endif /* MODULE */ - -/* - * Free bus specific resources - */ -static void i2o_pci_dispose(struct i2o_controller *c) -{ - I2O_IRQ_WRITE32(c,0xFFFFFFFF); - if(c->bus.pci.irq > 0) - free_irq(c->bus.pci.irq, c); - iounmap(((u8 *)c->post_port)-0x40); - -#ifdef CONFIG_MTRR - if(c->bus.pci.mtrr_reg0 > 0) - mtrr_del(c->bus.pci.mtrr_reg0, 0, 0); - if(c->bus.pci.mtrr_reg1 > 0) - mtrr_del(c->bus.pci.mtrr_reg1, 0, 0); -#endif -} - -/* - * No real bus specific handling yet (note that later we will - * need to 'steal' PCI devices on i960 mainboards) - */ - -static int i2o_pci_bind(struct i2o_controller *c, struct i2o_device *dev) -{ - MOD_INC_USE_COUNT; - return 0; -} - -static int i2o_pci_unbind(struct i2o_controller *c, struct i2o_device *dev) -{ - MOD_DEC_USE_COUNT; - return 0; -} - -/* - * Bus specific enable/disable functions - */ -static void i2o_pci_enable(struct i2o_controller *c) -{ - I2O_IRQ_WRITE32(c, 0); - c->enabled = 1; -} - -static void i2o_pci_disable(struct i2o_controller *c) -{ - I2O_IRQ_WRITE32(c, 0xFFFFFFFF); - c->enabled = 0; -} - -/* - * Bus specific interrupt handler - */ - -static void i2o_pci_interrupt(int irq, void *dev_id, struct pt_regs *r) -{ - struct i2o_controller *c = dev_id; -#ifdef MODULE - core->run_queue(c); -#else - i2o_run_queue(c); -#endif /* MODULE */ -} - -/* - * Install a PCI (or in theory AGP) i2o controller - * - * TODO: Add support for polled controllers - */ -int __init i2o_pci_install(struct pci_dev *dev) -{ - struct i2o_controller *c=kmalloc(sizeof(struct i2o_controller), - GFP_KERNEL); - u8 *mem; - u32 memptr = 0; - u32 size; - - int i; - - if(c==NULL) - { - printk(KERN_ERR "i2o: Insufficient memory to add controller.\n"); - return -ENOMEM; - } - memset(c, 0, sizeof(*c)); - - for(i=0; i<6; i++) - { - /* Skip I/O spaces */ - if(!(pci_resource_flags(dev, i) & IORESOURCE_IO)) - { - memptr = pci_resource_start(dev, i); - break; - } - } - - if(i==6) - { - printk(KERN_ERR "i2o: I2O controller has no memory regions defined.\n"); - kfree(c); - return -EINVAL; - } - - size = dev->resource[i].end-dev->resource[i].start+1; - /* Map the I2O controller */ - - printk(KERN_INFO "i2o: PCI I2O controller at 0x%08X size=%d\n", memptr, size); - mem = ioremap(memptr, size); - if(mem==NULL) - { - printk(KERN_ERR "i2o: Unable to map controller.\n"); - kfree(c); - return -EINVAL; - } - - c->bus.pci.irq = -1; - c->bus.pci.queue_buggy = 0; - c->bus.pci.dpt = 0; - c->bus.pci.short_req = 0; - c->bus.pci.pdev = dev; - - c->irq_mask = (volatile u32 *)(mem+0x34); - c->post_port = (volatile u32 *)(mem+0x40); - c->reply_port = (volatile u32 *)(mem+0x44); - - c->mem_phys = memptr; - c->mem_offset = (u32)mem; - c->destructor = i2o_pci_dispose; - - c->bind = i2o_pci_bind; - c->unbind = i2o_pci_unbind; - c->bus_enable = i2o_pci_enable; - c->bus_disable = i2o_pci_disable; - - c->type = I2O_TYPE_PCI; - - /* - * Cards that fall apart if you hit them with large I/O - * loads... - */ - - if(dev->vendor == PCI_VENDOR_ID_NCR && dev->device == 0x0630) - { - c->bus.pci.short_req=1; - printk(KERN_INFO "I2O: Symbios FC920 workarounds activated.\n"); - } - if(dev->subsystem_vendor == PCI_VENDOR_ID_PROMISE) - { - c->bus.pci.queue_buggy=1; - printk(KERN_INFO "I2O: Promise workarounds activated.\n"); - } - - /* - * Cards that go bananas if you quiesce them before you reset - * them - */ - - if(dev->vendor == PCI_VENDOR_ID_DPT) - c->bus.pci.dpt=1; - - /* - * Enable Write Combining MTRR for IOP's memory region - */ -#ifdef CONFIG_MTRR - c->bus.pci.mtrr_reg0 = - mtrr_add(c->mem_phys, size, MTRR_TYPE_WRCOMB, 1); -/* -* If it is an INTEL i960 I/O processor then set the first 64K to Uncacheable -* since the region contains the Messaging unit which shouldn't be cached. -*/ - c->bus.pci.mtrr_reg1 = -1; - if(dev->vendor == PCI_VENDOR_ID_INTEL || dev->vendor == PCI_VENDOR_ID_DPT) - { - printk(KERN_INFO "I2O: MTRR workaround for Intel i960 processor\n"); - c->bus.pci.mtrr_reg1 = mtrr_add(c->mem_phys, 65536, MTRR_TYPE_UNCACHABLE, 1); - if(c->bus.pci.mtrr_reg1< 0) - printk(KERN_INFO "i2o_pci: Error in setting MTRR_TYPE_UNCACHABLE\n"); - } - -#endif - - I2O_IRQ_WRITE32(c,0xFFFFFFFF); - -#ifdef MODULE - i = core->install(c); -#else - i = i2o_install_controller(c); -#endif /* MODULE */ - - if(i<0) - { - printk(KERN_ERR "i2o: Unable to install controller.\n"); - kfree(c); - iounmap(mem); - return i; - } - - c->bus.pci.irq = dev->irq; - if(c->bus.pci.irq) - { - i=request_irq(dev->irq, i2o_pci_interrupt, SA_SHIRQ, - c->name, c); - if(i<0) - { - printk(KERN_ERR "%s: unable to allocate interrupt %d.\n", - c->name, dev->irq); - c->bus.pci.irq = -1; -#ifdef MODULE - core->delete(c); -#else - i2o_delete_controller(c); -#endif /* MODULE */ - iounmap(mem); - return -EBUSY; - } - } - - printk(KERN_INFO "%s: Installed at IRQ%d\n", c->name, dev->irq); - I2O_IRQ_WRITE32(c,0x0); - c->enabled = 1; - return 0; -} - -int __init i2o_pci_scan(void) -{ - struct pci_dev *dev; - int count=0; - - printk(KERN_INFO "i2o: Checking for PCI I2O controllers...\n"); - - pci_for_each_dev(dev) - { - if((dev->class>>8)!=PCI_CLASS_INTELLIGENT_I2O) - continue; - if((dev->class&0xFF)>1) - { - printk(KERN_INFO "i2o: I2O Controller found but does not support I2O 1.5 (skipping).\n"); - continue; - } - if (pci_enable_device(dev)) - continue; - printk(KERN_INFO "i2o: I2O controller on bus %d at %d.\n", - dev->bus->number, dev->devfn); - pci_set_master(dev); - if(i2o_pci_install(dev)==0) - count++; - } - if(count) - printk(KERN_INFO "i2o: %d I2O controller%s found and installed.\n", count, - count==1?"":"s"); - return count?count:-ENODEV; -} - -#ifdef I2O_HOTPLUG_SUPPORT -/* - * Activate a newly found PCI I2O controller - * Not used now, but will be needed in future for - * hot plug PCI support - */ -static void i2o_pci_activate(i2o_controller * c) -{ - int i=0; - struct i2o_controller *c; - - if(c->type == I2O_TYPE_PCI) - { - I2O_IRQ_WRITE32(c,0); -#ifdef MODULE - if(core->activate(c)) -#else - if(i2o_activate_controller(c)) -#endif /* MODULE */ - { - printk("%s: Failed to initialize.\n", c->name); -#ifdef MODULE - core->unlock(c); - core->delete(c); -#else - i2o_unlock_controller(c); - i2o_delete_controller(c); -#endif - continue; - } - } -} -#endif // I2O_HOTPLUG_SUPPORT - -#ifdef MODULE - -int i2o_pci_core_attach(struct i2o_core_func_table *table) -{ - MOD_INC_USE_COUNT; - - core = table; - - return i2o_pci_scan(); -} - -void i2o_pci_core_detach(void) -{ - core = NULL; - - MOD_DEC_USE_COUNT; -} - -int init_module(void) -{ - printk(KERN_INFO "Linux I2O PCI support (c) 1999 Red Hat Software.\n"); - - core = NULL; - - return 0; - -} - -void cleanup_module(void) -{ -} - -EXPORT_SYMBOL(i2o_pci_core_attach); -EXPORT_SYMBOL(i2o_pci_core_detach); - -MODULE_AUTHOR("Red Hat Software"); -MODULE_DESCRIPTION("I2O PCI Interface"); - -#else -void __init i2o_pci_init(void) -{ - printk(KERN_INFO "Linux I2O PCI support (c) 1999 Red Hat Software.\n"); - i2o_pci_scan(); -} -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_proc.c linux.ac/drivers/i2o/i2o_proc.c --- linux.vanilla/drivers/i2o/i2o_proc.c Sun Aug 12 19:16:18 2001 +++ linux.ac/drivers/i2o/i2o_proc.c Thu Jan 1 01:00:00 1970 @@ -1,3379 +0,0 @@ -/* - * procfs handler for Linux I2O subsystem - * - * (c) Copyright 1999 Deepak Saxena - * - * Originally written by Deepak Saxena(deepak@plexity.net) - * - * 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 is an initial test release. The code is based on the design - * of the ide procfs system (drivers/block/ide-proc.c). Some code - * taken from i2o-core module by Alan Cox. - * - * DISCLAIMER: This code is still under development/test and may cause - * your system to behave unpredictably. Use at your own discretion. - * - * LAN entries by Juha Sievänen (Juha.Sievanen@cs.Helsinki.FI), - * Auvo Häkkinen (Auvo.Hakkinen@cs.Helsinki.FI) - * University of Helsinki, Department of Computer Science - */ - -/* - * set tabstop=3 - */ - -/* - * TODO List - * - * - Add support for any version 2.0 spec changes once 2.0 IRTOS is - * is available to test with - * - Clean up code to use official structure definitions - */ - -// FIXME! -#define FMT_U64_HEX "0x%08x%08x" -#define U64_VAL(pu64) *((u32*)(pu64)+1), *((u32*)(pu64)) - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "i2o_lan.h" - -/* - * Structure used to define /proc entries - */ -typedef struct _i2o_proc_entry_t -{ - char *name; /* entry name */ - mode_t mode; /* mode */ - read_proc_t *read_proc; /* read func */ - write_proc_t *write_proc; /* write func */ -} i2o_proc_entry; - -// #define DRIVERDEBUG - -static int i2o_proc_read_lct(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_hrt(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_status(char *, char **, off_t, int, int *, void *); - -static int i2o_proc_read_hw(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_ddm_table(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_driver_store(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_drivers_stored(char *, char **, off_t, int, int *, void *); - -static int i2o_proc_read_groups(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_phys_device(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_claimed(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_users(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_priv_msgs(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_authorized_users(char *, char **, off_t, int, int *, void *); - -static int i2o_proc_read_dev_name(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_dev_identity(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_ddm_identity(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_uinfo(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_sgl_limits(char *, char **, off_t, int, int *, void *); - -static int i2o_proc_read_sensors(char *, char **, off_t, int, int *, void *); - -static int print_serial_number(char *, int, u8 *, int); - -static int i2o_proc_create_entries(void *, i2o_proc_entry *, - struct proc_dir_entry *); -static void i2o_proc_remove_entries(i2o_proc_entry *, struct proc_dir_entry *); -static int i2o_proc_add_controller(struct i2o_controller *, - struct proc_dir_entry * ); -static void i2o_proc_remove_controller(struct i2o_controller *, - struct proc_dir_entry * ); -static void i2o_proc_add_device(struct i2o_device *, struct proc_dir_entry *); -static void i2o_proc_remove_device(struct i2o_device *); -static int create_i2o_procfs(void); -static int destroy_i2o_procfs(void); -static void i2o_proc_new_dev(struct i2o_controller *, struct i2o_device *); -static void i2o_proc_dev_del(struct i2o_controller *, struct i2o_device *); - -static int i2o_proc_read_lan_dev_info(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_mac_addr(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_mcast_addr(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_batch_control(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_operation(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_media_operation(char *, char **, off_t, int, - int *, void *); -static int i2o_proc_read_lan_alt_addr(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_tx_info(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_rx_info(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_hist_stats(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_eth_stats(char *, char **, off_t, int, - int *, void *); -static int i2o_proc_read_lan_tr_stats(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_fddi_stats(char *, char **, off_t, int, int *, - void *); - -static struct proc_dir_entry *i2o_proc_dir_root; - -/* - * I2O OSM descriptor - */ -static struct i2o_handler i2o_proc_handler = -{ - NULL, - i2o_proc_new_dev, - i2o_proc_dev_del, - NULL, - "I2O procfs Layer", - 0, - 0xffffffff // All classes -}; - -/* - * IOP specific entries...write field just in case someone - * ever wants one. - */ -static i2o_proc_entry generic_iop_entries[] = -{ - {"hrt", S_IFREG|S_IRUGO, i2o_proc_read_hrt, NULL}, - {"lct", S_IFREG|S_IRUGO, i2o_proc_read_lct, NULL}, - {"status", S_IFREG|S_IRUGO, i2o_proc_read_status, NULL}, - {"hw", S_IFREG|S_IRUGO, i2o_proc_read_hw, NULL}, - {"ddm_table", S_IFREG|S_IRUGO, i2o_proc_read_ddm_table, NULL}, - {"driver_store", S_IFREG|S_IRUGO, i2o_proc_read_driver_store, NULL}, - {"drivers_stored", S_IFREG|S_IRUGO, i2o_proc_read_drivers_stored, NULL}, - {NULL, 0, NULL, NULL} -}; - -/* - * Device specific entries - */ -static i2o_proc_entry generic_dev_entries[] = -{ - {"groups", S_IFREG|S_IRUGO, i2o_proc_read_groups, NULL}, - {"phys_dev", S_IFREG|S_IRUGO, i2o_proc_read_phys_device, NULL}, - {"claimed", S_IFREG|S_IRUGO, i2o_proc_read_claimed, NULL}, - {"users", S_IFREG|S_IRUGO, i2o_proc_read_users, NULL}, - {"priv_msgs", S_IFREG|S_IRUGO, i2o_proc_read_priv_msgs, NULL}, - {"authorized_users", S_IFREG|S_IRUGO, i2o_proc_read_authorized_users, NULL}, - {"dev_identity", S_IFREG|S_IRUGO, i2o_proc_read_dev_identity, NULL}, - {"ddm_identity", S_IFREG|S_IRUGO, i2o_proc_read_ddm_identity, NULL}, - {"user_info", S_IFREG|S_IRUGO, i2o_proc_read_uinfo, NULL}, - {"sgl_limits", S_IFREG|S_IRUGO, i2o_proc_read_sgl_limits, NULL}, - {"sensors", S_IFREG|S_IRUGO, i2o_proc_read_sensors, NULL}, - {NULL, 0, NULL, NULL} -}; - -/* - * Storage unit specific entries (SCSI Periph, BS) with device names - */ -static i2o_proc_entry rbs_dev_entries[] = -{ - {"dev_name", S_IFREG|S_IRUGO, i2o_proc_read_dev_name, NULL}, - {NULL, 0, NULL, NULL} -}; - -#define SCSI_TABLE_SIZE 13 -static char *scsi_devices[] = -{ - "Direct-Access Read/Write", - "Sequential-Access Storage", - "Printer", - "Processor", - "WORM Device", - "CD-ROM Device", - "Scanner Device", - "Optical Memory Device", - "Medium Changer Device", - "Communications Device", - "Graphics Art Pre-Press Device", - "Graphics Art Pre-Press Device", - "Array Controller Device" -}; - -/* private */ - -/* - * Generic LAN specific entries - * - * Should groups with r/w entries have their own subdirectory? - * - */ -static i2o_proc_entry lan_entries[] = -{ - {"lan_dev_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_dev_info, NULL}, - {"lan_mac_addr", S_IFREG|S_IRUGO, i2o_proc_read_lan_mac_addr, NULL}, - {"lan_mcast_addr", S_IFREG|S_IRUGO|S_IWUSR, - i2o_proc_read_lan_mcast_addr, NULL}, - {"lan_batch_ctrl", S_IFREG|S_IRUGO|S_IWUSR, - i2o_proc_read_lan_batch_control, NULL}, - {"lan_operation", S_IFREG|S_IRUGO, i2o_proc_read_lan_operation, NULL}, - {"lan_media_operation", S_IFREG|S_IRUGO, - i2o_proc_read_lan_media_operation, NULL}, - {"lan_alt_addr", S_IFREG|S_IRUGO, i2o_proc_read_lan_alt_addr, NULL}, - {"lan_tx_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_tx_info, NULL}, - {"lan_rx_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_rx_info, NULL}, - - {"lan_hist_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_hist_stats, NULL}, - {NULL, 0, NULL, NULL} -}; - -/* - * Port specific LAN entries - * - */ -static i2o_proc_entry lan_eth_entries[] = -{ - {"lan_eth_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_eth_stats, NULL}, - {NULL, 0, NULL, NULL} -}; - -static i2o_proc_entry lan_tr_entries[] = -{ - {"lan_tr_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_tr_stats, NULL}, - {NULL, 0, NULL, NULL} -}; - -static i2o_proc_entry lan_fddi_entries[] = -{ - {"lan_fddi_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_fddi_stats, NULL}, - {NULL, 0, NULL, NULL} -}; - - -static char *chtostr(u8 *chars, int n) -{ - char tmp[256]; - tmp[0] = 0; - return strncat(tmp, (char *)chars, n); -} - -static int i2o_report_query_status(char *buf, int block_status, char *group) -{ - switch (block_status) - { - case -ETIMEDOUT: - return sprintf(buf, "Timeout reading group %s.\n",group); - case -ENOMEM: - return sprintf(buf, "No free memory to read the table.\n"); - case -I2O_PARAMS_STATUS_INVALID_GROUP_ID: - return sprintf(buf, "Group %s not supported.\n", group); - default: - return sprintf(buf, "Error reading group %s. BlockStatus 0x%02X\n", - group, -block_status); - } -} - -static char* bus_strings[] = -{ - "Local Bus", - "ISA", - "EISA", - "MCA", - "PCI", - "PCMCIA", - "NUBUS", - "CARDBUS" -}; - -static spinlock_t i2o_proc_lock = SPIN_LOCK_UNLOCKED; - -int i2o_proc_read_hrt(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_controller *c = (struct i2o_controller *)data; - i2o_hrt *hrt = (i2o_hrt *)c->hrt; - u32 bus; - int count; - int i; - - spin_lock(&i2o_proc_lock); - - len = 0; - - if(hrt->hrt_version) - { - len += sprintf(buf+len, - "HRT table for controller is too new a version.\n"); - spin_unlock(&i2o_proc_lock); - return len; - } - - count = hrt->num_entries; - - if((count * hrt->entry_len + 8) > 2048) { - printk(KERN_WARNING "i2o_proc: HRT does not fit into buffer\n"); - len += sprintf(buf+len, - "HRT table too big to fit in buffer.\n"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "HRT has %d entries of %d bytes each.\n", - count, hrt->entry_len << 2); - - for(i = 0; i < count; i++) - { - len += sprintf(buf+len, "Entry %d:\n", i); - len += sprintf(buf+len, " Adapter ID: %0#10x\n", - hrt->hrt_entry[i].adapter_id); - len += sprintf(buf+len, " Controlling tid: %0#6x\n", - hrt->hrt_entry[i].parent_tid); - - if(hrt->hrt_entry[i].bus_type != 0x80) - { - bus = hrt->hrt_entry[i].bus_type; - len += sprintf(buf+len, " %s Information\n", bus_strings[bus]); - - switch(bus) - { - case I2O_BUS_LOCAL: - len += sprintf(buf+len, " IOBase: %0#6x,", - hrt->hrt_entry[i].bus.local_bus.LbBaseIOPort); - len += sprintf(buf+len, " MemoryBase: %0#10x\n", - hrt->hrt_entry[i].bus.local_bus.LbBaseMemoryAddress); - break; - - case I2O_BUS_ISA: - len += sprintf(buf+len, " IOBase: %0#6x,", - hrt->hrt_entry[i].bus.isa_bus.IsaBaseIOPort); - len += sprintf(buf+len, " MemoryBase: %0#10x,", - hrt->hrt_entry[i].bus.isa_bus.IsaBaseMemoryAddress); - len += sprintf(buf+len, " CSN: %0#4x,", - hrt->hrt_entry[i].bus.isa_bus.CSN); - break; - - case I2O_BUS_EISA: - len += sprintf(buf+len, " IOBase: %0#6x,", - hrt->hrt_entry[i].bus.eisa_bus.EisaBaseIOPort); - len += sprintf(buf+len, " MemoryBase: %0#10x,", - hrt->hrt_entry[i].bus.eisa_bus.EisaBaseMemoryAddress); - len += sprintf(buf+len, " Slot: %0#4x,", - hrt->hrt_entry[i].bus.eisa_bus.EisaSlotNumber); - break; - - case I2O_BUS_MCA: - len += sprintf(buf+len, " IOBase: %0#6x,", - hrt->hrt_entry[i].bus.mca_bus.McaBaseIOPort); - len += sprintf(buf+len, " MemoryBase: %0#10x,", - hrt->hrt_entry[i].bus.mca_bus.McaBaseMemoryAddress); - len += sprintf(buf+len, " Slot: %0#4x,", - hrt->hrt_entry[i].bus.mca_bus.McaSlotNumber); - break; - - case I2O_BUS_PCI: - len += sprintf(buf+len, " Bus: %0#4x", - hrt->hrt_entry[i].bus.pci_bus.PciBusNumber); - len += sprintf(buf+len, " Dev: %0#4x", - hrt->hrt_entry[i].bus.pci_bus.PciDeviceNumber); - len += sprintf(buf+len, " Func: %0#4x", - hrt->hrt_entry[i].bus.pci_bus.PciFunctionNumber); - len += sprintf(buf+len, " Vendor: %0#6x", - hrt->hrt_entry[i].bus.pci_bus.PciVendorID); - len += sprintf(buf+len, " Device: %0#6x\n", - hrt->hrt_entry[i].bus.pci_bus.PciDeviceID); - break; - - default: - len += sprintf(buf+len, " Unsupported Bus Type\n"); - } - } - else - len += sprintf(buf+len, " Unknown Bus Type\n"); - } - - spin_unlock(&i2o_proc_lock); - - return len; -} - -int i2o_proc_read_lct(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_controller *c = (struct i2o_controller*)data; - i2o_lct *lct = (i2o_lct *)c->lct; - int entries; - int i; - -#define BUS_TABLE_SIZE 3 - static char *bus_ports[] = - { - "Generic Bus", - "SCSI Bus", - "Fibre Channel Bus" - }; - - spin_lock(&i2o_proc_lock); - len = 0; - - entries = (lct->table_size - 3)/9; - - len += sprintf(buf, "LCT contains %d %s\n", entries, - entries == 1 ? "entry" : "entries"); - if(lct->boot_tid) - len += sprintf(buf+len, "Boot Device @ ID %d\n", lct->boot_tid); - - len += - sprintf(buf+len, "Current Change Indicator: %#10x\n", lct->change_ind); - - for(i = 0; i < entries; i++) - { - len += sprintf(buf+len, "Entry %d\n", i); - len += sprintf(buf+len, " Class, SubClass : %s", i2o_get_class_name(lct->lct_entry[i].class_id)); - - /* - * Classes which we'll print subclass info for - */ - switch(lct->lct_entry[i].class_id & 0xFFF) - { - case I2O_CLASS_RANDOM_BLOCK_STORAGE: - switch(lct->lct_entry[i].sub_class) - { - case 0x00: - len += sprintf(buf+len, ", Direct-Access Read/Write"); - break; - - case 0x04: - len += sprintf(buf+len, ", WORM Drive"); - break; - - case 0x05: - len += sprintf(buf+len, ", CD-ROM Drive"); - break; - - case 0x07: - len += sprintf(buf+len, ", Optical Memory Device"); - break; - - default: - len += sprintf(buf+len, ", Unknown (0x%02x)", - lct->lct_entry[i].sub_class); - break; - } - break; - - case I2O_CLASS_LAN: - switch(lct->lct_entry[i].sub_class & 0xFF) - { - case 0x30: - len += sprintf(buf+len, ", Ethernet"); - break; - - case 0x40: - len += sprintf(buf+len, ", 100base VG"); - break; - - case 0x50: - len += sprintf(buf+len, ", IEEE 802.5/Token-Ring"); - break; - - case 0x60: - len += sprintf(buf+len, ", ANSI X3T9.5 FDDI"); - break; - - case 0x70: - len += sprintf(buf+len, ", Fibre Channel"); - break; - - default: - len += sprintf(buf+len, ", Unknown Sub-Class (0x%02x)", - lct->lct_entry[i].sub_class & 0xFF); - break; - } - break; - - case I2O_CLASS_SCSI_PERIPHERAL: - if(lct->lct_entry[i].sub_class < SCSI_TABLE_SIZE) - len += sprintf(buf+len, ", %s", - scsi_devices[lct->lct_entry[i].sub_class]); - else - len += sprintf(buf+len, ", Unknown Device Type"); - break; - - case I2O_CLASS_BUS_ADAPTER_PORT: - if(lct->lct_entry[i].sub_class < BUS_TABLE_SIZE) - len += sprintf(buf+len, ", %s", - bus_ports[lct->lct_entry[i].sub_class]); - else - len += sprintf(buf+len, ", Unknown Bus Type"); - break; - } - len += sprintf(buf+len, "\n"); - - len += sprintf(buf+len, " Local TID : 0x%03x\n", lct->lct_entry[i].tid); - len += sprintf(buf+len, " User TID : 0x%03x\n", lct->lct_entry[i].user_tid); - len += sprintf(buf+len, " Parent TID : 0x%03x\n", - lct->lct_entry[i].parent_tid); - len += sprintf(buf+len, " Identity Tag : 0x%x%x%x%x%x%x%x%x\n", - lct->lct_entry[i].identity_tag[0], - lct->lct_entry[i].identity_tag[1], - lct->lct_entry[i].identity_tag[2], - lct->lct_entry[i].identity_tag[3], - lct->lct_entry[i].identity_tag[4], - lct->lct_entry[i].identity_tag[5], - lct->lct_entry[i].identity_tag[6], - lct->lct_entry[i].identity_tag[7]); - len += sprintf(buf+len, " Change Indicator : %0#10x\n", - lct->lct_entry[i].change_ind); - len += sprintf(buf+len, " Event Capab Mask : %0#10x\n", - lct->lct_entry[i].device_flags); - } - - spin_unlock(&i2o_proc_lock); - return len; -} - -int i2o_proc_read_status(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_controller *c = (struct i2o_controller*)data; - char prodstr[25]; - int version; - - spin_lock(&i2o_proc_lock); - len = 0; - - i2o_status_get(c); // reread the status block - - len += sprintf(buf+len,"Organization ID : %0#6x\n", - c->status_block->org_id); - - version = c->status_block->i2o_version; - -/* FIXME for Spec 2.0 - if (version == 0x02) { - len += sprintf(buf+len,"Lowest I2O version supported: "); - switch(workspace[2]) { - case 0x00: - len += sprintf(buf+len,"1.0\n"); - break; - case 0x01: - len += sprintf(buf+len,"1.5\n"); - break; - case 0x02: - len += sprintf(buf+len,"2.0\n"); - break; - } - - len += sprintf(buf+len, "Highest I2O version supported: "); - switch(workspace[3]) { - case 0x00: - len += sprintf(buf+len,"1.0\n"); - break; - case 0x01: - len += sprintf(buf+len,"1.5\n"); - break; - case 0x02: - len += sprintf(buf+len,"2.0\n"); - break; - } - } -*/ - len += sprintf(buf+len,"IOP ID : %0#5x\n", - c->status_block->iop_id); - len += sprintf(buf+len,"Host Unit ID : %0#6x\n", - c->status_block->host_unit_id); - len += sprintf(buf+len,"Segment Number : %0#5x\n", - c->status_block->segment_number); - - len += sprintf(buf+len, "I2O version : "); - switch (version) { - case 0x00: - len += sprintf(buf+len,"1.0\n"); - break; - case 0x01: - len += sprintf(buf+len,"1.5\n"); - break; - case 0x02: - len += sprintf(buf+len,"2.0\n"); - break; - default: - len += sprintf(buf+len,"Unknown version\n"); - } - - len += sprintf(buf+len, "IOP State : "); - switch (c->status_block->iop_state) { - case 0x01: - len += sprintf(buf+len,"INIT\n"); - break; - - case 0x02: - len += sprintf(buf+len,"RESET\n"); - break; - - case 0x04: - len += sprintf(buf+len,"HOLD\n"); - break; - - case 0x05: - len += sprintf(buf+len,"READY\n"); - break; - - case 0x08: - len += sprintf(buf+len,"OPERATIONAL\n"); - break; - - case 0x10: - len += sprintf(buf+len,"FAILED\n"); - break; - - case 0x11: - len += sprintf(buf+len,"FAULTED\n"); - break; - - default: - len += sprintf(buf+len,"Unknown\n"); - break; - } - - len += sprintf(buf+len,"Messenger Type : "); - switch (c->status_block->msg_type) { - case 0x00: - len += sprintf(buf+len,"Memory mapped\n"); - break; - case 0x01: - len += sprintf(buf+len,"Memory mapped only\n"); - break; - case 0x02: - len += sprintf(buf+len,"Remote only\n"); - break; - case 0x03: - len += sprintf(buf+len,"Memory mapped and remote\n"); - break; - default: - len += sprintf(buf+len,"Unknown\n"); - } - - len += sprintf(buf+len,"Inbound Frame Size : %d bytes\n", - c->status_block->inbound_frame_size<<2); - len += sprintf(buf+len,"Max Inbound Frames : %d\n", - c->status_block->max_inbound_frames); - len += sprintf(buf+len,"Current Inbound Frames : %d\n", - c->status_block->cur_inbound_frames); - len += sprintf(buf+len,"Max Outbound Frames : %d\n", - c->status_block->max_outbound_frames); - - /* Spec doesn't say if NULL terminated or not... */ - memcpy(prodstr, c->status_block->product_id, 24); - prodstr[24] = '\0'; - len += sprintf(buf+len,"Product ID : %s\n", prodstr); - len += sprintf(buf+len,"Expected LCT Size : %d bytes\n", - c->status_block->expected_lct_size); - - len += sprintf(buf+len,"IOP Capabilities\n"); - len += sprintf(buf+len," Context Field Size Support : "); - switch (c->status_block->iop_capabilities & 0x0000003) { - case 0: - len += sprintf(buf+len,"Supports only 32-bit context fields\n"); - break; - case 1: - len += sprintf(buf+len,"Supports only 64-bit context fields\n"); - break; - case 2: - len += sprintf(buf+len,"Supports 32-bit and 64-bit context fields, " - "but not concurrently\n"); - break; - case 3: - len += sprintf(buf+len,"Supports 32-bit and 64-bit context fields " - "concurrently\n"); - break; - default: - len += sprintf(buf+len,"0x%08x\n",c->status_block->iop_capabilities); - } - len += sprintf(buf+len," Current Context Field Size : "); - switch (c->status_block->iop_capabilities & 0x0000000C) { - case 0: - len += sprintf(buf+len,"not configured\n"); - break; - case 4: - len += sprintf(buf+len,"Supports only 32-bit context fields\n"); - break; - case 8: - len += sprintf(buf+len,"Supports only 64-bit context fields\n"); - break; - case 12: - len += sprintf(buf+len,"Supports both 32-bit or 64-bit context fields " - "concurrently\n"); - break; - default: - len += sprintf(buf+len,"\n"); - } - len += sprintf(buf+len," Inbound Peer Support : %s\n", - (c->status_block->iop_capabilities & 0x00000010) ? "Supported" : "Not supported"); - len += sprintf(buf+len," Outbound Peer Support : %s\n", - (c->status_block->iop_capabilities & 0x00000020) ? "Supported" : "Not supported"); - len += sprintf(buf+len," Peer to Peer Support : %s\n", - (c->status_block->iop_capabilities & 0x00000040) ? "Supported" : "Not supported"); - - len += sprintf(buf+len, "Desired private memory size : %d kB\n", - c->status_block->desired_mem_size>>10); - len += sprintf(buf+len, "Allocated private memory size : %d kB\n", - c->status_block->current_mem_size>>10); - len += sprintf(buf+len, "Private memory base address : %0#10x\n", - c->status_block->current_mem_base); - len += sprintf(buf+len, "Desired private I/O size : %d kB\n", - c->status_block->desired_io_size>>10); - len += sprintf(buf+len, "Allocated private I/O size : %d kB\n", - c->status_block->current_io_size>>10); - len += sprintf(buf+len, "Private I/O base address : %0#10x\n", - c->status_block->current_io_base); - - spin_unlock(&i2o_proc_lock); - - return len; -} - -int i2o_proc_read_hw(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_controller *c = (struct i2o_controller*)data; - static u32 work32[5]; - static u8 *work8 = (u8*)work32; - static u16 *work16 = (u16*)work32; - int token; - u32 hwcap; - - static char *cpu_table[] = - { - "Intel 80960 series", - "AMD2900 series", - "Motorola 68000 series", - "ARM series", - "MIPS series", - "Sparc series", - "PowerPC series", - "Intel x86 series" - }; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(c, ADAPTER_TID, 0x0000, -1, &work32, sizeof(work32)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0000 IOP Hardware"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "I2O Vendor ID : %0#6x\n", work16[0]); - len += sprintf(buf+len, "Product ID : %0#6x\n", work16[1]); - len += sprintf(buf+len, "CPU : "); - if(work8[16] > 8) - len += sprintf(buf+len, "Unknown\n"); - else - len += sprintf(buf+len, "%s\n", cpu_table[work8[16]]); - /* Anyone using ProcessorVersion? */ - - len += sprintf(buf+len, "RAM : %dkB\n", work32[1]>>10); - len += sprintf(buf+len, "Non-Volatile Mem : %dkB\n", work32[2]>>10); - - hwcap = work32[3]; - len += sprintf(buf+len, "Capabilities : 0x%08x\n", hwcap); - len += sprintf(buf+len, " [%s] Self booting\n", - (hwcap&0x00000001) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Upgradable IRTOS\n", - (hwcap&0x00000002) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Supports downloading DDMs\n", - (hwcap&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Supports installing DDMs\n", - (hwcap&0x00000008) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Battery-backed RAM\n", - (hwcap&0x00000010) ? "+" : "-"); - - spin_unlock(&i2o_proc_lock); - - return len; -} - - -/* Executive group 0003h - Executing DDM List (table) */ -int i2o_proc_read_ddm_table(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_controller *c = (struct i2o_controller*)data; - int token; - int i; - - typedef struct _i2o_exec_execute_ddm_table { - u16 ddm_tid; - u8 module_type; - u8 reserved; - u16 i2o_vendor_id; - u16 module_id; - u8 module_name_version[28]; - u32 data_size; - u32 code_size; - } i2o_exec_execute_ddm_table; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - i2o_exec_execute_ddm_table ddm_table[MAX_I2O_MODULES]; - } result; - - i2o_exec_execute_ddm_table ddm_table; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - c, ADAPTER_TID, - 0x0003, -1, - NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0003 Executing DDM List"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "Tid Module_type Vendor Mod_id Module_name Vrs Data_size Code_size\n"); - ddm_table=result.ddm_table[0]; - - for(i=0; i < result.row_count; ddm_table=result.ddm_table[++i]) - { - len += sprintf(buf+len, "0x%03x ", ddm_table.ddm_tid & 0xFFF); - - switch(ddm_table.module_type) - { - case 0x01: - len += sprintf(buf+len, "Downloaded DDM "); - break; - case 0x22: - len += sprintf(buf+len, "Embedded DDM "); - break; - default: - len += sprintf(buf+len, " "); - } - - len += sprintf(buf+len, "%-#7x", ddm_table.i2o_vendor_id); - len += sprintf(buf+len, "%-#8x", ddm_table.module_id); - len += sprintf(buf+len, "%-29s", chtostr(ddm_table.module_name_version, 28)); - len += sprintf(buf+len, "%9d ", ddm_table.data_size); - len += sprintf(buf+len, "%8d", ddm_table.code_size); - - len += sprintf(buf+len, "\n"); - } - - spin_unlock(&i2o_proc_lock); - - return len; -} - - -/* Executive group 0004h - Driver Store (scalar) */ -int i2o_proc_read_driver_store(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_controller *c = (struct i2o_controller*)data; - u32 work32[8]; - int token; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(c, ADAPTER_TID, 0x0004, -1, &work32, sizeof(work32)); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0004 Driver Store"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "Module limit : %d\n" - "Module count : %d\n" - "Current space : %d kB\n" - "Free space : %d kB\n", - work32[0], work32[1], work32[2]>>10, work32[3]>>10); - - spin_unlock(&i2o_proc_lock); - - return len; -} - - -/* Executive group 0005h - Driver Store Table (table) */ -int i2o_proc_read_drivers_stored(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - typedef struct _i2o_driver_store { - u16 stored_ddm_index; - u8 module_type; - u8 reserved; - u16 i2o_vendor_id; - u16 module_id; - u8 module_name_version[28]; - u8 date[8]; - u32 module_size; - u32 mpb_size; - u32 module_flags; - } i2o_driver_store_table; - - struct i2o_controller *c = (struct i2o_controller*)data; - int token; - int i; - - typedef struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - i2o_driver_store_table dst[MAX_I2O_MODULES]; - } i2o_driver_result_table; - - i2o_driver_result_table *result; - i2o_driver_store_table *dst; - - spin_lock(&i2o_proc_lock); - - len = 0; - - result = kmalloc(sizeof(i2o_driver_result_table), GFP_KERNEL); - if(result == NULL) - return -ENOMEM; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - c, ADAPTER_TID, 0x0005, -1, NULL, 0, - result, sizeof(*result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0005 DRIVER STORE TABLE"); - spin_unlock(&i2o_proc_lock); - kfree(result); - return len; - } - - len += sprintf(buf+len, "# Module_type Vendor Mod_id Module_name Vrs" - "Date Mod_size Par_size Flags\n"); - for(i=0, dst=&result->dst[0]; i < result->row_count; dst=&result->dst[++i]) - { - len += sprintf(buf+len, "%-3d", dst->stored_ddm_index); - switch(dst->module_type) - { - case 0x01: - len += sprintf(buf+len, "Downloaded DDM "); - break; - case 0x22: - len += sprintf(buf+len, "Embedded DDM "); - break; - default: - len += sprintf(buf+len, " "); - } - -#if 0 - if(c->i2oversion == 0x02) - len += sprintf(buf+len, "%-d", dst->module_state); -#endif - - len += sprintf(buf+len, "%-#7x", dst->i2o_vendor_id); - len += sprintf(buf+len, "%-#8x", dst->module_id); - len += sprintf(buf+len, "%-29s", chtostr(dst->module_name_version,28)); - len += sprintf(buf+len, "%-9s", chtostr(dst->date,8)); - len += sprintf(buf+len, "%8d ", dst->module_size); - len += sprintf(buf+len, "%8d ", dst->mpb_size); - len += sprintf(buf+len, "0x%04x", dst->module_flags); -#if 0 - if(c->i2oversion == 0x02) - len += sprintf(buf+len, "%d", - dst->notification_level); -#endif - len += sprintf(buf+len, "\n"); - } - - spin_unlock(&i2o_proc_lock); - kfree(result); - return len; -} - - -/* Generic group F000h - Params Descriptor (table) */ -int i2o_proc_read_groups(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - u8 properties; - - typedef struct _i2o_group_info - { - u16 group_number; - u16 field_count; - u16 row_count; - u8 properties; - u8 reserved; - } i2o_group_info; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - i2o_group_info group[256]; - } result; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, 0xF000, -1, NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len = i2o_report_query_status(buf+len, token, "0xF000 Params Descriptor"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "# Group FieldCount RowCount Type Add Del Clear\n"); - - for (i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%-3d", i); - len += sprintf(buf+len, "0x%04X ", result.group[i].group_number); - len += sprintf(buf+len, "%10d ", result.group[i].field_count); - len += sprintf(buf+len, "%8d ", result.group[i].row_count); - - properties = result.group[i].properties; - if (properties & 0x1) len += sprintf(buf+len, "Table "); - else len += sprintf(buf+len, "Scalar "); - if (properties & 0x2) len += sprintf(buf+len, " + "); - else len += sprintf(buf+len, " - "); - if (properties & 0x4) len += sprintf(buf+len, " + "); - else len += sprintf(buf+len, " - "); - if (properties & 0x8) len += sprintf(buf+len, " + "); - else len += sprintf(buf+len, " - "); - - len += sprintf(buf+len, "\n"); - } - - if (result.more_flag) - len += sprintf(buf+len, "There is more...\n"); - - spin_unlock(&i2o_proc_lock); - - return len; -} - - -/* Generic group F001h - Physical Device Table (table) */ -int i2o_proc_read_phys_device(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - u32 adapter_id[64]; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF001, -1, NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF001 Physical Device Table"); - spin_unlock(&i2o_proc_lock); - return len; - } - - if (result.row_count) - len += sprintf(buf+len, "# AdapterId\n"); - - for (i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%-2d", i); - len += sprintf(buf+len, "%#7x\n", result.adapter_id[i]); - } - - if (result.more_flag) - len += sprintf(buf+len, "There is more...\n"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* Generic group F002h - Claimed Table (table) */ -int i2o_proc_read_claimed(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - - struct { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - u16 claimed_tid[64]; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF002, -1, NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF002 Claimed Table"); - spin_unlock(&i2o_proc_lock); - return len; - } - - if (result.row_count) - len += sprintf(buf+len, "# ClaimedTid\n"); - - for (i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%-2d", i); - len += sprintf(buf+len, "%#7x\n", result.claimed_tid[i]); - } - - if (result.more_flag) - len += sprintf(buf+len, "There is more...\n"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* Generic group F003h - User Table (table) */ -int i2o_proc_read_users(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - - typedef struct _i2o_user_table - { - u16 instance; - u16 user_tid; - u8 claim_type; - u8 reserved1; - u16 reserved2; - } i2o_user_table; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - i2o_user_table user[64]; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF003, -1, NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF003 User Table"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "# Instance UserTid ClaimType\n"); - - for(i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%-3d", i); - len += sprintf(buf+len, "%#8x ", result.user[i].instance); - len += sprintf(buf+len, "%#7x ", result.user[i].user_tid); - len += sprintf(buf+len, "%#9x\n", result.user[i].claim_type); - } - - if (result.more_flag) - len += sprintf(buf+len, "There is more...\n"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* Generic group F005h - Private message extensions (table) (optional) */ -int i2o_proc_read_priv_msgs(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - - typedef struct _i2o_private - { - u16 ext_instance; - u16 organization_id; - u16 x_function_code; - } i2o_private; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - i2o_private extension[64]; - } result; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF000, -1, - NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF005 Private Message Extensions (optional)"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "Instance# OrgId FunctionCode\n"); - - for(i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%0#9x ", result.extension[i].ext_instance); - len += sprintf(buf+len, "%0#6x ", result.extension[i].organization_id); - len += sprintf(buf+len, "%0#6x", result.extension[i].x_function_code); - - len += sprintf(buf+len, "\n"); - } - - if(result.more_flag) - len += sprintf(buf+len, "There is more...\n"); - - spin_unlock(&i2o_proc_lock); - - return len; -} - - -/* Generic group F006h - Authorized User Table (table) */ -int i2o_proc_read_authorized_users(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - u32 alternate_tid[64]; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF006, -1, - NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF006 Autohorized User Table"); - spin_unlock(&i2o_proc_lock); - return len; - } - - if (result.row_count) - len += sprintf(buf+len, "# AlternateTid\n"); - - for(i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%-2d", i); - len += sprintf(buf+len, "%#7x ", result.alternate_tid[i]); - } - - if (result.more_flag) - len += sprintf(buf+len, "There is more...\n"); - - spin_unlock(&i2o_proc_lock); - return len; -} - - -/* Generic group F100h - Device Identity (scalar) */ -int i2o_proc_read_dev_identity(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[128]; // allow for "stuff" + up to 256 byte (max) serial number - // == (allow) 512d bytes (max) - static u16 *work16 = (u16*)work32; - int token; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF100, -1, - &work32, sizeof(work32)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token ,"0xF100 Device Identity"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Device Class : %s\n", i2o_get_class_name(work16[0])); - len += sprintf(buf+len, "Owner TID : %0#5x\n", work16[2]); - len += sprintf(buf+len, "Parent TID : %0#5x\n", work16[3]); - len += sprintf(buf+len, "Vendor info : %s\n", chtostr((u8 *)(work32+2), 16)); - len += sprintf(buf+len, "Product info : %s\n", chtostr((u8 *)(work32+6), 16)); - len += sprintf(buf+len, "Description : %s\n", chtostr((u8 *)(work32+10), 16)); - len += sprintf(buf+len, "Product rev. : %s\n", chtostr((u8 *)(work32+14), 8)); - - len += sprintf(buf+len, "Serial number : "); - len = print_serial_number(buf, len, - (u8*)(work32+16), - /* allow for SNLen plus - * possible trailing '\0' - */ - sizeof(work32)-(16*sizeof(u32))-2 - ); - len += sprintf(buf+len, "\n"); - - spin_unlock(&i2o_proc_lock); - - return len; -} - - -int i2o_proc_read_dev_name(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - - if ( d->dev_name[0] == '\0' ) - return 0; - - len = sprintf(buf, "%s\n", d->dev_name); - - return len; -} - - -/* Generic group F101h - DDM Identity (scalar) */ -int i2o_proc_read_ddm_identity(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - - struct - { - u16 ddm_tid; - u8 module_name[24]; - u8 module_rev[8]; - u8 sn_format; - u8 serial_number[12]; - u8 pad[256]; // allow up to 256 byte (max) serial number - } result; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF101, -1, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF101 DDM Identity"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Registering DDM TID : 0x%03x\n", result.ddm_tid); - len += sprintf(buf+len, "Module name : %s\n", chtostr(result.module_name, 24)); - len += sprintf(buf+len, "Module revision : %s\n", chtostr(result.module_rev, 8)); - - len += sprintf(buf+len, "Serial number : "); - len = print_serial_number(buf, len, result.serial_number, sizeof(result)-36); - /* allow for SNLen plus possible trailing '\0' */ - - len += sprintf(buf+len, "\n"); - - spin_unlock(&i2o_proc_lock); - - return len; -} - -/* Generic group F102h - User Information (scalar) */ -int i2o_proc_read_uinfo(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - - struct - { - u8 device_name[64]; - u8 service_name[64]; - u8 physical_location[64]; - u8 instance_number[4]; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF102, -1, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF102 User Information"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Device name : %s\n", chtostr(result.device_name, 64)); - len += sprintf(buf+len, "Service name : %s\n", chtostr(result.service_name, 64)); - len += sprintf(buf+len, "Physical name : %s\n", chtostr(result.physical_location, 64)); - len += sprintf(buf+len, "Instance number : %s\n", chtostr(result.instance_number, 4)); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* Generic group F103h - SGL Operating Limits (scalar) */ -int i2o_proc_read_sgl_limits(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[12]; - static u16 *work16 = (u16 *)work32; - static u8 *work8 = (u8 *)work32; - int token; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF103, -1, - &work32, sizeof(work32)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF103 SGL Operating Limits"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "SGL chain size : %d\n", work32[0]); - len += sprintf(buf+len, "Max SGL chain size : %d\n", work32[1]); - len += sprintf(buf+len, "SGL chain size target : %d\n", work32[2]); - len += sprintf(buf+len, "SGL frag count : %d\n", work16[6]); - len += sprintf(buf+len, "Max SGL frag count : %d\n", work16[7]); - len += sprintf(buf+len, "SGL frag count target : %d\n", work16[8]); - - if (d->i2oversion == 0x02) - { - len += sprintf(buf+len, "SGL data alignment : %d\n", work16[8]); - len += sprintf(buf+len, "SGL addr limit : %d\n", work8[20]); - len += sprintf(buf+len, "SGL addr sizes supported : "); - if (work8[21] & 0x01) - len += sprintf(buf+len, "32 bit "); - if (work8[21] & 0x02) - len += sprintf(buf+len, "64 bit "); - if (work8[21] & 0x04) - len += sprintf(buf+len, "96 bit "); - if (work8[21] & 0x08) - len += sprintf(buf+len, "128 bit "); - len += sprintf(buf+len, "\n"); - } - - spin_unlock(&i2o_proc_lock); - - return len; -} - -/* Generic group F200h - Sensors (scalar) */ -int i2o_proc_read_sensors(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - - struct - { - u16 sensor_instance; - u8 component; - u16 component_instance; - u8 sensor_class; - u8 sensor_type; - u8 scaling_exponent; - u32 actual_reading; - u32 minimum_reading; - u32 low2lowcat_treshold; - u32 lowcat2low_treshold; - u32 lowwarn2low_treshold; - u32 low2lowwarn_treshold; - u32 norm2lowwarn_treshold; - u32 lowwarn2norm_treshold; - u32 nominal_reading; - u32 hiwarn2norm_treshold; - u32 norm2hiwarn_treshold; - u32 high2hiwarn_treshold; - u32 hiwarn2high_treshold; - u32 hicat2high_treshold; - u32 hi2hicat_treshold; - u32 maximum_reading; - u8 sensor_state; - u16 event_enable; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF200, -1, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF200 Sensors (optional)"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "Sensor instance : %d\n", result.sensor_instance); - - len += sprintf(buf+len, "Component : %d = ", result.component); - switch (result.component) - { - case 0: len += sprintf(buf+len, "Other"); - break; - case 1: len += sprintf(buf+len, "Planar logic Board"); - break; - case 2: len += sprintf(buf+len, "CPU"); - break; - case 3: len += sprintf(buf+len, "Chassis"); - break; - case 4: len += sprintf(buf+len, "Power Supply"); - break; - case 5: len += sprintf(buf+len, "Storage"); - break; - case 6: len += sprintf(buf+len, "External"); - break; - } - len += sprintf(buf+len,"\n"); - - len += sprintf(buf+len, "Component instance : %d\n", result.component_instance); - len += sprintf(buf+len, "Sensor class : %s\n", - result.sensor_class ? "Analog" : "Digital"); - - len += sprintf(buf+len, "Sensor type : %d = ",result.sensor_type); - switch (result.sensor_type) - { - case 0: len += sprintf(buf+len, "Other\n"); - break; - case 1: len += sprintf(buf+len, "Thermal\n"); - break; - case 2: len += sprintf(buf+len, "DC voltage (DC volts)\n"); - break; - case 3: len += sprintf(buf+len, "AC voltage (AC volts)\n"); - break; - case 4: len += sprintf(buf+len, "DC current (DC amps)\n"); - break; - case 5: len += sprintf(buf+len, "AC current (AC volts)\n"); - break; - case 6: len += sprintf(buf+len, "Door open\n"); - break; - case 7: len += sprintf(buf+len, "Fan operational\n"); - break; - } - - len += sprintf(buf+len, "Scaling exponent : %d\n", result.scaling_exponent); - len += sprintf(buf+len, "Actual reading : %d\n", result.actual_reading); - len += sprintf(buf+len, "Minimum reading : %d\n", result.minimum_reading); - len += sprintf(buf+len, "Low2LowCat treshold : %d\n", result.low2lowcat_treshold); - len += sprintf(buf+len, "LowCat2Low treshold : %d\n", result.lowcat2low_treshold); - len += sprintf(buf+len, "LowWarn2Low treshold : %d\n", result.lowwarn2low_treshold); - len += sprintf(buf+len, "Low2LowWarn treshold : %d\n", result.low2lowwarn_treshold); - len += sprintf(buf+len, "Norm2LowWarn treshold : %d\n", result.norm2lowwarn_treshold); - len += sprintf(buf+len, "LowWarn2Norm treshold : %d\n", result.lowwarn2norm_treshold); - len += sprintf(buf+len, "Nominal reading : %d\n", result.nominal_reading); - len += sprintf(buf+len, "HiWarn2Norm treshold : %d\n", result.hiwarn2norm_treshold); - len += sprintf(buf+len, "Norm2HiWarn treshold : %d\n", result.norm2hiwarn_treshold); - len += sprintf(buf+len, "High2HiWarn treshold : %d\n", result.high2hiwarn_treshold); - len += sprintf(buf+len, "HiWarn2High treshold : %d\n", result.hiwarn2high_treshold); - len += sprintf(buf+len, "HiCat2High treshold : %d\n", result.hicat2high_treshold); - len += sprintf(buf+len, "High2HiCat treshold : %d\n", result.hi2hicat_treshold); - len += sprintf(buf+len, "Maximum reading : %d\n", result.maximum_reading); - - len += sprintf(buf+len, "Sensor state : %d = ", result.sensor_state); - switch (result.sensor_state) - { - case 0: len += sprintf(buf+len, "Normal\n"); - break; - case 1: len += sprintf(buf+len, "Abnormal\n"); - break; - case 2: len += sprintf(buf+len, "Unknown\n"); - break; - case 3: len += sprintf(buf+len, "Low Catastrophic (LoCat)\n"); - break; - case 4: len += sprintf(buf+len, "Low (Low)\n"); - break; - case 5: len += sprintf(buf+len, "Low Warning (LoWarn)\n"); - break; - case 6: len += sprintf(buf+len, "High Warning (HiWarn)\n"); - break; - case 7: len += sprintf(buf+len, "High (High)\n"); - break; - case 8: len += sprintf(buf+len, "High Catastrophic (HiCat)\n"); - break; - } - - len += sprintf(buf+len, "Event_enable : 0x%02X\n", result.event_enable); - len += sprintf(buf+len, " [%s] Operational state change. \n", - (result.event_enable & 0x01) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] Low catastrophic. \n", - (result.event_enable & 0x02) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] Low reading. \n", - (result.event_enable & 0x04) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] Low warning. \n", - (result.event_enable & 0x08) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] Change back to normal from out of range state. \n", - (result.event_enable & 0x10) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] High warning. \n", - (result.event_enable & 0x20) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] High reading. \n", - (result.event_enable & 0x40) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] High catastrophic. \n", - (result.event_enable & 0x80) ? "+" : "-" ); - - spin_unlock(&i2o_proc_lock); - return len; -} - - -static int print_serial_number(char *buff, int pos, u8 *serialno, int max_len) -{ - int i; - - /* 19990419 -sralston - * The I2O v1.5 (and v2.0 so far) "official specification" - * got serial numbers WRONG! - * Apparently, and despite what Section 3.4.4 says and - * Figure 3-35 shows (pg 3-39 in the pdf doc), - * the convention / consensus seems to be: - * + First byte is SNFormat - * + Second byte is SNLen (but only if SNFormat==7 (?)) - * + (v2.0) SCSI+BS may use IEEE Registered (64 or 128 bit) format - */ - switch(serialno[0]) - { - case I2O_SNFORMAT_BINARY: /* Binary */ - pos += sprintf(buff+pos, "0x"); - for(i = 0; i < serialno[1]; i++) - { - pos += sprintf(buff+pos, "%02X", serialno[2+i]); - } - break; - - case I2O_SNFORMAT_ASCII: /* ASCII */ - if ( serialno[1] < ' ' ) /* printable or SNLen? */ - { - /* sanity */ - max_len = (max_len < serialno[1]) ? max_len : serialno[1]; - serialno[1+max_len] = '\0'; - - /* just print it */ - pos += sprintf(buff+pos, "%s", &serialno[2]); - } - else - { - /* print chars for specified length */ - for(i = 0; i < serialno[1]; i++) - { - pos += sprintf(buff+pos, "%c", serialno[2+i]); - } - } - break; - - case I2O_SNFORMAT_UNICODE: /* UNICODE */ - pos += sprintf(buff+pos, "UNICODE Format. Can't Display\n"); - break; - - case I2O_SNFORMAT_LAN48_MAC: /* LAN-48 MAC Address */ - pos += sprintf(buff+pos, - "LAN-48 MAC address @ %02X:%02X:%02X:%02X:%02X:%02X", - serialno[2], serialno[3], - serialno[4], serialno[5], - serialno[6], serialno[7]); - break; - - case I2O_SNFORMAT_WAN: /* WAN MAC Address */ - /* FIXME: Figure out what a WAN access address looks like?? */ - pos += sprintf(buff+pos, "WAN Access Address"); - break; - -/* plus new in v2.0 */ - case I2O_SNFORMAT_LAN64_MAC: /* LAN-64 MAC Address */ - /* FIXME: Figure out what a LAN-64 address really looks like?? */ - pos += sprintf(buff+pos, - "LAN-64 MAC address @ [?:%02X:%02X:?] %02X:%02X:%02X:%02X:%02X:%02X", - serialno[8], serialno[9], - serialno[2], serialno[3], - serialno[4], serialno[5], - serialno[6], serialno[7]); - break; - - - case I2O_SNFORMAT_DDM: /* I2O DDM */ - pos += sprintf(buff+pos, - "DDM: Tid=%03Xh, Rsvd=%04Xh, OrgId=%04Xh", - *(u16*)&serialno[2], - *(u16*)&serialno[4], - *(u16*)&serialno[6]); - break; - - case I2O_SNFORMAT_IEEE_REG64: /* IEEE Registered (64-bit) */ - case I2O_SNFORMAT_IEEE_REG128: /* IEEE Registered (128-bit) */ - /* FIXME: Figure if this is even close?? */ - pos += sprintf(buff+pos, - "IEEE NodeName(hi,lo)=(%08Xh:%08Xh), PortName(hi,lo)=(%08Xh:%08Xh)\n", - *(u32*)&serialno[2], - *(u32*)&serialno[6], - *(u32*)&serialno[10], - *(u32*)&serialno[14]); - break; - - - case I2O_SNFORMAT_UNKNOWN: /* Unknown 0 */ - case I2O_SNFORMAT_UNKNOWN2: /* Unknown 0xff */ - default: - pos += sprintf(buff+pos, "Unknown data format (0x%02x)", - serialno[0]); - break; - } - - return pos; -} - -const char * i2o_get_connector_type(int conn) -{ - int idx = 16; - static char *i2o_connector_type[] = { - "OTHER", - "UNKNOWN", - "AUI", - "UTP", - "BNC", - "RJ45", - "STP DB9", - "FIBER MIC", - "APPLE AUI", - "MII", - "DB9", - "HSSDC", - "DUPLEX SC FIBER", - "DUPLEX ST FIBER", - "TNC/BNC", - "HW DEFAULT" - }; - - switch(conn) - { - case 0x00000000: - idx = 0; - break; - case 0x00000001: - idx = 1; - break; - case 0x00000002: - idx = 2; - break; - case 0x00000003: - idx = 3; - break; - case 0x00000004: - idx = 4; - break; - case 0x00000005: - idx = 5; - break; - case 0x00000006: - idx = 6; - break; - case 0x00000007: - idx = 7; - break; - case 0x00000008: - idx = 8; - break; - case 0x00000009: - idx = 9; - break; - case 0x0000000A: - idx = 10; - break; - case 0x0000000B: - idx = 11; - break; - case 0x0000000C: - idx = 12; - break; - case 0x0000000D: - idx = 13; - break; - case 0x0000000E: - idx = 14; - break; - case 0xFFFFFFFF: - idx = 15; - break; - } - - return i2o_connector_type[idx]; -} - - -const char * i2o_get_connection_type(int conn) -{ - int idx = 0; - static char *i2o_connection_type[] = { - "Unknown", - "AUI", - "10BASE5", - "FIORL", - "10BASE2", - "10BROAD36", - "10BASE-T", - "10BASE-FP", - "10BASE-FB", - "10BASE-FL", - "100BASE-TX", - "100BASE-FX", - "100BASE-T4", - "1000BASE-SX", - "1000BASE-LX", - "1000BASE-CX", - "1000BASE-T", - "100VG-ETHERNET", - "100VG-TOKEN RING", - "4MBIT TOKEN RING", - "16 Mb Token Ring", - "125 MBAUD FDDI", - "Point-to-point", - "Arbitrated loop", - "Public loop", - "Fabric", - "Emulation", - "Other", - "HW default" - }; - - switch(conn) - { - case I2O_LAN_UNKNOWN: - idx = 0; - break; - case I2O_LAN_AUI: - idx = 1; - break; - case I2O_LAN_10BASE5: - idx = 2; - break; - case I2O_LAN_FIORL: - idx = 3; - break; - case I2O_LAN_10BASE2: - idx = 4; - break; - case I2O_LAN_10BROAD36: - idx = 5; - break; - case I2O_LAN_10BASE_T: - idx = 6; - break; - case I2O_LAN_10BASE_FP: - idx = 7; - break; - case I2O_LAN_10BASE_FB: - idx = 8; - break; - case I2O_LAN_10BASE_FL: - idx = 9; - break; - case I2O_LAN_100BASE_TX: - idx = 10; - break; - case I2O_LAN_100BASE_FX: - idx = 11; - break; - case I2O_LAN_100BASE_T4: - idx = 12; - break; - case I2O_LAN_1000BASE_SX: - idx = 13; - break; - case I2O_LAN_1000BASE_LX: - idx = 14; - break; - case I2O_LAN_1000BASE_CX: - idx = 15; - break; - case I2O_LAN_1000BASE_T: - idx = 16; - break; - case I2O_LAN_100VG_ETHERNET: - idx = 17; - break; - case I2O_LAN_100VG_TR: - idx = 18; - break; - case I2O_LAN_4MBIT: - idx = 19; - break; - case I2O_LAN_16MBIT: - idx = 20; - break; - case I2O_LAN_125MBAUD: - idx = 21; - break; - case I2O_LAN_POINT_POINT: - idx = 22; - break; - case I2O_LAN_ARB_LOOP: - idx = 23; - break; - case I2O_LAN_PUBLIC_LOOP: - idx = 24; - break; - case I2O_LAN_FABRIC: - idx = 25; - break; - case I2O_LAN_EMULATION: - idx = 26; - break; - case I2O_LAN_OTHER: - idx = 27; - break; - case I2O_LAN_DEFAULT: - idx = 28; - break; - } - - return i2o_connection_type[idx]; -} - - -/* LAN group 0000h - Device info (scalar) */ -int i2o_proc_read_lan_dev_info(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[56]; - static u8 *work8 = (u8*)work32; - static u16 *work16 = (u16*)work32; - static u64 *work64 = (u64*)work32; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0000, -1, &work32, 56*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token, "0x0000 LAN Device Info"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "LAN Type : "); - switch (work16[0]) - { - case 0x0030: - len += sprintf(buf+len, "Ethernet, "); - break; - case 0x0040: - len += sprintf(buf+len, "100Base VG, "); - break; - case 0x0050: - len += sprintf(buf+len, "Token Ring, "); - break; - case 0x0060: - len += sprintf(buf+len, "FDDI, "); - break; - case 0x0070: - len += sprintf(buf+len, "Fibre Channel, "); - break; - default: - len += sprintf(buf+len, "Unknown type (0x%04x), ", work16[0]); - break; - } - - if (work16[1]&0x00000001) - len += sprintf(buf+len, "emulated LAN, "); - else - len += sprintf(buf+len, "physical LAN port, "); - - if (work16[1]&0x00000002) - len += sprintf(buf+len, "full duplex\n"); - else - len += sprintf(buf+len, "simplex\n"); - - len += sprintf(buf+len, "Address format : "); - switch(work8[4]) { - case 0x00: - len += sprintf(buf+len, "IEEE 48bit\n"); - break; - case 0x01: - len += sprintf(buf+len, "FC IEEE\n"); - break; - default: - len += sprintf(buf+len, "Unknown (0x%02x)\n", work8[4]); - break; - } - - len += sprintf(buf+len, "State : "); - switch(work8[5]) - { - case 0x00: - len += sprintf(buf+len, "Unknown\n"); - break; - case 0x01: - len += sprintf(buf+len, "Unclaimed\n"); - break; - case 0x02: - len += sprintf(buf+len, "Operational\n"); - break; - case 0x03: - len += sprintf(buf+len, "Suspended\n"); - break; - case 0x04: - len += sprintf(buf+len, "Resetting\n"); - break; - case 0x05: - len += sprintf(buf+len, "ERROR: "); - if(work16[3]&0x0001) - len += sprintf(buf+len, "TxCU inoperative "); - if(work16[3]&0x0002) - len += sprintf(buf+len, "RxCU inoperative "); - if(work16[3]&0x0004) - len += sprintf(buf+len, "Local mem alloc "); - len += sprintf(buf+len, "\n"); - break; - case 0x06: - len += sprintf(buf+len, "Operational no Rx\n"); - break; - case 0x07: - len += sprintf(buf+len, "Suspended no Rx\n"); - break; - default: - len += sprintf(buf+len, "Unspecified\n"); - break; - } - - len += sprintf(buf+len, "Min packet size : %d\n", work32[2]); - len += sprintf(buf+len, "Max packet size : %d\n", work32[3]); - len += sprintf(buf+len, "HW address : " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - work8[16],work8[17],work8[18],work8[19], - work8[20],work8[21],work8[22],work8[23]); - - len += sprintf(buf+len, "Max Tx wire speed : %d bps\n", (int)work64[3]); - len += sprintf(buf+len, "Max Rx wire speed : %d bps\n", (int)work64[4]); - - len += sprintf(buf+len, "Min SDU packet size : 0x%08x\n", work32[10]); - len += sprintf(buf+len, "Max SDU packet size : 0x%08x\n", work32[11]); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0001h - MAC address table (scalar) */ -int i2o_proc_read_lan_mac_addr(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[48]; - static u8 *work8 = (u8*)work32; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0001, -1, &work32, 48*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0001 LAN MAC Address"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Active address : " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - work8[0],work8[1],work8[2],work8[3], - work8[4],work8[5],work8[6],work8[7]); - len += sprintf(buf+len, "Current address : " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - work8[8],work8[9],work8[10],work8[11], - work8[12],work8[13],work8[14],work8[15]); - len += sprintf(buf+len, "Functional address mask : " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - work8[16],work8[17],work8[18],work8[19], - work8[20],work8[21],work8[22],work8[23]); - - len += sprintf(buf+len,"HW/DDM capabilities : 0x%08x\n", work32[7]); - len += sprintf(buf+len," [%s] Unicast packets supported\n", - (work32[7]&0x00000001)?"+":"-"); - len += sprintf(buf+len," [%s] Promiscuous mode supported\n", - (work32[7]&0x00000002)?"+":"-"); - len += sprintf(buf+len," [%s] Promiscuous multicast mode supported\n", - (work32[7]&0x00000004)?"+":"-"); - len += sprintf(buf+len," [%s] Broadcast reception disabling supported\n", - (work32[7]&0x00000100)?"+":"-"); - len += sprintf(buf+len," [%s] Multicast reception disabling supported\n", - (work32[7]&0x00000200)?"+":"-"); - len += sprintf(buf+len," [%s] Functional address disabling supported\n", - (work32[7]&0x00000400)?"+":"-"); - len += sprintf(buf+len," [%s] MAC reporting supported\n", - (work32[7]&0x00000800)?"+":"-"); - - len += sprintf(buf+len,"Filter mask : 0x%08x\n", work32[6]); - len += sprintf(buf+len," [%s] Unicast packets disable\n", - (work32[6]&0x00000001)?"+":"-"); - len += sprintf(buf+len," [%s] Promiscuous mode enable\n", - (work32[6]&0x00000002)?"+":"-"); - len += sprintf(buf+len," [%s] Promiscuous multicast mode enable\n", - (work32[6]&0x00000004)?"+":"-"); - len += sprintf(buf+len," [%s] Broadcast packets disable\n", - (work32[6]&0x00000100)?"+":"-"); - len += sprintf(buf+len," [%s] Multicast packets disable\n", - (work32[6]&0x00000200)?"+":"-"); - len += sprintf(buf+len," [%s] Functional address disable\n", - (work32[6]&0x00000400)?"+":"-"); - - if (work32[7]&0x00000800) { - len += sprintf(buf+len, " MAC reporting mode : "); - if (work32[6]&0x00000800) - len += sprintf(buf+len, "Pass only priority MAC packets to user\n"); - else if (work32[6]&0x00001000) - len += sprintf(buf+len, "Pass all MAC packets to user\n"); - else if (work32[6]&0x00001800) - len += sprintf(buf+len, "Pass all MAC packets (promiscuous) to user\n"); - else - len += sprintf(buf+len, "Do not pass MAC packets to user\n"); - } - len += sprintf(buf+len, "Number of multicast addresses : %d\n", work32[8]); - len += sprintf(buf+len, "Perfect filtering for max %d multicast addresses\n", - work32[9]); - len += sprintf(buf+len, "Imperfect filtering for max %d multicast addresses\n", - work32[10]); - - spin_unlock(&i2o_proc_lock); - - return len; -} - -/* LAN group 0002h - Multicast MAC address table (table) */ -int i2o_proc_read_lan_mcast_addr(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - u8 mc_addr[8]; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - u8 mc_addr[256][8]; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, 0x0002, -1, - NULL, 0, &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x002 LAN Multicast MAC Address"); - spin_unlock(&i2o_proc_lock); - return len; - } - - for (i = 0; i < result.row_count; i++) - { - memcpy(mc_addr, result.mc_addr[i], 8); - - len += sprintf(buf+len, "MC MAC address[%d]: " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - i, mc_addr[0], mc_addr[1], mc_addr[2], - mc_addr[3], mc_addr[4], mc_addr[5], - mc_addr[6], mc_addr[7]); - } - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0003h - Batch Control (scalar) */ -int i2o_proc_read_lan_batch_control(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[9]; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0003, -1, &work32, 9*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0003 LAN Batch Control"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Batch mode "); - if (work32[0]&0x00000001) - len += sprintf(buf+len, "disabled"); - else - len += sprintf(buf+len, "enabled"); - if (work32[0]&0x00000002) - len += sprintf(buf+len, " (current setting)"); - if (work32[0]&0x00000004) - len += sprintf(buf+len, ", forced"); - else - len += sprintf(buf+len, ", toggle"); - len += sprintf(buf+len, "\n"); - - len += sprintf(buf+len, "Max Rx batch count : %d\n", work32[5]); - len += sprintf(buf+len, "Max Rx batch delay : %d\n", work32[6]); - len += sprintf(buf+len, "Max Tx batch delay : %d\n", work32[7]); - len += sprintf(buf+len, "Max Tx batch count : %d\n", work32[8]); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0004h - LAN Operation (scalar) */ -int i2o_proc_read_lan_operation(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[5]; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0004, -1, &work32, 20); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0004 LAN Operation"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Packet prepadding (32b words) : %d\n", work32[0]); - len += sprintf(buf+len, "Transmission error reporting : %s\n", - (work32[1]&1)?"on":"off"); - len += sprintf(buf+len, "Bad packet handling : %s\n", - (work32[1]&0x2)?"by host":"by DDM"); - len += sprintf(buf+len, "Packet orphan limit : %d\n", work32[2]); - - len += sprintf(buf+len, "Tx modes : 0x%08x\n", work32[3]); - len += sprintf(buf+len, " [%s] HW CRC suppression\n", - (work32[3]&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW IPv4 checksum\n", - (work32[3]&0x00000100) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW TCP checksum\n", - (work32[3]&0x00000200) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW UDP checksum\n", - (work32[3]&0x00000400) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW RSVP checksum\n", - (work32[3]&0x00000800) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW ICMP checksum\n", - (work32[3]&0x00001000) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Loopback suppression enable\n", - (work32[3]&0x00002000) ? "+" : "-"); - - len += sprintf(buf+len, "Rx modes : 0x%08x\n", work32[4]); - len += sprintf(buf+len, " [%s] FCS in payload\n", - (work32[4]&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW IPv4 checksum validation\n", - (work32[4]&0x00000100) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW TCP checksum validation\n", - (work32[4]&0x00000200) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW UDP checksum validation\n", - (work32[4]&0x00000400) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW RSVP checksum validation\n", - (work32[4]&0x00000800) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW ICMP checksum validation\n", - (work32[4]&0x00001000) ? "+" : "-"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0005h - Media operation (scalar) */ -int i2o_proc_read_lan_media_operation(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - - struct - { - u32 connector_type; - u32 connection_type; - u64 current_tx_wire_speed; - u64 current_rx_wire_speed; - u8 duplex_mode; - u8 link_status; - u8 reserved; - u8 duplex_mode_target; - u32 connector_type_target; - u32 connection_type_target; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0005, -1, &result, sizeof(result)); - if (token < 0) { - len += i2o_report_query_status(buf+len, token, "0x0005 LAN Media Operation"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Connector type : %s\n", - i2o_get_connector_type(result.connector_type)); - len += sprintf(buf+len, "Connection type : %s\n", - i2o_get_connection_type(result.connection_type)); - - len += sprintf(buf+len, "Current Tx wire speed : %d bps\n", (int)result.current_tx_wire_speed); - len += sprintf(buf+len, "Current Rx wire speed : %d bps\n", (int)result.current_rx_wire_speed); - len += sprintf(buf+len, "Duplex mode : %s duplex\n", - (result.duplex_mode)?"Full":"Half"); - - len += sprintf(buf+len, "Link status : "); - switch (result.link_status) - { - case 0x00: - len += sprintf(buf+len, "Unknown\n"); - break; - case 0x01: - len += sprintf(buf+len, "Normal\n"); - break; - case 0x02: - len += sprintf(buf+len, "Failure\n"); - break; - case 0x03: - len += sprintf(buf+len, "Reset\n"); - break; - default: - len += sprintf(buf+len, "Unspecified\n"); - } - - len += sprintf(buf+len, "Duplex mode target : "); - switch (result.duplex_mode_target){ - case 0: - len += sprintf(buf+len, "Half duplex\n"); - break; - case 1: - len += sprintf(buf+len, "Full duplex\n"); - break; - default: - len += sprintf(buf+len, "\n"); - } - - len += sprintf(buf+len, "Connector type target : %s\n", - i2o_get_connector_type(result.connector_type_target)); - len += sprintf(buf+len, "Connection type target : %s\n", - i2o_get_connection_type(result.connection_type_target)); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0006h - Alternate address (table) (optional) */ -int i2o_proc_read_lan_alt_addr(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - u8 alt_addr[8]; - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - u8 alt_addr[256][8]; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0x0006, -1, NULL, 0, &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token, "0x0006 LAN Alternate Address (optional)"); - spin_unlock(&i2o_proc_lock); - return len; - } - - for (i=0; i < result.row_count; i++) - { - memcpy(alt_addr,result.alt_addr[i],8); - len += sprintf(buf+len, "Alternate address[%d]: " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - i, alt_addr[0], alt_addr[1], alt_addr[2], - alt_addr[3], alt_addr[4], alt_addr[5], - alt_addr[6], alt_addr[7]); - } - - spin_unlock(&i2o_proc_lock); - return len; -} - - -/* LAN group 0007h - Transmit info (scalar) */ -int i2o_proc_read_lan_tx_info(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[8]; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0007, -1, &work32, 8*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0007 LAN Transmit Info"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Tx Max SG elements per packet : %d\n", work32[0]); - len += sprintf(buf+len, "Tx Max SG elements per chain : %d\n", work32[1]); - len += sprintf(buf+len, "Tx Max outstanding packets : %d\n", work32[2]); - len += sprintf(buf+len, "Tx Max packets per request : %d\n", work32[3]); - - len += sprintf(buf+len, "Tx modes : 0x%08x\n", work32[4]); - len += sprintf(buf+len, " [%s] No DA in SGL\n", - (work32[4]&0x00000002) ? "+" : "-"); - len += sprintf(buf+len, " [%s] CRC suppression\n", - (work32[4]&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] MAC insertion\n", - (work32[4]&0x00000010) ? "+" : "-"); - len += sprintf(buf+len, " [%s] RIF insertion\n", - (work32[4]&0x00000020) ? "+" : "-"); - len += sprintf(buf+len, " [%s] IPv4 checksum generation\n", - (work32[4]&0x00000100) ? "+" : "-"); - len += sprintf(buf+len, " [%s] TCP checksum generation\n", - (work32[4]&0x00000200) ? "+" : "-"); - len += sprintf(buf+len, " [%s] UDP checksum generation\n", - (work32[4]&0x00000400) ? "+" : "-"); - len += sprintf(buf+len, " [%s] RSVP checksum generation\n", - (work32[4]&0x00000800) ? "+" : "-"); - len += sprintf(buf+len, " [%s] ICMP checksum generation\n", - (work32[4]&0x00001000) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Loopback enabled\n", - (work32[4]&0x00010000) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Loopback suppression enabled\n", - (work32[4]&0x00020000) ? "+" : "-"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0008h - Receive info (scalar) */ -int i2o_proc_read_lan_rx_info(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[8]; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0008, -1, &work32, 8*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0008 LAN Receive Info"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf ,"Rx Max size of chain element : %d\n", work32[0]); - len += sprintf(buf+len, "Rx Max Buckets : %d\n", work32[1]); - len += sprintf(buf+len, "Rx Max Buckets in Reply : %d\n", work32[3]); - len += sprintf(buf+len, "Rx Max Packets in Bucket : %d\n", work32[4]); - len += sprintf(buf+len, "Rx Max Buckets in Post : %d\n", work32[5]); - - len += sprintf(buf+len, "Rx Modes : 0x%08x\n", work32[2]); - len += sprintf(buf+len, " [%s] FCS reception\n", - (work32[2]&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] IPv4 checksum validation \n", - (work32[2]&0x00000100) ? "+" : "-"); - len += sprintf(buf+len, " [%s] TCP checksum validation \n", - (work32[2]&0x00000200) ? "+" : "-"); - len += sprintf(buf+len, " [%s] UDP checksum validation \n", - (work32[2]&0x00000400) ? "+" : "-"); - len += sprintf(buf+len, " [%s] RSVP checksum validation \n", - (work32[2]&0x00000800) ? "+" : "-"); - len += sprintf(buf+len, " [%s] ICMP checksum validation \n", - (work32[2]&0x00001000) ? "+" : "-"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -static int i2o_report_opt_field(char *buf, char *field_name, - int field_nbr, int supp_fields, u64 *value) -{ - if (supp_fields & (1 << field_nbr)) - return sprintf(buf, "%-24s : " FMT_U64_HEX "\n", field_name, U64_VAL(value)); - else - return sprintf(buf, "%-24s : Not supported\n", field_name); -} - -/* LAN group 0100h - LAN Historical statistics (scalar) */ -/* LAN group 0180h - Supported Optional Historical Statistics (scalar) */ -/* LAN group 0182h - Optional Non Media Specific Transmit Historical Statistics (scalar) */ -/* LAN group 0183h - Optional Non Media Specific Receive Historical Statistics (scalar) */ - -int i2o_proc_read_lan_hist_stats(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - - struct - { - u64 tx_packets; - u64 tx_bytes; - u64 rx_packets; - u64 rx_bytes; - u64 tx_errors; - u64 rx_errors; - u64 rx_dropped; - u64 adapter_resets; - u64 adapter_suspends; - } stats; // 0x0100 - - static u64 supp_groups[4]; // 0x0180 - - struct - { - u64 tx_retries; - u64 tx_directed_bytes; - u64 tx_directed_packets; - u64 tx_multicast_bytes; - u64 tx_multicast_packets; - u64 tx_broadcast_bytes; - u64 tx_broadcast_packets; - u64 tx_group_addr_packets; - u64 tx_short_packets; - } tx_stats; // 0x0182 - - struct - { - u64 rx_crc_errors; - u64 rx_directed_bytes; - u64 rx_directed_packets; - u64 rx_multicast_bytes; - u64 rx_multicast_packets; - u64 rx_broadcast_bytes; - u64 rx_broadcast_packets; - u64 rx_group_addr_packets; - u64 rx_short_packets; - u64 rx_long_packets; - u64 rx_runt_packets; - } rx_stats; // 0x0183 - - struct - { - u64 ipv4_generate; - u64 ipv4_validate_success; - u64 ipv4_validate_errors; - u64 tcp_generate; - u64 tcp_validate_success; - u64 tcp_validate_errors; - u64 udp_generate; - u64 udp_validate_success; - u64 udp_validate_errors; - u64 rsvp_generate; - u64 rsvp_validate_success; - u64 rsvp_validate_errors; - u64 icmp_generate; - u64 icmp_validate_success; - u64 icmp_validate_errors; - } chksum_stats; // 0x0184 - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0100, -1, &stats, sizeof(stats)); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x100 LAN Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "Tx packets : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_packets)); - len += sprintf(buf+len, "Tx bytes : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_bytes)); - len += sprintf(buf+len, "Rx packets : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_packets)); - len += sprintf(buf+len, "Rx bytes : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_bytes)); - len += sprintf(buf+len, "Tx errors : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_errors)); - len += sprintf(buf+len, "Rx errors : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_errors)); - len += sprintf(buf+len, "Rx dropped : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_dropped)); - len += sprintf(buf+len, "Adapter resets : " FMT_U64_HEX "\n", - U64_VAL(&stats.adapter_resets)); - len += sprintf(buf+len, "Adapter suspends : " FMT_U64_HEX "\n", - U64_VAL(&stats.adapter_suspends)); - - /* Optional statistics follows */ - /* Get 0x0180 to see which optional groups/fields are supported */ - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0180, -1, &supp_groups, sizeof(supp_groups)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token, "0x180 LAN Supported Optional Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - if (supp_groups[1]) /* 0x0182 */ - { - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0182, -1, &tx_stats, sizeof(tx_stats)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x182 LAN Optional Tx Historical Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "==== Optional TX statistics (group 0182h)\n"); - - len += i2o_report_opt_field(buf+len, "Tx RetryCount", - 0, supp_groups[1], &tx_stats.tx_retries); - len += i2o_report_opt_field(buf+len, "Tx DirectedBytes", - 1, supp_groups[1], &tx_stats.tx_directed_bytes); - len += i2o_report_opt_field(buf+len, "Tx DirectedPackets", - 2, supp_groups[1], &tx_stats.tx_directed_packets); - len += i2o_report_opt_field(buf+len, "Tx MulticastBytes", - 3, supp_groups[1], &tx_stats.tx_multicast_bytes); - len += i2o_report_opt_field(buf+len, "Tx MulticastPackets", - 4, supp_groups[1], &tx_stats.tx_multicast_packets); - len += i2o_report_opt_field(buf+len, "Tx BroadcastBytes", - 5, supp_groups[1], &tx_stats.tx_broadcast_bytes); - len += i2o_report_opt_field(buf+len, "Tx BroadcastPackets", - 6, supp_groups[1], &tx_stats.tx_broadcast_packets); - len += i2o_report_opt_field(buf+len, "Tx TotalGroupAddrPackets", - 7, supp_groups[1], &tx_stats.tx_group_addr_packets); - len += i2o_report_opt_field(buf+len, "Tx TotalPacketsTooShort", - 8, supp_groups[1], &tx_stats.tx_short_packets); - } - - if (supp_groups[2]) /* 0x0183 */ - { - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0183, -1, &rx_stats, sizeof(rx_stats)); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x183 LAN Optional Rx Historical Stats"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "==== Optional RX statistics (group 0183h)\n"); - - len += i2o_report_opt_field(buf+len, "Rx CRCErrorCount", - 0, supp_groups[2], &rx_stats.rx_crc_errors); - len += i2o_report_opt_field(buf+len, "Rx DirectedBytes", - 1, supp_groups[2], &rx_stats.rx_directed_bytes); - len += i2o_report_opt_field(buf+len, "Rx DirectedPackets", - 2, supp_groups[2], &rx_stats.rx_directed_packets); - len += i2o_report_opt_field(buf+len, "Rx MulticastBytes", - 3, supp_groups[2], &rx_stats.rx_multicast_bytes); - len += i2o_report_opt_field(buf+len, "Rx MulticastPackets", - 4, supp_groups[2], &rx_stats.rx_multicast_packets); - len += i2o_report_opt_field(buf+len, "Rx BroadcastBytes", - 5, supp_groups[2], &rx_stats.rx_broadcast_bytes); - len += i2o_report_opt_field(buf+len, "Rx BroadcastPackets", - 6, supp_groups[2], &rx_stats.rx_broadcast_packets); - len += i2o_report_opt_field(buf+len, "Rx TotalGroupAddrPackets", - 7, supp_groups[2], &rx_stats.rx_group_addr_packets); - len += i2o_report_opt_field(buf+len, "Rx TotalPacketsTooShort", - 8, supp_groups[2], &rx_stats.rx_short_packets); - len += i2o_report_opt_field(buf+len, "Rx TotalPacketsTooLong", - 9, supp_groups[2], &rx_stats.rx_long_packets); - len += i2o_report_opt_field(buf+len, "Rx TotalPacketsRunt", - 10, supp_groups[2], &rx_stats.rx_runt_packets); - } - - if (supp_groups[3]) /* 0x0184 */ - { - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0184, -1, &chksum_stats, sizeof(chksum_stats)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x184 LAN Optional Chksum Historical Stats"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "==== Optional CHKSUM statistics (group 0x0184)\n"); - - len += i2o_report_opt_field(buf+len, "IPv4 Generate", - 0, supp_groups[3], &chksum_stats.ipv4_generate); - len += i2o_report_opt_field(buf+len, "IPv4 ValidateSuccess", - 1, supp_groups[3], &chksum_stats.ipv4_validate_success); - len += i2o_report_opt_field(buf+len, "IPv4 ValidateError", - 2, supp_groups[3], &chksum_stats.ipv4_validate_errors); - len += i2o_report_opt_field(buf+len, "TCP Generate", - 3, supp_groups[3], &chksum_stats.tcp_generate); - len += i2o_report_opt_field(buf+len, "TCP ValidateSuccess", - 4, supp_groups[3], &chksum_stats.tcp_validate_success); - len += i2o_report_opt_field(buf+len, "TCP ValidateError", - 5, supp_groups[3], &chksum_stats.tcp_validate_errors); - len += i2o_report_opt_field(buf+len, "UDP Generate", - 6, supp_groups[3], &chksum_stats.udp_generate); - len += i2o_report_opt_field(buf+len, "UDP ValidateSuccess", - 7, supp_groups[3], &chksum_stats.udp_validate_success); - len += i2o_report_opt_field(buf+len, "UDP ValidateError", - 8, supp_groups[3], &chksum_stats.udp_validate_errors); - len += i2o_report_opt_field(buf+len, "RSVP Generate", - 9, supp_groups[3], &chksum_stats.rsvp_generate); - len += i2o_report_opt_field(buf+len, "RSVP ValidateSuccess", - 10, supp_groups[3], &chksum_stats.rsvp_validate_success); - len += i2o_report_opt_field(buf+len, "RSVP ValidateError", - 11, supp_groups[3], &chksum_stats.rsvp_validate_errors); - len += i2o_report_opt_field(buf+len, "ICMP Generate", - 12, supp_groups[3], &chksum_stats.icmp_generate); - len += i2o_report_opt_field(buf+len, "ICMP ValidateSuccess", - 13, supp_groups[3], &chksum_stats.icmp_validate_success); - len += i2o_report_opt_field(buf+len, "ICMP ValidateError", - 14, supp_groups[3], &chksum_stats.icmp_validate_errors); - } - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0200h - Required Ethernet Statistics (scalar) */ -/* LAN group 0280h - Optional Ethernet Statistics Supported (scalar) */ -/* LAN group 0281h - Optional Ethernet Historical Statistics (scalar) */ -int i2o_proc_read_lan_eth_stats(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - - struct - { - u64 rx_align_errors; - u64 tx_one_collisions; - u64 tx_multiple_collisions; - u64 tx_deferred; - u64 tx_late_collisions; - u64 tx_max_collisions; - u64 tx_carrier_lost; - u64 tx_excessive_deferrals; - } stats; - - static u64 supp_fields; - struct - { - u64 rx_overrun; - u64 tx_underrun; - u64 tx_heartbeat_failure; - } hist_stats; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0200, -1, &stats, sizeof(stats)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0200 LAN Ethernet Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "Rx alignment errors : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_align_errors)); - len += sprintf(buf+len, "Tx one collisions : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_one_collisions)); - len += sprintf(buf+len, "Tx multicollisions : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_multiple_collisions)); - len += sprintf(buf+len, "Tx deferred : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_deferred)); - len += sprintf(buf+len, "Tx late collisions : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_late_collisions)); - len += sprintf(buf+len, "Tx max collisions : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_max_collisions)); - len += sprintf(buf+len, "Tx carrier lost : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_carrier_lost)); - len += sprintf(buf+len, "Tx excessive deferrals : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_excessive_deferrals)); - - /* Optional Ethernet statistics follows */ - /* Get 0x0280 to see which optional fields are supported */ - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0280, -1, &supp_fields, sizeof(supp_fields)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0280 LAN Supported Optional Ethernet Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - if (supp_fields) /* 0x0281 */ - { - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0281, -1, &stats, sizeof(stats)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0281 LAN Optional Ethernet Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "==== Optional ETHERNET statistics (group 0x0281)\n"); - - len += i2o_report_opt_field(buf+len, "Rx Overrun", - 0, supp_fields, &hist_stats.rx_overrun); - len += i2o_report_opt_field(buf+len, "Tx Underrun", - 1, supp_fields, &hist_stats.tx_underrun); - len += i2o_report_opt_field(buf+len, "Tx HeartbeatFailure", - 2, supp_fields, &hist_stats.tx_heartbeat_failure); - } - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0300h - Required Token Ring Statistics (scalar) */ -/* LAN group 0380h, 0381h - Optional Statistics not yet defined (TODO) */ -int i2o_proc_read_lan_tr_stats(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u64 work64[13]; - int token; - - static char *ring_status[] = - { - "", - "", - "", - "", - "", - "Ring Recovery", - "Single Station", - "Counter Overflow", - "Remove Received", - "", - "Auto-Removal Error 1", - "Lobe Wire Fault", - "Transmit Beacon", - "Soft Error", - "Hard Error", - "Signal Loss" - }; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0300, -1, &work64, sizeof(work64)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0300 Token Ring Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "LineErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[0])); - len += sprintf(buf+len, "LostFrames : " FMT_U64_HEX "\n", - U64_VAL(&work64[1])); - len += sprintf(buf+len, "ACError : " FMT_U64_HEX "\n", - U64_VAL(&work64[2])); - len += sprintf(buf+len, "TxAbortDelimiter : " FMT_U64_HEX "\n", - U64_VAL(&work64[3])); - len += sprintf(buf+len, "BursErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[4])); - len += sprintf(buf+len, "FrameCopiedErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[5])); - len += sprintf(buf+len, "FrequencyErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[6])); - len += sprintf(buf+len, "InternalErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[7])); - len += sprintf(buf+len, "LastRingStatus : %s\n", ring_status[work64[8]]); - len += sprintf(buf+len, "TokenError : " FMT_U64_HEX "\n", - U64_VAL(&work64[9])); - len += sprintf(buf+len, "UpstreamNodeAddress : " FMT_U64_HEX "\n", - U64_VAL(&work64[10])); - len += sprintf(buf+len, "LastRingID : " FMT_U64_HEX "\n", - U64_VAL(&work64[11])); - len += sprintf(buf+len, "LastBeaconType : " FMT_U64_HEX "\n", - U64_VAL(&work64[12])); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0400h - Required FDDI Statistics (scalar) */ -/* LAN group 0480h, 0481h - Optional Statistics, not yet defined (TODO) */ -int i2o_proc_read_lan_fddi_stats(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u64 work64[11]; - int token; - - static char *conf_state[] = - { - "Isolated", - "Local a", - "Local b", - "Local ab", - "Local s", - "Wrap a", - "Wrap b", - "Wrap ab", - "Wrap s", - "C-Wrap a", - "C-Wrap b", - "C-Wrap s", - "Through", - }; - - static char *ring_state[] = - { - "Isolated", - "Non-op", - "Rind-op", - "Detect", - "Non-op-Dup", - "Ring-op-Dup", - "Directed", - "Trace" - }; - - static char *link_state[] = - { - "Off", - "Break", - "Trace", - "Connect", - "Next", - "Signal", - "Join", - "Verify", - "Active", - "Maintenance" - }; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0400, -1, &work64, sizeof(work64)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0400 FDDI Required Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "ConfigurationState : %s\n", conf_state[work64[0]]); - len += sprintf(buf+len, "UpstreamNode : " FMT_U64_HEX "\n", - U64_VAL(&work64[1])); - len += sprintf(buf+len, "DownStreamNode : " FMT_U64_HEX "\n", - U64_VAL(&work64[2])); - len += sprintf(buf+len, "FrameErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[3])); - len += sprintf(buf+len, "FramesLost : " FMT_U64_HEX "\n", - U64_VAL(&work64[4])); - len += sprintf(buf+len, "RingMgmtState : %s\n", ring_state[work64[5]]); - len += sprintf(buf+len, "LCTFailures : " FMT_U64_HEX "\n", - U64_VAL(&work64[6])); - len += sprintf(buf+len, "LEMRejects : " FMT_U64_HEX "\n", - U64_VAL(&work64[7])); - len += sprintf(buf+len, "LEMCount : " FMT_U64_HEX "\n", - U64_VAL(&work64[8])); - len += sprintf(buf+len, "LConnectionState : %s\n", - link_state[work64[9]]); - - spin_unlock(&i2o_proc_lock); - return len; -} - -static int i2o_proc_create_entries(void *data, i2o_proc_entry *pentry, - struct proc_dir_entry *parent) -{ - struct proc_dir_entry *ent; - - while(pentry->name != NULL) - { - ent = create_proc_entry(pentry->name, pentry->mode, parent); - if(!ent) return -1; - - ent->data = data; - ent->read_proc = pentry->read_proc; - ent->write_proc = pentry->write_proc; - ent->nlink = 1; - - pentry++; - } - - return 0; -} - -static void i2o_proc_remove_entries(i2o_proc_entry *pentry, - struct proc_dir_entry *parent) -{ - while(pentry->name != NULL) - { - remove_proc_entry(pentry->name, parent); - pentry++; - } -} - -static int i2o_proc_add_controller(struct i2o_controller *pctrl, - struct proc_dir_entry *root ) -{ - struct proc_dir_entry *dir, *dir1; - struct i2o_device *dev; - char buff[10]; - - sprintf(buff, "iop%d", pctrl->unit); - - dir = proc_mkdir(buff, root); - if(!dir) - return -1; - - pctrl->proc_entry = dir; - - i2o_proc_create_entries(pctrl, generic_iop_entries, dir); - - for(dev = pctrl->devices; dev; dev = dev->next) - { - sprintf(buff, "%0#5x", dev->lct_data.tid); - - dir1 = proc_mkdir(buff, dir); - dev->proc_entry = dir1; - - if(!dir1) - printk(KERN_INFO "i2o_proc: Could not allocate proc dir\n"); - - i2o_proc_add_device(dev, dir1); - } - - return 0; -} - -void i2o_proc_new_dev(struct i2o_controller *c, struct i2o_device *d) -{ - char buff[10]; - -#ifdef DRIVERDEBUG - printk(KERN_INFO "Adding new device to /proc/i2o/iop%d\n", c->unit); -#endif - sprintf(buff, "%0#5x", d->lct_data.tid); - - d->proc_entry = proc_mkdir(buff, c->proc_entry); - - if(!d->proc_entry) - { - printk(KERN_WARNING "i2o: Could not allocate procdir!\n"); - return; - } - - i2o_proc_add_device(d, d->proc_entry); -} - -void i2o_proc_add_device(struct i2o_device *dev, struct proc_dir_entry *dir) -{ - i2o_proc_create_entries(dev, generic_dev_entries, dir); - - /* Inform core that we want updates about this device's status */ - i2o_device_notify_on(dev, &i2o_proc_handler); - switch(dev->lct_data.class_id) - { - case I2O_CLASS_SCSI_PERIPHERAL: - case I2O_CLASS_RANDOM_BLOCK_STORAGE: - i2o_proc_create_entries(dev, rbs_dev_entries, dir); - break; - case I2O_CLASS_LAN: - i2o_proc_create_entries(dev, lan_entries, dir); - switch(dev->lct_data.sub_class) - { - case I2O_LAN_ETHERNET: - i2o_proc_create_entries(dev, lan_eth_entries, dir); - break; - case I2O_LAN_FDDI: - i2o_proc_create_entries(dev, lan_fddi_entries, dir); - break; - case I2O_LAN_TR: - i2o_proc_create_entries(dev, lan_tr_entries, dir); - break; - default: - break; - } - break; - default: - break; - } -} - -static void i2o_proc_remove_controller(struct i2o_controller *pctrl, - struct proc_dir_entry *parent) -{ - char buff[10]; - struct i2o_device *dev; - - /* Remove unused device entries */ - for(dev=pctrl->devices; dev; dev=dev->next) - i2o_proc_remove_device(dev); - - if(!atomic_read(&pctrl->proc_entry->count)) - { - sprintf(buff, "iop%d", pctrl->unit); - - i2o_proc_remove_entries(generic_iop_entries, pctrl->proc_entry); - - remove_proc_entry(buff, parent); - pctrl->proc_entry = NULL; - } -} - -void i2o_proc_remove_device(struct i2o_device *dev) -{ - struct proc_dir_entry *de=dev->proc_entry; - char dev_id[10]; - - sprintf(dev_id, "%0#5x", dev->lct_data.tid); - - i2o_device_notify_off(dev, &i2o_proc_handler); - /* Would it be safe to remove _files_ even if they are in use? */ - if((de) && (!atomic_read(&de->count))) - { - i2o_proc_remove_entries(generic_dev_entries, de); - switch(dev->lct_data.class_id) - { - case I2O_CLASS_SCSI_PERIPHERAL: - case I2O_CLASS_RANDOM_BLOCK_STORAGE: - i2o_proc_remove_entries(rbs_dev_entries, de); - break; - case I2O_CLASS_LAN: - { - i2o_proc_remove_entries(lan_entries, de); - switch(dev->lct_data.sub_class) - { - case I2O_LAN_ETHERNET: - i2o_proc_remove_entries(lan_eth_entries, de); - break; - case I2O_LAN_FDDI: - i2o_proc_remove_entries(lan_fddi_entries, de); - break; - case I2O_LAN_TR: - i2o_proc_remove_entries(lan_tr_entries, de); - break; - } - } - remove_proc_entry(dev_id, dev->controller->proc_entry); - } - } -} - -void i2o_proc_dev_del(struct i2o_controller *c, struct i2o_device *d) -{ -#ifdef DRIVERDEBUG - printk(KERN_INFO, "Deleting device %d from iop%d\n", - d->lct_data.tid, c->unit); -#endif - - i2o_proc_remove_device(d); -} - -static int create_i2o_procfs(void) -{ - struct i2o_controller *pctrl = NULL; - int i; - - i2o_proc_dir_root = proc_mkdir("i2o", 0); - if(!i2o_proc_dir_root) - return -1; - - for(i = 0; i < MAX_I2O_CONTROLLERS; i++) - { - pctrl = i2o_find_controller(i); - if(pctrl) - { - i2o_proc_add_controller(pctrl, i2o_proc_dir_root); - i2o_unlock_controller(pctrl); - } - }; - - return 0; -} - -static int __exit destroy_i2o_procfs(void) -{ - struct i2o_controller *pctrl = NULL; - int i; - - for(i = 0; i < MAX_I2O_CONTROLLERS; i++) - { - pctrl = i2o_find_controller(i); - if(pctrl) - { - i2o_proc_remove_controller(pctrl, i2o_proc_dir_root); - i2o_unlock_controller(pctrl); - } - } - - if(!atomic_read(&i2o_proc_dir_root->count)) - remove_proc_entry("i2o", 0); - else - return -1; - - return 0; -} - -int __init i2o_proc_init(void) -{ - if (i2o_install_handler(&i2o_proc_handler) < 0) - { - printk(KERN_ERR "i2o_proc: Unable to install PROC handler.\n"); - return 0; - } - - if(create_i2o_procfs()) - return -EBUSY; - - return 0; -} - -MODULE_AUTHOR("Deepak Saxena"); -MODULE_DESCRIPTION("I2O procfs Handler"); - -static void __exit i2o_proc_exit(void) -{ - destroy_i2o_procfs(); - i2o_remove_handler(&i2o_proc_handler); -} - -#ifdef MODULE -module_init(i2o_proc_init); -#endif -module_exit(i2o_proc_exit); - diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_scsi.c linux.ac/drivers/i2o/i2o_scsi.c --- linux.vanilla/drivers/i2o/i2o_scsi.c Sun Aug 12 19:16:18 2001 +++ linux.ac/drivers/i2o/i2o_scsi.c Thu Jan 1 01:00:00 1970 @@ -1,912 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * Complications for I2O scsi - * - * o Each (bus,lun) is a logical device in I2O. We keep a map - * table. We spoof failed selection for unmapped units - * o Request sense buffers can come back for free. - * o Scatter gather is a bit dynamic. We have to investigate at - * setup time. - * o Some of our resources are dynamically shared. The i2o core - * needs a message reservation protocol to avoid swap v net - * deadlocking. We need to back off queue requests. - * - * In general the firmware wants to help. Where its help isn't performance - * useful we just ignore the aid. Its not worth the code in truth. - * - * Fixes: - * Steve Ralston : Scatter gather now works - * - * To Do - * 64bit cleanups - * Fix the resource management problems. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../scsi/scsi.h" -#include "../scsi/hosts.h" -#include "../scsi/sd.h" -#include "i2o_scsi.h" - -#define VERSION_STRING "Version 0.0.1" - -#define dprintk(x) - -#define MAXHOSTS 32 - -struct i2o_scsi_host -{ - struct i2o_controller *controller; - s16 task[16][8]; /* Allow 16 devices for now */ - unsigned long tagclock[16][8]; /* Tag clock for queueing */ - s16 bus_task; /* The adapter TID */ -}; - -static int scsi_context; -static int lun_done; -static int i2o_scsi_hosts; - -static u32 *retry[32]; -static struct i2o_controller *retry_ctrl[32]; -static struct timer_list retry_timer; -static int retry_ct = 0; - -static atomic_t queue_depth; - -/* - * SG Chain buffer support... - */ - -#define SG_MAX_FRAGS 64 - -/* - * FIXME: we should allocate one of these per bus we find as we - * locate them not in a lump at boot. - */ - -typedef struct _chain_buf -{ - u32 sg_flags_cnt[SG_MAX_FRAGS]; - u32 sg_buf[SG_MAX_FRAGS]; -} chain_buf; - -#define SG_CHAIN_BUF_SZ sizeof(chain_buf) - -#define SG_MAX_BUFS (i2o_num_controllers * I2O_SCSI_CAN_QUEUE) -#define SG_CHAIN_POOL_SZ (SG_MAX_BUFS * SG_CHAIN_BUF_SZ) - -static int max_sg_len = 0; -static chain_buf *sg_chain_pool = NULL; -static int sg_chain_tag = 0; -static int sg_max_frags = SG_MAX_FRAGS; - -/* - * Retry congested frames. This actually needs pushing down into - * i2o core. We should only bother the OSM with this when we can't - * queue and retry the frame. Or perhaps we should call the OSM - * and its default handler should be this in the core, and this - * call a 2nd "I give up" handler in the OSM ? - */ - -static void i2o_retry_run(unsigned long f) -{ - int i; - unsigned long flags; - - save_flags(flags); - cli(); - - for(i=0;i>12)&0xFFF, - m[1]&0xFFF, - m[1]>>24); - printk("Failure Code %d.\n", m[4]>>24); - if(m[4]&(1<<16)) - printk("Format error.\n"); - if(m[4]&(1<<17)) - printk("Path error.\n"); - if(m[4]&(1<<18)) - printk("Path State.\n"); - if(m[4]&(1<<18)) - printk("Congestion.\n"); - - m=(u32 *)bus_to_virt(m[7]); - printk("Failing message is %p.\n", m); - - if((m[4]&(1<<18)) && retry_ct < 32) - { - retry_ctrl[retry_ct]=c; - retry[retry_ct]=m; - if(!retry_ct++) - { - retry_timer.expires=jiffies+1; - add_timer(&retry_timer); - } - } - else - { - /* Create a scsi error for this */ - current_command = (Scsi_Cmnd *)m[3]; - printk("Aborted %ld\n", current_command->serial_number); - - spin_lock_irq(&io_request_lock); - current_command->result = DID_ERROR << 16; - current_command->scsi_done(current_command); - spin_unlock_irq(&io_request_lock); - - /* Now flush the message by making it a NOP */ - m[0]&=0x00FFFFFF; - m[0]|=(I2O_CMD_UTIL_NOP)<<24; - i2o_post_message(c,virt_to_bus(m)); - } - return; - } - - - /* - * Low byte is device status, next is adapter status, - * (then one byte reserved), then request status. - */ - ds=(u8)m[4]; - as=(u8)(m[4]>>8); - st=(u8)(m[4]>>24); - - dprintk(("i2o got a scsi reply %08X: ", m[0])); - dprintk(("m[2]=%08X: ", m[2])); - dprintk(("m[4]=%08X\n", m[4])); - - if(m[2]&0x80000000) - { - if(m[2]&0x40000000) - { - dprintk(("Event.\n")); - lun_done=1; - return; - } - printk(KERN_ERR "i2o_scsi: bus reset reply.\n"); - return; - } - - current_command = (Scsi_Cmnd *)m[3]; - - /* - * Is this a control request coming back - eg an abort ? - */ - - if(current_command==NULL) - { - if(st) - dprintk(("SCSI abort: %08X", m[4])); - dprintk(("SCSI abort completed.\n")); - return; - } - - dprintk(("Completed %ld\n", current_command->serial_number)); - - atomic_dec(&queue_depth); - - if(st == 0x06) - { - if(m[5] < current_command->underflow) - { - int i; - printk(KERN_ERR "SCSI: underflow 0x%08X 0x%08X\n", - m[5], current_command->underflow); - printk("Cmd: "); - for(i=0;i<15;i++) - printk("%02X ", current_command->cmnd[i]); - printk(".\n"); - } - else st=0; - } - - if(st) - { - /* An error has occurred */ - - dprintk((KERN_DEBUG "SCSI error %08X", m[4])); - - if (as == 0x0E) - /* SCSI Reset */ - current_command->result = DID_RESET << 16; - else if (as == 0x0F) - current_command->result = DID_PARITY << 16; - else - current_command->result = DID_ERROR << 16; - } - else - /* - * It worked maybe ? - */ - current_command->result = DID_OK << 16 | ds; - spin_lock(&io_request_lock); - current_command->scsi_done(current_command); - spin_unlock(&io_request_lock); - return; -} - -struct i2o_handler i2o_scsi_handler= -{ - i2o_scsi_reply, - NULL, - NULL, - NULL, - "I2O SCSI OSM", - 0, - I2O_CLASS_SCSI_PERIPHERAL -}; - -static int i2o_find_lun(struct i2o_controller *c, struct i2o_device *d, int *target, int *lun) -{ - u8 reply[8]; - - if(i2o_query_scalar(c, d->lct_data.tid, 0, 3, reply, 4)<0) - return -1; - - *target=reply[0]; - - if(i2o_query_scalar(c, d->lct_data.tid, 0, 4, reply, 8)<0) - return -1; - - *lun=reply[1]; - - dprintk(("SCSI (%d,%d)\n", *target, *lun)); - return 0; -} - -void i2o_scsi_init(struct i2o_controller *c, struct i2o_device *d, struct Scsi_Host *shpnt) -{ - struct i2o_device *unit; - struct i2o_scsi_host *h =(struct i2o_scsi_host *)shpnt->hostdata; - int lun; - int target; - - h->controller=c; - h->bus_task=d->lct_data.tid; - - for(target=0;target<16;target++) - for(lun=0;lun<8;lun++) - h->task[target][lun] = -1; - - for(unit=c->devices;unit!=NULL;unit=unit->next) - { - dprintk(("Class %03X, parent %d, want %d.\n", - unit->lct_data.class_id, unit->lct_data.parent_tid, d->lct_data.tid)); - - /* Only look at scsi and fc devices */ - if ( (unit->lct_data.class_id != I2O_CLASS_SCSI_PERIPHERAL) - && (unit->lct_data.class_id != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL) - ) - continue; - - /* On our bus ? */ - dprintk(("Found a disk (%d).\n", unit->lct_data.tid)); - if ((unit->lct_data.parent_tid == d->lct_data.tid) - || (unit->lct_data.parent_tid == d->lct_data.parent_tid) - ) - { - u16 limit; - dprintk(("Its ours.\n")); - if(i2o_find_lun(c, unit, &target, &lun)==-1) - { - printk(KERN_ERR "i2o_scsi: Unable to get lun for tid %d.\n", unit->lct_data.tid); - continue; - } - dprintk(("Found disk %d %d.\n", target, lun)); - h->task[target][lun]=unit->lct_data.tid; - h->tagclock[target][lun]=jiffies; - - /* Get the max fragments/request */ - i2o_query_scalar(c, d->lct_data.tid, 0xF103, 3, &limit, 2); - - /* sanity */ - if ( limit == 0 ) - { - printk(KERN_WARNING "i2o_scsi: Ignoring unreasonable SG limit of 0 from IOP!\n"); - limit = 1; - } - - shpnt->sg_tablesize = limit; - - dprintk(("i2o_scsi: set scatter-gather to %d.\n", - shpnt->sg_tablesize)); - } - } -} - -int i2o_scsi_detect(Scsi_Host_Template * tpnt) -{ - unsigned long flags; - struct Scsi_Host *shpnt = NULL; - int i; - int count; - - printk("i2o_scsi.c: %s\n", VERSION_STRING); - - if(i2o_install_handler(&i2o_scsi_handler)<0) - { - printk(KERN_ERR "i2o_scsi: Unable to install OSM handler.\n"); - return 0; - } - scsi_context = i2o_scsi_handler.context; - - if((sg_chain_pool = kmalloc(SG_CHAIN_POOL_SZ, GFP_KERNEL)) == NULL) - { - printk("i2o_scsi: Unable to alloc %d byte SG chain buffer pool.\n", SG_CHAIN_POOL_SZ); - printk("i2o_scsi: SG chaining DISABLED!\n"); - sg_max_frags = 11; - } - else - { - printk(" chain_pool: %d bytes @ %p\n", SG_CHAIN_POOL_SZ, sg_chain_pool); - printk(" (%d byte buffers X %d can_queue X %d i2o controllers)\n", - SG_CHAIN_BUF_SZ, I2O_SCSI_CAN_QUEUE, i2o_num_controllers); - sg_max_frags = SG_MAX_FRAGS; // 64 - } - - init_timer(&retry_timer); - retry_timer.data = 0UL; - retry_timer.function = i2o_retry_run; - -// printk("SCSI OSM at %d.\n", scsi_context); - - for (count = 0, i = 0; i < MAX_I2O_CONTROLLERS; i++) - { - struct i2o_controller *c=i2o_find_controller(i); - struct i2o_device *d; - /* - * This controller doesn't exist. - */ - - if(c==NULL) - continue; - - /* - * Fixme - we need some altered device locking. This - * is racing with device addition in theory. Easy to fix. - */ - - for(d=c->devices;d!=NULL;d=d->next) - { - /* - * bus_adapter, SCSI (obsolete), or FibreChannel busses only - */ - if( (d->lct_data.class_id!=I2O_CLASS_BUS_ADAPTER_PORT) // bus_adapter -// && (d->lct_data.class_id!=I2O_CLASS_FIBRE_CHANNEL_PORT) // FC_PORT - ) - continue; - - shpnt = scsi_register(tpnt, sizeof(struct i2o_scsi_host)); - if(shpnt==NULL) - continue; - save_flags(flags); - cli(); - shpnt->unique_id = (u32)d; - shpnt->io_port = 0; - shpnt->n_io_port = 0; - shpnt->irq = 0; - shpnt->this_id = /* Good question */15; - restore_flags(flags); - i2o_scsi_init(c, d, shpnt); - count++; - } - } - i2o_scsi_hosts = count; - - if(count==0) - { - if(sg_chain_pool!=NULL) - { - kfree(sg_chain_pool); - sg_chain_pool = NULL; - } - flush_pending(); - del_timer(&retry_timer); - i2o_remove_handler(&i2o_scsi_handler); - } - - return count; -} - -int i2o_scsi_release(struct Scsi_Host *host) -{ - if(--i2o_scsi_hosts==0) - { - if(sg_chain_pool!=NULL) - { - kfree(sg_chain_pool); - sg_chain_pool = NULL; - } - flush_pending(); - del_timer(&retry_timer); - i2o_remove_handler(&i2o_scsi_handler); - } - return 0; -} - - -const char *i2o_scsi_info(struct Scsi_Host *SChost) -{ - struct i2o_scsi_host *hostdata; - - hostdata = (struct i2o_scsi_host *)SChost->hostdata; - - return(&hostdata->controller->name[0]); -} - - -/* - * From the wd93 driver: - * Returns true if there will be a DATA_OUT phase with this command, - * false otherwise. - * (Thanks to Joerg Dorchain for the research and suggestion.) - * - */ -static int is_dir_out(Scsi_Cmnd *cmd) -{ - switch (cmd->cmnd[0]) - { - case WRITE_6: case WRITE_10: case WRITE_12: - case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER: - case WRITE_VERIFY: case WRITE_VERIFY_12: - case COMPARE: case COPY: case COPY_VERIFY: - case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: - case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12: - case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE: - case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT: - case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK: - case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG: - case 0xea: - return 1; - default: - return 0; - } -} - -int i2o_scsi_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) -{ - int i; - int tid; - struct i2o_controller *c; - Scsi_Cmnd *current_command; - struct Scsi_Host *host; - struct i2o_scsi_host *hostdata; - u32 *msg, *mptr; - u32 m; - u32 *lenptr; - int direction; - int scsidir; - u32 len; - u32 reqlen; - u32 tag; - - static int max_qd = 1; - - /* - * Do the incoming paperwork - */ - - host = SCpnt->host; - hostdata = (struct i2o_scsi_host *)host->hostdata; - SCpnt->scsi_done = done; - - if(SCpnt->target > 15) - { - printk(KERN_ERR "i2o_scsi: Wild target %d.\n", SCpnt->target); - return -1; - } - - tid = hostdata->task[SCpnt->target][SCpnt->lun]; - - dprintk(("qcmd: Tid = %d\n", tid)); - - current_command = SCpnt; /* set current command */ - current_command->scsi_done = done; /* set ptr to done function */ - - /* We don't have such a device. Pretend we did the command - and that selection timed out */ - - if(tid == -1) - { - SCpnt->result = DID_NO_CONNECT << 16; - done(SCpnt); - return 0; - } - - dprintk(("Real scsi messages.\n")); - - c = hostdata->controller; - - /* - * Obtain an I2O message. Right now we _have_ to obtain one - * until the scsi layer stuff is cleaned up. - */ - - do - { - mb(); - m = I2O_POST_READ32(c); - } - while(m==0xFFFFFFFF); - msg = (u32 *)(c->mem_offset + m); - - /* - * Put together a scsi execscb message - */ - - len = SCpnt->request_bufflen; - direction = 0x00000000; // SGL IN (osm<--iop) - - /* - * The scsi layer should be handling this stuff - */ - - scsidir = 0x00000000; // DATA NO XFER - if(len) - { - if(is_dir_out(SCpnt)) - { - direction=0x04000000; // SGL OUT (osm-->iop) - scsidir =0x80000000; // DATA OUT (iop-->dev) - } - else - { - scsidir =0x40000000; // DATA IN (iop<--dev) - } - } - - __raw_writel(I2O_CMD_SCSI_EXEC<<24|HOST_TID<<12|tid, &msg[1]); - __raw_writel(scsi_context, &msg[2]); /* So the I2O layer passes to us */ - /* Sorry 64bit folks. FIXME */ - __raw_writel((u32)SCpnt, &msg[3]); /* We want the SCSI control block back */ - - /* LSI_920_PCI_QUIRK - * - * Intermittant observations of msg frame word data corruption - * observed on msg[4] after: - * WRITE, READ-MODIFY-WRITE - * operations. 19990606 -sralston - * - * (Hence we build this word via tag. Its good practice anyway - * we don't want fetches over PCI needlessly) - */ - - tag=0; - - /* - * Attach tags to the devices - */ - if(SCpnt->device->tagged_supported) - { - /* - * Some drives are too stupid to handle fairness issues - * with tagged queueing. We throw in the odd ordered - * tag to stop them starving themselves. - */ - if((jiffies - hostdata->tagclock[SCpnt->target][SCpnt->lun]) > (5*HZ)) - { - tag=0x01800000; /* ORDERED! */ - hostdata->tagclock[SCpnt->target][SCpnt->lun]=jiffies; - } - else - { - /* Hmmm... I always see value of 0 here, - * of which {HEAD_OF, ORDERED, SIMPLE} are NOT! -sralston - */ - if(SCpnt->tag == HEAD_OF_QUEUE_TAG) - tag=0x01000000; - else if(SCpnt->tag == ORDERED_QUEUE_TAG) - tag=0x01800000; - } - } - - /* Direction, disconnect ok, tag, CDBLen */ - __raw_writel(scsidir|0x20000000|SCpnt->cmd_len|tag, &msg[4]); - - mptr=msg+5; - - /* - * Write SCSI command into the message - always 16 byte block - */ - - memcpy_toio(mptr, SCpnt->cmnd, 16); - mptr+=4; - lenptr=mptr++; /* Remember me - fill in when we know */ - - reqlen = 12; // SINGLE SGE - - /* - * Now fill in the SGList and command - * - * FIXME: we need to set the sglist limits according to the - * message size of the I2O controller. We might only have room - * for 6 or so worst case - */ - - if(SCpnt->use_sg) - { - struct scatterlist *sg = (struct scatterlist *)SCpnt->request_buffer; - int chain = 0; - - len = 0; - - if((sg_max_frags > 11) && (SCpnt->use_sg > 11)) - { - chain = 1; - /* - * Need to chain! - */ - __raw_writel(direction|0xB0000000|(SCpnt->use_sg*2*4), mptr++); - __raw_writel(virt_to_bus(sg_chain_pool + sg_chain_tag), mptr); - mptr = (u32*)(sg_chain_pool + sg_chain_tag); - if (SCpnt->use_sg > max_sg_len) - { - max_sg_len = SCpnt->use_sg; - printk("i2o_scsi: Chain SG! SCpnt=%p, SG_FragCnt=%d, SG_idx=%d\n", - SCpnt, SCpnt->use_sg, sg_chain_tag); - } - if ( ++sg_chain_tag == SG_MAX_BUFS ) - sg_chain_tag = 0; - for(i = 0 ; i < SCpnt->use_sg; i++) - { - *mptr++=direction|0x10000000|sg->length; - len+=sg->length; - *mptr++=virt_to_bus(sg->address); - sg++; - } - mptr[-2]=direction|0xD0000000|(sg-1)->length; - } - else - { - for(i = 0 ; i < SCpnt->use_sg; i++) - { - __raw_writel(direction|0x10000000|sg->length, mptr++); - len+=sg->length; - __raw_writel(virt_to_bus(sg->address), mptr++); - sg++; - } - - /* Make this an end of list. Again evade the 920 bug and - unwanted PCI read traffic */ - - __raw_writel(direction|0xD0000000|(sg-1)->length, &mptr[-2]); - } - - if(!chain) - reqlen = mptr - msg; - - __raw_writel(len, lenptr); - - if(len != SCpnt->underflow) - printk("Cmd len %08X Cmd underflow %08X\n", - len, SCpnt->underflow); - } - else - { - dprintk(("non sg for %p, %d\n", SCpnt->request_buffer, - SCpnt->request_bufflen)); - __raw_writel(len = SCpnt->request_bufflen, lenptr); - if(len == 0) - { - reqlen = 9; - } - else - { - __raw_writel(0xD0000000|direction|SCpnt->request_bufflen, mptr++); - __raw_writel(virt_to_bus(SCpnt->request_buffer), mptr++); - } - } - - /* - * Stick the headers on - */ - - __raw_writel(reqlen<<16 | SGL_OFFSET_10, msg); - - /* Queue the message */ - i2o_post_message(c,m); - - atomic_inc(&queue_depth); - - if(atomic_read(&queue_depth)> max_qd) - { - max_qd=atomic_read(&queue_depth); - printk("Queue depth now %d.\n", max_qd); - } - - mb(); - dprintk(("Issued %ld\n", current_command->serial_number)); - - return 0; -} - -static void internal_done(Scsi_Cmnd * SCpnt) -{ - SCpnt->SCp.Status++; -} - -int i2o_scsi_command(Scsi_Cmnd * SCpnt) -{ - i2o_scsi_queuecommand(SCpnt, internal_done); - SCpnt->SCp.Status = 0; - while (!SCpnt->SCp.Status) - barrier(); - return SCpnt->result; -} - -int i2o_scsi_abort(Scsi_Cmnd * SCpnt) -{ - struct i2o_controller *c; - struct Scsi_Host *host; - struct i2o_scsi_host *hostdata; - u32 *msg; - u32 m; - int tid; - - printk("i2o_scsi: Aborting command block.\n"); - - host = SCpnt->host; - hostdata = (struct i2o_scsi_host *)host->hostdata; - tid = hostdata->task[SCpnt->target][SCpnt->lun]; - if(tid==-1) - { - printk(KERN_ERR "impossible command to abort.\n"); - return SCSI_ABORT_NOT_RUNNING; - } - c = hostdata->controller; - - /* - * Obtain an I2O message. Right now we _have_ to obtain one - * until the scsi layer stuff is cleaned up. - */ - - do - { - mb(); - m = I2O_POST_READ32(c); - } - while(m==0xFFFFFFFF); - msg = (u32 *)(c->mem_offset + m); - - __raw_writel(FIVE_WORD_MSG_SIZE, &msg[0]); - __raw_writel(I2O_CMD_SCSI_ABORT<<24|HOST_TID<<12|tid, &msg[1]); - __raw_writel(scsi_context, &msg[2]); - __raw_writel(0, &msg[3]); /* Not needed for an abort */ - __raw_writel((u32)SCpnt, &msg[4]); - wmb(); - i2o_post_message(c,m); - wmb(); - return SCSI_ABORT_PENDING; -} - -int i2o_scsi_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags) -{ - int tid; - struct i2o_controller *c; - struct Scsi_Host *host; - struct i2o_scsi_host *hostdata; - u32 m; - u32 *msg; - - /* - * Find the TID for the bus - */ - - printk("i2o_scsi: Attempting to reset the bus.\n"); - - host = SCpnt->host; - hostdata = (struct i2o_scsi_host *)host->hostdata; - tid = hostdata->bus_task; - c = hostdata->controller; - - /* - * Now send a SCSI reset request. Any remaining commands - * will be aborted by the IOP. We need to catch the reply - * possibly ? - */ - - m = I2O_POST_READ32(c); - - /* - * No free messages, try again next time - no big deal - */ - - if(m == 0xFFFFFFFF) - return SCSI_RESET_PUNT; - - msg = (u32 *)(c->mem_offset + m); - __raw_writel(FOUR_WORD_MSG_SIZE|SGL_OFFSET_0, &msg[0]); - __raw_writel(I2O_CMD_SCSI_BUSRESET<<24|HOST_TID<<12|tid, &msg[1]); - __raw_writel(scsi_context|0x80000000, &msg[2]); - /* We use the top bit to split controller and unit transactions */ - /* Now store unit,tid so we can tie the completion back to a specific device */ - __raw_writel(c->unit << 16 | tid, &msg[3]); - wmb(); - i2o_post_message(c,m); - return SCSI_RESET_PENDING; -} - -/* - * This is anyones guess quite frankly. - */ - -int i2o_scsi_bios_param(Disk * disk, kdev_t dev, int *ip) -{ - int size; - - size = disk->capacity; - ip[0] = 64; /* heads */ - ip[1] = 32; /* sectors */ - if ((ip[2] = size >> 11) > 1024) { /* cylinders, test for big disk */ - ip[0] = 255; /* heads */ - ip[1] = 63; /* sectors */ - ip[2] = size / (255 * 63); /* cylinders */ - } - return 0; -} - -MODULE_AUTHOR("Red Hat Software"); - -static Scsi_Host_Template driver_template = I2OSCSI; - -#include "../scsi/scsi_module.c" diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_scsi.h linux.ac/drivers/i2o/i2o_scsi.h --- linux.vanilla/drivers/i2o/i2o_scsi.h Mon Dec 11 21:20:04 2000 +++ linux.ac/drivers/i2o/i2o_scsi.h Thu Jan 1 01:00:00 1970 @@ -1,47 +0,0 @@ -#ifndef _I2O_SCSI_H -#define _I2O_SCSI_H - -#if !defined(LINUX_VERSION_CODE) -#include -#endif - -#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) - -#include -#include - -#define I2O_SCSI_ID 15 -#define I2O_SCSI_CAN_QUEUE 4 -#define I2O_SCSI_CMD_PER_LUN 6 - -extern int i2o_scsi_detect(Scsi_Host_Template *); -extern const char *i2o_scsi_info(struct Scsi_Host *); -extern int i2o_scsi_command(Scsi_Cmnd *); -extern int i2o_scsi_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); -extern int i2o_scsi_abort(Scsi_Cmnd *); -extern int i2o_scsi_reset(Scsi_Cmnd *, unsigned int); -extern int i2o_scsi_bios_param(Disk *, kdev_t, int *); -extern void i2o_scsi_setup(char *str, int *ints); -extern int i2o_scsi_release(struct Scsi_Host *host); - -#define I2OSCSI { \ - next: NULL, \ - proc_name: "i2o_scsi", \ - name: "I2O SCSI Layer", \ - detect: i2o_scsi_detect, \ - release: i2o_scsi_release, \ - info: i2o_scsi_info, \ - command: i2o_scsi_command, \ - queuecommand: i2o_scsi_queuecommand, \ - abort: i2o_scsi_abort, \ - reset: i2o_scsi_reset, \ - bios_param: i2o_scsi_bios_param, \ - can_queue: I2O_SCSI_CAN_QUEUE, \ - this_id: I2O_SCSI_ID, \ - sg_tablesize: 8, \ - cmd_per_lun: I2O_SCSI_CMD_PER_LUN, \ - unchecked_isa_dma: 0, \ - use_clustering: ENABLE_CLUSTERING \ - } - -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/Config.in linux.ac/drivers/ide/Config.in --- linux.vanilla/drivers/ide/Config.in Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/ide/Config.in Thu Oct 11 14:31:02 2001 @@ -168,7 +168,8 @@ bool ' IGNORE word93 Validation BITS' CONFIG_IDEDMA_IVB fi -if [ "$CONFIG_BLK_DEV_TIVO" = "y" ]; then +if [ "$CONFIG_BLK_DEV_TIVO" = "y" -o \ + "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then define_bool CONFIG_DMA_NONPCI y else define_bool CONFIG_DMA_NONPCI n diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/Makefile linux.ac/drivers/ide/Makefile --- linux.vanilla/drivers/ide/Makefile Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/ide/Makefile Thu Oct 11 14:31:32 2001 @@ -69,7 +69,7 @@ # The virtualised raid layers MUST come after the ide itself or bad stuff # will happen. -obj-$(CONFIG_BLK_DEV_ATARAID) += ataraid.o +obj-$(CONFIG_BLK_DEV_ATARAID) += ataraid.o obj-$(CONFIG_BLK_DEV_ATARAID_PDC) += pdcraid.o obj-$(CONFIG_BLK_DEV_ATARAID_HPT) += hptraid.o diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/hptraid.c linux.ac/drivers/ide/hptraid.c --- linux.vanilla/drivers/ide/hptraid.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/ide/hptraid.c Wed Oct 10 01:47:45 2001 @@ -15,6 +15,8 @@ Based on work Copyleft (C) 2001 by Wilfried Weissmann Copyright (C) 1994-96 Marc ZYNGIER + Based on work done by Sřren Schmidt for FreeBSD + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/hptraid.h linux.ac/drivers/ide/hptraid.h --- linux.vanilla/drivers/ide/hptraid.h Tue Sep 18 07:23:40 2001 +++ linux.ac/drivers/ide/hptraid.h Wed Oct 10 01:47:45 2001 @@ -1,4 +1,32 @@ - +/*- + * Copyright (c) 2000,2001 Sřren Schmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + struct highpoint_raid_conf { int8_t filler1[32]; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide-cd.c linux.ac/drivers/ide/ide-cd.c --- linux.vanilla/drivers/ide/ide-cd.c Thu Aug 16 17:30:45 2001 +++ linux.ac/drivers/ide/ide-cd.c Wed Oct 10 01:47:46 2001 @@ -1885,7 +1885,7 @@ return cdrom_queue_packet_command (drive, &pc); } -static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity, +static int cdrom_read_capacity(ide_drive_t *drive, unsigned *capacity, struct request_sense *sense) { struct { @@ -2927,7 +2927,7 @@ static unsigned long ide_cdrom_capacity (ide_drive_t *drive) { - unsigned long capacity; + unsigned capacity; if (cdrom_read_capacity(drive, &capacity, NULL)) return 0; @@ -2997,6 +2997,8 @@ MODULE_PARM(ignore, "s"); MODULE_DESCRIPTION("ATAPI CD-ROM Driver"); +MODULE_LICENSE("GPL"); + static void __exit ide_cdrom_exit(void) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide-cd.h linux.ac/drivers/ide/ide-cd.h --- linux.vanilla/drivers/ide/ide-cd.h Sun Sep 23 18:32:30 2001 +++ linux.ac/drivers/ide/ide-cd.h Fri Oct 12 12:47:29 2001 @@ -150,7 +150,7 @@ struct atapi_toc { int last_session_lba; int xa_flag; - unsigned long capacity; + unsigned capacity; struct atapi_toc_header hdr; struct atapi_toc_entry ent[MAX_TRACKS+1]; /* One extra for the leadout. */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide-disk.c linux.ac/drivers/ide/ide-disk.c --- linux.vanilla/drivers/ide/ide-disk.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/ide/ide-disk.c Thu Oct 11 22:07:00 2001 @@ -481,7 +481,7 @@ static void idedisk_release (struct inode *inode, struct file *filp, ide_drive_t *drive) { if (drive->removable && !drive->usage) { - invalidate_bdev(inode->i_bdev, 0); + invalidate_buffers(inode->i_rdev); if (drive->doorlocking && ide_wait_cmd(drive, WIN_DOORUNLOCK, 0, 0, 0, NULL)) drive->doorlocking = 0; } @@ -890,3 +890,4 @@ module_init(idedisk_init); module_exit(idedisk_exit); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide-floppy.c linux.ac/drivers/ide/ide-floppy.c --- linux.vanilla/drivers/ide/ide-floppy.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/ide/ide-floppy.c Thu Oct 11 22:07:00 2001 @@ -1750,7 +1750,7 @@ if (!drive->usage) { idefloppy_floppy_t *floppy = drive->driver_data; - invalidate_bdev (inode->i_bdev, 0); + invalidate_buffers (inode->i_rdev); /* IOMEGA Clik! drives do not support lock/unlock commands */ if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags)) { @@ -2116,3 +2116,4 @@ module_init(idefloppy_init); module_exit(idefloppy_exit); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide-probe.c linux.ac/drivers/ide/ide-probe.c --- linux.vanilla/drivers/ide/ide-probe.c Sat Sep 8 20:02:32 2001 +++ linux.ac/drivers/ide/ide-probe.c Wed Oct 10 01:47:46 2001 @@ -779,7 +779,7 @@ /* IDE can do up to 128K per request. */ *max_sect++ = 255; #endif - *max_ra++ = MAX_READAHEAD; + *max_ra++ = vm_max_readahead; } for (unit = 0; unit < units; ++unit) @@ -928,4 +928,5 @@ { ide_probe = NULL; } +MODULE_LICENSE("GPL"); #endif /* MODULE */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide-tape.c linux.ac/drivers/ide/ide-tape.c --- linux.vanilla/drivers/ide/ide-tape.c Mon Aug 13 22:56:19 2001 +++ linux.ac/drivers/ide/ide-tape.c Thu Oct 11 09:46:36 2001 @@ -429,8 +429,6 @@ #include -#define NO_LONGER_REQUIRED (1) - /* * OnStream support */ @@ -2101,10 +2099,6 @@ if (status.b.check && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) status.b.check = 0; if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) { /* Error detected */ -#if IDETAPE_DEBUG_LOG - if (tape->debug_level >= 1) - printk (KERN_INFO "ide-tape: %s: I/O error, ",tape->name); -#endif /* IDETAPE_DEBUG_LOG */ if (pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) { printk (KERN_ERR "ide-tape: I/O error in request sense command\n"); return ide_do_reset (drive); @@ -2178,7 +2172,7 @@ pc->current_position+=bcount.all; #if IDETAPE_DEBUG_LOG if (tape->debug_level >= 2) - printk(KERN_INFO "ide-tape: [cmd %x] transferred %d bytes on that interrupt\n", pc->c[0], bcount.all); + printk(KERN_INFO "ide-tape: [cmd %x] done %d\n", pc->c[0], bcount.all); #endif ide_set_handler (drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL); /* And set the interrupt handler again */ return ide_started; @@ -2297,7 +2291,7 @@ } #if IDETAPE_DEBUG_LOG if (tape->debug_level >= 2) - printk (KERN_INFO "ide-tape: Retry number - %d\n", pc->retries); + printk (KERN_INFO "ide-tape: Retry number - %d, [cmd %x]\n", pc->retries, pc->c[0]); #endif /* IDETAPE_DEBUG_LOG */ pc->retries++; @@ -2472,7 +2466,8 @@ status.all = GET_STAT(); if (status.b.dsc) { if (status.b.check) { /* Error detected */ - printk (KERN_ERR "ide-tape: %s: I/O error, ",tape->name); + printk (KERN_ERR "ide-tape: %s: I/O error: pc = %2x, key = %2x, asc = %2x, ascq = %2x\n", + tape->name, pc->c[0], tape->sense_key, tape->asc, tape->ascq); return idetape_retry_pc (drive); /* Retry operation */ } pc->error = 0; @@ -3096,10 +3091,10 @@ idetape_tape_t *tape = drive->driver_data; idetape_read_position_result_t *result; -//#if IDETAPE_DEBUG_LOG -// if (tape->debug_level >= 4) +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) printk (KERN_INFO "ide-tape: Reached idetape_read_position_callback\n"); -//#endif /* IDETAPE_DEBUG_LOG */ +#endif /* IDETAPE_DEBUG_LOG */ if (!tape->pc->error) { result = (idetape_read_position_result_t *) tape->pc->buffer; @@ -3273,30 +3268,15 @@ idetape_pc_t pc; int position; -//#if IDETAPE_DEBUG_LOG -// if (tape->debug_level >= 4) - printk (KERN_INFO "ide-tape: Reached idetape_read_position\n"); -//#endif /* IDETAPE_DEBUG_LOG */ +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk (KERN_INFO "ide-tape: Reached idetape_read_position\n"); +#endif /* IDETAPE_DEBUG_LOG */ -#ifdef NO_LONGER_REQUIRED - idetape_flush_tape_buffers(drive); -#endif idetape_create_read_position_cmd(&pc); if (idetape_queue_pc_tail (drive, &pc)) return -1; position = tape->first_frame_position; -#ifdef NO_LONGER_REQUIRED - if (tape->onstream) { - if ((position != tape->last_frame_position - tape->blocks_in_buffer) && - (position != tape->last_frame_position + tape->blocks_in_buffer)) { - if (tape->blocks_in_buffer == 0) { - printk("ide-tape: %s: correcting read position %d, %d, %d\n", tape->name, position, tape->last_frame_position, tape->blocks_in_buffer); - position = tape->last_frame_position; - tape->first_frame_position = position; - } - } - } -#endif return position; } @@ -4804,7 +4784,7 @@ */ if (tape->first_frame_position + tape->nr_stages >= tape->capacity - OS_EW) { #if ONSTREAM_DEBUG - printk(KERN_INFO, "chrdev_write: Write truncated at EOM early warning"); + printk(KERN_INFO "chrdev_write: Write truncated at EOM early warning"); #endif if (tape->chrdev_direction == idetape_direction_write) idetape_write_release(inode); @@ -6182,6 +6162,8 @@ }; MODULE_DESCRIPTION("ATAPI Streaming TAPE Driver"); +MODULE_LICENSE("GPL"); + static void __exit idetape_exit (void) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide.c linux.ac/drivers/ide/ide.c --- linux.vanilla/drivers/ide/ide.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/ide/ide.c Thu Oct 11 14:32:30 2001 @@ -3749,6 +3749,7 @@ #ifdef MODULE char *options = NULL; MODULE_PARM(options,"s"); +MODULE_LICENSE("GPL"); static void __init parse_options (char *line) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/pdcadma.c linux.ac/drivers/ide/pdcadma.c --- linux.vanilla/drivers/ide/pdcadma.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/ide/pdcadma.c Wed Oct 10 01:47:46 2001 @@ -0,0 +1,109 @@ +/* + * linux/drivers/ide/pdcadma.c Version 0.01 June 21, 2001 + * + * Copyright (C) 1999-2000 Andre Hedrick + * May be copied or modified under the terms of the GNU General Public License + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "ide_modes.h" + +#undef DISPLAY_PDCADMA_TIMINGS + +#if defined(DISPLAY_PDCADMA_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include + +static int pdcadma_get_info(char *, char **, off_t, int); +extern int (*pdcadma_display_info)(char *, char **, off_t, int); /* ide-proc.c */ +extern char *ide_media_verbose(ide_drive_t *); +static struct pci_dev *bmide_dev; + +static int pdcadma_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + u32 bibma = pci_resource_start(bmide_dev, 4); + + p += sprintf(p, "\n PDC ADMA %04X Chipset.\n", bmide_dev->device); + p += sprintf(p, "UDMA\n"); + p += sprintf(p, "PIO\n"); + + return p-buffer; /* => must be less than 4k! */ +} +#endif /* defined(DISPLAY_PDCADMA_TIMINGS) && defined(CONFIG_PROC_FS) */ + +byte pdcadma_proc = 0; + +extern char *ide_xfer_verbose (byte xfer_rate); + +#ifdef CONFIG_BLK_DEV_IDEDMA +/* + * pdcadma_dmaproc() initiates/aborts (U)DMA read/write operations on a drive. + */ + +int pdcadma_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + func = ide_dma_off_quietly; + default: + break; + } + return ide_dmaproc(func, drive); /* use standard DMA stuff */ +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +unsigned int __init pci_init_pdcadma (struct pci_dev *dev, const char *name) +{ +#if defined(DISPLAY_PDCADMA_TIMINGS) && defined(CONFIG_PROC_FS) + if (!pdcadma_proc) { + pdcadma_proc = 1; + bmide_dev = dev; + pdcadma_display_info = &pdcadma_get_info; + } +#endif /* DISPLAY_PDCADMA_TIMINGS && CONFIG_PROC_FS */ + return 0; +} + +unsigned int __init ata66_pdcadma (ide_hwif_t *hwif) +{ + return 1; +} + +void __init ide_init_pdcadma (ide_hwif_t *hwif) +{ + hwif->autodma = 0; + hwif->dma_base = 0; + +// hwif->tuneproc = &pdcadma_tune_drive; +// hwif->speedproc = &pdcadma_tune_chipset; + +// if (hwif->dma_base) { +// hwif->dmaproc = &pdcadma_dmaproc; +// hwif->autodma = 1; +// } +} + +void __init ide_dmacapable_pdcadma (ide_hwif_t *hwif, unsigned long dmabase) +{ +// ide_setup_dma(hwif, dmabase, 8); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/pdcraid.c linux.ac/drivers/ide/pdcraid.c --- linux.vanilla/drivers/ide/pdcraid.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/ide/pdcraid.c Thu Oct 11 14:32:44 2001 @@ -12,9 +12,7 @@ Authors: Arjan van de Ven - - - + Based on work done by Sřren Schmidt for FreeBSD */ @@ -54,6 +52,12 @@ {IDE2_MAJOR, 64, -1 }, {IDE3_MAJOR, 0, -1 }, {IDE3_MAJOR, 64, -1 }, + {IDE4_MAJOR, 0, -1 }, + {IDE4_MAJOR, 64, -1 }, + {IDE5_MAJOR, 0, -1 }, + {IDE5_MAJOR, 64, -1 }, + {IDE6_MAJOR, 0, -1 }, + {IDE6_MAJOR, 64, -1 }, }; @@ -550,14 +554,8 @@ request_queue_t *q; int i,count; - probedisk(0, device, raidlevel); - probedisk(1, device, raidlevel); - probedisk(2, device, raidlevel); - probedisk(3, device, raidlevel); - probedisk(4, device, raidlevel); - probedisk(5, device, raidlevel); - probedisk(6, device, raidlevel); - probedisk(7, device, raidlevel); + for (i=0; i<14; i++) + probedisk(i, device, raidlevel); if (raidlevel==0) fill_cutoff(device); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/pdcraid.h linux.ac/drivers/ide/pdcraid.h --- linux.vanilla/drivers/ide/pdcraid.h Tue Sep 18 07:23:40 2001 +++ linux.ac/drivers/ide/pdcraid.h Wed Oct 10 01:47:46 2001 @@ -1,3 +1,32 @@ +/*- + * Copyright (c) 2000,2001 Sřren Schmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + struct promise_raid_conf { char promise_id[24]; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/rapide.c linux.ac/drivers/ide/rapide.c --- linux.vanilla/drivers/ide/rapide.c Wed Jun 27 22:12:04 2001 +++ linux.ac/drivers/ide/rapide.c Wed Oct 10 01:47:46 2001 @@ -69,6 +69,7 @@ } #ifdef MODULE +MODULE_LICENSE("GPL"); int init_module (void) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/serverworks.c linux.ac/drivers/ide/serverworks.c --- linux.vanilla/drivers/ide/serverworks.c Sun Sep 9 18:43:02 2001 +++ linux.ac/drivers/ide/serverworks.c Wed Oct 10 01:47:46 2001 @@ -499,6 +499,40 @@ switch (func) { case ide_dma_check: return config_drive_xfer_rate(drive); + case ide_dma_end: + { + ide_hwif_t *hwif = HWIF(drive); + unsigned long dma_base = hwif->dma_base; + + if(inb(dma_base+0x02)&1) + { +#if 0 + int i; + printk(KERN_ERR "Curious - OSB4 thinks the DMA is still running.\n"); + for(i=0;i<10;i++) + { + if(!(inb(dma_base+0x02)&1)) + { + printk(KERN_ERR "OSB4 now finished.\n"); + break; + } + udelay(5); + } +#endif + printk(KERN_CRIT "Serverworks OSB4 in impossible state.\n"); + printk(KERN_CRIT "Disable UDMA or if you are using Seagate then try switching disk types\n"); + printk(KERN_CRIT "on this controller. Please report this event to osb4-bug@ide.cabal.tm\n"); +#if 0 + /* Panic might sys_sync -> death by corrupt disk */ + panic("OSB4: continuing might cause disk corruption.\n"); +#else + printk(KERN_CRIT "OSB4: continuing might cause disk corruption.\n"); + while(1) + rep_nop(); +#endif + } + /* and drop through */ + } default : break; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ieee1394/ieee1394_syms.c linux.ac/drivers/ieee1394/ieee1394_syms.c --- linux.vanilla/drivers/ieee1394/ieee1394_syms.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/ieee1394/ieee1394_syms.c Thu Oct 11 17:59:31 2001 @@ -84,4 +84,5 @@ EXPORT_SYMBOL(hpsb_register_protocol); EXPORT_SYMBOL(hpsb_unregister_protocol); EXPORT_SYMBOL(hpsb_release_unit_directory); + MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ieee1394/pcilynx.c linux.ac/drivers/ieee1394/pcilynx.c --- linux.vanilla/drivers/ieee1394/pcilynx.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/ieee1394/pcilynx.c Thu Oct 11 14:34:55 2001 @@ -1637,8 +1637,8 @@ static void __exit pcilynx_cleanup(void) { + pci_unregister_driver(&lynx_pcidriver); hpsb_unregister_lowlevel(&lynx_template); - pci_unregister_driver(&lynx_pcidriver); PRINT_G(KERN_INFO, "removed " PCILYNX_DRIVER_NAME " module"); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ieee1394/sbp2.c linux.ac/drivers/ieee1394/sbp2.c --- linux.vanilla/drivers/ieee1394/sbp2.c Thu Oct 11 13:52:08 2001 +++ linux.ac/drivers/ieee1394/sbp2.c Fri Oct 12 09:55:43 2001 @@ -2960,6 +2960,7 @@ MODULE_AUTHOR("James Goodwin "); MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver"); MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME); +MODULE_LICENSE("GPL"); /* SCSI host template */ static Scsi_Host_Template driver_template = { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/input/keybdev.c linux.ac/drivers/input/keybdev.c --- linux.vanilla/drivers/input/keybdev.c Tue Sep 18 22:10:43 2001 +++ linux.ac/drivers/input/keybdev.c Wed Oct 10 01:47:46 2001 @@ -246,3 +246,4 @@ MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Input driver to keyboard driver binding"); MODULE_PARM(jp_kbd_109, "i"); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/macintosh/via-pmu68k.c linux.ac/drivers/macintosh/via-pmu68k.c --- linux.vanilla/drivers/macintosh/via-pmu68k.c Fri Feb 9 19:30:23 2001 +++ linux.ac/drivers/macintosh/via-pmu68k.c Wed Oct 10 01:47:54 2001 @@ -95,10 +95,10 @@ static int data_len; static int adb_int_pending; static int pmu_adb_flags; -static int adb_dev_map = 0; +static int adb_dev_map; static struct adb_request bright_req_1, bright_req_2, bright_req_3; static int pmu_kind = PMU_UNKNOWN; -static int pmu_fully_inited = 0; +static int pmu_fully_inited; int asleep; struct notifier_block *sleep_notifier_list; @@ -122,13 +122,13 @@ static void set_volume(int level); struct adb_driver via_pmu_driver = { - "68K PMU", - pmu_probe, - pmu_init, - pmu_send_request, - pmu_autopoll, - pmu_poll, - pmu_reset_bus + name: "68K PMU", + probe: pmu_probe, + init: pmu_init, + send_request: pmu_send_request, + autopoll: pmu_autopoll, + poll: pmu_poll, + reset_bus: pmu_reset_bus }; /* @@ -260,7 +260,7 @@ /* Enable backlight */ pmu_enable_backlight(1); - printk("adb: PMU 68K driver v0.5 for Unified ADB.\n"); + printk(KERN_INFO "adb: PMU 68K driver v0.5 for Unified ADB.\n"); return 0; } @@ -742,7 +742,7 @@ } int backlight_level = -1; -int backlight_enabled = 0; +int backlight_enabled; #define LEVEL_TO_BRIGHT(lev) ((lev) < 1? 0x7f: 0x4a - ((lev) << 1)) @@ -1018,7 +1018,6 @@ static int /*__openfirmware*/ pmu_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) { - int error; __u32 value; switch (cmd) { @@ -1027,10 +1026,10 @@ case PMU_IOC_GET_BACKLIGHT: return put_user(backlight_level, (__u32 *)arg); case PMU_IOC_SET_BACKLIGHT: - error = get_user(value, (__u32 *)arg); - if (!error) - pmu_set_brightness(value); - return error; + if (get_user(value, (__u32 *)arg)) + return -EFAULT; + pmu_set_brightness(value); + return 0; case PMU_IOC_GET_MODEL: return put_user(pmu_kind, (__u32 *)arg); } @@ -1045,7 +1044,9 @@ }; static struct miscdevice pmu_device = { - PMU_MINOR, "pmu", &pmu_device_fops + minor: PMU_MINOR, + name: "pmu", + fops: &pmu_device_fops, }; void pmu_device_init(void) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/md/Makefile linux.ac/drivers/md/Makefile --- linux.vanilla/drivers/md/Makefile Fri Sep 14 22:22:18 2001 +++ linux.ac/drivers/md/Makefile Wed Oct 10 01:47:54 2001 @@ -6,7 +6,7 @@ export-objs := md.o xor.o list-multi := lvm-mod.o -lvm-mod-objs := lvm.o lvm-snap.o +lvm-mod-objs := lvm.o lvm-snap.o lvm-fs.o # Note: link order is important. All raid personalities # and xor.o must come before md.o, as they each initialise diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/md/lvm-fs.c linux.ac/drivers/md/lvm-fs.c --- linux.vanilla/drivers/md/lvm-fs.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/md/lvm-fs.c Wed Oct 10 01:47:54 2001 @@ -0,0 +1,623 @@ +/* + * kernel/lvm-fs.c + * + * Copyright (C) 2001 Sistina Software + * + * January,February 2001 + * + * LVM driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * LVM driver 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 GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/* + * Changelog + * + * 11/01/2001 - First version (Joe Thornber) + * 21/03/2001 - added display of stripes and stripe size (HM) + * 04/10/2001 - corrected devfs_register() call in lvm_init_fs() + * 11/04/2001 - don't devfs_register("lvm") as user-space always does it + * 10/05/2001 - show more of PV name in /proc/lvm/global + * + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "lvm-internal.h" + + +static int _proc_read_vg(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int _proc_read_lv(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int _proc_read_pv(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int _proc_read_global(char *page, char **start, off_t off, + int count, int *eof, void *data); + +static int _vg_info(vg_t *vg_ptr, char *buf); +static int _lv_info(vg_t *vg_ptr, lv_t *lv_ptr, char *buf); +static int _pv_info(pv_t *pv_ptr, char *buf); + +static void _show_uuid(const char *src, char *b, char *e); + +#if 0 +static devfs_handle_t lvm_devfs_handle; +#endif +static devfs_handle_t vg_devfs_handle[MAX_VG]; +static devfs_handle_t ch_devfs_handle[MAX_VG]; +static devfs_handle_t lv_devfs_handle[MAX_LV]; + +static struct proc_dir_entry *lvm_proc_dir = NULL; +static struct proc_dir_entry *lvm_proc_vg_subdir = NULL; + +/* inline functions */ + +/* public interface */ +void __init lvm_init_fs() { + struct proc_dir_entry *pde; + +/* User-space has already registered this */ +#if 0 + lvm_devfs_handle = devfs_register( + 0 , "lvm", 0, LVM_CHAR_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, + &lvm_chr_fops, NULL); +#endif + + lvm_proc_dir = create_proc_entry(LVM_DIR, S_IFDIR, &proc_root); + if (lvm_proc_dir) { + lvm_proc_vg_subdir = create_proc_entry(LVM_VG_SUBDIR, S_IFDIR, + lvm_proc_dir); + pde = create_proc_entry(LVM_GLOBAL, S_IFREG, lvm_proc_dir); + if ( pde != NULL) pde->read_proc = _proc_read_global; + } +} + +void lvm_fin_fs() { +#if 0 + devfs_unregister (lvm_devfs_handle); +#endif + + remove_proc_entry(LVM_GLOBAL, lvm_proc_dir); + remove_proc_entry(LVM_VG_SUBDIR, lvm_proc_dir); + remove_proc_entry(LVM_DIR, &proc_root); +} + +void lvm_fs_create_vg(vg_t *vg_ptr) { + struct proc_dir_entry *pde; + + vg_devfs_handle[vg_ptr->vg_number] = + devfs_mk_dir(0, vg_ptr->vg_name, NULL); + + ch_devfs_handle[vg_ptr->vg_number] = devfs_register( + vg_devfs_handle[vg_ptr->vg_number] , "group", + DEVFS_FL_DEFAULT, LVM_CHAR_MAJOR, vg_ptr->vg_number, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, + &lvm_chr_fops, NULL); + + vg_ptr->vg_dir_pde = create_proc_entry(vg_ptr->vg_name, S_IFDIR, + lvm_proc_vg_subdir); + + if((pde = create_proc_entry("group", S_IFREG, vg_ptr->vg_dir_pde))) { + pde->read_proc = _proc_read_vg; + pde->data = vg_ptr; + } + + vg_ptr->lv_subdir_pde = + create_proc_entry(LVM_LV_SUBDIR, S_IFDIR, vg_ptr->vg_dir_pde); + + vg_ptr->pv_subdir_pde = + create_proc_entry(LVM_PV_SUBDIR, S_IFDIR, vg_ptr->vg_dir_pde); +} + +void lvm_fs_remove_vg(vg_t *vg_ptr) { + int i; + + devfs_unregister(ch_devfs_handle[vg_ptr->vg_number]); + devfs_unregister(vg_devfs_handle[vg_ptr->vg_number]); + + /* remove lv's */ + for(i = 0; i < vg_ptr->lv_max; i++) + if(vg_ptr->lv[i]) lvm_fs_remove_lv(vg_ptr, vg_ptr->lv[i]); + + /* remove pv's */ + for(i = 0; i < vg_ptr->pv_max; i++) + if(vg_ptr->pv[i]) lvm_fs_remove_pv(vg_ptr, vg_ptr->pv[i]); + + if(vg_ptr->vg_dir_pde) { + remove_proc_entry(LVM_LV_SUBDIR, vg_ptr->vg_dir_pde); + vg_ptr->lv_subdir_pde = NULL; + + remove_proc_entry(LVM_PV_SUBDIR, vg_ptr->vg_dir_pde); + vg_ptr->pv_subdir_pde = NULL; + + remove_proc_entry("group", vg_ptr->vg_dir_pde); + vg_ptr->vg_dir_pde = NULL; + + remove_proc_entry(vg_ptr->vg_name, lvm_proc_vg_subdir); + } +} + + +static inline const char *_basename(const char *str) { + const char *name = strrchr(str, '/'); + name = name ? name + 1 : str; + return name; +} + +devfs_handle_t lvm_fs_create_lv(vg_t *vg_ptr, lv_t *lv) { + struct proc_dir_entry *pde; + const char *name = _basename(lv->lv_name); + + lv_devfs_handle[MINOR(lv->lv_dev)] = devfs_register( + vg_devfs_handle[vg_ptr->vg_number], name, + DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, MINOR(lv->lv_dev), + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, + &lvm_blk_dops, NULL); + + if(vg_ptr->lv_subdir_pde && + (pde = create_proc_entry(name, S_IFREG, vg_ptr->lv_subdir_pde))) { + pde->read_proc = _proc_read_lv; + pde->data = lv; + } + return lv_devfs_handle[MINOR(lv->lv_dev)]; +} + +void lvm_fs_remove_lv(vg_t *vg_ptr, lv_t *lv) { + devfs_unregister(lv_devfs_handle[MINOR(lv->lv_dev)]); + + if(vg_ptr->lv_subdir_pde) { + const char *name = _basename(lv->lv_name); + remove_proc_entry(name, vg_ptr->lv_subdir_pde); + } +} + + +static inline void _make_pv_name(const char *src, char *b, char *e) { + int offset = strlen(LVM_DIR_PREFIX); + if(strncmp(src, LVM_DIR_PREFIX, offset)) + offset = 0; + + e--; + src += offset; + while(*src && (b != e)) { + *b++ = (*src == '/') ? '_' : *src; + src++; + } + *b = '\0'; +} + +void lvm_fs_create_pv(vg_t *vg_ptr, pv_t *pv) { + struct proc_dir_entry *pde; + char name[NAME_LEN]; + + if(!vg_ptr->pv_subdir_pde) + return; + + _make_pv_name(pv->pv_name, name, name + sizeof(name)); + if((pde = create_proc_entry(name, S_IFREG, vg_ptr->pv_subdir_pde))) { + pde->read_proc = _proc_read_pv; + pde->data = pv; + } +} + +void lvm_fs_remove_pv(vg_t *vg_ptr, pv_t *pv) { + char name[NAME_LEN]; + + if(!vg_ptr->pv_subdir_pde) + return; + + _make_pv_name(pv->pv_name, name, name + sizeof(name)); + remove_proc_entry(name, vg_ptr->pv_subdir_pde); +} + + +static int _proc_read_vg(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int sz = 0; + vg_t *vg_ptr = data; + char uuid[NAME_LEN]; + + sz += sprintf(page + sz, "name: %s\n", vg_ptr->vg_name); + sz += sprintf(page + sz, "size: %u\n", + vg_ptr->pe_total * vg_ptr->pe_size / 2); + sz += sprintf(page + sz, "access: %u\n", vg_ptr->vg_access); + sz += sprintf(page + sz, "status: %u\n", vg_ptr->vg_status); + sz += sprintf(page + sz, "number: %u\n", vg_ptr->vg_number); + sz += sprintf(page + sz, "LV max: %u\n", vg_ptr->lv_max); + sz += sprintf(page + sz, "LV current: %u\n", vg_ptr->lv_cur); + sz += sprintf(page + sz, "LV open: %u\n", vg_ptr->lv_open); + sz += sprintf(page + sz, "PV max: %u\n", vg_ptr->pv_max); + sz += sprintf(page + sz, "PV current: %u\n", vg_ptr->pv_cur); + sz += sprintf(page + sz, "PV active: %u\n", vg_ptr->pv_act); + sz += sprintf(page + sz, "PE size: %u\n", vg_ptr->pe_size / 2); + sz += sprintf(page + sz, "PE total: %u\n", vg_ptr->pe_total); + sz += sprintf(page + sz, "PE allocated: %u\n", vg_ptr->pe_allocated); + + _show_uuid(vg_ptr->vg_uuid, uuid, uuid + sizeof(uuid)); + sz += sprintf(page + sz, "uuid: %s\n", uuid); + + return sz; +} + +static int _proc_read_lv(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int sz = 0; + lv_t *lv = data; + + sz += sprintf(page + sz, "name: %s\n", lv->lv_name); + sz += sprintf(page + sz, "size: %u\n", lv->lv_size); + sz += sprintf(page + sz, "access: %u\n", lv->lv_access); + sz += sprintf(page + sz, "status: %u\n", lv->lv_status); + sz += sprintf(page + sz, "number: %u\n", lv->lv_number); + sz += sprintf(page + sz, "open: %u\n", lv->lv_open); + sz += sprintf(page + sz, "allocation: %u\n", lv->lv_allocation); + if(lv->lv_stripes > 1) { + sz += sprintf(page + sz, "stripes: %u\n", + lv->lv_stripes); + sz += sprintf(page + sz, "stripesize: %u\n", + lv->lv_stripesize); + } + sz += sprintf(page + sz, "device: %02u:%02u\n", + MAJOR(lv->lv_dev), MINOR(lv->lv_dev)); + + return sz; +} + +static int _proc_read_pv(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int sz = 0; + pv_t *pv = data; + char uuid[NAME_LEN]; + + sz += sprintf(page + sz, "name: %s\n", pv->pv_name); + sz += sprintf(page + sz, "size: %u\n", pv->pv_size); + sz += sprintf(page + sz, "status: %u\n", pv->pv_status); + sz += sprintf(page + sz, "number: %u\n", pv->pv_number); + sz += sprintf(page + sz, "allocatable: %u\n", pv->pv_allocatable); + sz += sprintf(page + sz, "LV current: %u\n", pv->lv_cur); + sz += sprintf(page + sz, "PE size: %u\n", pv->pe_size / 2); + sz += sprintf(page + sz, "PE total: %u\n", pv->pe_total); + sz += sprintf(page + sz, "PE allocated: %u\n", pv->pe_allocated); + sz += sprintf(page + sz, "device: %02u:%02u\n", + MAJOR(pv->pv_dev), MINOR(pv->pv_dev)); + + _show_uuid(pv->pv_uuid, uuid, uuid + sizeof(uuid)); + sz += sprintf(page + sz, "uuid: %s\n", uuid); + + return sz; +} + +static int _proc_read_global(char *page, char **start, off_t pos, int count, + int *eof, void *data) { + +#define LVM_PROC_BUF ( i == 0 ? dummy_buf : &buf[sz]) + + int c, i, l, p, v, vg_counter, pv_counter, lv_counter, lv_open_counter, + lv_open_total, pe_t_bytes, hash_table_bytes, lv_block_exception_t_bytes, seconds; + static off_t sz; + off_t sz_last; + static char *buf = NULL; + static char dummy_buf[160]; /* sized for 2 lines */ + vg_t *vg_ptr; + lv_t *lv_ptr; + pv_t *pv_ptr; + + +#ifdef DEBUG_LVM_PROC_GET_INFO + printk(KERN_DEBUG + "%s - lvm_proc_get_global_info CALLED pos: %lu count: %d whence: %d\n", + lvm_name, pos, count, whence); +#endif + + if(pos != 0 && buf != NULL) + goto out; + + sz_last = vg_counter = pv_counter = lv_counter = lv_open_counter = \ + lv_open_total = pe_t_bytes = hash_table_bytes = \ + lv_block_exception_t_bytes = 0; + + /* get some statistics */ + for (v = 0; v < ABS_MAX_VG; v++) { + if ((vg_ptr = vg[v]) != NULL) { + vg_counter++; + pv_counter += vg_ptr->pv_cur; + lv_counter += vg_ptr->lv_cur; + if (vg_ptr->lv_cur > 0) { + for (l = 0; l < vg[v]->lv_max; l++) { + if ((lv_ptr = vg_ptr->lv[l]) != NULL) { + pe_t_bytes += lv_ptr->lv_allocated_le; + hash_table_bytes += lv_ptr->lv_snapshot_hash_table_size; + if (lv_ptr->lv_block_exception != NULL) + lv_block_exception_t_bytes += lv_ptr->lv_remap_end; + if (lv_ptr->lv_open > 0) { + lv_open_counter++; + lv_open_total += lv_ptr->lv_open; + } + } + } + } + } + } + + pe_t_bytes *= sizeof(pe_t); + lv_block_exception_t_bytes *= sizeof(lv_block_exception_t); + + if (buf != NULL) { + P_KFREE("%s -- vfree %d\n", lvm_name, __LINE__); + lock_kernel(); + vfree(buf); + unlock_kernel(); + buf = NULL; + } + /* 2 times: first to get size to allocate buffer, + 2nd to fill the malloced buffer */ + for (i = 0; i < 2; i++) { + sz = 0; + sz += sprintf(LVM_PROC_BUF, + "LVM " +#ifdef MODULE + "module" +#else + "driver" +#endif + " %s\n\n" + "Total: %d VG%s %d PV%s %d LV%s ", + lvm_version, + vg_counter, vg_counter == 1 ? "" : "s", + pv_counter, pv_counter == 1 ? "" : "s", + lv_counter, lv_counter == 1 ? "" : "s"); + sz += sprintf(LVM_PROC_BUF, + "(%d LV%s open", + lv_open_counter, + lv_open_counter == 1 ? "" : "s"); + if (lv_open_total > 0) + sz += sprintf(LVM_PROC_BUF, + " %d times)\n", + lv_open_total); + else + sz += sprintf(LVM_PROC_BUF, ")"); + sz += sprintf(LVM_PROC_BUF, + "\nGlobal: %lu bytes malloced IOP version: %d ", + vg_counter * sizeof(vg_t) + + pv_counter * sizeof(pv_t) + + lv_counter * sizeof(lv_t) + + pe_t_bytes + hash_table_bytes + lv_block_exception_t_bytes + sz_last, + lvm_iop_version); + + seconds = CURRENT_TIME - loadtime; + if (seconds < 0) + loadtime = CURRENT_TIME + seconds; + if (seconds / 86400 > 0) { + sz += sprintf(LVM_PROC_BUF, "%d day%s ", + seconds / 86400, + seconds / 86400 == 0 || + seconds / 86400 > 1 ? "s" : ""); + } + sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n", + (seconds % 86400) / 3600, + (seconds % 3600) / 60, + seconds % 60); + + if (vg_counter > 0) { + for (v = 0; v < ABS_MAX_VG; v++) { + /* volume group */ + if ((vg_ptr = vg[v]) != NULL) { + sz += _vg_info(vg_ptr, LVM_PROC_BUF); + + /* physical volumes */ + sz += sprintf(LVM_PROC_BUF, + "\n PV%s ", + vg_ptr->pv_cur == 1 ? ": " : "s:"); + c = 0; + for (p = 0; p < vg_ptr->pv_max; p++) { + if ((pv_ptr = vg_ptr->pv[p]) != NULL) { + sz += _pv_info(pv_ptr, LVM_PROC_BUF); + + c++; + if (c < vg_ptr->pv_cur) + sz += sprintf(LVM_PROC_BUF, + "\n "); + } + } + + /* logical volumes */ + sz += sprintf(LVM_PROC_BUF, + "\n LV%s ", + vg_ptr->lv_cur == 1 ? ": " : "s:"); + c = 0; + for (l = 0; l < vg_ptr->lv_max; l++) { + if ((lv_ptr = vg_ptr->lv[l]) != NULL) { + sz += _lv_info(vg_ptr, lv_ptr, LVM_PROC_BUF); + c++; + if (c < vg_ptr->lv_cur) + sz += sprintf(LVM_PROC_BUF, + "\n "); + } + } + if (vg_ptr->lv_cur == 0) sz += sprintf(LVM_PROC_BUF, "none"); + sz += sprintf(LVM_PROC_BUF, "\n"); + } + } + } + if (buf == NULL) { + lock_kernel(); + buf = vmalloc(sz); + unlock_kernel(); + if (buf == NULL) { + sz = 0; + return sprintf(page, "%s - vmalloc error at line %d\n", + lvm_name, __LINE__); + } + } + sz_last = sz; + } + + out: + if (pos > sz - 1) { + lock_kernel(); + vfree(buf); + unlock_kernel(); + buf = NULL; + return 0; + } + *start = &buf[pos]; + if (sz - pos < count) + return sz - pos; + else + return count; + +#undef LVM_PROC_BUF +} + +/* + * provide VG info for proc filesystem use (global) + */ +static int _vg_info(vg_t *vg_ptr, char *buf) { + int sz = 0; + char inactive_flag = ' '; + + if (!(vg_ptr->vg_status & VG_ACTIVE)) inactive_flag = 'I'; + sz = sprintf(buf, + "\nVG: %c%s [%d PV, %d LV/%d open] " + " PE Size: %d KB\n" + " Usage [KB/PE]: %d /%d total " + "%d /%d used %d /%d free", + inactive_flag, + vg_ptr->vg_name, + vg_ptr->pv_cur, + vg_ptr->lv_cur, + vg_ptr->lv_open, + vg_ptr->pe_size >> 1, + vg_ptr->pe_size * vg_ptr->pe_total >> 1, + vg_ptr->pe_total, + vg_ptr->pe_allocated * vg_ptr->pe_size >> 1, + vg_ptr->pe_allocated, + (vg_ptr->pe_total - vg_ptr->pe_allocated) * + vg_ptr->pe_size >> 1, + vg_ptr->pe_total - vg_ptr->pe_allocated); + return sz; +} + + +/* + * provide LV info for proc filesystem use (global) + */ +static int _lv_info(vg_t *vg_ptr, lv_t *lv_ptr, char *buf) { + int sz = 0; + char inactive_flag = 'A', allocation_flag = ' ', + stripes_flag = ' ', rw_flag = ' ', *basename; + + if (!(lv_ptr->lv_status & LV_ACTIVE)) + inactive_flag = 'I'; + rw_flag = 'R'; + if (lv_ptr->lv_access & LV_WRITE) + rw_flag = 'W'; + allocation_flag = 'D'; + if (lv_ptr->lv_allocation & LV_CONTIGUOUS) + allocation_flag = 'C'; + stripes_flag = 'L'; + if (lv_ptr->lv_stripes > 1) + stripes_flag = 'S'; + sz += sprintf(buf+sz, + "[%c%c%c%c", + inactive_flag, + rw_flag, + allocation_flag, + stripes_flag); + if (lv_ptr->lv_stripes > 1) + sz += sprintf(buf+sz, "%-2d", + lv_ptr->lv_stripes); + else + sz += sprintf(buf+sz, " "); + + /* FIXME: use _basename */ + basename = strrchr(lv_ptr->lv_name, '/'); + if ( basename == 0) basename = lv_ptr->lv_name; + else basename++; + sz += sprintf(buf+sz, "] %-25s", basename); + if (strlen(basename) > 25) + sz += sprintf(buf+sz, + "\n "); + sz += sprintf(buf+sz, "%9d /%-6d ", + lv_ptr->lv_size >> 1, + lv_ptr->lv_size / vg_ptr->pe_size); + + if (lv_ptr->lv_open == 0) + sz += sprintf(buf+sz, "close"); + else + sz += sprintf(buf+sz, "%dx open", + lv_ptr->lv_open); + + return sz; +} + + +/* + * provide PV info for proc filesystem use (global) + */ +static int _pv_info(pv_t *pv, char *buf) { + int sz = 0; + char inactive_flag = 'A', allocation_flag = ' '; + char *pv_name = NULL; + + if (!(pv->pv_status & PV_ACTIVE)) + inactive_flag = 'I'; + allocation_flag = 'A'; + if (!(pv->pv_allocatable & PV_ALLOCATABLE)) + allocation_flag = 'N'; + pv_name = strchr(pv->pv_name+1,'/'); + if ( pv_name == 0) pv_name = pv->pv_name; + else pv_name++; + sz = sprintf(buf, + "[%c%c] %-21s %8d /%-6d " + "%8d /%-6d %8d /%-6d", + inactive_flag, + allocation_flag, + pv_name, + pv->pe_total * pv->pe_size >> 1, + pv->pe_total, + pv->pe_allocated * pv->pe_size >> 1, + pv->pe_allocated, + (pv->pe_total - pv->pe_allocated) * + pv->pe_size >> 1, + pv->pe_total - pv->pe_allocated); + return sz; +} + +static void _show_uuid(const char *src, char *b, char *e) { + int i; + + e--; + for(i = 0; *src && (b != e); i++) { + if(i && !(i & 0x3)) + *b++ = '-'; + *b++ = *src++; + } + *b = '\0'; +} +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/md/lvm-internal.h linux.ac/drivers/md/lvm-internal.h --- linux.vanilla/drivers/md/lvm-internal.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/md/lvm-internal.h Thu Oct 11 23:20:27 2001 @@ -0,0 +1,101 @@ +/* + * kernel/lvm-internal.h + * + * Copyright (C) 2001 Sistina Software + * + * + * LVM driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * LVM driver 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 GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/* + * Changelog + * + * 05/01/2001:Joe Thornber - Factored this file out of lvm.c + * + */ + +#ifndef LVM_INTERNAL_H +#define LVM_INTERNAL_H + +#include + +#define _LVM_INTERNAL_H_VERSION "LVM "LVM_RELEASE_NAME" ("LVM_RELEASE_DATE")" + +/* global variables, defined in lvm.c */ +extern char *lvm_version; +extern ushort lvm_iop_version; +extern int loadtime; +extern const char *const lvm_name; + + +extern vg_t *vg[]; +extern struct file_operations lvm_chr_fops; + +extern struct block_device_operations lvm_blk_dops; + + +/* debug macros */ +#ifdef DEBUG_IOCTL +#define P_IOCTL(fmt, args...) printk(KERN_DEBUG "lvm ioctl: " fmt, ## args) +#else +#define P_IOCTL(fmt, args...) +#endif + +#ifdef DEBUG_MAP +#define P_MAP(fmt, args...) printk(KERN_DEBUG "lvm map: " fmt, ## args) +#else +#define P_MAP(fmt, args...) +#endif + +#ifdef DEBUG_KFREE +#define P_KFREE(fmt, args...) printk(KERN_DEBUG "lvm kfree: " fmt, ## args) +#else +#define P_KFREE(fmt, args...) +#endif + +#ifdef DEBUG_DEVICE +#define P_DEV(fmt, args...) printk(KERN_DEBUG "lvm device: " fmt, ## args) +#else +#define P_DEV(fmt, args...) +#endif + + +/* lvm-snap.c */ +int lvm_get_blksize(kdev_t); +int lvm_snapshot_alloc(lv_t *); +int lvm_snapshot_fill_COW_page(vg_t *, lv_t *); +int lvm_snapshot_COW(kdev_t, ulong, ulong, ulong, vg_t *vg, lv_t *); +int lvm_snapshot_remap_block(kdev_t *, ulong *, ulong, lv_t *); +void lvm_snapshot_release(lv_t *); +int lvm_write_COW_table_block(vg_t *, lv_t *); +void lvm_hash_link(lv_block_exception_t *, kdev_t, ulong, lv_t *); +int lvm_snapshot_alloc_hash_table(lv_t *); +void lvm_drop_snapshot(vg_t *vg, lv_t *, const char *); + + +/* lvm_fs.c */ +void lvm_init_fs(void); +void lvm_fin_fs(void); + +void lvm_fs_create_vg(vg_t *vg_ptr); +void lvm_fs_remove_vg(vg_t *vg_ptr); +devfs_handle_t lvm_fs_create_lv(vg_t *vg_ptr, lv_t *lv); +void lvm_fs_remove_lv(vg_t *vg_ptr, lv_t *lv); +void lvm_fs_create_pv(vg_t *vg_ptr, pv_t *pv); +void lvm_fs_remove_pv(vg_t *vg_ptr, pv_t *pv); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/md/lvm-snap.c linux.ac/drivers/md/lvm-snap.c --- linux.vanilla/drivers/md/lvm-snap.c Mon Sep 10 16:00:55 2001 +++ linux.ac/drivers/md/lvm-snap.c Wed Oct 10 01:47:54 2001 @@ -26,10 +26,19 @@ * * 05/07/2000 - implemented persistent snapshot support * 23/11/2000 - used cpu_to_le64 rather than my own macro + * 25/01/2001 - Put LockPage back in + * 01/02/2001 - A dropped snapshot is now set as inactive + * 12/03/2001 - lvm_pv_get_number changes: + * o made it static + * o renamed it to _pv_get_number + * o pv number is returned in new uint * arg + * o -1 returned on error + * lvm_snapshot_fill_COW_table has a return value too. * */ #include +#include #include #include #include @@ -38,28 +47,43 @@ #include -#include "lvm-snap.h" +#include "lvm-internal.h" + +static char *lvm_snap_version __attribute__ ((unused)) = + "LVM "LVM_RELEASE_NAME" snapshot code ("LVM_RELEASE_DATE")\n"; -static char *lvm_snap_version __attribute__ ((unused)) = "LVM 0.9.1_beta2 snapshot code (18/01/2001)\n"; extern const char *const lvm_name; extern int lvm_blocksizes[]; void lvm_snapshot_release(lv_t *); +static int _write_COW_table_block(vg_t *vg, lv_t *lv, int idx, + const char **reason); +static void _disable_snapshot(vg_t *vg, lv_t *lv); -uint lvm_pv_get_number(vg_t * vg, kdev_t rdev) -{ + +static int _pv_get_number(vg_t * vg, kdev_t rdev, uint *pvn) { uint p; + for(p = 0; p < vg->pv_max; p++) { + if(vg->pv[p] == NULL) + continue; + + if(vg->pv[p]->pv_dev == rdev) + break; - for ( p = 0; p < vg->pv_max; p++) - { - if ( vg->pv[p] == NULL) continue; - if ( vg->pv[p]->pv_dev == rdev) break; } - return vg->pv[p]->pv_number; -} + if(p >= vg->pv_max) { + /* bad news, the snapshot COW table is probably corrupt */ + printk(KERN_ERR + "%s -- _pv_get_number failed for rdev = %u\n", + lvm_name, rdev); + return -1; + } + *pvn = vg->pv[p]->pv_number; + return 0; +} #define hashfn(dev,block,mask,chunk_size) \ ((HASHDEV(dev)^((block)/(chunk_size))) & (mask)) @@ -133,7 +157,7 @@ return ret; } -void lvm_drop_snapshot(lv_t * lv_snap, const char * reason) +void lvm_drop_snapshot(vg_t *vg, lv_t *lv_snap, const char *reason) { kdev_t last_dev; int i; @@ -142,6 +166,9 @@ or error on this snapshot --> release it */ invalidate_buffers(lv_snap->lv_dev); + /* wipe the snapshot since it's inconsistent now */ + _disable_snapshot(vg, lv_snap); + for (i = last_dev = 0; i < lv_snap->lv_remap_ptr; i++) { if ( lv_snap->lv_block_exception[i].rdev_new != last_dev) { last_dev = lv_snap->lv_block_exception[i].rdev_new; @@ -150,26 +177,33 @@ } lvm_snapshot_release(lv_snap); + lv_snap->lv_status &= ~LV_ACTIVE; printk(KERN_INFO - "%s -- giving up to snapshot %s on %s due %s\n", + "%s -- giving up to snapshot %s on %s: %s\n", lvm_name, lv_snap->lv_snapshot_org->lv_name, lv_snap->lv_name, reason); } -static inline void lvm_snapshot_prepare_blocks(unsigned long * blocks, - unsigned long start, - int nr_sectors, - int blocksize) +static inline int lvm_snapshot_prepare_blocks(unsigned long *blocks, + unsigned long start, + int nr_sectors, + int blocksize) { int i, sectors_per_block, nr_blocks; - sectors_per_block = blocksize >> 9; + sectors_per_block = blocksize / SECTOR_SIZE; + + if(start & (sectors_per_block - 1)) + return 0; + nr_blocks = nr_sectors / sectors_per_block; start /= sectors_per_block; for (i = 0; i < nr_blocks; i++) blocks[i] = start++; + + return 1; } inline int lvm_get_blksize(kdev_t dev) @@ -209,128 +243,59 @@ #endif -void lvm_snapshot_fill_COW_page(vg_t * vg, lv_t * lv_snap) +int lvm_snapshot_fill_COW_page(vg_t * vg, lv_t * lv_snap) { - int id = 0, is = lv_snap->lv_remap_ptr; - ulong blksize_snap; - lv_COW_table_disk_t * lv_COW_table = - ( lv_COW_table_disk_t *) page_address(lv_snap->lv_COW_table_page); + uint pvn; + int id = 0, is = lv_snap->lv_remap_ptr; + ulong blksize_snap; + lv_COW_table_disk_t * lv_COW_table = (lv_COW_table_disk_t *) + page_address(lv_snap->lv_COW_table_iobuf->maplist[0]); + + if (is == 0) + return 0; - if (is == 0) return; is--; - blksize_snap = lvm_get_blksize(lv_snap->lv_block_exception[is].rdev_new); + blksize_snap = + lvm_get_blksize(lv_snap->lv_block_exception[is].rdev_new); is -= is % (blksize_snap / sizeof(lv_COW_table_disk_t)); memset(lv_COW_table, 0, blksize_snap); for ( ; is < lv_snap->lv_remap_ptr; is++, id++) { /* store new COW_table entry */ - lv_COW_table[id].pv_org_number = cpu_to_le64(lvm_pv_get_number(vg, lv_snap->lv_block_exception[is].rdev_org)); - lv_COW_table[id].pv_org_rsector = cpu_to_le64(lv_snap->lv_block_exception[is].rsector_org); - lv_COW_table[id].pv_snap_number = cpu_to_le64(lvm_pv_get_number(vg, lv_snap->lv_block_exception[is].rdev_new)); - lv_COW_table[id].pv_snap_rsector = cpu_to_le64(lv_snap->lv_block_exception[is].rsector_new); + lv_block_exception_t *be = lv_snap->lv_block_exception + is; + if(_pv_get_number(vg, be->rdev_org, &pvn)) + goto bad; + + lv_COW_table[id].pv_org_number = cpu_to_le64(pvn); + lv_COW_table[id].pv_org_rsector = cpu_to_le64(be->rsector_org); + if(_pv_get_number(vg, be->rdev_new, &pvn)) + goto bad; + + lv_COW_table[id].pv_snap_number = cpu_to_le64(pvn); + lv_COW_table[id].pv_snap_rsector = + cpu_to_le64(be->rsector_new); } + + return 0; + + bad: + printk(KERN_ERR "%s -- lvm_snapshot_fill_COW_page failed", lvm_name); + return -1; } /* * writes a COW exception table sector to disk (HM) - * */ -int lvm_write_COW_table_block(vg_t * vg, lv_t * lv_snap) +int lvm_write_COW_table_block(vg_t * vg, lv_t *lv_snap) { - int blksize_snap; - int end_of_table; - int idx = lv_snap->lv_remap_ptr, idx_COW_table; - int nr_pages_tmp; - int length_tmp; - ulong snap_pe_start, COW_table_sector_offset, - COW_entries_per_pe, COW_chunks_per_pe, COW_entries_per_block; - const char * reason; - kdev_t snap_phys_dev; - struct kiobuf * iobuf = lv_snap->lv_iobuf; - struct page * page_tmp; - lv_COW_table_disk_t * lv_COW_table = - ( lv_COW_table_disk_t *) page_address(lv_snap->lv_COW_table_page); - - idx--; - - COW_chunks_per_pe = LVM_GET_COW_TABLE_CHUNKS_PER_PE(vg, lv_snap); - COW_entries_per_pe = LVM_GET_COW_TABLE_ENTRIES_PER_PE(vg, lv_snap); - - /* get physical addresse of destination chunk */ - snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new; - snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size; - - blksize_snap = lvm_get_blksize(snap_phys_dev); - - COW_entries_per_block = blksize_snap / sizeof(lv_COW_table_disk_t); - idx_COW_table = idx % COW_entries_per_pe % COW_entries_per_block; - - if ( idx_COW_table == 0) memset(lv_COW_table, 0, blksize_snap); - - /* sector offset into the on disk COW table */ - COW_table_sector_offset = (idx % COW_entries_per_pe) / (SECTOR_SIZE / sizeof(lv_COW_table_disk_t)); - - /* COW table block to write next */ - iobuf->blocks[0] = (snap_pe_start + COW_table_sector_offset) >> (blksize_snap >> 10); - - /* store new COW_table entry */ - lv_COW_table[idx_COW_table].pv_org_number = cpu_to_le64(lvm_pv_get_number(vg, lv_snap->lv_block_exception[idx].rdev_org)); - lv_COW_table[idx_COW_table].pv_org_rsector = cpu_to_le64(lv_snap->lv_block_exception[idx].rsector_org); - lv_COW_table[idx_COW_table].pv_snap_number = cpu_to_le64(lvm_pv_get_number(vg, snap_phys_dev)); - lv_COW_table[idx_COW_table].pv_snap_rsector = cpu_to_le64(lv_snap->lv_block_exception[idx].rsector_new); - - length_tmp = iobuf->length; - iobuf->length = blksize_snap; - page_tmp = iobuf->maplist[0]; - iobuf->maplist[0] = lv_snap->lv_COW_table_page; - nr_pages_tmp = iobuf->nr_pages; - iobuf->nr_pages = 1; - - if (brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev, - iobuf->blocks, blksize_snap) != blksize_snap) - goto fail_raw_write; - - - /* initialization of next COW exception table block with zeroes */ - end_of_table = idx % COW_entries_per_pe == COW_entries_per_pe - 1; - if (idx_COW_table % COW_entries_per_block == COW_entries_per_block - 1 || end_of_table) - { - /* don't go beyond the end */ - if (idx + 1 >= lv_snap->lv_remap_end) goto good_out; - - memset(lv_COW_table, 0, blksize_snap); - - if (end_of_table) - { - idx++; - snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new; - snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size; - blksize_snap = lvm_get_blksize(snap_phys_dev); - iobuf->blocks[0] = snap_pe_start >> (blksize_snap >> 10); - } else iobuf->blocks[0]++; - - if (brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev, - iobuf->blocks, blksize_snap) != blksize_snap) - goto fail_raw_write; - } - - - good_out: - iobuf->length = length_tmp; - iobuf->maplist[0] = page_tmp; - iobuf->nr_pages = nr_pages_tmp; - return 0; - - /* slow path */ - out: - lvm_drop_snapshot(lv_snap, reason); - return 1; - - fail_raw_write: - reason = "write error"; - goto out; + int r; + const char *err; + if((r = _write_COW_table_block(vg, lv_snap, + lv_snap->lv_remap_ptr - 1, &err))) + lvm_drop_snapshot(vg, lv_snap, err); + return r; } /* @@ -345,7 +310,7 @@ unsigned long org_phys_sector, unsigned long org_pe_start, unsigned long org_virt_sector, - lv_t * lv_snap) + vg_t *vg, lv_t* lv_snap) { const char * reason; unsigned long org_start, snap_start, snap_phys_dev, virt_start, pe_off; @@ -370,13 +335,11 @@ #ifdef DEBUG_SNAPSHOT printk(KERN_INFO "%s -- COW: " - "org %02d:%02d faulting %lu start %lu, " - "snap %02d:%02d start %lu, " + "org %s faulting %lu start %lu, snap %s start %lu, " "size %d, pe_start %lu pe_off %lu, virt_sec %lu\n", lvm_name, - MAJOR(org_phys_dev), MINOR(org_phys_dev), org_phys_sector, - org_start, - MAJOR(snap_phys_dev), MINOR(snap_phys_dev), snap_start, + kdevname(org_phys_dev), org_phys_sector, org_start, + kdevname(snap_phys_dev), snap_start, chunk_size, org_pe_start, pe_off, org_virt_sector); @@ -400,14 +363,18 @@ iobuf->length = nr_sectors << 9; - lvm_snapshot_prepare_blocks(iobuf->blocks, org_start, - nr_sectors, blksize_org); + if(!lvm_snapshot_prepare_blocks(iobuf->blocks, org_start, + nr_sectors, blksize_org)) + goto fail_prepare; + if (brw_kiovec(READ, 1, &iobuf, org_phys_dev, iobuf->blocks, blksize_org) != (nr_sectors<<9)) goto fail_raw_read; - lvm_snapshot_prepare_blocks(iobuf->blocks, snap_start, - nr_sectors, blksize_snap); + if(!lvm_snapshot_prepare_blocks(iobuf->blocks, snap_start, + nr_sectors, blksize_snap)) + goto fail_prepare; + if (brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev, iobuf->blocks, blksize_snap) != (nr_sectors<<9)) goto fail_raw_write; @@ -435,7 +402,7 @@ /* slow path */ out: - lvm_drop_snapshot(lv_snap, reason); + lvm_drop_snapshot(vg, lv_snap, reason); return 1; fail_out_of_space: @@ -450,20 +417,24 @@ fail_blksize: reason = "blocksize error"; goto out; + + fail_prepare: + reason = "couldn't prepare kiovec blocks " + "(start probably isn't block aligned)"; + goto out; } int lvm_snapshot_alloc_iobuf_pages(struct kiobuf * iobuf, int sectors) { int bytes, nr_pages, err, i; - bytes = sectors << 9; + bytes = sectors * SECTOR_SIZE; nr_pages = (bytes + ~PAGE_MASK) >> PAGE_SHIFT; err = expand_kiobuf(iobuf, nr_pages); - if (err) - goto out; + if (err) goto out; err = -ENOMEM; - iobuf->locked = 0; + iobuf->locked = 1; iobuf->nr_pages = 0; for (i = 0; i < nr_pages; i++) { @@ -474,6 +445,7 @@ goto out; iobuf->maplist[i] = page; + LockPage(page); iobuf->nr_pages++; } iobuf->offset = 0; @@ -521,47 +493,58 @@ while (buckets--) INIT_LIST_HEAD(hash+buckets); err = 0; - out: +out: return err; } int lvm_snapshot_alloc(lv_t * lv_snap) { - int err, blocksize, max_sectors; + int ret, max_sectors; + int nbhs = KIO_MAX_SECTORS; - err = alloc_kiovec(1, &lv_snap->lv_iobuf); - if (err) - goto out; + /* allocate kiovec to do chunk io */ + ret = alloc_kiovec_sz(1, &lv_snap->lv_iobuf, &nbhs); + if (ret) goto out; - blocksize = lvm_blocksizes[MINOR(lv_snap->lv_dev)]; max_sectors = KIO_MAX_SECTORS << (PAGE_SHIFT-9); - err = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_iobuf, max_sectors); - if (err) - goto out_free_kiovec; - - err = lvm_snapshot_alloc_hash_table(lv_snap); - if (err) - goto out_free_kiovec; + ret = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_iobuf, max_sectors); + if (ret) goto out_free_kiovec; + /* allocate kiovec to do exception table io */ + ret = alloc_kiovec_sz(1, &lv_snap->lv_COW_table_iobuf, &nbhs); + if (ret) goto out_free_kiovec; - lv_snap->lv_COW_table_page = alloc_page(GFP_KERNEL); - if (!lv_snap->lv_COW_table_page) - goto out_free_kiovec; + ret = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_COW_table_iobuf, + PAGE_SIZE/SECTOR_SIZE); + if (ret) goto out_free_both_kiovecs; - out: - return err; + ret = lvm_snapshot_alloc_hash_table(lv_snap); + if (ret) goto out_free_both_kiovecs; + + +out: + return ret; + +out_free_both_kiovecs: + unmap_kiobuf(lv_snap->lv_COW_table_iobuf); + free_kiovec_sz(1, &lv_snap->lv_COW_table_iobuf, &nbhs); + lv_snap->lv_COW_table_iobuf = NULL; - out_free_kiovec: +out_free_kiovec: unmap_kiobuf(lv_snap->lv_iobuf); - free_kiovec(1, &lv_snap->lv_iobuf); - vfree(lv_snap->lv_snapshot_hash_table); + free_kiovec_sz(1, &lv_snap->lv_iobuf, &nbhs); + lv_snap->lv_iobuf = NULL; + if (lv_snap->lv_snapshot_hash_table != NULL) + vfree(lv_snap->lv_snapshot_hash_table); lv_snap->lv_snapshot_hash_table = NULL; goto out; } void lvm_snapshot_release(lv_t * lv) { + int nbhs = KIO_MAX_SECTORS; + if (lv->lv_block_exception) { vfree(lv->lv_block_exception); @@ -577,12 +560,129 @@ { kiobuf_wait_for_io(lv->lv_iobuf); unmap_kiobuf(lv->lv_iobuf); - free_kiovec(1, &lv->lv_iobuf); + free_kiovec_sz(1, &lv->lv_iobuf, &nbhs); lv->lv_iobuf = NULL; } - if (lv->lv_COW_table_page) + if (lv->lv_COW_table_iobuf) { - free_page((ulong)lv->lv_COW_table_page); - lv->lv_COW_table_page = NULL; + kiobuf_wait_for_io(lv->lv_COW_table_iobuf); + unmap_kiobuf(lv->lv_COW_table_iobuf); + free_kiovec_sz(1, &lv->lv_COW_table_iobuf, &nbhs); + lv->lv_COW_table_iobuf = NULL; } } + + +static int _write_COW_table_block(vg_t *vg, lv_t *lv_snap, + int idx, const char **reason) { + int blksize_snap; + int end_of_table; + int idx_COW_table; + uint pvn; + ulong snap_pe_start, COW_table_sector_offset, + COW_entries_per_pe, COW_chunks_per_pe, COW_entries_per_block; + ulong blocks[1]; + kdev_t snap_phys_dev; + lv_block_exception_t *be; + struct kiobuf * COW_table_iobuf = lv_snap->lv_COW_table_iobuf; + lv_COW_table_disk_t * lv_COW_table = + ( lv_COW_table_disk_t *) page_address(lv_snap->lv_COW_table_iobuf->maplist[0]); + + COW_chunks_per_pe = LVM_GET_COW_TABLE_CHUNKS_PER_PE(vg, lv_snap); + COW_entries_per_pe = LVM_GET_COW_TABLE_ENTRIES_PER_PE(vg, lv_snap); + + /* get physical addresse of destination chunk */ + snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new; + snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size; + + blksize_snap = lvm_get_blksize(snap_phys_dev); + + COW_entries_per_block = blksize_snap / sizeof(lv_COW_table_disk_t); + idx_COW_table = idx % COW_entries_per_pe % COW_entries_per_block; + + if ( idx_COW_table == 0) memset(lv_COW_table, 0, blksize_snap); + + /* sector offset into the on disk COW table */ + COW_table_sector_offset = (idx % COW_entries_per_pe) / (SECTOR_SIZE / sizeof(lv_COW_table_disk_t)); + + /* COW table block to write next */ + blocks[0] = (snap_pe_start + COW_table_sector_offset) >> (blksize_snap >> 10); + + /* store new COW_table entry */ + be = lv_snap->lv_block_exception + idx; + if(_pv_get_number(vg, be->rdev_org, &pvn)) + goto fail_pv_get_number; + + lv_COW_table[idx_COW_table].pv_org_number = cpu_to_le64(pvn); + lv_COW_table[idx_COW_table].pv_org_rsector = + cpu_to_le64(be->rsector_org); + if(_pv_get_number(vg, snap_phys_dev, &pvn)) + goto fail_pv_get_number; + + lv_COW_table[idx_COW_table].pv_snap_number = cpu_to_le64(pvn); + lv_COW_table[idx_COW_table].pv_snap_rsector = + cpu_to_le64(be->rsector_new); + + COW_table_iobuf->length = blksize_snap; + + if (brw_kiovec(WRITE, 1, &COW_table_iobuf, snap_phys_dev, + blocks, blksize_snap) != blksize_snap) + goto fail_raw_write; + + /* initialization of next COW exception table block with zeroes */ + end_of_table = idx % COW_entries_per_pe == COW_entries_per_pe - 1; + if (idx_COW_table % COW_entries_per_block == COW_entries_per_block - 1 || end_of_table) + { + /* don't go beyond the end */ + if (idx + 1 >= lv_snap->lv_remap_end) goto out; + + memset(lv_COW_table, 0, blksize_snap); + + if (end_of_table) + { + idx++; + snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new; + snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size; + blksize_snap = lvm_get_blksize(snap_phys_dev); + blocks[0] = snap_pe_start >> (blksize_snap >> 10); + } else blocks[0]++; + + if (brw_kiovec(WRITE, 1, &COW_table_iobuf, snap_phys_dev, + blocks, blksize_snap) != + blksize_snap) + goto fail_raw_write; + } + + out: + return 0; + + fail_raw_write: + *reason = "write error"; + return 1; + + fail_pv_get_number: + *reason = "_pv_get_number failed"; + return 1; +} + +/* + * FIXME_1.2 + * This function is a bit of a hack; we need to ensure that the + * snapshot is never made active again, because it will surely be + * corrupt. At the moment we do not have access to the LVM metadata + * from within the kernel. So we set the first exception to point to + * sector 1 (which will always be within the metadata, and as such + * invalid). User land tools will check for this when they are asked + * to activate the snapshot and prevent this from happening. + */ + +static void _disable_snapshot(vg_t *vg, lv_t *lv) { + const char *err; + lv->lv_block_exception[0].rsector_org = LVM_SNAPSHOT_DROPPED_SECTOR; + if(_write_COW_table_block(vg, lv, 0, &err) < 0) { + printk(KERN_ERR "%s -- couldn't disable snapshot: %s\n", + lvm_name, err); + } +} + +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/md/lvm-snap.h linux.ac/drivers/md/lvm-snap.h --- linux.vanilla/drivers/md/lvm-snap.h Mon Jan 29 00:11:20 2001 +++ linux.ac/drivers/md/lvm-snap.h Thu Jan 1 01:00:00 1970 @@ -1,47 +0,0 @@ -/* - * kernel/lvm-snap.h - * - * Copyright (C) 2001 Sistina Software - * - * - * LVM driver is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * LVM driver 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 GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - */ - -/* - * Changelog - * - * 05/01/2001:Joe Thornber - Factored this file out of lvm.c - * - */ - -#ifndef LVM_SNAP_H -#define LVM_SNAP_H - -/* external snapshot calls */ -extern inline int lvm_get_blksize(kdev_t); -extern int lvm_snapshot_alloc(lv_t *); -extern void lvm_snapshot_fill_COW_page(vg_t *, lv_t *); -extern int lvm_snapshot_COW(kdev_t, ulong, ulong, ulong, lv_t *); -extern int lvm_snapshot_remap_block(kdev_t *, ulong *, ulong, lv_t *); -extern void lvm_snapshot_release(lv_t *); -extern int lvm_write_COW_table_block(vg_t *, lv_t *); -extern inline void lvm_hash_link(lv_block_exception_t *, - kdev_t, ulong, lv_t *); -extern int lvm_snapshot_alloc_hash_table(lv_t *); -extern void lvm_drop_snapshot(lv_t *, const char *); - -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/md/lvm.c linux.ac/drivers/md/lvm.c --- linux.vanilla/drivers/md/lvm.c Mon Sep 17 21:25:26 2001 +++ linux.ac/drivers/md/lvm.c Wed Oct 10 01:47:55 2001 @@ -147,25 +147,51 @@ * 08/01/2001 - Removed conditional compiles related to PROC_FS, * procfs is always supported now. (JT) * 12/01/2001 - avoided flushing logical volume in case of shrinking - * because of unecessary overhead in case of heavy updates + * because of unnecessary overhead in case of heavy updates + * 25/01/2001 - Allow RO open of an inactive LV so it can be reactivated. + * 31/01/2001 - If you try and BMAP a snapshot you now get an -EPERM + * 01/02/2001 - factored __remap_snapshot out of lvm_map + * 12/02/2001 - move devfs code to create VG before LVs + * 14/02/2001 - tidied device defines for blk.h + * - tidied debug statements + * - more lvm_map tidying + * 14/02/2001 - bug: vg[] member not set back to NULL if activation fails + * 28/02/2001 - introduced the P_DEV macro and changed some internel + * functions to be static [AD] + * 28/02/2001 - factored lvm_get_snapshot_use_rate out of blk_ioctl [AD] + * - fixed user address accessing bug in lvm_do_lv_create() + * where the check for an existing LV takes place right at + * the beginning + * 01/03/2001 - Add VG_CREATE_OLD for IOP 10 compatibility + * 02/03/2001 - Don't destroy usermode pointers in lv_t structures duing LV_ + * STATUS_BYxxx and remove redundant lv_t variables from same. + * 05/03/2001 - restore copying pe_t array in lvm_do_lv_status_byname. For + * lvdisplay -v (PC) + * - restore copying pe_t array in lvm_do_lv_status_byindex (HM) + * - added copying pe_t array in lvm_do_lv_status_bydev (HM) + * - enhanced lvm_do_lv_status_by{name,index,dev} to be capable + * to copy the lv_block_exception_t array to userspace (HM) + * 08/03/2001 - factored lvm_do_pv_flush out of lvm_chr_ioctl [HM] + * 09/03/2001 - Added _lock_open_count to ensure we only drop the lock + * when the locking process closes. * 05/04/2001 - lvm_map bugs: don't use b_blocknr/b_dev in lvm_map, it * destroys stacking devices. call b_end_io on failed maps. * (Jens Axboe) + * - Defer writes to an extent that is being moved [JT + AD] + * 28/05/2001 - implemented missing BLKSSZGET ioctl [AD] * */ -static char *lvm_version = "LVM version 0.9.1_beta2 by Heinz Mauelshagen (18/01/2001)\n"; -static char *lvm_short_version = "version 0.9.1_beta2 (18/01/2001)"; - -#define MAJOR_NR LVM_BLK_MAJOR -#define DEVICE_OFF(device) +#define MAJOR_NR LVM_BLK_MAJOR +#define DEVICE_OFF(device) +#define LOCAL_END_REQUEST /* lvm_do_lv_create calls fsync_dev_lockfs()/unlockfs() */ /* #define LVM_VFS_ENHANCEMENT */ #include -#include + #include #include @@ -180,6 +206,7 @@ #include #include #include +#include #include #include #include @@ -195,38 +222,16 @@ #include #include -#include "lvm-snap.h" +#include "lvm-internal.h" -#define LVM_CORRECT_READ_AHEAD(a) \ -do { \ - if ((a) < LVM_MIN_READ_AHEAD || \ - (a) > LVM_MAX_READ_AHEAD) \ - (a) = LVM_DEFAULT_READ_AHEAD; \ - read_ahead[MAJOR_NR] = (a); \ -} while(0) +#define LVM_CORRECT_READ_AHEAD( a) \ + if ( a < LVM_MIN_READ_AHEAD || \ + a > LVM_MAX_READ_AHEAD) a = LVM_MAX_READ_AHEAD; #ifndef WRITEA # define WRITEA WRITE #endif -/* debug macros */ -#ifdef DEBUG_IOCTL -#define P_IOCTL(fmt, args...) printk(KERN_DEBUG "lvm ioctl: " fmt, ## args) -#else -#define P_IOCTL(fmt, args...) -#endif - -#ifdef DEBUG_MAP -#define P_MAP(fmt, args...) printk(KERN_DEBUG "lvm map: " fmt, ## args) -#else -#define P_MAP(fmt, args...) -#endif - -#ifdef DEBUG_KFREE -#define P_KFREE(fmt, args...) printk(KERN_DEBUG "lvm kfree: " fmt, ## args) -#else -#define P_KFREE(fmt, args...) -#endif /* * External function prototypes @@ -236,27 +241,14 @@ static int lvm_blk_ioctl(struct inode *, struct file *, uint, ulong); static int lvm_blk_open(struct inode *, struct file *); -static int lvm_chr_open(struct inode *, struct file *); - -static int lvm_chr_close(struct inode *, struct file *); static int lvm_blk_close(struct inode *, struct file *); +static int lvm_get_snapshot_use_rate(lv_t *lv_ptr, void *arg); static int lvm_user_bmap(struct inode *, struct lv_bmap *); +static int lvm_chr_open(struct inode *, struct file *); +static int lvm_chr_close(struct inode *, struct file *); static int lvm_chr_ioctl(struct inode *, struct file *, uint, ulong); -int lvm_proc_read_vg_info(char *, char **, off_t, int, int *, void *); -int lvm_proc_read_lv_info(char *, char **, off_t, int, int *, void *); -int lvm_proc_read_pv_info(char *, char **, off_t, int, int *, void *); -static int lvm_proc_get_global_info(char *, char **, off_t, int, int *, void *); - -void lvm_do_create_devfs_entry_of_vg ( vg_t *); - -void lvm_do_create_proc_entry_of_vg ( vg_t *); -void lvm_do_remove_proc_entry_of_vg ( vg_t *); -void lvm_do_create_proc_entry_of_lv ( vg_t *, lv_t *); -void lvm_do_remove_proc_entry_of_lv ( vg_t *, lv_t *); -void lvm_do_create_proc_entry_of_pv ( vg_t *, pv_t *); -void lvm_do_remove_proc_entry_of_pv ( vg_t *, pv_t *); /* End external function prototypes */ @@ -288,33 +280,45 @@ static int lvm_do_pv_change(vg_t*, void*); static int lvm_do_pv_status(vg_t *, void *); +static int lvm_do_pv_flush(void *); -static int lvm_do_vg_create(int, void *); +static int lvm_do_vg_create(void *, int minor); static int lvm_do_vg_extend(vg_t *, void *); static int lvm_do_vg_reduce(vg_t *, void *); static int lvm_do_vg_rename(vg_t *, void *); static int lvm_do_vg_remove(int); static void lvm_geninit(struct gendisk *); -static char *lvm_show_uuid ( char *); +static void __update_hardsectsize(lv_t *lv); + + +static void _queue_io(struct buffer_head *bh, int rw); +static struct buffer_head *_dequeue_io(void); +static void _flush_io(struct buffer_head *bh); + +static int _open_pv(pv_t *pv); +static void _close_pv(pv_t *pv); + +static unsigned long _sectors_to_k(unsigned long sect); + #ifdef LVM_HD_NAME void lvm_hd_name(char *, int); #endif /* END Internal function prototypes */ -/* volume group descriptor area pointers */ -static vg_t *vg[ABS_MAX_VG]; +/* variables */ +char *lvm_version = "LVM version "LVM_RELEASE_NAME"("LVM_RELEASE_DATE")"; +ushort lvm_iop_version = LVM_DRIVER_IOP_VERSION; +int loadtime = 0; +const char *const lvm_name = LVM_NAME; + -static devfs_handle_t lvm_devfs_handle; -static devfs_handle_t vg_devfs_handle[MAX_VG]; -static devfs_handle_t ch_devfs_handle[MAX_VG]; -static devfs_handle_t lv_devfs_handle[MAX_LV]; +/* volume group descriptor area pointers */ +vg_t *vg[ABS_MAX_VG]; static pv_t *pvp = NULL; static lv_t *lvp = NULL; static pe_t *pep = NULL; -static pe_t *pep1 = NULL; -static char *basename = NULL; /* map from block minor number to VG and LV numbers */ @@ -327,9 +331,8 @@ /* Request structures (lvm_chr_ioctl()) */ static pv_change_req_t pv_change_req; -static pv_flush_req_t pv_flush_req; static pv_status_req_t pv_status_req; -static pe_lock_req_t pe_lock_req; +volatile static pe_lock_req_t pe_lock_req; static le_remap_req_t le_remap_req; static lv_req_t lv_req; @@ -339,32 +342,27 @@ static char pv_name[NAME_LEN]; /* static char rootvg[NAME_LEN] = { 0, }; */ -const char *const lvm_name = LVM_NAME; static int lock = 0; -static int loadtime = 0; +static int _lock_open_count = 0; static uint vg_count = 0; static long lvm_chr_open_count = 0; -static ushort lvm_iop_version = LVM_DRIVER_IOP_VERSION; static DECLARE_WAIT_QUEUE_HEAD(lvm_wait); -static DECLARE_WAIT_QUEUE_HEAD(lvm_map_wait); static spinlock_t lvm_lock = SPIN_LOCK_UNLOCKED; static spinlock_t lvm_snapshot_lock = SPIN_LOCK_UNLOCKED; -static struct proc_dir_entry *lvm_proc_dir = NULL; -static struct proc_dir_entry *lvm_proc_vg_subdir = NULL; -struct proc_dir_entry *pde = NULL; +static struct buffer_head *_pe_requests; +static DECLARE_RWSEM(_pe_lock); -static struct file_operations lvm_chr_fops = -{ + +struct file_operations lvm_chr_fops = { open: lvm_chr_open, release: lvm_chr_close, ioctl: lvm_chr_ioctl, }; - /* block device operations structure needed for 2.3.38? and above */ -static struct block_device_operations lvm_blk_dops = +struct block_device_operations lvm_blk_dops = { open: lvm_blk_open, release: lvm_blk_close, @@ -374,10 +372,10 @@ /* gendisk structures */ static struct hd_struct lvm_hd_struct[MAX_LV]; -static int lvm_blocksizes[MAX_LV] = -{0,}; -static int lvm_size[MAX_LV] = -{0,}; +static int lvm_blocksizes[MAX_LV]; +static int lvm_hardsectsizes[MAX_LV]; +static int lvm_size[MAX_LV]; + static struct gendisk lvm_gendisk = { major: MAJOR_NR, @@ -394,30 +392,24 @@ */ int lvm_init(void) { - if (register_chrdev(LVM_CHAR_MAJOR, lvm_name, &lvm_chr_fops) < 0) { - printk(KERN_ERR "%s -- register_chrdev failed\n", lvm_name); + if (devfs_register_chrdev(LVM_CHAR_MAJOR, + lvm_name, &lvm_chr_fops) < 0) { + printk(KERN_ERR "%s -- devfs_register_chrdev failed\n", + lvm_name); return -EIO; } - if (register_blkdev(MAJOR_NR, lvm_name, &lvm_blk_dops) < 0) + + if (devfs_register_blkdev(MAJOR_NR, lvm_name, &lvm_blk_dops) < 0) { - printk("%s -- register_blkdev failed\n", lvm_name); - if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0) - printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name); + printk("%s -- devfs_register_blkdev failed\n", lvm_name); + if (devfs_unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0) + printk(KERN_ERR + "%s -- devfs_unregister_chrdev failed\n", + lvm_name); return -EIO; } - lvm_devfs_handle = devfs_register( - 0 , "lvm", 0, 0, LVM_CHAR_MAJOR, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, - &lvm_chr_fops, NULL); - - lvm_proc_dir = create_proc_entry (LVM_DIR, S_IFDIR, &proc_root); - if (lvm_proc_dir != NULL) { - lvm_proc_vg_subdir = create_proc_entry (LVM_VG_SUBDIR, S_IFDIR, lvm_proc_dir); - pde = create_proc_entry(LVM_GLOBAL, S_IFREG, lvm_proc_dir); - if ( pde != NULL) pde->read_proc = &lvm_proc_get_global_info; - } - + lvm_init_fs(); lvm_init_vars(); lvm_geninit(&lvm_gendisk); @@ -431,20 +423,19 @@ blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), lvm_make_request_fn); + /* initialise the pe lock */ + pe_lock_req.lock = UNLOCK_PE; + /* optional read root VGDA */ /* if ( *rootvg != 0) vg_read_with_pv_and_lv ( rootvg, &vg); */ - printk(KERN_INFO - "%s%s -- " #ifdef MODULE - "Module" + printk(KERN_INFO "%s module loaded\n", lvm_version); #else - "Driver" + printk(KERN_INFO "%s\n", lvm_version); #endif - " successfully initialized\n", - lvm_version, lvm_name); return 0; } /* lvm_init() */ @@ -455,15 +446,12 @@ */ static void lvm_cleanup(void) { - devfs_unregister (lvm_devfs_handle); - - if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0) { - printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name); - } - if (unregister_blkdev(MAJOR_NR, lvm_name) < 0) { - printk(KERN_ERR "%s -- unregister_blkdev failed\n", lvm_name); - } - + if (devfs_unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0) + printk(KERN_ERR "%s -- devfs_unregister_chrdev failed\n", + lvm_name); + if (devfs_unregister_blkdev(MAJOR_NR, lvm_name) < 0) + printk(KERN_ERR "%s -- devfs_unregister_blkdev failed\n", + lvm_name); del_gendisk(&lvm_gendisk); @@ -471,15 +459,14 @@ blksize_size[MAJOR_NR] = NULL; hardsect_size[MAJOR_NR] = NULL; - remove_proc_entry(LVM_GLOBAL, lvm_proc_dir); - remove_proc_entry(LVM_VG_SUBDIR, lvm_proc_dir); - remove_proc_entry(LVM_DIR, &proc_root); - #ifdef LVM_HD_NAME /* reference from linux/drivers/block/genhd.c */ lvm_hd_name_ptr = NULL; #endif + /* unregister with procfs and devfs */ + lvm_fin_fs(); + printk(KERN_INFO "%s -- Module successfully deactivated\n", lvm_name); return; @@ -489,7 +476,7 @@ /* * support function to initialize lvm variables */ -void __init lvm_init_vars(void) +static void __init lvm_init_vars(void) { int v; @@ -498,8 +485,8 @@ lvm_lock = lvm_snapshot_lock = SPIN_LOCK_UNLOCKED; pe_lock_req.lock = UNLOCK_PE; - pe_lock_req.data.lv_dev = \ - pe_lock_req.data.pv_dev = \ + pe_lock_req.data.lv_dev = 0; + pe_lock_req.data.pv_dev = 0; pe_lock_req.data.pv_offset = 0; /* Initialize VG pointers */ @@ -522,19 +509,18 @@ * ********************************************************************/ +#define MODE_TO_STR(mode) (mode) & FMODE_READ ? "READ" : "", \ + (mode) & FMODE_WRITE ? "WRITE" : "" + /* * character device open routine */ -static int lvm_chr_open(struct inode *inode, - struct file *file) +static int lvm_chr_open(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + unsigned int minor = MINOR(inode->i_rdev); -#ifdef DEBUG - printk(KERN_DEBUG - "%s -- lvm_chr_open MINOR: %d VG#: %d mode: 0x%X lock: %d\n", - lvm_name, minor, VG_CHR(minor), file->f_mode, lock); -#endif + P_DEV("chr_open MINOR: %d VG#: %d mode: %s%s lock: %d\n", + minor, VG_CHR(minor), MODE_TO_STR(file->f_mode), lock); /* super user validation */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -542,6 +528,11 @@ /* Group special file open */ if (VG_CHR(minor) > MAX_VG) return -ENXIO; + spin_lock(&lvm_lock); + if(lock == current->pid) + _lock_open_count++; + spin_unlock(&lvm_lock); + lvm_chr_open_count++; MOD_INC_USE_COUNT; @@ -558,7 +549,7 @@ * */ static int lvm_chr_ioctl(struct inode *inode, struct file *file, - uint command, ulong a) + uint command, ulong a) { int minor = MINOR(inode->i_rdev); uint extendable, l, v; @@ -569,9 +560,8 @@ /* otherwise cc will complain about unused variables */ (void) lvm_lock; - P_IOCTL("%s -- lvm_chr_ioctl: command: 0x%X MINOR: %d " - "VG#: %d mode: 0x%X\n", - lvm_name, command, minor, VG_CHR(minor), file->f_mode); + P_IOCTL("chr MINOR: %d command: 0x%X arg: %p VG#: %d mode: %s%s\n", + minor, command, arg, VG_CHR(minor), MODE_TO_STR(file->f_mode)); #ifdef LVM_TOTAL_RESET if (lvm_reset_spindown > 0) return -EACCES; @@ -619,9 +609,13 @@ physical volume (move's done in user space's pvmove) */ return lvm_do_pe_lock_unlock(vg_ptr,arg); - case VG_CREATE: + case VG_CREATE_OLD: /* create a VGDA */ - return lvm_do_vg_create(minor, arg); + return lvm_do_vg_create(arg, minor); + + case VG_CREATE: + /* create a VGDA, assume VG number is filled in */ + return lvm_do_vg_create(arg, -1); case VG_EXTEND: /* extend a volume group */ @@ -672,7 +666,7 @@ case VG_STATUS_GET_NAMELIST: - /* get volume group count */ + /* get volume group names */ for (l = v = 0; v < ABS_MAX_VG; v++) { if (vg[v] != NULL) { if (copy_to_user(arg + l * NAME_LEN, @@ -727,6 +721,7 @@ case LV_STATUS_BYDEV: + /* get status of a logical volume by device */ return lvm_do_lv_status_bydev(vg_ptr, arg); @@ -742,18 +737,12 @@ case PV_FLUSH: /* physical volume buffer flush/invalidate */ - if (copy_from_user(&pv_flush_req, arg, - sizeof(pv_flush_req)) != 0) - return -EFAULT; - - fsync_dev(pv_flush_req.pv_dev); - invalidate_buffers(pv_flush_req.pv_dev); - return 0; + return lvm_do_pv_flush(arg); default: printk(KERN_WARNING - "%s -- lvm_chr_ioctl: unknown command %x\n", + "%s -- lvm_chr_ioctl: unknown command 0x%x\n", lvm_name, command); return -EINVAL; } @@ -767,11 +756,8 @@ */ static int lvm_chr_close(struct inode *inode, struct file *file) { -#ifdef DEBUG - int minor = MINOR(inode->i_rdev); - printk(KERN_DEBUG - "%s -- lvm_chr_close VG#: %d\n", lvm_name, VG_CHR(minor)); -#endif + P_DEV("chr_close MINOR: %d VG#: %d\n", + MINOR(inode->i_rdev), VG_CHR(MINOR(inode->i_rdev))); #ifdef LVM_TOTAL_RESET if (lvm_reset_spindown > 0) { @@ -781,10 +767,17 @@ #endif if (lvm_chr_open_count > 0) lvm_chr_open_count--; - if (lock == current->pid) { - lock = 0; /* release lock */ - wake_up_interruptible(&lvm_wait); + + spin_lock(&lvm_lock); + if(lock == current->pid) { + if(!_lock_open_count) { + P_DEV("chr_close: unlocking LVM for pid %d\n", lock); + lock = 0; + wake_up_interruptible(&lvm_wait); + } else + _lock_open_count--; } + spin_unlock(&lvm_lock); MOD_DEC_USE_COUNT; @@ -808,11 +801,8 @@ lv_t *lv_ptr; vg_t *vg_ptr = vg[VG_BLK(minor)]; -#ifdef DEBUG_LVM_BLK_OPEN - printk(KERN_DEBUG - "%s -- lvm_blk_open MINOR: %d VG#: %d LV#: %d mode: 0x%X\n", - lvm_name, minor, VG_BLK(minor), LV_BLK(minor), file->f_mode); -#endif + P_DEV("blk_open MINOR: %d VG#: %d LV#: %d mode: %s%s\n", + minor, VG_BLK(minor), LV_BLK(minor), MODE_TO_STR(file->f_mode)); #ifdef LVM_TOTAL_RESET if (lvm_reset_spindown > 0) @@ -829,8 +819,12 @@ if (lv_ptr->lv_status & LV_SPINDOWN) return -EPERM; /* Check inactive LV and open for read/write */ - if (!(lv_ptr->lv_status & LV_ACTIVE)) - return -EPERM; + /* We need to be able to "read" an inactive LV + to re-activate it again */ + if ((file->f_mode & FMODE_WRITE) && + (!(lv_ptr->lv_status & LV_ACTIVE))) + return -EPERM; + if (!(lv_ptr->lv_access & LV_WRITE) && (file->f_mode & FMODE_WRITE)) return -EACCES; @@ -842,12 +836,7 @@ MOD_INC_USE_COUNT; -#ifdef DEBUG_LVM_BLK_OPEN - printk(KERN_DEBUG - "%s -- lvm_blk_open MINOR: %d VG#: %d LV#: %d size: %d\n", - lvm_name, minor, VG_BLK(minor), LV_BLK(minor), - lv_ptr->lv_size); -#endif + P_DEV("blk_open OK, LV size %d\n", lv_ptr->lv_size); return 0; } @@ -867,16 +856,18 @@ void *arg = (void *) a; struct hd_geometry *hd = (struct hd_geometry *) a; - P_IOCTL("%s -- lvm_blk_ioctl MINOR: %d command: 0x%X arg: %X " - "VG#: %dl LV#: %d\n", - lvm_name, minor, command, (ulong) arg, - VG_BLK(minor), LV_BLK(minor)); + P_IOCTL("blk MINOR: %d command: 0x%X arg: %p VG#: %d LV#: %d " + "mode: %s%s\n", minor, command, arg, VG_BLK(minor), + LV_BLK(minor), MODE_TO_STR(file->f_mode)); switch (command) { + case BLKSSZGET: + /* get block device sector size as needed e.g. by fdisk */ + return put_user(get_hardsect_size(inode->i_rdev), (int *) arg); + case BLKGETSIZE: /* return device size */ - P_IOCTL("%s -- lvm_blk_ioctl -- BLKGETSIZE: %u\n", - lvm_name, lv_ptr->lv_size); + P_IOCTL("BLKGETSIZE: %u\n", lv_ptr->lv_size); if (put_user(lv_ptr->lv_size, (long *)arg)) return -EFAULT; break; @@ -891,7 +882,7 @@ /* flush buffer cache */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; - P_IOCTL("%s -- lvm_blk_ioctl -- BLKFLSBUF\n", lvm_name); + P_IOCTL("BLKFLSBUF\n"); fsync_dev(inode->i_rdev); invalidate_buffers(inode->i_rdev); @@ -902,20 +893,19 @@ /* set read ahead for block device */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; - P_IOCTL("%s -- lvm_blk_ioctl -- BLKRASET: %d sectors for %02X:%02X\n", - lvm_name, (long) arg, MAJOR(inode->i_rdev), minor); + P_IOCTL("BLKRASET: %ld sectors for %s\n", + (long) arg, kdevname(inode->i_rdev)); if ((long) arg < LVM_MIN_READ_AHEAD || (long) arg > LVM_MAX_READ_AHEAD) return -EINVAL; lv_ptr->lv_read_ahead = (long) arg; - read_ahead[MAJOR_NR] = lv_ptr->lv_read_ahead; break; case BLKRAGET: /* get current read ahead setting */ - P_IOCTL("%s -- lvm_blk_ioctl -- BLKRAGET\n", lvm_name); + P_IOCTL("BLKRAGET %d\n", lv_ptr->lv_read_ahead); if (put_user(lv_ptr->lv_read_ahead, (long *)arg)) return -EFAULT; break; @@ -941,10 +931,10 @@ copy_to_user((long *) &hd->start, &start, sizeof(start)) != 0) return -EFAULT; - } - P_IOCTL("%s -- lvm_blk_ioctl -- cylinders: %d\n", - lvm_name, lv_ptr->lv_size / heads / sectors); + P_IOCTL("%s -- lvm_blk_ioctl -- cylinders: %d\n", + lvm_name, cylinders); + } break; @@ -968,6 +958,11 @@ break; case LV_BMAP: + /* turn logical block into (dev_t, block). non privileged. */ + /* don't bmap a snapshot, since the mapping can change */ + if(lv_ptr->lv_access & LV_SNAPSHOT) + return -EPERM; + /* turn logical block into (dev_t, block). non privileged. */ /* don't bmap a snapshot, since the mapping can change */ if (lv_ptr->lv_access & LV_SNAPSHOT) @@ -983,40 +978,11 @@ break; case LV_SNAPSHOT_USE_RATE: - if (!(lv_ptr->lv_access & LV_SNAPSHOT)) return -EPERM; - { - lv_snapshot_use_rate_req_t lv_snapshot_use_rate_req; - - if (copy_from_user(&lv_snapshot_use_rate_req, arg, - sizeof(lv_snapshot_use_rate_req_t))) - return -EFAULT; - if (lv_snapshot_use_rate_req.rate < 0 || - lv_snapshot_use_rate_req.rate > 100) return -EFAULT; - - switch (lv_snapshot_use_rate_req.block) - { - case 0: - lv_ptr->lv_snapshot_use_rate = lv_snapshot_use_rate_req.rate; - if (lv_ptr->lv_remap_ptr * 100 / lv_ptr->lv_remap_end < lv_ptr->lv_snapshot_use_rate) - interruptible_sleep_on (&lv_ptr->lv_snapshot_wait); - break; - - case O_NONBLOCK: - break; - - default: - return -EFAULT; - } - lv_snapshot_use_rate_req.rate = lv_ptr->lv_remap_ptr * 100 / lv_ptr->lv_remap_end; - if (copy_to_user(arg, &lv_snapshot_use_rate_req, - sizeof(lv_snapshot_use_rate_req_t))) - return -EFAULT; - } - break; + return lvm_get_snapshot_use_rate(lv_ptr, arg); default: printk(KERN_WARNING - "%s -- lvm_blk_ioctl: unknown command %d\n", + "%s -- lvm_blk_ioctl: unknown command 0x%x\n", lvm_name, command); return -EINVAL; } @@ -1034,11 +1000,8 @@ vg_t *vg_ptr = vg[VG_BLK(minor)]; lv_t *lv_ptr = vg_ptr->lv[LV_BLK(minor)]; -#ifdef DEBUG - printk(KERN_DEBUG - "%s -- lvm_blk_close MINOR: %d VG#: %d LV#: %d\n", - lvm_name, minor, VG_BLK(minor), LV_BLK(minor)); -#endif + P_DEV("blk_close MINOR: %d VG#: %d LV#: %d\n", + minor, VG_BLK(minor), LV_BLK(minor)); if (lv_ptr->lv_open == 1) vg_ptr->lv_open--; lv_ptr->lv_open--; @@ -1048,6 +1011,38 @@ return 0; } /* lvm_blk_close() */ +static int lvm_get_snapshot_use_rate(lv_t *lv, void *arg) +{ + lv_snapshot_use_rate_req_t lv_rate_req; + + if (!(lv->lv_access & LV_SNAPSHOT)) + return -EPERM; + + if (copy_from_user(&lv_rate_req, arg, sizeof(lv_rate_req))) + return -EFAULT; + + if (lv_rate_req.rate < 0 || lv_rate_req.rate > 100) + return -EINVAL; + + switch (lv_rate_req.block) { + case 0: + lv->lv_snapshot_use_rate = lv_rate_req.rate; + if (lv->lv_remap_ptr * 100 / lv->lv_remap_end < + lv->lv_snapshot_use_rate) + interruptible_sleep_on(&lv->lv_snapshot_wait); + break; + + case O_NONBLOCK: + break; + + default: + return -EINVAL; + } + lv_rate_req.rate = lv->lv_remap_ptr * 100 / lv->lv_remap_end; + + return copy_to_user(arg, &lv_rate_req, + sizeof(lv_rate_req)) ? -EFAULT : 0; +} static int lvm_user_bmap(struct inode *inode, struct lv_bmap *user_result) { @@ -1062,6 +1057,7 @@ bh.b_blocknr = block; bh.b_dev = bh.b_rdev = inode->i_rdev; bh.b_size = lvm_get_blksize(bh.b_dev); + bh.b_rsector = block * (bh.b_size >> 9); if ((err=lvm_map(&bh, READ)) < 0) { printk("lvm map failed: %d\n", err); return -EINVAL; @@ -1074,557 +1070,202 @@ /* - * provide VG info for proc filesystem use (global) + * block device support function for /usr/src/linux/drivers/block/ll_rw_blk.c + * (see init_module/lvm_init) */ -int lvm_vg_info(vg_t *vg_ptr, char *buf) { - int sz = 0; - char inactive_flag = ' '; - - if (!(vg_ptr->vg_status & VG_ACTIVE)) inactive_flag = 'I'; - sz = sprintf(buf, - "\nVG: %c%s [%d PV, %d LV/%d open] " - " PE Size: %d KB\n" - " Usage [KB/PE]: %d /%d total " - "%d /%d used %d /%d free", - inactive_flag, - vg_ptr->vg_name, - vg_ptr->pv_cur, - vg_ptr->lv_cur, - vg_ptr->lv_open, - vg_ptr->pe_size >> 1, - vg_ptr->pe_size * vg_ptr->pe_total >> 1, - vg_ptr->pe_total, - vg_ptr->pe_allocated * vg_ptr->pe_size >> 1, - vg_ptr->pe_allocated, - (vg_ptr->pe_total - vg_ptr->pe_allocated) * - vg_ptr->pe_size >> 1, - vg_ptr->pe_total - vg_ptr->pe_allocated); - return sz; -} +static void __remap_snapshot(kdev_t rdev, ulong rsector, + ulong pe_start, lv_t *lv, vg_t *vg) { + /* copy a chunk from the origin to a snapshot device */ + down_write(&lv->lv_lock); -/* - * provide LV info for proc filesystem use (global) - */ -int lvm_lv_info(vg_t *vg_ptr, lv_t *lv_ptr, char *buf) { - int sz = 0; - char inactive_flag = 'A', allocation_flag = ' ', - stripes_flag = ' ', rw_flag = ' '; - - if (!(lv_ptr->lv_status & LV_ACTIVE)) - inactive_flag = 'I'; - rw_flag = 'R'; - if (lv_ptr->lv_access & LV_WRITE) - rw_flag = 'W'; - allocation_flag = 'D'; - if (lv_ptr->lv_allocation & LV_CONTIGUOUS) - allocation_flag = 'C'; - stripes_flag = 'L'; - if (lv_ptr->lv_stripes > 1) - stripes_flag = 'S'; - sz += sprintf(buf+sz, - "[%c%c%c%c", - inactive_flag, - rw_flag, - allocation_flag, - stripes_flag); - if (lv_ptr->lv_stripes > 1) - sz += sprintf(buf+sz, "%-2d", - lv_ptr->lv_stripes); - else - sz += sprintf(buf+sz, " "); - basename = strrchr(lv_ptr->lv_name, '/'); - if ( basename == 0) basename = lv_ptr->lv_name; - else basename++; - sz += sprintf(buf+sz, "] %-25s", basename); - if (strlen(basename) > 25) - sz += sprintf(buf+sz, - "\n "); - sz += sprintf(buf+sz, "%9d /%-6d ", - lv_ptr->lv_size >> 1, - lv_ptr->lv_size / vg_ptr->pe_size); + /* we must redo lvm_snapshot_remap_block in order to avoid a + race condition in the gap where no lock was held */ + if (!lvm_snapshot_remap_block(&rdev, &rsector, pe_start, lv) && + !lvm_snapshot_COW(rdev, rsector, pe_start, rsector, vg, lv)) + lvm_write_COW_table_block(vg, lv); - if (lv_ptr->lv_open == 0) - sz += sprintf(buf+sz, "close"); - else - sz += sprintf(buf+sz, "%dx open", - lv_ptr->lv_open); - - return sz; + up_write(&lv->lv_lock); } - -/* - * provide PV info for proc filesystem use (global) - */ -int lvm_pv_info(pv_t *pv_ptr, char *buf) { - int sz = 0; - char inactive_flag = 'A', allocation_flag = ' '; - char *pv_name = NULL; - - if (!(pv_ptr->pv_status & PV_ACTIVE)) - inactive_flag = 'I'; - allocation_flag = 'A'; - if (!(pv_ptr->pv_allocatable & PV_ALLOCATABLE)) - allocation_flag = 'N'; - pv_name = strrchr(pv_ptr->pv_name+1,'/'); - if ( pv_name == 0) pv_name = pv_ptr->pv_name; - else pv_name++; - sz = sprintf(buf, - "[%c%c] %-21s %8d /%-6d " - "%8d /%-6d %8d /%-6d", - inactive_flag, - allocation_flag, - pv_name, - pv_ptr->pe_total * - pv_ptr->pe_size >> 1, - pv_ptr->pe_total, - pv_ptr->pe_allocated * - pv_ptr->pe_size >> 1, - pv_ptr->pe_allocated, - (pv_ptr->pe_total - - pv_ptr->pe_allocated) * - pv_ptr->pe_size >> 1, - pv_ptr->pe_total - - pv_ptr->pe_allocated); - return sz; +static inline void _remap_snapshot(kdev_t rdev, ulong rsector, + ulong pe_start, lv_t *lv, vg_t *vg) { + int r; + + /* check to see if this chunk is already in the snapshot */ + down_read(&lv->lv_lock); + r = lvm_snapshot_remap_block(&rdev, &rsector, pe_start, lv); + up_read(&lv->lv_lock); + + if (!r) + /* we haven't yet copied this block to the snapshot */ + __remap_snapshot(rdev, rsector, pe_start, lv, vg); } /* - * Support functions /proc-Filesystem + * extents destined for a pe that is on the move should be deferred */ +static inline int _should_defer(kdev_t pv, ulong sector, uint32_t pe_size) { + return ((pe_lock_req.lock == LOCK_PE) && + (pv == pe_lock_req.data.pv_dev) && + (sector >= pe_lock_req.data.pv_offset) && + (sector < (pe_lock_req.data.pv_offset + pe_size))); +} -#define LVM_PROC_BUF ( i == 0 ? dummy_buf : &buf[sz]) - -/* - * provide global LVM information - */ -static int lvm_proc_get_global_info(char *page, char **start, off_t pos, int count, int *eof, void *data) +static inline int _defer_extent(struct buffer_head *bh, int rw, + kdev_t pv, ulong sector, uint32_t pe_size) { - int c, i, l, p, v, vg_counter, pv_counter, lv_counter, lv_open_counter, - lv_open_total, pe_t_bytes, hash_table_bytes, lv_block_exception_t_bytes, seconds; - static off_t sz; - off_t sz_last; - static char *buf = NULL; - static char dummy_buf[160]; /* sized for 2 lines */ - vg_t *vg_ptr; - lv_t *lv_ptr; - pv_t *pv_ptr; - - -#ifdef DEBUG_LVM_PROC_GET_INFO - printk(KERN_DEBUG - "%s - lvm_proc_get_global_info CALLED pos: %lu count: %d whence: %d\n", - lvm_name, pos, count, whence); -#endif - - MOD_INC_USE_COUNT; - - if (pos == 0 || buf == NULL) { - sz_last = vg_counter = pv_counter = lv_counter = lv_open_counter = \ - lv_open_total = pe_t_bytes = hash_table_bytes = \ - lv_block_exception_t_bytes = 0; - - /* search for activity */ - for (v = 0; v < ABS_MAX_VG; v++) { - if ((vg_ptr = vg[v]) != NULL) { - vg_counter++; - pv_counter += vg_ptr->pv_cur; - lv_counter += vg_ptr->lv_cur; - if (vg_ptr->lv_cur > 0) { - for (l = 0; l < vg[v]->lv_max; l++) { - if ((lv_ptr = vg_ptr->lv[l]) != NULL) { - pe_t_bytes += lv_ptr->lv_allocated_le; - hash_table_bytes += lv_ptr->lv_snapshot_hash_table_size; - if (lv_ptr->lv_block_exception != NULL) - lv_block_exception_t_bytes += lv_ptr->lv_remap_end; - if (lv_ptr->lv_open > 0) { - lv_open_counter++; - lv_open_total += lv_ptr->lv_open; - } - } - } - } - } - } - pe_t_bytes *= sizeof(pe_t); - lv_block_exception_t_bytes *= sizeof(lv_block_exception_t); - - if (buf != NULL) { - P_KFREE("%s -- vfree %d\n", lvm_name, __LINE__); - lock_kernel(); - vfree(buf); - unlock_kernel(); - buf = NULL; - } - /* 2 times: first to get size to allocate buffer, - 2nd to fill the malloced buffer */ - for (i = 0; i < 2; i++) { - sz = 0; - sz += sprintf(LVM_PROC_BUF, - "LVM " -#ifdef MODULE - "module" -#else - "driver" -#endif - " %s\n\n" - "Total: %d VG%s %d PV%s %d LV%s ", - lvm_short_version, - vg_counter, vg_counter == 1 ? "" : "s", - pv_counter, pv_counter == 1 ? "" : "s", - lv_counter, lv_counter == 1 ? "" : "s"); - sz += sprintf(LVM_PROC_BUF, - "(%d LV%s open", - lv_open_counter, - lv_open_counter == 1 ? "" : "s"); - if (lv_open_total > 0) - sz += sprintf(LVM_PROC_BUF, - " %d times)\n", - lv_open_total); - else - sz += sprintf(LVM_PROC_BUF, ")"); - sz += sprintf(LVM_PROC_BUF, - "\nGlobal: %lu bytes malloced IOP version: %d ", - vg_counter * sizeof(vg_t) + - pv_counter * sizeof(pv_t) + - lv_counter * sizeof(lv_t) + - pe_t_bytes + hash_table_bytes + lv_block_exception_t_bytes + sz_last, - lvm_iop_version); - - seconds = CURRENT_TIME - loadtime; - if (seconds < 0) - loadtime = CURRENT_TIME + seconds; - if (seconds / 86400 > 0) { - sz += sprintf(LVM_PROC_BUF, "%d day%s ", - seconds / 86400, - seconds / 86400 == 0 || - seconds / 86400 > 1 ? "s" : ""); - } - sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n", - (seconds % 86400) / 3600, - (seconds % 3600) / 60, - seconds % 60); - - if (vg_counter > 0) { - for (v = 0; v < ABS_MAX_VG; v++) { - /* volume group */ - if ((vg_ptr = vg[v]) != NULL) { - sz += lvm_vg_info(vg_ptr, LVM_PROC_BUF); - - /* physical volumes */ - sz += sprintf(LVM_PROC_BUF, - "\n PV%s ", - vg_ptr->pv_cur == 1 ? ": " : "s:"); - c = 0; - for (p = 0; p < vg_ptr->pv_max; p++) { - if ((pv_ptr = vg_ptr->pv[p]) != NULL) { - sz += lvm_pv_info(pv_ptr, LVM_PROC_BUF); - - c++; - if (c < vg_ptr->pv_cur) - sz += sprintf(LVM_PROC_BUF, - "\n "); - } - } - - /* logical volumes */ - sz += sprintf(LVM_PROC_BUF, - "\n LV%s ", - vg_ptr->lv_cur == 1 ? ": " : "s:"); - c = 0; - for (l = 0; l < vg_ptr->lv_max; l++) { - if ((lv_ptr = vg_ptr->lv[l]) != NULL) { - sz += lvm_lv_info(vg_ptr, lv_ptr, LVM_PROC_BUF); - c++; - if (c < vg_ptr->lv_cur) - sz += sprintf(LVM_PROC_BUF, - "\n "); - } - } - if (vg_ptr->lv_cur == 0) sz += sprintf(LVM_PROC_BUF, "none"); - sz += sprintf(LVM_PROC_BUF, "\n"); - } - } - } - if (buf == NULL) { - lock_kernel(); - buf = vmalloc(sz); - unlock_kernel(); - if (buf == NULL) { - sz = 0; - MOD_DEC_USE_COUNT; - return sprintf(page, "%s - vmalloc error at line %d\n", - lvm_name, __LINE__); - } - } - sz_last = sz; + if (pe_lock_req.lock == LOCK_PE) { + down_read(&_pe_lock); + if (_should_defer(pv, sector, pe_size)) { + up_read(&_pe_lock); + down_write(&_pe_lock); + if (_should_defer(pv, sector, pe_size)) + _queue_io(bh, rw); + up_write(&_pe_lock); + return 1; } + up_read(&_pe_lock); } - MOD_DEC_USE_COUNT; - if (pos > sz - 1) { - lock_kernel(); - vfree(buf); - unlock_kernel(); - buf = NULL; - return 0; - } - *start = &buf[pos]; - if (sz - pos < count) - return sz - pos; - else - return count; -} /* lvm_proc_get_global_info() */ - - -/* - * provide VG information - */ -int lvm_proc_read_vg_info(char *page, char **start, off_t off, - int count, int *eof, void *data) { - int sz = 0; - vg_t *vg = data; - - sz += sprintf ( page+sz, "name: %s\n", vg->vg_name); - sz += sprintf ( page+sz, "size: %u\n", - vg->pe_total * vg->pe_size / 2); - sz += sprintf ( page+sz, "access: %u\n", vg->vg_access); - sz += sprintf ( page+sz, "status: %u\n", vg->vg_status); - sz += sprintf ( page+sz, "number: %u\n", vg->vg_number); - sz += sprintf ( page+sz, "LV max: %u\n", vg->lv_max); - sz += sprintf ( page+sz, "LV current: %u\n", vg->lv_cur); - sz += sprintf ( page+sz, "LV open: %u\n", vg->lv_open); - sz += sprintf ( page+sz, "PV max: %u\n", vg->pv_max); - sz += sprintf ( page+sz, "PV current: %u\n", vg->pv_cur); - sz += sprintf ( page+sz, "PV active: %u\n", vg->pv_act); - sz += sprintf ( page+sz, "PE size: %u\n", vg->pe_size / 2); - sz += sprintf ( page+sz, "PE total: %u\n", vg->pe_total); - sz += sprintf ( page+sz, "PE allocated: %u\n", vg->pe_allocated); - sz += sprintf ( page+sz, "uuid: %s\n", lvm_show_uuid(vg->vg_uuid)); - - return sz; -} - - -/* - * provide LV information - */ -int lvm_proc_read_lv_info(char *page, char **start, off_t off, - int count, int *eof, void *data) { - int sz = 0; - lv_t *lv = data; - - sz += sprintf ( page+sz, "name: %s\n", lv->lv_name); - sz += sprintf ( page+sz, "size: %u\n", lv->lv_size); - sz += sprintf ( page+sz, "access: %u\n", lv->lv_access); - sz += sprintf ( page+sz, "status: %u\n", lv->lv_status); - sz += sprintf ( page+sz, "number: %u\n", lv->lv_number); - sz += sprintf ( page+sz, "open: %u\n", lv->lv_open); - sz += sprintf ( page+sz, "allocation: %u\n", lv->lv_allocation); - sz += sprintf ( page+sz, "device: %02u:%02u\n", - MAJOR(lv->lv_dev), MINOR(lv->lv_dev)); - - return sz; -} - - -/* - * provide PV information - */ -int lvm_proc_read_pv_info(char *page, char **start, off_t off, - int count, int *eof, void *data) { - int sz = 0; - pv_t *pv = data; - - sz += sprintf ( page+sz, "name: %s\n", pv->pv_name); - sz += sprintf ( page+sz, "size: %u\n", pv->pv_size); - sz += sprintf ( page+sz, "status: %u\n", pv->pv_status); - sz += sprintf ( page+sz, "number: %u\n", pv->pv_number); - sz += sprintf ( page+sz, "allocatable: %u\n", pv->pv_allocatable); - sz += sprintf ( page+sz, "LV current: %u\n", pv->lv_cur); - sz += sprintf ( page+sz, "PE size: %u\n", pv->pe_size / 2); - sz += sprintf ( page+sz, "PE total: %u\n", pv->pe_total); - sz += sprintf ( page+sz, "PE allocated: %u\n", pv->pe_allocated); - sz += sprintf ( page+sz, "device: %02u:%02u\n", - MAJOR(pv->pv_dev), MINOR(pv->pv_dev)); - sz += sprintf ( page+sz, "uuid: %s\n", lvm_show_uuid(pv->pv_uuid)); - - - return sz; + return 0; } - -/* - * block device support function for /usr/src/linux/drivers/block/ll_rw_blk.c - * (see init_module/lvm_init) - */ static int lvm_map(struct buffer_head *bh, int rw) { int minor = MINOR(bh->b_rdev); - int ret = 0; ulong index; ulong pe_start; ulong size = bh->b_size >> 9; - ulong rsector_tmp = bh->b_rsector; - ulong rsector_sav; - kdev_t rdev_tmp = bh->b_rdev; - kdev_t rdev_sav; + ulong rsector_org = bh->b_rsector; + ulong rsector_map; + kdev_t rdev_map; vg_t *vg_this = vg[VG_BLK(minor)]; lv_t *lv = vg_this->lv[LV_BLK(minor)]; + down_read(&lv->lv_lock); if (!(lv->lv_status & LV_ACTIVE)) { printk(KERN_ALERT "%s - lvm_map: ll_rw_blk for inactive LV %s\n", lvm_name, lv->lv_name); - return -1; + goto bad; } if ((rw == WRITE || rw == WRITEA) && !(lv->lv_access & LV_WRITE)) { printk(KERN_CRIT - "%s - lvm_map: ll_rw_blk write for readonly LV %s\n", + "%s - lvm_map: ll_rw_blk write for readonly LV %s\n", lvm_name, lv->lv_name); - return -1; + goto bad; } - P_MAP("%s - lvm_map minor:%d *rdev: %02d:%02d *rsector: %lu " - "size:%lu\n", + P_MAP("%s - lvm_map minor: %d *rdev: %s *rsector: %lu size:%lu\n", lvm_name, minor, - MAJOR(rdev_tmp), - MINOR(rdev_tmp), - rsector_tmp, size); + kdevname(bh->b_rdev), + rsector_org, size); - if (rsector_tmp + size > lv->lv_size) { + if (rsector_org + size > lv->lv_size) { printk(KERN_ALERT "%s - lvm_map access beyond end of device; *rsector: " "%lu or size: %lu wrong for minor: %2d\n", - lvm_name, rsector_tmp, size, minor); - return -1; + lvm_name, rsector_org, size, minor); + goto bad; } - rsector_sav = rsector_tmp; - rdev_sav = rdev_tmp; -lvm_second_remap: - /* linear mapping */ - if (lv->lv_stripes < 2) { + + if (lv->lv_stripes < 2) { /* linear mapping */ /* get the index */ - index = rsector_tmp / vg_this->pe_size; + index = rsector_org / vg_this->pe_size; pe_start = lv->lv_current_pe[index].pe; - rsector_tmp = lv->lv_current_pe[index].pe + - (rsector_tmp % vg_this->pe_size); - rdev_tmp = lv->lv_current_pe[index].dev; - - P_MAP("lv_current_pe[%ld].pe: %ld rdev: %02d:%02d " - "rsector:%ld\n", - index, - lv->lv_current_pe[index].pe, - MAJOR(rdev_tmp), - MINOR(rdev_tmp), - rsector_tmp); + rsector_map = lv->lv_current_pe[index].pe + + (rsector_org % vg_this->pe_size); + rdev_map = lv->lv_current_pe[index].dev; + + P_MAP("lv_current_pe[%ld].pe: %d rdev: %s rsector:%ld\n", + index, lv->lv_current_pe[index].pe, + kdevname(rdev_map), rsector_map); - /* striped mapping */ - } else { + } else { /* striped mapping */ ulong stripe_index; ulong stripe_length; stripe_length = vg_this->pe_size * lv->lv_stripes; - stripe_index = (rsector_tmp % stripe_length) / lv->lv_stripesize; - index = rsector_tmp / stripe_length + - (stripe_index % lv->lv_stripes) * - (lv->lv_allocated_le / lv->lv_stripes); + stripe_index = (rsector_org % stripe_length) / + lv->lv_stripesize; + index = rsector_org / stripe_length + + (stripe_index % lv->lv_stripes) * + (lv->lv_allocated_le / lv->lv_stripes); pe_start = lv->lv_current_pe[index].pe; - rsector_tmp = lv->lv_current_pe[index].pe + - (rsector_tmp % stripe_length) - - (stripe_index % lv->lv_stripes) * lv->lv_stripesize - - stripe_index / lv->lv_stripes * - (lv->lv_stripes - 1) * lv->lv_stripesize; - rdev_tmp = lv->lv_current_pe[index].dev; - } - - P_MAP("lv_current_pe[%ld].pe: %ld rdev: %02d:%02d rsector:%ld\n" - "stripe_length: %ld stripe_index: %ld\n", - index, - lv->lv_current_pe[index].pe, - MAJOR(rdev_tmp), - MINOR(rdev_tmp), - rsector_tmp, - stripe_length, - stripe_index); + rsector_map = lv->lv_current_pe[index].pe + + (rsector_org % stripe_length) - + (stripe_index % lv->lv_stripes) * lv->lv_stripesize - + stripe_index / lv->lv_stripes * + (lv->lv_stripes - 1) * lv->lv_stripesize; + rdev_map = lv->lv_current_pe[index].dev; + + P_MAP("lv_current_pe[%ld].pe: %d rdev: %s rsector:%ld\n" + "stripe_length: %ld stripe_index: %ld\n", + index, lv->lv_current_pe[index].pe, kdevname(rdev_map), + rsector_map, stripe_length, stripe_index); + } + + /* + * Queue writes to physical extents on the move until move completes. + * Don't get _pe_lock until there is a reasonable expectation that + * we need to queue this request, because this is in the fast path. + */ + if (rw == WRITE || rw == WRITEA) { + if(_defer_extent(bh, rw, rdev_map, + rsector_map, vg_this->pe_size)) { - /* handle physical extents on the move */ - if (pe_lock_req.lock == LOCK_PE) { - if (rdev_tmp == pe_lock_req.data.pv_dev && - rsector_tmp >= pe_lock_req.data.pv_offset && - rsector_tmp < (pe_lock_req.data.pv_offset + - vg_this->pe_size)) { - sleep_on(&lvm_map_wait); - rsector_tmp = rsector_sav; - rdev_tmp = rdev_sav; - goto lvm_second_remap; + up_read(&lv->lv_lock); + return 0; } - } - /* statistic */ - if (rw == WRITE || rw == WRITEA) - lv->lv_current_pe[index].writes++; - else - lv->lv_current_pe[index].reads++; + + lv->lv_current_pe[index].writes++; /* statistic */ + } else + lv->lv_current_pe[index].reads++; /* statistic */ /* snapshot volume exception handling on physical device address base */ - if (lv->lv_access & (LV_SNAPSHOT|LV_SNAPSHOT_ORG)) { - /* original logical volume */ - if (lv->lv_access & LV_SNAPSHOT_ORG) { - /* Serializes the access to the lv_snapshot_next list */ - down(&lv->lv_snapshot_sem); - if (rw == WRITE || rw == WRITEA) - { - lv_t *lv_ptr; - - /* start with first snapshot and loop thrugh all of them */ - for (lv_ptr = lv->lv_snapshot_next; - lv_ptr != NULL; - lv_ptr = lv_ptr->lv_snapshot_next) { - /* Check for inactive snapshot */ - if (!(lv_ptr->lv_status & LV_ACTIVE)) continue; - /* Serializes the COW with the accesses to the snapshot device */ - down(&lv_ptr->lv_snapshot_sem); - /* do we still have exception storage for this snapshot free? */ - if (lv_ptr->lv_block_exception != NULL) { - rdev_sav = rdev_tmp; - rsector_sav = rsector_tmp; - if (!lvm_snapshot_remap_block(&rdev_tmp, - &rsector_tmp, - pe_start, - lv_ptr)) { - /* create a new mapping */ - if (!(ret = lvm_snapshot_COW(rdev_tmp, - rsector_tmp, - pe_start, - rsector_sav, - lv_ptr))) - ret = lvm_write_COW_table_block(vg_this, - lv_ptr); - } - rdev_tmp = rdev_sav; - rsector_tmp = rsector_sav; - } - up(&lv_ptr->lv_snapshot_sem); - } - } - up(&lv->lv_snapshot_sem); - } else { - /* remap snapshot logical volume */ - down(&lv->lv_snapshot_sem); - if (lv->lv_block_exception != NULL) - lvm_snapshot_remap_block(&rdev_tmp, &rsector_tmp, pe_start, lv); - up(&lv->lv_snapshot_sem); - } - } - bh->b_rdev = rdev_tmp; - bh->b_rsector = rsector_tmp; + if (!(lv->lv_access & (LV_SNAPSHOT|LV_SNAPSHOT_ORG))) + goto out; - return ret; + if (lv->lv_access & LV_SNAPSHOT) { /* remap snapshot */ + if (lv->lv_block_exception) + lvm_snapshot_remap_block(&rdev_map, &rsector_map, + pe_start, lv); + else + goto bad; + + } else if (rw == WRITE || rw == WRITEA) { /* snapshot origin */ + lv_t *snap; + + /* start with first snapshot and loop through all of + them */ + for (snap = lv->lv_snapshot_next; snap; + snap = snap->lv_snapshot_next) { + /* Check for inactive snapshot */ + if (!(snap->lv_status & LV_ACTIVE)) + continue; + + /* Serializes the COW with the accesses to the + snapshot device */ + _remap_snapshot(rdev_map, rsector_map, + pe_start, snap, vg_this); + } + } + + out: + bh->b_rdev = rdev_map; + bh->b_rsector = rsector_map; + up_read(&lv->lv_lock); + return 1; + + bad: + buffer_IO_error(bh); + up_read(&lv->lv_lock); + return -1; } /* lvm_map() */ @@ -1657,13 +1298,8 @@ */ static int lvm_make_request_fn(request_queue_t *q, int rw, - struct buffer_head *bh) -{ - if (lvm_map(bh, rw) >= 0) - return 1; - - buffer_IO_error(bh); - return 0; + struct buffer_head *bh) { + return (lvm_map(bh, rw) <= 0) ? 0 : 1; } @@ -1680,8 +1316,7 @@ lock_try_again: spin_lock(&lvm_lock); if (lock != 0 && lock != current->pid) { - P_IOCTL("lvm_do_lock_lvm: %s is locked by pid %d ...\n", - lvm_name, lock); + P_DEV("lvm_do_lock_lvm: locked by pid %d ...\n", lock); spin_unlock(&lvm_lock); interruptible_sleep_on(&lvm_wait); if (current->sigpending != 0) @@ -1693,6 +1328,7 @@ goto lock_try_again; } lock = current->pid; + P_DEV("lvm_do_lock_lvm: locking LVM for pid %d\n", lock); spin_unlock(&lvm_lock); return 0; } /* lvm_do_lock_lvm */ @@ -1703,33 +1339,60 @@ */ static int lvm_do_pe_lock_unlock(vg_t *vg_ptr, void *arg) { + pe_lock_req_t new_lock; + struct buffer_head *bh; uint p; if (vg_ptr == NULL) return -ENXIO; - if (copy_from_user(&pe_lock_req, arg, - sizeof(pe_lock_req_t)) != 0) return -EFAULT; + if (copy_from_user(&new_lock, arg, sizeof(new_lock)) != 0) + return -EFAULT; - switch (pe_lock_req.lock) { + switch (new_lock.lock) { case LOCK_PE: for (p = 0; p < vg_ptr->pv_max; p++) { if (vg_ptr->pv[p] != NULL && - pe_lock_req.data.pv_dev == - vg_ptr->pv[p]->pv_dev) + new_lock.data.pv_dev == vg_ptr->pv[p]->pv_dev) break; } if (p == vg_ptr->pv_max) return -ENXIO; - pe_lock_req.lock = UNLOCK_PE; + /* + * this sync releaves memory pressure to lessen the + * likelyhood of pvmove being paged out - resulting in + * deadlock. + * + * This method of doing a pvmove is broken + */ fsync_dev(pe_lock_req.data.lv_dev); + + down_write(&_pe_lock); + if (pe_lock_req.lock == LOCK_PE) { + up_write(&_pe_lock); + return -EBUSY; + } + + /* Should we do to_kdev_t() on the pv_dev and lv_dev??? */ pe_lock_req.lock = LOCK_PE; + pe_lock_req.data.lv_dev = new_lock.data.lv_dev; + pe_lock_req.data.pv_dev = new_lock.data.pv_dev; + pe_lock_req.data.pv_offset = new_lock.data.pv_offset; + up_write(&_pe_lock); + + /* some requests may have got through since the fsync */ + fsync_dev(pe_lock_req.data.pv_dev); break; case UNLOCK_PE: + down_write(&_pe_lock); pe_lock_req.lock = UNLOCK_PE; - pe_lock_req.data.lv_dev = \ - pe_lock_req.data.pv_dev = \ + pe_lock_req.data.lv_dev = 0; + pe_lock_req.data.pv_dev = 0; pe_lock_req.data.pv_offset = 0; - wake_up(&lvm_map_wait); + bh = _dequeue_io(); + up_write(&_pe_lock); + + /* handle all deferred io for this PE */ + _flush_io(bh); break; default: @@ -1766,6 +1429,8 @@ le_remap_req.new_dev; lv_ptr->lv_current_pe[le].pe = le_remap_req.new_pe; + + __update_hardsectsize(lv_ptr); return 0; } } @@ -1779,7 +1444,7 @@ /* * character device support function VGDA create */ -int lvm_do_vg_create(int minor, void *arg) +static int lvm_do_vg_create(void *arg, int minor) { int ret = 0; ulong l, ls = 0, p, size; @@ -1787,8 +1452,6 @@ vg_t *vg_ptr; lv_t **snap_lv_ptr; - if (vg[VG_CHR(minor)] != NULL) return -EPERM; - if ((vg_ptr = kmalloc(sizeof(vg_t),GFP_KERNEL)) == NULL) { printk(KERN_CRIT "%s -- VG_CREATE: kmalloc error VG at line %d\n", @@ -1797,32 +1460,47 @@ } /* get the volume group structure */ if (copy_from_user(vg_ptr, arg, sizeof(vg_t)) != 0) { + P_IOCTL("lvm_do_vg_create ERROR: copy VG ptr %p (%d bytes)\n", + arg, sizeof(vg_t)); kfree(vg_ptr); return -EFAULT; } + /* VG_CREATE now uses minor number in VG structure */ + if (minor == -1) minor = vg_ptr->vg_number; + + /* Validate it */ + if (vg[VG_CHR(minor)] != NULL) { + P_IOCTL("lvm_do_vg_create ERROR: VG %d in use\n", minor); + kfree(vg_ptr); + return -EPERM; + } + /* we are not that active so far... */ vg_ptr->vg_status &= ~VG_ACTIVE; - vg[VG_CHR(minor)] = vg_ptr; - vg[VG_CHR(minor)]->pe_allocated = 0; + vg_ptr->pe_allocated = 0; if (vg_ptr->pv_max > ABS_MAX_PV) { printk(KERN_WARNING "%s -- Can't activate VG: ABS_MAX_PV too small\n", lvm_name); kfree(vg_ptr); - vg[VG_CHR(minor)] = NULL; return -EPERM; } + if (vg_ptr->lv_max > ABS_MAX_LV) { printk(KERN_WARNING "%s -- Can't activate VG: ABS_MAX_LV too small for %u\n", lvm_name, vg_ptr->lv_max); kfree(vg_ptr); - vg_ptr = NULL; return -EPERM; } + /* create devfs and procfs entries */ + lvm_fs_create_vg(vg_ptr); + + vg[VG_CHR(minor)] = vg_ptr; + /* get the physical volume structures */ vg_ptr->pv_act = vg_ptr->pv_cur = 0; for (p = 0; p < vg_ptr->pv_max; p++) { @@ -1852,6 +1530,8 @@ /* user space address */ if ((lvp = vg_ptr->lv[l]) != NULL) { if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0) { + P_IOCTL("ERROR: copying LV ptr %p (%d bytes)\n", + lvp, sizeof(lv_t)); lvm_do_vg_remove(minor); return -EFAULT; } @@ -1870,8 +1550,6 @@ } } - lvm_do_create_devfs_entry_of_vg ( vg_ptr); - /* Second path to correct snapshot logical volumes which are not in place during first path above */ for (l = 0; l < ls; l++) { @@ -1886,8 +1564,6 @@ } } - lvm_do_create_proc_entry_of_vg ( vg_ptr); - vfree(snap_lv_ptr); vg_count++; @@ -1919,7 +1595,6 @@ if ( ret != 0) return ret; pv_ptr = vg_ptr->pv[p]; vg_ptr->pe_total += pv_ptr->pe_total; - lvm_do_create_proc_entry_of_pv(vg_ptr, pv_ptr); return 0; } } @@ -1969,10 +1644,12 @@ lv_t *lv_ptr = NULL; pv_t *pv_ptr = NULL; + if (vg_ptr == NULL) return -ENXIO; + if (copy_from_user(vg_name, arg, sizeof(vg_name)) != 0) return -EFAULT; - lvm_do_remove_proc_entry_of_vg ( vg_ptr); + lvm_fs_remove_vg(vg_ptr); strncpy ( vg_ptr->vg_name, vg_name, sizeof ( vg_name)-1); for ( l = 0; l < vg_ptr->lv_max; l++) @@ -1994,7 +1671,7 @@ strncpy(pv_ptr->vg_name, vg_name, NAME_LEN); } - lvm_do_create_proc_entry_of_vg ( vg_ptr); + lvm_fs_create_vg(vg_ptr); return 0; } /* lvm_do_vg_rename */ @@ -2021,6 +1698,9 @@ /* let's go inactive */ vg_ptr->vg_status &= ~VG_ACTIVE; + /* remove from procfs and devfs */ + lvm_fs_remove_vg(vg_ptr); + /* free LVs */ /* first free snapshot logical volumes */ for (i = 0; i < vg_ptr->lv_max; i++) { @@ -2048,11 +1728,6 @@ } } - devfs_unregister (ch_devfs_handle[vg_ptr->vg_number]); - devfs_unregister (vg_devfs_handle[vg_ptr->vg_number]); - - lvm_do_remove_proc_entry_of_vg ( vg_ptr); - P_KFREE("%s -- kfree %d\n", lvm_name, __LINE__); kfree(vg_ptr); vg[VG_CHR(minor)] = NULL; @@ -2069,66 +1744,111 @@ * character device support function physical volume create */ static int lvm_do_pv_create(pv_t *pvp, vg_t *vg_ptr, ulong p) { - pv_t *pv_ptr = NULL; + pv_t *pv; + int err; - pv_ptr = vg_ptr->pv[p] = kmalloc(sizeof(pv_t),GFP_KERNEL); - if (pv_ptr == NULL) { + pv = kmalloc(sizeof(pv_t),GFP_KERNEL); + if (pv == NULL) { printk(KERN_CRIT - "%s -- VG_CREATE: kmalloc error PV at line %d\n", + "%s -- PV_CREATE: kmalloc error PV at line %d\n", lvm_name, __LINE__); return -ENOMEM; } - if (copy_from_user(pv_ptr, pvp, sizeof(pv_t)) != 0) { + + memset(pv, 0, sizeof(*pv)); + + if (copy_from_user(pv, pvp, sizeof(pv_t)) != 0) { + P_IOCTL("lvm_do_pv_create ERROR: copy PV ptr %p (%d bytes)\n", + pvp, sizeof(pv_t)); + kfree(pv); return -EFAULT; } + + if ((err = _open_pv(pv))) { + kfree(pv); + return err; + } + /* We don't need the PE list in kernel space as with LVs pe_t list (see below) */ - pv_ptr->pe = NULL; - pv_ptr->pe_allocated = 0; - pv_ptr->pv_status = PV_ACTIVE; + pv->pe = NULL; + pv->pe_allocated = 0; + pv->pv_status = PV_ACTIVE; vg_ptr->pv_act++; vg_ptr->pv_cur++; + lvm_fs_create_pv(vg_ptr, pv); + vg_ptr->pv[p] = pv; return 0; } /* lvm_do_pv_create() */ /* - * character device support function physical volume create + * character device support function physical volume remove */ static int lvm_do_pv_remove(vg_t *vg_ptr, ulong p) { - pv_t *pv_ptr = vg_ptr->pv[p]; + pv_t *pv = vg_ptr->pv[p]; - lvm_do_remove_proc_entry_of_pv ( vg_ptr, pv_ptr); - vg_ptr->pe_total -= pv_ptr->pe_total; + lvm_fs_remove_pv(vg_ptr, pv); + + vg_ptr->pe_total -= pv->pe_total; vg_ptr->pv_cur--; vg_ptr->pv_act--; -#ifdef LVM_GET_INODE - lvm_clear_inode(pv_ptr->inode); -#endif - kfree(pv_ptr); + + _close_pv(pv); + kfree(pv); + vg_ptr->pv[p] = NULL; return 0; } +static void __update_hardsectsize(lv_t *lv) { + int le, e; + int max_hardsectsize = 0, hardsectsize; + + for (le = 0; le < lv->lv_allocated_le; le++) { + hardsectsize = get_hardsect_size(lv->lv_current_pe[le].dev); + if (hardsectsize == 0) + hardsectsize = 512; + if (hardsectsize > max_hardsectsize) + max_hardsectsize = hardsectsize; + } + + /* only perform this operation on active snapshots */ + if ((lv->lv_access & LV_SNAPSHOT) && + (lv->lv_status & LV_ACTIVE)) { + for (e = 0; e < lv->lv_remap_end; e++) { + hardsectsize = get_hardsect_size( lv->lv_block_exception[e].rdev_new); + if (hardsectsize == 0) + hardsectsize = 512; + if (hardsectsize > max_hardsectsize) + max_hardsectsize = hardsectsize; + } + } + + lvm_hardsectsizes[MINOR(lv->lv_dev)] = max_hardsectsize; +} + /* * character device support function logical volume create */ static int lvm_do_lv_create(int minor, char *lv_name, lv_t *lv) { - int e, ret, l, le, l_new, p, size; + int e, ret, l, le, l_new, p, size, activate = 1; ulong lv_status_save; lv_block_exception_t *lvbe = lv->lv_block_exception; vg_t *vg_ptr = vg[VG_CHR(minor)]; lv_t *lv_ptr = NULL; - if ((pep = lv->lv_current_pe) == NULL) return -EINVAL; - if (lv->lv_chunk_size > LVM_SNAPSHOT_MAX_CHUNK) + if (!(pep = lv->lv_current_pe)) return -EINVAL; - for (l = 0; l < vg_ptr->lv_max; l++) { + if (_sectors_to_k(lv->lv_chunk_size) > LVM_SNAPSHOT_MAX_CHUNK) + return -EINVAL; + + for (l = 0; l < vg_ptr->lv_cur; l++) { if (vg_ptr->lv[l] != NULL && strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0) return -EEXIST; @@ -2157,16 +1877,17 @@ lv_status_save = lv_ptr->lv_status; lv_ptr->lv_status &= ~LV_ACTIVE; - lv_ptr->lv_snapshot_org = \ - lv_ptr->lv_snapshot_prev = \ + lv_ptr->lv_snapshot_org = NULL; + lv_ptr->lv_snapshot_prev = NULL; lv_ptr->lv_snapshot_next = NULL; lv_ptr->lv_block_exception = NULL; lv_ptr->lv_iobuf = NULL; + lv_ptr->lv_COW_table_iobuf = NULL; lv_ptr->lv_snapshot_hash_table = NULL; lv_ptr->lv_snapshot_hash_table_size = 0; lv_ptr->lv_snapshot_hash_mask = 0; - lv_ptr->lv_COW_table_page = NULL; - init_MUTEX(&lv_ptr->lv_snapshot_sem); + init_rwsem(&lv_ptr->lv_lock); + lv_ptr->lv_snapshot_use_rate = 0; vg_ptr->lv[l] = lv_ptr; @@ -2185,6 +1906,8 @@ return -ENOMEM; } if (copy_from_user(lv_ptr->lv_current_pe, pep, size)) { + P_IOCTL("ERROR: copying PE ptr %p (%d bytes)\n", + pep, sizeof(size)); vfree(lv_ptr->lv_current_pe); kfree(lv_ptr); vg_ptr->lv[l] = NULL; @@ -2206,6 +1929,15 @@ vg_ptr->lv[LV_BLK(lv_ptr->lv_snapshot_minor)]; if (lv_ptr->lv_snapshot_org != NULL) { size = lv_ptr->lv_remap_end * sizeof(lv_block_exception_t); + + if(!size) { + printk(KERN_WARNING + "%s -- zero length exception table requested\n", + lvm_name); + kfree(lv_ptr); + return -EINVAL; + } + if ((lv_ptr->lv_block_exception = vmalloc(size)) == NULL) { printk(KERN_CRIT "%s -- lvm_do_lv_create: vmalloc error LV_BLOCK_EXCEPTION " @@ -2223,6 +1955,17 @@ vg_ptr->lv[l] = NULL; return -EFAULT; } + + if(lv_ptr->lv_block_exception[0].rsector_org == + LVM_SNAPSHOT_DROPPED_SECTOR) + { + printk(KERN_WARNING + "%s -- lvm_do_lv_create: snapshot has been dropped and will not be activated\n", + lvm_name); + activate = 0; + } + + /* point to the original logical volume */ lv_ptr = lv_ptr->lv_snapshot_org; @@ -2256,10 +1999,13 @@ lv_ptr->lv_block_exception[e].rsector_org, lv_ptr); /* need to fill the COW exception table data into the page for disk i/o */ - lvm_snapshot_fill_COW_page(vg_ptr, lv_ptr); + if(lvm_snapshot_fill_COW_page(vg_ptr, lv_ptr)) { + kfree(lv_ptr); + vg_ptr->lv[l] = NULL; + return -EINVAL; + } init_waitqueue_head(&lv_ptr->lv_snapshot_wait); } else { - vfree(lv_ptr->lv_block_exception); kfree(lv_ptr); vg_ptr->lv[l] = NULL; return -EFAULT; @@ -2281,21 +2027,7 @@ vg_ptr->lv_cur++; lv_ptr->lv_status = lv_status_save; - { - char *lv_tmp, *lv_buf = lv->lv_name; - - strtok(lv->lv_name, "/"); /* /dev */ - while((lv_tmp = strtok(NULL, "/")) != NULL) - lv_buf = lv_tmp; - - lv_devfs_handle[lv->lv_number] = devfs_register( - vg_devfs_handle[vg_ptr->vg_number], lv_buf, - DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, lv->lv_number, - S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, - &lvm_blk_dops, NULL); - } - - lvm_do_create_proc_entry_of_lv ( vg_ptr, lv_ptr); + __update_hardsectsize(lv_ptr); /* optionally add our new snapshot LV */ if (lv_ptr->lv_access & LV_SNAPSHOT) { @@ -2308,7 +2040,7 @@ fsync_dev_lockfs(org->lv_dev); #endif - down(&org->lv_snapshot_sem); + down_write(&org->lv_lock); org->lv_access |= LV_SNAPSHOT_ORG; lv_ptr->lv_access &= ~LV_SNAPSHOT_ORG; /* this can only hide an userspace bug */ @@ -2316,11 +2048,15 @@ for (last = org; last->lv_snapshot_next; last = last->lv_snapshot_next); lv_ptr->lv_snapshot_prev = last; last->lv_snapshot_next = lv_ptr; - up(&org->lv_snapshot_sem); + up_write(&org->lv_lock); } /* activate the logical volume */ - lv_ptr->lv_status |= LV_ACTIVE; + if(activate) + lv_ptr->lv_status |= LV_ACTIVE; + else + lv_ptr->lv_status &= ~LV_ACTIVE; + if ( lv_ptr->lv_access & LV_WRITE) set_device_ro(lv_ptr->lv_dev, 0); else @@ -2328,13 +2064,15 @@ #ifdef LVM_VFS_ENHANCEMENT /* VFS function call to unlock the filesystem */ - if (lv_ptr->lv_access & LV_SNAPSHOT) { + if (lv_ptr->lv_access & LV_SNAPSHOT) unlockfs(lv_ptr->lv_snapshot_org->lv_dev); - } #endif lv_ptr->vg = vg_ptr; + lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].de = + lvm_fs_create_lv(vg_ptr, lv_ptr); + return 0; } /* lvm_do_lv_create() */ @@ -2372,13 +2110,15 @@ lv_ptr->lv_snapshot_next != NULL) return -EPERM; + lvm_fs_remove_lv(vg_ptr, lv_ptr); + if (lv_ptr->lv_access & LV_SNAPSHOT) { /* * Atomically make the the snapshot invisible * to the original lv before playing with it. */ lv_t * org = lv_ptr->lv_snapshot_org; - down(&org->lv_snapshot_sem); + down_write(&org->lv_lock); /* remove this snapshot logical volume from the chain */ lv_ptr->lv_snapshot_prev->lv_snapshot_next = lv_ptr->lv_snapshot_next; @@ -2386,11 +2126,13 @@ lv_ptr->lv_snapshot_next->lv_snapshot_prev = lv_ptr->lv_snapshot_prev; } - up(&org->lv_snapshot_sem); /* no more snapshots? */ - if (!org->lv_snapshot_next) + if (!org->lv_snapshot_next) { org->lv_access &= ~LV_SNAPSHOT_ORG; + } + up_write(&org->lv_lock); + lvm_snapshot_release(lv_ptr); /* Update the VG PE(s) used by snapshot reserve space. */ @@ -2410,6 +2152,7 @@ /* reset generic hd */ lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = -1; lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = 0; + lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].de = 0; lvm_size[MINOR(lv_ptr->lv_dev)] = 0; /* reset VG/LV mapping */ @@ -2433,10 +2176,6 @@ vfree(lv_ptr->lv_current_pe); } - devfs_unregister(lv_devfs_handle[lv_ptr->lv_number]); - - lvm_do_remove_proc_entry_of_lv ( vg_ptr, lv_ptr); - P_KFREE("%s -- kfree %d\n", lvm_name, __LINE__); kfree(lv_ptr); vg_ptr->lv[l] = NULL; @@ -2446,205 +2185,217 @@ /* - * character device support function logical volume extend / reduce + * logical volume extend / reduce */ -static int lvm_do_lv_extend_reduce(int minor, char *lv_name, lv_t *lv) -{ - ulong end, l, le, p, size, old_allocated_le; - vg_t *vg_ptr = vg[VG_CHR(minor)]; - lv_t *lv_ptr; - pe_t *pe; - - if ((pep = lv->lv_current_pe) == NULL) return -EINVAL; - - for (l = 0; l < vg_ptr->lv_max; l++) { - if (vg_ptr->lv[l] != NULL && - strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0) - break; - } - if (l == vg_ptr->lv_max) return -ENXIO; - lv_ptr = vg_ptr->lv[l]; - - /* check for active snapshot */ - if (lv->lv_access & LV_SNAPSHOT) - { - ulong e; - lv_block_exception_t *lvbe, *lvbe_old; - struct list_head * lvs_hash_table_old; - - if (lv->lv_block_exception == NULL) return -ENXIO; - size = lv->lv_remap_end * sizeof ( lv_block_exception_t); - if ((lvbe = vmalloc(size)) == NULL) - { - printk(KERN_CRIT - "%s -- lvm_do_lv_extend_reduce: vmalloc error LV_BLOCK_EXCEPTION " - "of %lu Byte at line %d\n", - lvm_name, size, __LINE__); - return -ENOMEM; - } - if (lv->lv_remap_end > lv_ptr->lv_remap_end) - { - if (copy_from_user(lvbe, lv->lv_block_exception, size)) - { - vfree(lvbe); - return -EFAULT; - } - } - - lvbe_old = lv_ptr->lv_block_exception; - lvs_hash_table_old = lv_ptr->lv_snapshot_hash_table; - - /* we need to play on the safe side here... */ - down(&lv_ptr->lv_snapshot_org->lv_snapshot_sem); - if (lv_ptr->lv_block_exception == NULL || - lv_ptr->lv_remap_ptr > lv_ptr->lv_remap_end) - { - up(&lv_ptr->lv_snapshot_org->lv_snapshot_sem); - vfree(lvbe); - return -EPERM; - } - memcpy(lvbe, - lv_ptr->lv_block_exception, - (lv->lv_remap_end > lv_ptr->lv_remap_end ? - lv_ptr->lv_remap_ptr : lv->lv_remap_end) * sizeof(lv_block_exception_t)); - - lv_ptr->lv_block_exception = lvbe; - lv_ptr->lv_remap_end = lv->lv_remap_end; - if (lvm_snapshot_alloc_hash_table(lv_ptr) != 0) - { - lvm_drop_snapshot(lv_ptr, "no memory for hash table"); - up(&lv_ptr->lv_snapshot_org->lv_snapshot_sem); - vfree(lvbe_old); - vfree(lvs_hash_table_old); - return -ENOMEM; - } - - for (e = 0; e < lv_ptr->lv_remap_ptr; e++) - lvm_hash_link (lv_ptr->lv_block_exception + e, - lv_ptr->lv_block_exception[e].rdev_org, - lv_ptr->lv_block_exception[e].rsector_org, lv_ptr); - - up(&lv_ptr->lv_snapshot_org->lv_snapshot_sem); - - vfree(lvbe_old); - vfree(lvs_hash_table_old); - - return 0; - } +static int __extend_reduce_snapshot(vg_t *vg_ptr, lv_t *old_lv, lv_t *new_lv) { + ulong size; + lv_block_exception_t *lvbe; + + if (!new_lv->lv_block_exception) + return -ENXIO; + + size = new_lv->lv_remap_end * sizeof(lv_block_exception_t); + if ((lvbe = vmalloc(size)) == NULL) { + printk(KERN_CRIT + "%s -- lvm_do_lv_extend_reduce: vmalloc " + "error LV_BLOCK_EXCEPTION of %lu Byte at line %d\n", + lvm_name, size, __LINE__); + return -ENOMEM; + } + if ((new_lv->lv_remap_end > old_lv->lv_remap_end) && + (copy_from_user(lvbe, new_lv->lv_block_exception, size))) { + vfree(lvbe); + return -EFAULT; + } + new_lv->lv_block_exception = lvbe; - /* we drop in here in case it is an original logical volume */ - if ((pe = vmalloc(size = lv->lv_current_le * sizeof(pe_t))) == NULL) { - printk(KERN_CRIT - "%s -- lvm_do_lv_extend_reduce: vmalloc error LV_CURRENT_PE " - "of %lu Byte at line %d\n", - lvm_name, size, __LINE__); - return -ENOMEM; - } - /* get the PE structures from user space */ - if (copy_from_user(pe, pep, size)) { - vfree(pe); - return -EFAULT; - } + if (lvm_snapshot_alloc_hash_table(new_lv)) { + vfree(new_lv->lv_block_exception); + return -ENOMEM; + } - /* reduce allocation counters on PV(s) */ - for (le = 0; le < lv_ptr->lv_allocated_le; le++) { - vg_ptr->pe_allocated--; - for (p = 0; p < vg_ptr->pv_cur; p++) { - if (vg_ptr->pv[p]->pv_dev == - lv_ptr->lv_current_pe[le].dev) { - vg_ptr->pv[p]->pe_allocated--; - break; - } - } - } + return 0; +} +static int __extend_reduce(vg_t *vg_ptr, lv_t *old_lv, lv_t *new_lv) { + ulong size, l, p, end; + pe_t *pe; + + /* allocate space for new pe structures */ + size = new_lv->lv_current_le * sizeof(pe_t); + if ((pe = vmalloc(size)) == NULL) { + printk(KERN_CRIT + "%s -- lvm_do_lv_extend_reduce: " + "vmalloc error LV_CURRENT_PE of %lu Byte at line %d\n", + lvm_name, size, __LINE__); + return -ENOMEM; + } - /* save pointer to "old" lv/pe pointer array */ - pep1 = lv_ptr->lv_current_pe; - end = lv_ptr->lv_current_le; - - /* save open counter... */ - lv->lv_open = lv_ptr->lv_open; - lv->lv_snapshot_prev = lv_ptr->lv_snapshot_prev; - lv->lv_snapshot_next = lv_ptr->lv_snapshot_next; - lv->lv_snapshot_org = lv_ptr->lv_snapshot_org; + /* get the PE structures from user space */ + if (copy_from_user(pe, new_lv->lv_current_pe, size)) { + if(old_lv->lv_access & LV_SNAPSHOT) + vfree(new_lv->lv_snapshot_hash_table); + vfree(pe); + return -EFAULT; + } - lv->lv_current_pe = pe; + new_lv->lv_current_pe = pe; - /* save # of old allocated logical extents */ - old_allocated_le = lv_ptr->lv_allocated_le; + /* reduce allocation counters on PV(s) */ + for (l = 0; l < old_lv->lv_allocated_le; l++) { + vg_ptr->pe_allocated--; + for (p = 0; p < vg_ptr->pv_cur; p++) { + if (vg_ptr->pv[p]->pv_dev == + old_lv->lv_current_pe[l].dev) { + vg_ptr->pv[p]->pe_allocated--; + break; + } + } + } - /* copy preloaded LV */ - memcpy((char *) lv_ptr, (char *) lv, sizeof(lv_t)); + /* extend the PE count in PVs */ + for (l = 0; l < new_lv->lv_allocated_le; l++) { + vg_ptr->pe_allocated++; + for (p = 0; p < vg_ptr->pv_cur; p++) { + if (vg_ptr->pv[p]->pv_dev == + new_lv->lv_current_pe[l].dev) { + vg_ptr->pv[p]->pe_allocated++; + break; + } + } + } - lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = 0; - lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size; - lvm_size[MINOR(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1; - /* vg_lv_map array doesn't have to be changed here */ + /* save availiable i/o statistic data */ + if (old_lv->lv_stripes < 2) { /* linear logical volume */ + end = min(old_lv->lv_current_le, new_lv->lv_current_le); + for (l = 0; l < end; l++) { + new_lv->lv_current_pe[l].reads += + old_lv->lv_current_pe[l].reads; - LVM_CORRECT_READ_AHEAD(lv_ptr->lv_read_ahead); + new_lv->lv_current_pe[l].writes += + old_lv->lv_current_pe[l].writes; + } - /* save availiable i/o statistic data */ - /* linear logical volume */ - if (lv_ptr->lv_stripes < 2) { - /* Check what last LE shall be used */ - if (end > lv_ptr->lv_current_le) end = lv_ptr->lv_current_le; - for (le = 0; le < end; le++) { - lv_ptr->lv_current_pe[le].reads += pep1[le].reads; - lv_ptr->lv_current_pe[le].writes += pep1[le].writes; - } - /* striped logical volume */ - } else { - uint i, j, source, dest, end, old_stripe_size, new_stripe_size; + } else { /* striped logical volume */ + uint i, j, source, dest, end, old_stripe_size, new_stripe_size; - old_stripe_size = old_allocated_le / lv_ptr->lv_stripes; - new_stripe_size = lv_ptr->lv_allocated_le / lv_ptr->lv_stripes; - end = old_stripe_size; - if (end > new_stripe_size) end = new_stripe_size; - for (i = source = dest = 0; - i < lv_ptr->lv_stripes; i++) { - for (j = 0; j < end; j++) { - lv_ptr->lv_current_pe[dest + j].reads += - pep1[source + j].reads; - lv_ptr->lv_current_pe[dest + j].writes += - pep1[source + j].writes; - } - source += old_stripe_size; - dest += new_stripe_size; - } - } + old_stripe_size = old_lv->lv_allocated_le / old_lv->lv_stripes; + new_stripe_size = new_lv->lv_allocated_le / new_lv->lv_stripes; + end = min(old_stripe_size, new_stripe_size); + + for (i = source = dest = 0; + i < new_lv->lv_stripes; i++) { + for (j = 0; j < end; j++) { + new_lv->lv_current_pe[dest + j].reads += + old_lv->lv_current_pe[source + j].reads; + new_lv->lv_current_pe[dest + j].writes += + old_lv->lv_current_pe[source + j].writes; + } + source += old_stripe_size; + dest += new_stripe_size; + } + } - /* extend the PE count in PVs */ - for (le = 0; le < lv_ptr->lv_allocated_le; le++) { - vg_ptr->pe_allocated++; - for (p = 0; p < vg_ptr->pv_cur; p++) { - if (vg_ptr->pv[p]->pv_dev == - lv_ptr->lv_current_pe[le].dev) { - vg_ptr->pv[p]->pe_allocated++; - break; - } - } - } + return 0; +} - vfree ( pep1); - pep1 = NULL; +static int lvm_do_lv_extend_reduce(int minor, char *lv_name, lv_t *new_lv) +{ + int r; + ulong l, e, size; + vg_t *vg_ptr = vg[VG_CHR(minor)]; + lv_t *old_lv; + pe_t *pe; + + if ((pe = new_lv->lv_current_pe) == NULL) + return -EINVAL; + + for (l = 0; l < vg_ptr->lv_max; l++) + if (vg_ptr->lv[l] && !strcmp(vg_ptr->lv[l]->lv_name, lv_name)) + break; + + if (l == vg_ptr->lv_max) + return -ENXIO; + + old_lv = vg_ptr->lv[l]; + + if (old_lv->lv_access & LV_SNAPSHOT) { + /* only perform this operation on active snapshots */ + if (old_lv->lv_status & LV_ACTIVE) + r = __extend_reduce_snapshot(vg_ptr, old_lv, new_lv); + else + r = -EPERM; + + } else + r = __extend_reduce(vg_ptr, old_lv, new_lv); + + if(r) + return r; + + /* copy relevent fields */ + down_write(&old_lv->lv_lock); + + if(new_lv->lv_access & LV_SNAPSHOT) { + size = (new_lv->lv_remap_end > old_lv->lv_remap_end) ? + old_lv->lv_remap_ptr : new_lv->lv_remap_end; + size *= sizeof(lv_block_exception_t); + memcpy(new_lv->lv_block_exception, + old_lv->lv_block_exception, size); + + old_lv->lv_remap_end = new_lv->lv_remap_end; + old_lv->lv_block_exception = new_lv->lv_block_exception; + old_lv->lv_snapshot_hash_table = + new_lv->lv_snapshot_hash_table; + old_lv->lv_snapshot_hash_table_size = + new_lv->lv_snapshot_hash_table_size; + old_lv->lv_snapshot_hash_mask = + new_lv->lv_snapshot_hash_mask; + + for (e = 0; e < new_lv->lv_remap_ptr; e++) + lvm_hash_link(new_lv->lv_block_exception + e, + new_lv->lv_block_exception[e].rdev_org, + new_lv->lv_block_exception[e].rsector_org, + new_lv); + + } else { + + vfree(old_lv->lv_current_pe); + vfree(old_lv->lv_snapshot_hash_table); + + old_lv->lv_size = new_lv->lv_size; + old_lv->lv_allocated_le = new_lv->lv_allocated_le; + old_lv->lv_current_le = new_lv->lv_current_le; + old_lv->lv_current_pe = new_lv->lv_current_pe; + lvm_gendisk.part[MINOR(old_lv->lv_dev)].nr_sects = + old_lv->lv_size; + lvm_size[MINOR(old_lv->lv_dev)] = old_lv->lv_size >> 1; + + if (old_lv->lv_access & LV_SNAPSHOT_ORG) { + lv_t *snap; + for(snap = old_lv->lv_snapshot_next; snap; + snap = snap->lv_snapshot_next) { + down_write(&snap->lv_lock); + snap->lv_current_pe = old_lv->lv_current_pe; + snap->lv_allocated_le = + old_lv->lv_allocated_le; + snap->lv_current_le = old_lv->lv_current_le; + snap->lv_size = old_lv->lv_size; + + lvm_gendisk.part[MINOR(snap->lv_dev)].nr_sects + = old_lv->lv_size; + lvm_size[MINOR(snap->lv_dev)] = + old_lv->lv_size >> 1; + __update_hardsectsize(snap); + up_write(&snap->lv_lock); + } + } + } - if (lv->lv_access & LV_SNAPSHOT_ORG) - { - /* Correct the snapshot size information */ - while ((lv_ptr = lv_ptr->lv_snapshot_next) != NULL) - { - lv_ptr->lv_current_pe = lv_ptr->lv_snapshot_org->lv_current_pe; - lv_ptr->lv_allocated_le = lv_ptr->lv_snapshot_org->lv_allocated_le; - lv_ptr->lv_current_le = lv_ptr->lv_snapshot_org->lv_current_le; - lv_ptr->lv_size = lv_ptr->lv_snapshot_org->lv_size; - lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size; - lvm_size[MINOR(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1; - } - } + __update_hardsectsize(old_lv); + up_write(&old_lv->lv_lock); - return 0; + return 0; } /* lvm_do_lv_extend_reduce() */ @@ -2654,10 +2405,10 @@ static int lvm_do_lv_status_byname(vg_t *vg_ptr, void *arg) { uint l; - ulong size; - lv_t lv; - lv_t *lv_ptr; lv_status_byname_req_t lv_status_byname_req; + void *saved_ptr1; + void *saved_ptr2; + lv_t *lv_ptr; if (vg_ptr == NULL) return -ENXIO; if (copy_from_user(&lv_status_byname_req, arg, @@ -2665,28 +2416,31 @@ return -EFAULT; if (lv_status_byname_req.lv == NULL) return -EINVAL; - if (copy_from_user(&lv, lv_status_byname_req.lv, - sizeof(lv_t)) != 0) - return -EFAULT; for (l = 0; l < vg_ptr->lv_max; l++) { - lv_ptr = vg_ptr->lv[l]; - if (lv_ptr != NULL && + if ((lv_ptr = vg_ptr->lv[l]) != NULL && strcmp(lv_ptr->lv_name, - lv_status_byname_req.lv_name) == 0) { - if (copy_to_user(lv_status_byname_req.lv, + lv_status_byname_req.lv_name) == 0) { + /* Save usermode pointers */ + if (copy_from_user(&saved_ptr1, &lv_status_byname_req.lv->lv_current_pe, sizeof(void*)) != 0) + return -EFAULT; + if (copy_from_user(&saved_ptr2, &lv_status_byname_req.lv->lv_block_exception, sizeof(void*)) != 0) + return -EFAULT; + if (copy_to_user(lv_status_byname_req.lv, lv_ptr, sizeof(lv_t)) != 0) return -EFAULT; - if (lv.lv_current_pe != NULL) { - size = lv_ptr->lv_allocated_le * - sizeof(pe_t); - if (copy_to_user(lv.lv_current_pe, + if (saved_ptr1 != NULL) { + if (copy_to_user(saved_ptr1, lv_ptr->lv_current_pe, - size) != 0) + lv_ptr->lv_allocated_le * + sizeof(pe_t)) != 0) return -EFAULT; } + /* Restore usermode pointers */ + if (copy_to_user(&lv_status_byname_req.lv->lv_current_pe, &saved_ptr1, sizeof(void*)) != 0) + return -EFAULT; return 0; } } @@ -2699,34 +2453,44 @@ */ static int lvm_do_lv_status_byindex(vg_t *vg_ptr,void *arg) { - ulong size; - lv_t lv; - lv_t *lv_ptr; lv_status_byindex_req_t lv_status_byindex_req; + void *saved_ptr1; + void *saved_ptr2; + lv_t *lv_ptr; if (vg_ptr == NULL) return -ENXIO; if (copy_from_user(&lv_status_byindex_req, arg, sizeof(lv_status_byindex_req)) != 0) return -EFAULT; - if ((lvp = lv_status_byindex_req.lv) == NULL) + if (lv_status_byindex_req.lv == NULL) + return -EINVAL; + if (lv_status_byindex_req.lv_index <0 || + lv_status_byindex_req.lv_index >= MAX_LV) return -EINVAL; if ( ( lv_ptr = vg_ptr->lv[lv_status_byindex_req.lv_index]) == NULL) return -ENXIO; - if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0) - return -EFAULT; + /* Save usermode pointers */ + if (copy_from_user(&saved_ptr1, &lv_status_byindex_req.lv->lv_current_pe, sizeof(void*)) != 0) + return -EFAULT; + if (copy_from_user(&saved_ptr2, &lv_status_byindex_req.lv->lv_block_exception, sizeof(void*)) != 0) + return -EFAULT; - if (copy_to_user(lvp, lv_ptr, sizeof(lv_t)) != 0) + if (copy_to_user(lv_status_byindex_req.lv, lv_ptr, sizeof(lv_t)) != 0) return -EFAULT; - - if (lv.lv_current_pe != NULL) { - size = lv_ptr->lv_allocated_le * sizeof(pe_t); - if (copy_to_user(lv.lv_current_pe, - lv_ptr->lv_current_pe, - size) != 0) + if (saved_ptr1 != NULL) { + if (copy_to_user(saved_ptr1, + lv_ptr->lv_current_pe, + lv_ptr->lv_allocated_le * + sizeof(pe_t)) != 0) return -EFAULT; } + + /* Restore usermode pointers */ + if (copy_to_user(&lv_status_byindex_req.lv->lv_current_pe, &saved_ptr1, sizeof(void *)) != 0) + return -EFAULT; + return 0; } /* lvm_do_lv_status_byindex() */ @@ -2737,6 +2501,9 @@ static int lvm_do_lv_status_bydev(vg_t * vg_ptr, void * arg) { int l; lv_status_bydev_req_t lv_status_bydev_req; + void *saved_ptr1; + void *saved_ptr2; + lv_t *lv_ptr; if (vg_ptr == NULL) return -ENXIO; if (copy_from_user(&lv_status_bydev_req, arg, @@ -2749,10 +2516,26 @@ } if ( l == vg_ptr->lv_max) return -ENXIO; + lv_ptr = vg_ptr->lv[l]; - if (copy_to_user(lv_status_bydev_req.lv, - vg_ptr->lv[l], sizeof(lv_t)) != 0) + /* Save usermode pointers */ + if (copy_from_user(&saved_ptr1, &lv_status_bydev_req.lv->lv_current_pe, sizeof(void*)) != 0) + return -EFAULT; + if (copy_from_user(&saved_ptr2, &lv_status_bydev_req.lv->lv_block_exception, sizeof(void*)) != 0) + return -EFAULT; + + if (copy_to_user(lv_status_bydev_req.lv, lv_ptr, sizeof(lv_t)) != 0) return -EFAULT; + if (saved_ptr1 != NULL) { + if (copy_to_user(saved_ptr1, + lv_ptr->lv_current_pe, + lv_ptr->lv_allocated_le * + sizeof(pe_t)) != 0) + return -EFAULT; + } + /* Restore usermode pointers */ + if (copy_to_user(&lv_status_bydev_req.lv->lv_current_pe, &saved_ptr1, sizeof(void *)) != 0) + return -EFAULT; return 0; } /* lvm_do_lv_status_bydev() */ @@ -2772,11 +2555,11 @@ if ( (lv_ptr = vg_ptr->lv[l]) == NULL) continue; if (lv_ptr->lv_dev == lv->lv_dev) { - lvm_do_remove_proc_entry_of_lv ( vg_ptr, lv_ptr); + lvm_fs_remove_lv(vg_ptr, lv_ptr); strncpy(lv_ptr->lv_name, lv_req->lv_name, NAME_LEN); - lvm_do_create_proc_entry_of_lv ( vg_ptr, lv_ptr); + lvm_fs_create_lv(vg_ptr, lv_ptr); break; } } @@ -2793,9 +2576,7 @@ { uint p; pv_t *pv_ptr; -#ifdef LVM_GET_INODE - struct inode *inode_sav; -#endif + struct block_device *bd; if (vg_ptr == NULL) return -ENXIO; if (copy_from_user(&pv_change_req, arg, @@ -2807,20 +2588,17 @@ if (pv_ptr != NULL && strcmp(pv_ptr->pv_name, pv_change_req.pv_name) == 0) { -#ifdef LVM_GET_INODE - inode_sav = pv_ptr->inode; -#endif + + bd = pv_ptr->bd; if (copy_from_user(pv_ptr, pv_change_req.pv, sizeof(pv_t)) != 0) return -EFAULT; + pv_ptr->bd = bd; /* We don't need the PE list in kernel space as with LVs pe_t list */ pv_ptr->pe = NULL; -#ifdef LVM_GET_INODE - pv_ptr->inode = inode_sav; -#endif return 0; } } @@ -2855,161 +2633,27 @@ return -ENXIO; } /* lvm_do_pv_status() */ - - -/* - * create a devfs entry for a volume group - */ -void lvm_do_create_devfs_entry_of_vg ( vg_t *vg_ptr) { - vg_devfs_handle[vg_ptr->vg_number] = devfs_mk_dir(0, vg_ptr->vg_name, NULL); - ch_devfs_handle[vg_ptr->vg_number] = devfs_register( - vg_devfs_handle[vg_ptr->vg_number] , "group", - DEVFS_FL_DEFAULT, LVM_CHAR_MAJOR, vg_ptr->vg_number, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, - &lvm_chr_fops, NULL); -} - - /* - * create a /proc entry for a logical volume + * character device support function flush and invalidate all buffers of a PV */ -void lvm_do_create_proc_entry_of_lv ( vg_t *vg_ptr, lv_t *lv_ptr) { - char *basename; - - if ( vg_ptr->lv_subdir_pde != NULL) { - basename = strrchr(lv_ptr->lv_name, '/'); - if (basename == NULL) basename = lv_ptr->lv_name; - else basename++; - pde = create_proc_entry(basename, S_IFREG, - vg_ptr->lv_subdir_pde); - if ( pde != NULL) { - pde->read_proc = lvm_proc_read_lv_info; - pde->data = lv_ptr; - } - } -} - - -/* - * remove a /proc entry for a logical volume - */ -void lvm_do_remove_proc_entry_of_lv ( vg_t *vg_ptr, lv_t *lv_ptr) { - char *basename; - - if ( vg_ptr->lv_subdir_pde != NULL) { - basename = strrchr(lv_ptr->lv_name, '/'); - if (basename == NULL) basename = lv_ptr->lv_name; - else basename++; - remove_proc_entry(basename, vg_ptr->lv_subdir_pde); - } -} - - -/* - * create a /proc entry for a physical volume - */ -void lvm_do_create_proc_entry_of_pv ( vg_t *vg_ptr, pv_t *pv_ptr) { - int offset = 0; - char *basename; - char buffer[NAME_LEN]; - - basename = pv_ptr->pv_name; - if (strncmp(basename, "/dev/", 5) == 0) offset = 5; - strncpy(buffer, basename + offset, sizeof(buffer)); - basename = buffer; - while ( ( basename = strchr ( basename, '/')) != NULL) *basename = '_'; - pde = create_proc_entry(buffer, S_IFREG, vg_ptr->pv_subdir_pde); - if ( pde != NULL) { - pde->read_proc = lvm_proc_read_pv_info; - pde->data = pv_ptr; - } -} - - -/* - * remove a /proc entry for a physical volume - */ -void lvm_do_remove_proc_entry_of_pv ( vg_t *vg_ptr, pv_t *pv_ptr) { - char *basename; - - basename = strrchr(pv_ptr->pv_name, '/'); - if ( vg_ptr->pv_subdir_pde != NULL) { - basename = strrchr(pv_ptr->pv_name, '/'); - if (basename == NULL) basename = pv_ptr->pv_name; - else basename++; - remove_proc_entry(basename, vg_ptr->pv_subdir_pde); - } -} +static int lvm_do_pv_flush(void *arg) +{ + pv_flush_req_t pv_flush_req; + if (copy_from_user(&pv_flush_req, arg, + sizeof(pv_flush_req)) != 0) + return -EFAULT; -/* - * create a /proc entry for a volume group - */ -void lvm_do_create_proc_entry_of_vg ( vg_t *vg_ptr) { - int l, p; - pv_t *pv_ptr; - lv_t *lv_ptr; + fsync_dev(pv_flush_req.pv_dev); + invalidate_buffers(pv_flush_req.pv_dev); - pde = create_proc_entry(vg_ptr->vg_name, S_IFDIR, - lvm_proc_vg_subdir); - if ( pde != NULL) { - vg_ptr->vg_dir_pde = pde; - pde = create_proc_entry("group", S_IFREG, - vg_ptr->vg_dir_pde); - if ( pde != NULL) { - pde->read_proc = lvm_proc_read_vg_info; - pde->data = vg_ptr; - } - pde = create_proc_entry(LVM_LV_SUBDIR, S_IFDIR, - vg_ptr->vg_dir_pde); - if ( pde != NULL) { - vg_ptr->lv_subdir_pde = pde; - for ( l = 0; l < vg_ptr->lv_max; l++) { - if ( ( lv_ptr = vg_ptr->lv[l]) == NULL) continue; - lvm_do_create_proc_entry_of_lv ( vg_ptr, lv_ptr); - } - } - pde = create_proc_entry(LVM_PV_SUBDIR, S_IFDIR, - vg_ptr->vg_dir_pde); - if ( pde != NULL) { - vg_ptr->pv_subdir_pde = pde; - for ( p = 0; p < vg_ptr->pv_max; p++) { - if ( ( pv_ptr = vg_ptr->pv[p]) == NULL) continue; - lvm_do_create_proc_entry_of_pv ( vg_ptr, pv_ptr); - } - } - } + return 0; } /* - * remove a /proc entry for a volume group - */ -void lvm_do_remove_proc_entry_of_vg ( vg_t *vg_ptr) { - int l, p; - lv_t *lv_ptr; - pv_t *pv_ptr; - - for ( l = 0; l < vg_ptr->lv_max; l++) { - if ( ( lv_ptr = vg_ptr->lv[l]) == NULL) continue; - lvm_do_remove_proc_entry_of_lv ( vg_ptr, vg_ptr->lv[l]); - } - for ( p = 0; p < vg_ptr->pv_max; p++) { - if ( ( pv_ptr = vg_ptr->pv[p]) == NULL) continue; - lvm_do_remove_proc_entry_of_pv ( vg_ptr, vg_ptr->pv[p]); - } - if ( vg_ptr->vg_dir_pde != NULL) { - remove_proc_entry(LVM_LV_SUBDIR, vg_ptr->vg_dir_pde); - remove_proc_entry(LVM_PV_SUBDIR, vg_ptr->vg_dir_pde); - remove_proc_entry("group", vg_ptr->vg_dir_pde); - remove_proc_entry(vg_ptr->vg_name, lvm_proc_vg_subdir); - } -} - - -/* * support function initialize gendisk variables */ -void __init lvm_geninit(struct gendisk *lvm_gdisk) +static void __init lvm_geninit(struct gendisk *lvm_gdisk) { int i = 0; @@ -3025,36 +2669,72 @@ blk_size[MAJOR_NR] = lvm_size; blksize_size[MAJOR_NR] = lvm_blocksizes; - hardsect_size[MAJOR_NR] = lvm_blocksizes; + hardsect_size[MAJOR_NR] = lvm_hardsectsizes; return; } /* lvm_gen_init() */ +static void _queue_io(struct buffer_head *bh, int rw) { + if (bh->b_reqnext) BUG(); + bh->b_reqnext = _pe_requests; + _pe_requests = bh; +} + +static struct buffer_head *_dequeue_io(void) +{ + struct buffer_head *bh = _pe_requests; + _pe_requests = NULL; + return bh; +} + +static void _flush_io(struct buffer_head *bh) +{ + while (bh) { + struct buffer_head *next = bh->b_reqnext; + bh->b_reqnext = NULL; + /* resubmit this buffer head */ + generic_make_request(WRITE, bh); + bh = next; + } +} + /* - * return a pointer to a '-' padded uuid + * we must open the pv's before we use them */ -static char *lvm_show_uuid ( char *uuidstr) { - int i, j; - static char uuid[NAME_LEN] = { 0, }; +static int _open_pv(pv_t *pv) { + int err; + struct block_device *bd; - memset ( uuid, 0, NAME_LEN); + if (!(bd = bdget(kdev_t_to_nr(pv->pv_dev)))) + return -ENOMEM; - i = 6; - memcpy ( uuid, uuidstr, i); - uuidstr += i; + err = blkdev_get(bd, FMODE_READ|FMODE_WRITE, 0, BDEV_FILE); + if (err) + return err; - for ( j = 0; j < 6; j++) { - uuid[i++] = '-'; - memcpy ( &uuid[i], uuidstr, 4); - uuidstr += 4; - i += 4; + pv->bd = bd; + return 0; +} + +static void _close_pv(pv_t *pv) { + if (pv) { + struct block_device *bdev = pv->bd; + pv->bd = NULL; + if (bdev) + blkdev_put(bdev, BDEV_FILE); } +} - memcpy ( &uuid[i], uuidstr, 2 ); +static unsigned long _sectors_to_k(unsigned long sect) +{ + if(SECTOR_SIZE > 1024) { + return sect * (SECTOR_SIZE / 1024); + } - return uuid; + return sect / (1024 / SECTOR_SIZE); } module_init(lvm_init); module_exit(lvm_cleanup); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/md/md.c linux.ac/drivers/md/md.c --- linux.vanilla/drivers/md/md.c Thu Oct 11 13:52:09 2001 +++ linux.ac/drivers/md/md.c Thu Oct 11 14:38:09 2001 @@ -2152,7 +2152,7 @@ return -EFAULT; nr = info.number; - if (nr >= MD_SB_DISKS) + if (nr >= mddev->sb->raid_disks+mddev->sb->spare_disks) return -EINVAL; SET_FROM_SB(major); @@ -3395,9 +3395,8 @@ /* * Tune reconstruction: */ - window = MAX_READAHEAD*(PAGE_SIZE/512); - printk(KERN_INFO "md: using %dk window, over a total of %d blocks.\n", - window/2,max_sectors/2); + window = vm_max_readahead*(PAGE_SIZE/512); + printk(KERN_INFO "md: using %dk window, over a total of %d blocks.\n",window/2,max_sectors/2); atomic_set(&mddev->recovery_active, 0); init_waitqueue_head(&mddev->recovery_wait); @@ -4036,4 +4035,4 @@ MD_EXPORT_SYMBOL(mddev_map); MD_EXPORT_SYMBOL(md_check_ordering); MD_EXPORT_SYMBOL(get_spare); - +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/bw-qcam.c linux.ac/drivers/media/video/bw-qcam.c --- linux.vanilla/drivers/media/video/bw-qcam.c Thu Oct 11 13:52:09 2001 +++ linux.ac/drivers/media/video/bw-qcam.c Wed Oct 10 01:47:55 2001 @@ -1,8 +1,6 @@ /* * QuickCam Driver For Video4Linux. * - * This version only works as a module. - * * Video4Linux conversion work by Alan Cox. * Parport compatibility by Phil Blundell. * Busy loop avoidance by Mark Cooke. @@ -991,11 +989,20 @@ MODULE_PARM(parport, "1-" __MODULE_STRING(MAX_CAMS) "s"); #endif -#ifdef MODULE -int init_module(void) +static void __exit exit_bw_qcams(void) +{ + unsigned int i; + + for (i = 0; i < num_cams; i++) + close_bwqcam(qcams[i]); +} + +static int __init init_bw_qcams(void) { struct parport *port; +#ifdef MODULE int n; + if(parport[0] && strncmp(parport[0], "auto", 4)){ /* user gave parport parameters */ for(n=0; parport[n] && nnext) init_bwqcam(port); return 0; -} #endif +} + +module_init(init_bw_qcams); +module_exit(exit_bw_qcams); + MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/cpia.c linux.ac/drivers/media/video/cpia.c --- linux.vanilla/drivers/media/video/cpia.c Thu Oct 11 13:52:09 2001 +++ linux.ac/drivers/media/video/cpia.c Thu Oct 11 17:59:57 2001 @@ -56,8 +56,9 @@ #ifdef MODULE MODULE_PARM(video_nr,"i"); -MODULE_AUTHOR("Scott J. Bertin & Peter Pregler & Johannes Erdfelt "); +MODULE_AUTHOR("Scott J. Bertin & Peter Pregler & Johannes Erdfelt "); MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras"); +MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("video"); #endif @@ -2869,7 +2870,7 @@ /* set video size */ video_size = match_videosize(vm.width, vm.height); - if (cam->video_size < 0) { + if (video_size < 0) { retval = -EINVAL; break; } @@ -3253,41 +3254,7 @@ } } -/**************************************************************************** - * - * Module routines - * - ***************************************************************************/ - -#ifdef MODULE -int init_module(void) -{ - printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, - CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); -#ifdef CONFIG_PROC_FS - proc_cpia_create(); -#endif -#ifdef CONFIG_KMOD -#ifdef CONFIG_VIDEO_CPIA_PP_MODULE - request_module("cpia_pp"); -#endif -#ifdef CONFIG_VIDEO_CPIA_USB_MODULE - request_module("cpia_usb"); -#endif -#endif -return 0; -} - -void cleanup_module(void) -{ -#ifdef CONFIG_PROC_FS - proc_cpia_destroy(); -#endif -} - -#else - -int cpia_init(struct video_init *unused) +static int __init cpia_init(void) { printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); @@ -3313,9 +3280,17 @@ return 0; } +static void __exit cpia_exit(void) +{ +#ifdef CONFIG_PROC_FS + proc_cpia_destroy(); +#endif +} + +module_init(cpia_init); +module_exit(cpia_exit); + /* Exported symbols for modules. */ EXPORT_SYMBOL(cpia_register_camera); EXPORT_SYMBOL(cpia_unregister_camera); - -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/cpia.h linux.ac/drivers/media/video/cpia.h --- linux.vanilla/drivers/media/video/cpia.h Fri Mar 2 19:12:10 2001 +++ linux.ac/drivers/media/video/cpia.h Fri Oct 12 12:51:52 2001 @@ -416,6 +416,26 @@ } while (0) +static inline void cpia_add_to_list(struct cam_data* l, struct cam_data* drv) +{ + drv->next = l; + drv->previous = &l; + l = drv; +} + + +static inline void cpia_remove_from_list(struct cam_data* drv) +{ + if (drv->previous != NULL) { + if (drv->next != NULL) + drv->next->previous = drv->previous; + *(drv->previous) = drv->next; + drv->previous = NULL; + drv->next = NULL; + } +} + + #endif /* __KERNEL__ */ #endif /* cpia_h */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/cpia_pp.c linux.ac/drivers/media/video/cpia_pp.c --- linux.vanilla/drivers/media/video/cpia_pp.c Fri Mar 2 19:12:10 2001 +++ linux.ac/drivers/media/video/cpia_pp.c Wed Oct 10 01:47:55 2001 @@ -123,6 +123,8 @@ MODULE_AUTHOR("B. Huisman & Peter Pregler "); MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras"); +MODULE_LICENSE("GPL"); + MODULE_PARM(parport, "1-" __MODULE_STRING(PARPORT_MAX) "s"); MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp."); #else @@ -158,6 +160,7 @@ }; static struct cam_data *cam_list; +static spinlock_t cam_list_lock_pp; #ifdef _CPIA_DEBUG_ #define DEB_PORT(port) { \ @@ -582,7 +585,9 @@ kfree(cam); return -ENXIO; } - ADD_TO_LIST(cam_list, cpia); + spin_lock( &cam_list_lock_pp ); + cpia_add_to_list(cam_list, cpia); + spin_unlock( &cam_list_lock_pp ); return 0; } @@ -591,11 +596,12 @@ { struct cam_data *cpia; + spin_lock( &cam_list_lock_pp ); for(cpia = cam_list; cpia != NULL; cpia = cpia->next) { struct pp_cam_entry *cam = cpia->lowlevel_data; if (cam && cam->port->number == port->number) { - REMOVE_FROM_LIST(cpia); - + cpia_remove_from_list(cpia); + spin_unlock( &cam_list_lock_pp ); cpia_unregister_camera(cpia); if(cam->open_count > 0) { @@ -660,6 +666,9 @@ LOG ("unable to register with parport\n"); return -EIO; } + + cam_list = NULL; + spin_lock_init( &cam_list_lock_pp ); return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/cpia_usb.c linux.ac/drivers/media/video/cpia_usb.c --- linux.vanilla/drivers/media/video/cpia_usb.c Thu Oct 11 13:52:09 2001 +++ linux.ac/drivers/media/video/cpia_usb.c Thu Oct 11 14:40:28 2001 @@ -104,6 +104,7 @@ }; static struct cam_data *cam_list; +static spinlock_t cam_list_lock_usb; static void cpia_usb_complete(struct urb *urb) { @@ -536,7 +537,9 @@ goto fail_all; } - ADD_TO_LIST(cam_list, cam); + spin_lock( &cam_list_lock_usb ); + cpia_add_to_list(cam_list, cam); + spin_unlock( &cam_list_lock_usb ); return cam; @@ -563,6 +566,8 @@ }; MODULE_DEVICE_TABLE (usb, cpia_id_table); +MODULE_LICENSE("GPL"); + static struct usb_driver cpia_driver = { name: "cpia", @@ -579,8 +584,10 @@ struct cam_data *cam = (struct cam_data *) ptr; struct usb_cpia *ucpia = (struct usb_cpia *) cam->lowlevel_data; - REMOVE_FROM_LIST(cam); - + spin_lock( &cam_list_lock_usb ); + cpia_remove_from_list(cam); + spin_unlock( &cam_list_lock_usb ); + /* Don't even try to reset the altsetting if we're disconnected */ cpia_usb_free_resources(ucpia, 0); @@ -620,7 +627,7 @@ static int __init usb_cpia_init(void) { cam_list = NULL; - + spin_lock_init(&cam_list_lock_usb); return usb_register(&cpia_driver); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/planb.c linux.ac/drivers/media/video/planb.c --- linux.vanilla/drivers/media/video/planb.c Thu Oct 11 13:52:09 2001 +++ linux.ac/drivers/media/video/planb.c Wed Oct 10 01:47:55 2001 @@ -55,12 +55,18 @@ #include "planb.h" #include "saa7196.h" - /* Would you mind for some ugly debugging? */ -//#define DEBUG(x...) printk(KERN_DEBUG ## x) /* Debug driver */ -#define DEBUG(x...) /* Don't debug driver */ -//#define IDEBUG(x...) printk(KERN_DEBUG ## x) /* Debug interrupt part */ +#if 0 +#define DEBUG(x...) printk(KERN_DEBUG ## x) /* Debug driver */ +#else +#define DEBUG(x...) /* Don't debug driver */ +#endif + +#if 0 +#define IDEBUG(x...) printk(KERN_DEBUG ## x) /* Debug interrupt part */ +#endif #define IDEBUG(x...) /* Don't debug interrupt part */ +#endif /* Ever seen a Mac with more than 1 of these? */ #define PLANB_MAX 1 @@ -2271,14 +2277,8 @@ } } -#ifdef MODULE - -int init_module(void) -{ -#else -int __init init_planbs(struct video_init *unused) +static int __init init_planbs(void) { -#endif int i; if (find_planb()<=0) @@ -2296,11 +2296,10 @@ return 0; } -#ifdef MODULE - -void cleanup_module(void) +static void __exit exit_planbs(void) { release_planb(); } -#endif +module_init(init_planbs); +module_exit(exit_planbs); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/saa7110.c linux.ac/drivers/media/video/saa7110.c --- linux.vanilla/drivers/media/video/saa7110.c Thu Oct 11 13:52:09 2001 +++ linux.ac/drivers/media/video/saa7110.c Wed Oct 10 01:47:56 2001 @@ -306,7 +306,7 @@ saa7110_write(decoder, 0x0D, 0x06); saa7110_write(decoder, 0x11, 0x2C); saa7110_write(decoder, 0x30, 0x81); -saa7110_write(decoder, 0x2A, 0xDF); + saa7110_write(decoder, 0x2A, 0xDF); break; case VIDEO_MODE_PAL: saa7110_write(decoder, 0x0D, 0x06); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/saa7146.h linux.ac/drivers/media/video/saa7146.h --- linux.vanilla/drivers/media/video/saa7146.h Mon Dec 11 21:15:51 2000 +++ linux.ac/drivers/media/video/saa7146.h Fri Oct 12 12:51:50 2001 @@ -92,7 +92,15 @@ wait_queue_head_t i2cq, debiq, audq, vidq; u8 *vidbuf, *audbuf, *osdbuf, *dmadebi; int audhead, vidhead, osdhead, audtail, vidtail, osdtail; + + /* lock guards between IRQ and writer. sem guards between + * parallel writes/ioctls and provides ioctl level atomicity + * to the apps + */ + spinlock_t lock; /* the device lock */ + struct semaphore sem; /* operation level lock */ + }; #endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/stradis.c linux.ac/drivers/media/video/stradis.c --- linux.vanilla/drivers/media/video/stradis.c Thu Oct 11 13:52:09 2001 +++ linux.ac/drivers/media/video/stradis.c Wed Oct 10 01:47:56 2001 @@ -1352,6 +1352,8 @@ static int saa_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { struct saa7146 *saa = (struct saa7146 *) dev; + int err; + switch (cmd) { case VIDIOCGCAP: { @@ -1400,6 +1402,7 @@ saa->win.color_fmt = format; saawrite(format|0x60, SAA7146_CLIP_FORMAT_CTRL); } + down(&saa->sem); saawrite(((p.brightness & 0xff00) << 16) | ((p.contrast & 0xfe00) << 7) | ((p.colour & 0xfe00) >> 9), SAA7146_BCS_CTRL); @@ -1409,12 +1412,14 @@ SAA7146_MC2_UPLD_HPS_V) << 16) | SAA7146_MC2_UPLD_HPS_H | SAA7146_MC2_UPLD_HPS_V, SAA7146_MC2); + up(&saa->sem); return 0; } case VIDIOCSWIN: { struct video_window vw; struct video_clip *vcp = NULL; + int err = -EFAULT; if (copy_from_user(&vw, arg, sizeof(vw))) return -EFAULT; @@ -1442,6 +1447,8 @@ if (saa->win.height > 576) saa->win.height = 576; } + + down(&saa->sem); /* stop capture */ saawrite((SAA7146_MC1_TR_E_1 << 16), SAA7146_MC1); @@ -1453,16 +1460,22 @@ if (vw.clipcount < 0) { if (copy_from_user(saa->dmavid2, vw.clips, VIDEO_CLIPMAP_SIZE)) - return -EFAULT; + goto out; + } else if(vw.clipcount > 2048) { + err = -EINVAL; + goto out; } else if (vw.clipcount > 0) { if ((vcp = vmalloc(sizeof(struct video_clip) * (vw.clipcount))) == NULL) - return -ENOMEM; + { + err = -ENOMEM; + goto out; + } if (copy_from_user(vcp, vw.clips, sizeof(struct video_clip) * vw.clipcount)) { vfree(vcp); - return -EFAULT; + goto out; } } else /* nothing clipped */ memset(saa->dmavid2, 0, VIDEO_CLIPMAP_SIZE); @@ -1475,7 +1488,10 @@ saawrite(((SAA7146_MC1_TR_E_1 | SAA7146_MC1_TR_E_2) << 16) | 0xffff, SAA7146_MC1); - return 0; + err = 0; +out: + up(&saa->sem); + return err; } case VIDIOCGWIN: { @@ -1495,18 +1511,22 @@ int v; if (copy_from_user(&v, arg, sizeof(v))) return -EFAULT; + down(&saa->sem); if (v == 0) { saa->cap &= ~1; saawrite((SAA7146_MC1_TR_E_1 << 16), SAA7146_MC1); } else { if (saa->win.vidadr == 0 || saa->win.width == 0 - || saa->win.height == 0) + || saa->win.height == 0) { + up(&saa->sem); return -EINVAL; + } saa->cap |= 1; saawrite((SAA7146_MC1_TR_E_1 << 16) | 0xffff, SAA7146_MC1); } + up(&saa->sem); return 0; } case VIDIOCGFBUF: @@ -1533,6 +1553,8 @@ v.depth != 24 && v.depth != 32 && v.width > 16 && v.height > 16 && v.bytesperline > 16) return -EINVAL; + + down(&saa->sem); if (v.base) saa->win.vidadr = (unsigned long) v.base; saa->win.sheight = v.height; @@ -1544,6 +1566,7 @@ DEBUG(printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n", v.base, v.width, v.height, saa->win.bpp, saa->win.bpl)); saa7146_set_winsize(saa); + up(&saa->sem); return 0; } case VIDIOCKEY: @@ -1571,6 +1594,9 @@ if (copy_from_user(&v, arg, sizeof(v))) return -EFAULT; i = (~(v.volume>>8))&0xff; + + down(&saa->sem); + if (!HaveCS4341) { if (v.flags & VIDEO_AUDIO_MUTE) { debiwrite(saa, debNormal, @@ -1594,6 +1620,8 @@ cs4341_setlevel(saa, i, i); } saa->audio_dev = v; + + up(&saa->sem); return 0; } @@ -1615,13 +1643,19 @@ if (copy_from_user((void *) &pmode, arg, sizeof(struct video_play_mode))) return -EFAULT; + + down(&saa->sem); + switch (pmode.mode) { case VID_PLAY_VID_OUT_MODE: if (pmode.p1 != VIDEO_MODE_NTSC && pmode.p1 != VIDEO_MODE_PAL) + { + up(&saa->sem); return -EINVAL; + } set_out_format(saa, pmode.p1); - return 0; + break; case VID_PLAY_GENLOCK: debiwrite(saa, debNormal, XILINX_CTL0, @@ -1630,7 +1664,7 @@ if (NewCard) set_genlock_offset(saa, pmode.p2); - return 0; + break; case VID_PLAY_NORMAL: debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, @@ -1638,7 +1672,7 @@ ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); saa->playmode = pmode.mode; - return 0; + break; case VID_PLAY_PAUSE: /* IBM removed the PAUSE command */ /* they say use SINGLE_FRAME now */ @@ -1652,18 +1686,18 @@ ChipControl, 2); } saa->playmode = pmode.mode; - return 0; + break; case VID_PLAY_FAST_FORWARD: ibm_send_command(saa, IBM_MP2_FAST_FORWARD, 0, 0); saa->playmode = pmode.mode; - return 0; + break; case VID_PLAY_SLOW_MOTION: ibm_send_command(saa, IBM_MP2_SLOW_MOTION, pmode.p1, 0); saa->playmode = pmode.mode; - return 0; + break; case VID_PLAY_IMMEDIATE_NORMAL: /* ensure transfers resume */ debiwrite(saa, debNormal, @@ -1672,7 +1706,7 @@ ibm_send_command(saa, IBM_MP2_IMED_NORM_PLAY, 0, 0); saa->playmode = VID_PLAY_NORMAL; - return 0; + break; case VID_PLAY_SWITCH_CHANNELS: saa->audhead = saa->audtail = 0; saa->vidhead = saa->vidtail = 0; @@ -1690,17 +1724,17 @@ ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); saa->playmode = VID_PLAY_NORMAL; - return 0; + break; case VID_PLAY_FREEZE_FRAME: ibm_send_command(saa, IBM_MP2_FREEZE_FRAME, 0, 0); saa->playmode = pmode.mode; - return 0; + break; case VID_PLAY_STILL_MODE: ibm_send_command(saa, IBM_MP2_SET_STILL_MODE, 0, 0); saa->playmode = pmode.mode; - return 0; + break; case VID_PLAY_MASTER_MODE: if (pmode.p1 == VID_PLAY_MASTER_NONE) saa->boardcfg[1] = 0x13; @@ -1715,7 +1749,7 @@ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); - return 0; + break; case VID_PLAY_ACTIVE_SCANLINES: if (CurrentMode == VIDEO_MODE_PAL) { if (pmode.p1 < 1 || @@ -1736,28 +1770,37 @@ } set_out_format(saa, CurrentMode); case VID_PLAY_RESET: - return do_ibm_reset(saa); + err = do_ibm_reset(saa); + up(&saa->sem); + return err; case VID_PLAY_END_MARK: if (saa->endmarktail < saa->endmarkhead) { if (saa->endmarkhead - - saa->endmarktail < 2) + saa->endmarktail < 2) { + up(&saa->sem); return -ENOSPC; - } else if (saa->endmarkhead <= - saa->endmarktail) { + } + } else { if (saa->endmarktail - saa->endmarkhead > - (MAX_MARKS - 2)) + (MAX_MARKS - 2)) { + up(&saa->sem); return -ENOSPC; - } else - return -ENOSPC; + } + } saa->endmark[saa->endmarktail] = saa->audtail; saa->endmarktail++; if (saa->endmarktail >= MAX_MARKS) saa->endmarktail = 0; + break; + default: + up(&saa->sem); + return -EINVAL; } - return -EINVAL; + up(&saa->sem); + return 0; } case VIDIOCSWRITEMODE: { @@ -1793,11 +1836,13 @@ return -EFAULT; } ucode.data = udata; + down(&saa->sem); if (!strncmp(ucode.loadwhat, "decoder.aud", 11) || !strncmp(ucode.loadwhat, "decoder.vid", 11)) i = initialize_ibmmpeg2(&ucode); else i = initialize_fpga(&ucode); + up(&saa->sem); vfree(udata); if (i) return -EINVAL; @@ -1818,14 +1863,6 @@ return -EFAULT; return 0; } - case VIDIOCSCHAN: /* this makes xawtv happy */ - { - struct video_channel v; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - /* do nothing */ - return 0; - } default: return -ENOIOCTLCMD; } @@ -1835,8 +1872,6 @@ static int saa_mmap(struct video_device *dev, const char *adr, unsigned long size) { - struct saa7146 *saa = (struct saa7146 *) dev; - printk(KERN_DEBUG "stradis%d: saa_mmap called\n", saa->nr); return -EINVAL; } @@ -1853,6 +1888,8 @@ unsigned long todo = count; int blocksize, split; unsigned long flags; + + down(&saa->sem); while (todo > 0) { if (saa->writemode == VID_WRITE_MPEG_AUD) { @@ -1883,19 +1920,27 @@ blocksize = todo; /* double check that we really have space */ if (!blocksize) - return -ENOSPC; + { + count =-ENOSPC; + goto out; + } if (split < blocksize) { if (copy_from_user(saa->audbuf + saa->audtail, buf, split)) - return -EFAULT; + { + count =-EFAULT; + goto out; + } buf += split; todo -= split; blocksize -= split; saa->audtail = 0; } if (copy_from_user(saa->audbuf + saa->audtail, buf, - blocksize)) - return -EFAULT; + blocksize)) { + count = -EFAULT; + goto out; + } saa->audtail += blocksize; todo -= blocksize; buf += blocksize; @@ -1928,11 +1973,17 @@ blocksize = todo; /* double check that we really have space */ if (!blocksize) - return -ENOSPC; + { + count =-ENOSPC; + goto out; + } if (split < blocksize) { if (copy_from_user(saa->vidbuf + saa->vidtail, buf, split)) - return -EFAULT; + { + count =-EFAULT; + goto out; + } buf += split; todo -= split; blocksize -= split; @@ -1940,16 +1991,25 @@ } if (copy_from_user(saa->vidbuf + saa->vidtail, buf, blocksize)) - return -EFAULT; + { + count =-EFAULT; + goto out; + } saa->vidtail += blocksize; todo -= blocksize; buf += blocksize; saa->vidtail &= 0x7ffff; } else if (saa->writemode == VID_WRITE_OSD) { if (count > 131072) - return -ENOSPC; + { + count = -ENOSPC; + goto out; + } if (copy_from_user(saa->osdbuf, buf, count)) - return -EFAULT; + { + count = -EFAULT; + goto out; + } buf += count; saa->osdhead = 0; saa->osdtail = count; @@ -1965,6 +2025,8 @@ saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); } } +out: + up(&saa->sem); return count; } @@ -2012,6 +2074,7 @@ saa = &saa7146s[num]; + init_MUTEX(&saa->sem); saa->endmarkhead = saa->endmarktail = 0; saa->win.x = saa->win.y = 0; saa->win.width = saa->win.cropwidth = 720; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/videodev.c linux.ac/drivers/media/video/videodev.c --- linux.vanilla/drivers/media/video/videodev.c Thu Oct 11 13:52:09 2001 +++ linux.ac/drivers/media/video/videodev.c Wed Oct 10 01:47:56 2001 @@ -63,29 +63,6 @@ #endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */ -#ifdef CONFIG_VIDEO_BWQCAM -extern int init_bw_qcams(struct video_init *); -#endif -#ifdef CONFIG_VIDEO_CPIA -extern int cpia_init(struct video_init *); -#endif -#ifdef CONFIG_VIDEO_PLANB -extern int init_planbs(struct video_init *); -#endif - -static struct video_init video_init_list[]={ -#ifdef CONFIG_VIDEO_BWQCAM - {"bw-qcam", init_bw_qcams}, -#endif -#ifdef CONFIG_VIDEO_CPIA - {"cpia", cpia_init}, -#endif -#ifdef CONFIG_VIDEO_PLANB - {"planb", init_planbs}, -#endif - {"end", NULL} -}; - /* * Read will do some smarts later on. Buffer pin etc. */ @@ -118,7 +95,7 @@ /* * Poll to see if we're readable, can probably be used for timing on incoming - * frames, etc.. + * frames, etc.. */ static unsigned int video_poll(struct file *file, poll_table * wait) @@ -222,8 +199,7 @@ /* * We need to do MMAP support */ - - + int video_mmap(struct file *file, struct vm_area_struct *vma) { int ret = -EINVAL; @@ -548,8 +524,6 @@ static int __init videodev_init(void) { - struct video_init *vfli = video_init_list; - printk(KERN_INFO "Linux video capture interface: v1.00\n"); if(devfs_register_chrdev(VIDEO_MAJOR,"video_capture", &video_fops)) { @@ -557,19 +531,10 @@ return -EIO; } - /* - * Init kernel installed video drivers - */ - #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) videodev_proc_create (); #endif - while(vfli->init!=NULL) - { - vfli->init(vfli); - vfli++; - } return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/isense.c linux.ac/drivers/message/fusion/isense.c --- linux.vanilla/drivers/message/fusion/isense.c Fri Sep 7 17:28:38 2001 +++ linux.ac/drivers/message/fusion/isense.c Wed Oct 10 01:47:56 2001 @@ -87,6 +87,7 @@ EXPORT_NO_SYMBOLS; MODULE_AUTHOR(MODULEAUTHOR); MODULE_DESCRIPTION(my_NAME); +MODULE_LICENSE("GPL"); /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ int __init isense_init(void) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/Config.in linux.ac/drivers/message/i2o/Config.in --- linux.vanilla/drivers/message/i2o/Config.in Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/Config.in Wed Oct 10 01:47:56 2001 @@ -0,0 +1,16 @@ +mainmenu_option next_comment +comment 'I2O device support' + +tristate 'I2O support' CONFIG_I2O + +if [ "$CONFIG_PCI" = "y" ]; then + dep_tristate ' I2O PCI support' CONFIG_I2O_PCI $CONFIG_I2O +fi +dep_tristate ' I2O Block OSM' CONFIG_I2O_BLOCK $CONFIG_I2O +if [ "$CONFIG_NET" = "y" ]; then + dep_tristate ' I2O LAN OSM' CONFIG_I2O_LAN $CONFIG_I2O +fi +dep_tristate ' I2O SCSI OSM' CONFIG_I2O_SCSI $CONFIG_I2O $CONFIG_SCSI +dep_tristate ' I2O /proc support' CONFIG_I2O_PROC $CONFIG_I2O + +endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/Makefile linux.ac/drivers/message/i2o/Makefile --- linux.vanilla/drivers/message/i2o/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/Makefile Wed Oct 10 01:47:56 2001 @@ -0,0 +1,20 @@ +# +# Makefile for the kernel I2O OSM. +# +# Note : at this point, these files are compiled on all systems. +# In the future, some of these should be built conditionally. +# + +O_TARGET := i2o.o + +export-objs := i2o_pci.o i2o_core.o i2o_config.o i2o_block.o i2o_lan.o i2o_scsi.o i2o_proc.o + +obj-$(CONFIG_I2O_PCI) += i2o_pci.o +obj-$(CONFIG_I2O) += i2o_core.o i2o_config.o +obj-$(CONFIG_I2O_BLOCK) += i2o_block.o +obj-$(CONFIG_I2O_LAN) += i2o_lan.o +obj-$(CONFIG_I2O_SCSI) += i2o_scsi.o +obj-$(CONFIG_I2O_PROC) += i2o_proc.o + +include $(TOPDIR)/Rules.make + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/README linux.ac/drivers/message/i2o/README --- linux.vanilla/drivers/message/i2o/README Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/README Wed Oct 10 01:47:56 2001 @@ -0,0 +1,98 @@ + + Linux I2O Support (c) Copyright 1999 Red Hat Software + and others. + + 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. + +AUTHORS (so far) + +Alan Cox, Building Number Three Ltd. + Core code, SCSI and Block OSMs + +Steve Ralston, LSI Logic Corp. + Debugging SCSI and Block OSM + +Deepak Saxena, Intel Corp. + Various core/block extensions + /proc interface, bug fixes + Ioctl interfaces for control + Debugging LAN OSM + +Philip Rumpf + Fixed assorted dumb SMP locking bugs + +Juha Sievanen, University of Helsinki Finland + LAN OSM code + /proc interface to LAN class + Bug fixes + Core code extensions + +Auvo Häkkinen, University of Helsinki Finland + LAN OSM code + /Proc interface to LAN class + Bug fixes + Core code extensions + +Taneli Vähäkangas, University of Helsinki Finland + Fixes to i2o_config + +CREDITS + + This work was made possible by + +Red Hat Software + Funding for the Building #3 part of the project + +Symbios Logic (Now LSI) + Host adapters, hints, known to work platforms when I hit + compatibility problems + +BoxHill Corporation + Loan of initial FibreChannel disk array used for development work. + +European Comission + Funding the work done by the University of Helsinki + +SysKonnect + Loan of FDDI and Gigabit Ethernet cards + +ASUSTeK + Loan of I2O motherboard + +STATUS: + +o The core setup works within limits. +o The scsi layer seems to almost work. + I'm still chasing down the hang bug. +o The block OSM is mostly functional +o LAN OSM works with FDDI and Ethernet cards. + +TO DO: + +General: +o Provide hidden address space if asked +o Long term message flow control +o PCI IOP's without interrupts are not supported yet +o Push FAIL handling into the core +o DDM control interfaces for module load etc +o Add I2O 2.0 support (Deffered to 2.5 kernel) + +Block: +o Multiple major numbers +o Read ahead and cache handling stuff. Talk to Ingo and people +o Power management +o Finish Media changers + +SCSI: +o Find the right way to associate drives/luns/busses + +Lan: +o Performance tuning +o Test Fibre Channel code + +Tape: +o Anyone seen anything implementing this ? + (D.S: Will attempt to do so if spare cycles permit) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/README.ioctl linux.ac/drivers/message/i2o/README.ioctl --- linux.vanilla/drivers/message/i2o/README.ioctl Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/README.ioctl Wed Oct 10 01:47:56 2001 @@ -0,0 +1,394 @@ + +Linux I2O User Space Interface +rev 0.3 - 04/20/99 + +============================================================================= +Originally written by Deepak Saxena(deepak@plexity.net) +Currently maintained by Deepak Saxena(deepak@plexity.net) +============================================================================= + +I. Introduction + +The Linux I2O subsystem provides a set of ioctl() commands that can be +utilized by user space applications to communicate with IOPs and devices +on individual IOPs. This document defines the specific ioctl() commands +that are available to the user and provides examples of their uses. + +This document assumes the reader is familiar with or has access to the +I2O specification as no I2O message parameters are outlined. For information +on the specification, see http://www.i2osig.org + +This document and the I2O user space interface are currently maintained +by Deepak Saxena. Please send all comments, errata, and bug fixes to +deepak@csociety.purdue.edu + +II. IOP Access + +Access to the I2O subsystem is provided through the device file named +/dev/i2o/ctl. This file is a character file with major number 10 and minor +number 166. It can be created through the following command: + + mknod /dev/i2o/ctl c 10 166 + +III. Determining the IOP Count + + SYNOPSIS + + ioctl(fd, I2OGETIOPS, int *count); + + u8 count[MAX_I2O_CONTROLLERS]; + + DESCRIPTION + + This function returns the system's active IOP table. count should + point to a buffer containing MAX_I2O_CONTROLLERS entries. Upon + returning, each entry will contain a non-zero value if the given + IOP unit is active, and NULL if it is inactive or non-existent. + + RETURN VALUE. + + Returns 0 if no errors occur, and -1 otherwise. If an error occurs, + errno is set appropriately: + + EFAULT Invalid user space pointer was passed + +IV. Getting Hardware Resource Table + + SYNOPSIS + + ioctl(fd, I2OHRTGET, struct i2o_cmd_hrt *hrt); + + struct i2o_cmd_hrtlct + { + u32 iop; /* IOP unit number */ + void *resbuf; /* Buffer for result */ + u32 *reslen; /* Buffer length in bytes */ + }; + + DESCRIPTION + + This function returns the Hardware Resource Table of the IOP specified + by hrt->iop in the buffer pointed to by hrt->resbuf. The actual size of + the data is written into *(hrt->reslen). + + RETURNS + + This function returns 0 if no errors occur. If an error occurs, -1 + is returned and errno is set appropriately: + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ENOBUFS Buffer not large enough. If this occurs, the required + buffer length is written into *(hrt->reslen) + +V. Getting Logical Configuration Table + + SYNOPSIS + + ioctl(fd, I2OLCTGET, struct i2o_cmd_lct *lct); + + struct i2o_cmd_hrtlct + { + u32 iop; /* IOP unit number */ + void *resbuf; /* Buffer for result */ + u32 *reslen; /* Buffer length in bytes */ + }; + + DESCRIPTION + + This function returns the Logical Configuration Table of the IOP specified + by lct->iop in the buffer pointed to by lct->resbuf. The actual size of + the data is written into *(lct->reslen). + + RETURNS + + This function returns 0 if no errors occur. If an error occurs, -1 + is returned and errno is set appropriately: + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ENOBUFS Buffer not large enough. If this occurs, the required + buffer length is written into *(lct->reslen) + +VI. Settting Parameters + + SYNOPSIS + + ioctl(fd, I2OPARMSET, struct i2o_parm_setget *ops); + + struct i2o_cmd_psetget + { + u32 iop; /* IOP unit number */ + u32 tid; /* Target device TID */ + void *opbuf; /* Operation List buffer */ + u32 oplen; /* Operation List buffer length in bytes */ + void *resbuf; /* Result List buffer */ + u32 *reslen; /* Result List buffer length in bytes */ + }; + + DESCRIPTION + + This function posts a UtilParamsSet message to the device identified + by ops->iop and ops->tid. The operation list for the message is + sent through the ops->opbuf buffer, and the result list is written + into the buffer pointed to by ops->resbuf. The number of bytes + written is placed into *(ops->reslen). + + RETURNS + + The return value is the size in bytes of the data written into + ops->resbuf if no errors occur. If an error occurs, -1 is returned + and errno is set appropriatly: + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ENOBUFS Buffer not large enough. If this occurs, the required + buffer length is written into *(ops->reslen) + ETIMEDOUT Timeout waiting for reply message + ENOMEM Kernel memory allocation error + + A return value of 0 does not mean that the value was actually + changed properly on the IOP. The user should check the result + list to determine the specific status of the transaction. + +VII. Getting Parameters + + SYNOPSIS + + ioctl(fd, I2OPARMGET, struct i2o_parm_setget *ops); + + struct i2o_parm_setget + { + u32 iop; /* IOP unit number */ + u32 tid; /* Target device TID */ + void *opbuf; /* Operation List buffer */ + u32 oplen; /* Operation List buffer length in bytes */ + void *resbuf; /* Result List buffer */ + u32 *reslen; /* Result List buffer length in bytes */ + }; + + DESCRIPTION + + This function posts a UtilParamsGet message to the device identified + by ops->iop and ops->tid. The operation list for the message is + sent through the ops->opbuf buffer, and the result list is written + into the buffer pointed to by ops->resbuf. The actual size of data + written is placed into *(ops->reslen). + + RETURNS + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ENOBUFS Buffer not large enough. If this occurs, the required + buffer length is written into *(ops->reslen) + ETIMEDOUT Timeout waiting for reply message + ENOMEM Kernel memory allocation error + + A return value of 0 does not mean that the value was actually + properly retreived. The user should check the result list + to determine the specific status of the transaction. + +VIII. Downloading Software + + SYNOPSIS + + ioctl(fd, I2OSWDL, struct i2o_sw_xfer *sw); + + struct i2o_sw_xfer + { + u32 iop; /* IOP unit number */ + u8 flags; /* DownloadFlags field */ + u8 sw_type; /* Software type */ + u32 sw_id; /* Software ID */ + void *buf; /* Pointer to software buffer */ + u32 *swlen; /* Length of software buffer */ + u32 *maxfrag; /* Number of fragments */ + u32 *curfrag; /* Current fragment number */ + }; + + DESCRIPTION + + This function downloads a software fragment pointed by sw->buf + to the iop identified by sw->iop. The DownloadFlags, SwID, SwType + and SwSize fields of the ExecSwDownload message are filled in with + the values of sw->flags, sw->sw_id, sw->sw_type and *(sw->swlen). + + The fragments _must_ be sent in order and be 8K in size. The last + fragment _may_ be shorter, however. The kernel will compute its + size based on information in the sw->swlen field. + + Please note that SW transfers can take a long time. + + RETURNS + + This function returns 0 no errors occur. If an error occurs, -1 + is returned and errno is set appropriatly: + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ETIMEDOUT Timeout waiting for reply message + ENOMEM Kernel memory allocation error + +IX. Uploading Software + + SYNOPSIS + + ioctl(fd, I2OSWUL, struct i2o_sw_xfer *sw); + + struct i2o_sw_xfer + { + u32 iop; /* IOP unit number */ + u8 flags; /* UploadFlags */ + u8 sw_type; /* Software type */ + u32 sw_id; /* Software ID */ + void *buf; /* Pointer to software buffer */ + u32 *swlen; /* Length of software buffer */ + u32 *maxfrag; /* Number of fragments */ + u32 *curfrag; /* Current fragment number */ + }; + + DESCRIPTION + + This function uploads a software fragment from the IOP identified + by sw->iop, sw->sw_type, sw->sw_id and optionally sw->swlen fields. + The UploadFlags, SwID, SwType and SwSize fields of the ExecSwUpload + message are filled in with the values of sw->flags, sw->sw_id, + sw->sw_type and *(sw->swlen). + + The fragments _must_ be requested in order and be 8K in size. The + user is responsible for allocating memory pointed by sw->buf. The + last fragment _may_ be shorter. + + Please note that SW transfers can take a long time. + + RETURNS + + This function returns 0 if no errors occur. If an error occurs, -1 + is returned and errno is set appropriatly: + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ETIMEDOUT Timeout waiting for reply message + ENOMEM Kernel memory allocation error + +X. Removing Software + + SYNOPSIS + + ioctl(fd, I2OSWDEL, struct i2o_sw_xfer *sw); + + struct i2o_sw_xfer + { + u32 iop; /* IOP unit number */ + u8 flags; /* RemoveFlags */ + u8 sw_type; /* Software type */ + u32 sw_id; /* Software ID */ + void *buf; /* Unused */ + u32 *swlen; /* Length of the software data */ + u32 *maxfrag; /* Unused */ + u32 *curfrag; /* Unused */ + }; + + DESCRIPTION + + This function removes software from the IOP identified by sw->iop. + The RemoveFlags, SwID, SwType and SwSize fields of the ExecSwRemove message + are filled in with the values of sw->flags, sw->sw_id, sw->sw_type and + *(sw->swlen). Give zero in *(sw->len) if the value is unknown. IOP uses + *(sw->swlen) value to verify correct identication of the module to remove. + The actual size of the module is written into *(sw->swlen). + + RETURNS + + This function returns 0 if no errors occur. If an error occurs, -1 + is returned and errno is set appropriatly: + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ETIMEDOUT Timeout waiting for reply message + ENOMEM Kernel memory allocation error + +X. Validating Configuration + + SYNOPSIS + + ioctl(fd, I2OVALIDATE, int *iop); + u32 iop; + + DESCRIPTION + + This function posts an ExecConfigValidate message to the controller + identified by iop. This message indicates that the the current + configuration is accepted. The iop changes the status of suspect drivers + to valid and may delete old drivers from its store. + + RETURNS + + This function returns 0 if no erro occur. If an error occurs, -1 is + returned and errno is set appropriatly: + + ETIMEDOUT Timeout waiting for reply message + ENXIO Invalid IOP number + +XI. Configuration Dialog + + SYNOPSIS + + ioctl(fd, I2OHTML, struct i2o_html *htquery); + struct i2o_html + { + u32 iop; /* IOP unit number */ + u32 tid; /* Target device ID */ + u32 page; /* HTML page */ + void *resbuf; /* Buffer for reply HTML page */ + u32 *reslen; /* Length in bytes of reply buffer */ + void *qbuf; /* Pointer to HTTP query string */ + u32 qlen; /* Length in bytes of query string buffer */ + }; + + DESCRIPTION + + This function posts an UtilConfigDialog message to the device identified + by htquery->iop and htquery->tid. The requested HTML page number is + provided by the htquery->page field, and the resultant data is stored + in the buffer pointed to by htquery->resbuf. If there is an HTTP query + string that is to be sent to the device, it should be sent in the buffer + pointed to by htquery->qbuf. If there is no query string, this field + should be set to NULL. The actual size of the reply received is written + into *(htquery->reslen). + + RETURNS + + This function returns 0 if no error occur. If an error occurs, -1 + is returned and errno is set appropriatly: + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ENOBUFS Buffer not large enough. If this occurs, the required + buffer length is written into *(ops->reslen) + ETIMEDOUT Timeout waiting for reply message + ENOMEM Kernel memory allocation error + +XII. Events + + In the process of determining this. Current idea is to have use + the select() interface to allow user apps to periodically poll + the /dev/i2o/ctl device for events. When select() notifies the user + that an event is available, the user would call read() to retrieve + a list of all the events that are pending for the specific device. + +============================================================================= +Revision History +============================================================================= + +Rev 0.1 - 04/01/99 +- Initial revision + +Rev 0.2 - 04/06/99 +- Changed return values to match UNIX ioctl() standard. Only return values + are 0 and -1. All errors are reported through errno. +- Added summary of proposed possible event interfaces + +Rev 0.3 - 04/20/99 +- Changed all ioctls() to use pointers to user data instead of actual data +- Updated error values to match the code diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_block.c linux.ac/drivers/message/i2o/i2o_block.c --- linux.vanilla/drivers/message/i2o/i2o_block.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_block.c Wed Oct 10 01:47:56 2001 @@ -0,0 +1,2043 @@ +/* + * I2O Random Block Storage Class OSM + * + * (C) Copyright 1999 Red Hat Software + * + * Written by Alan Cox, Building Number Three Ltd + * + * 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 is a beta test release. Most of the good code was taken + * from the nbd driver by Pavel Machek, who in turn took some of it + * from loop.c. Isn't free software great for reusability 8) + * + * Fixes/additions: + * Steve Ralston: + * Multiple device handling error fixes, + * Added a queue depth. + * Alan Cox: + * FC920 has an rmw bug. Dont or in the end marker. + * Removed queue walk, fixed for 64bitness. + * Deepak Saxena: + * Independent queues per IOP + * Support for dynamic device creation/deletion + * Code cleanup + * Support for larger I/Os through merge* functions + * (taken from DAC960 driver) + * Boji T Kannanthanam: + * Set the I2O Block devices to be detected in increasing + * order of TIDs during boot. + * Search and set the I2O block device that we boot off from as + * the first device to be claimed (as /dev/i2o/hda) + * Properly attach/detach I2O gendisk structure from the system + * gendisk list. The I2O block devices now appear in + * /proc/partitions. + * + * To do: + * Serial number scanning to find duplicates for FC multipathing + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MAJOR_NR I2O_MAJOR + +#include + +#define MAX_I2OB 16 + +#define MAX_I2OB_DEPTH 128 +#define MAX_I2OB_RETRIES 4 + +//#define DRIVERDEBUG +#ifdef DRIVERDEBUG +#define DEBUG( s ) printk( s ) +#else +#define DEBUG( s ) +#endif + +/* + * Events that this OSM is interested in + */ +#define I2OB_EVENT_MASK (I2O_EVT_IND_BSA_VOLUME_LOAD | \ + I2O_EVT_IND_BSA_VOLUME_UNLOAD | \ + I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ | \ + I2O_EVT_IND_BSA_CAPACITY_CHANGE | \ + I2O_EVT_IND_BSA_SCSI_SMART ) + + +/* + * I2O Block Error Codes - should be in a header file really... + */ +#define I2O_BSA_DSC_SUCCESS 0x0000 +#define I2O_BSA_DSC_MEDIA_ERROR 0x0001 +#define I2O_BSA_DSC_ACCESS_ERROR 0x0002 +#define I2O_BSA_DSC_DEVICE_FAILURE 0x0003 +#define I2O_BSA_DSC_DEVICE_NOT_READY 0x0004 +#define I2O_BSA_DSC_MEDIA_NOT_PRESENT 0x0005 +#define I2O_BSA_DSC_MEDIA_LOCKED 0x0006 +#define I2O_BSA_DSC_MEDIA_FAILURE 0x0007 +#define I2O_BSA_DSC_PROTOCOL_FAILURE 0x0008 +#define I2O_BSA_DSC_BUS_FAILURE 0x0009 +#define I2O_BSA_DSC_ACCESS_VIOLATION 0x000A +#define I2O_BSA_DSC_WRITE_PROTECTED 0x000B +#define I2O_BSA_DSC_DEVICE_RESET 0x000C +#define I2O_BSA_DSC_VOLUME_CHANGED 0x000D +#define I2O_BSA_DSC_TIMEOUT 0x000E + +/* + * Some of these can be made smaller later + */ + +static int i2ob_blksizes[MAX_I2OB<<4]; +static int i2ob_hardsizes[MAX_I2OB<<4]; +static int i2ob_sizes[MAX_I2OB<<4]; +static int i2ob_media_change_flag[MAX_I2OB]; +static u32 i2ob_max_sectors[MAX_I2OB<<4]; + +static int i2ob_context; + +/* + * I2O Block device descriptor + */ +struct i2ob_device +{ + struct i2o_controller *controller; + struct i2o_device *i2odev; + int unit; + int tid; + int flags; + int refcnt; + struct request *head, *tail; + request_queue_t *req_queue; + int max_segments; + int done_flag; + int constipated; + int depth; +}; + +/* + * FIXME: + * We should cache align these to avoid ping-ponging lines on SMP + * boxes under heavy I/O load... + */ +struct i2ob_request +{ + struct i2ob_request *next; + struct request *req; + int num; +}; + +/* + * Per IOP requst queue information + * + * We have a separate requeust_queue_t per IOP so that a heavilly + * loaded I2O block device on an IOP does not starve block devices + * across all I2O controllers. + * + */ +struct i2ob_iop_queue +{ + atomic_t queue_depth; + struct i2ob_request request_queue[MAX_I2OB_DEPTH]; + struct i2ob_request *i2ob_qhead; + request_queue_t req_queue; +}; +static struct i2ob_iop_queue *i2ob_queues[MAX_I2O_CONTROLLERS]; +static struct i2ob_request *i2ob_backlog[MAX_I2O_CONTROLLERS]; +static struct i2ob_request *i2ob_backlog_tail[MAX_I2O_CONTROLLERS]; + +/* + * Each I2O disk is one of these. + */ + +static struct i2ob_device i2ob_dev[MAX_I2OB<<4]; +static int i2ob_dev_count = 0; +static struct hd_struct i2ob[MAX_I2OB<<4]; +static struct gendisk i2ob_gendisk; /* Declared later */ + +/* + * Mutex and spin lock for event handling synchronization + * evt_msg contains the last event. + */ +static DECLARE_MUTEX_LOCKED(i2ob_evt_sem); +static DECLARE_COMPLETION(i2ob_thread_dead); +static spinlock_t i2ob_evt_lock = SPIN_LOCK_UNLOCKED; +static u32 evt_msg[MSG_FRAME_SIZE>>2]; + +static struct timer_list i2ob_timer; +static int i2ob_timer_started = 0; + +static void i2o_block_reply(struct i2o_handler *, struct i2o_controller *, + struct i2o_message *); +static void i2ob_new_device(struct i2o_controller *, struct i2o_device *); +static void i2ob_del_device(struct i2o_controller *, struct i2o_device *); +static void i2ob_reboot_event(void); +static int i2ob_install_device(struct i2o_controller *, struct i2o_device *, int); +static void i2ob_end_request(struct request *); +static void i2ob_request(request_queue_t *); +static int i2ob_backlog_request(struct i2o_controller *, struct i2ob_device *); +static int i2ob_init_iop(unsigned int); +static request_queue_t* i2ob_get_queue(kdev_t); +static int i2ob_query_device(struct i2ob_device *, int, int, void*, int); +static int do_i2ob_revalidate(kdev_t, int); +static int i2ob_evt(void *); + +static int evt_pid = 0; +static int evt_running = 0; +static int scan_unit = 0; + +/* + * I2O OSM registration structure...keeps getting bigger and bigger :) + */ +static struct i2o_handler i2o_block_handler = +{ + i2o_block_reply, + i2ob_new_device, + i2ob_del_device, + i2ob_reboot_event, + "I2O Block OSM", + 0, + I2O_CLASS_RANDOM_BLOCK_STORAGE +}; + +/* + * Get a message + */ + +static u32 i2ob_get(struct i2ob_device *dev) +{ + struct i2o_controller *c=dev->controller; + return I2O_POST_READ32(c); +} + +/* + * Turn a Linux block request into an I2O block read/write. + */ + +static int i2ob_send(u32 m, struct i2ob_device *dev, struct i2ob_request *ireq, u32 base, int unit) +{ + struct i2o_controller *c = dev->controller; + int tid = dev->tid; + unsigned long msg; + unsigned long mptr; + u64 offset; + struct request *req = ireq->req; + struct buffer_head *bh = req->bh; + int count = req->nr_sectors<<9; + char *last = NULL; + unsigned short size = 0; + + // printk(KERN_INFO "i2ob_send called\n"); + /* Map the message to a virtual address */ + msg = c->mem_offset + m; + + /* + * Build the message based on the request. + */ + __raw_writel(i2ob_context|(unit<<8), msg+8); + __raw_writel(ireq->num, msg+12); + __raw_writel(req->nr_sectors << 9, msg+20); + + /* + * Mask out partitions from now on + */ + unit &= 0xF0; + + /* This can be optimised later - just want to be sure its right for + starters */ + offset = ((u64)(req->sector+base)) << 9; + __raw_writel( offset & 0xFFFFFFFF, msg+24); + __raw_writel(offset>>32, msg+28); + mptr=msg+32; + + if(req->cmd == READ) + { + __raw_writel(I2O_CMD_BLOCK_READ<<24|HOST_TID<<12|tid, msg+4); + while(bh!=NULL) + { + if(bh->b_data == last) { + size += bh->b_size; + last += bh->b_size; + if(bh->b_reqnext) + __raw_writel(0x14000000|(size), mptr-8); + else + __raw_writel(0xD4000000|(size), mptr-8); + } + else + { + if(bh->b_reqnext) + __raw_writel(0x10000000|(bh->b_size), mptr); + else + __raw_writel(0xD0000000|(bh->b_size), mptr); + __raw_writel(virt_to_bus(bh->b_data), mptr+4); + mptr += 8; + size = bh->b_size; + last = bh->b_data + size; + } + + count -= bh->b_size; + bh = bh->b_reqnext; + } + /* + * Heuristic for now since the block layer doesnt give + * us enough info. If its a big write assume sequential + * readahead on controller. If its small then don't read + * ahead but do use the controller cache. + */ + if(size >= 8192) + __raw_writel((8<<24)|(1<<16)|8, msg+16); + else + __raw_writel((8<<24)|(1<<16)|4, msg+16); + } + else if(req->cmd == WRITE) + { + __raw_writel(I2O_CMD_BLOCK_WRITE<<24|HOST_TID<<12|tid, msg+4); + while(bh!=NULL) + { + if(bh->b_data == last) { + size += bh->b_size; + last += bh->b_size; + if(bh->b_reqnext) + __raw_writel(0x14000000|(size), mptr-8); + else + __raw_writel(0xD4000000|(size), mptr-8); + } + else + { + if(bh->b_reqnext) + __raw_writel(0x14000000|(bh->b_size), mptr); + else + __raw_writel(0xD4000000|(bh->b_size), mptr); + __raw_writel(virt_to_bus(bh->b_data), mptr+4); + mptr += 8; + size = bh->b_size; + last = bh->b_data + size; + } + + count -= bh->b_size; + bh = bh->b_reqnext; + } + + if(c->battery) + { + + if(size>16384) + __raw_writel(4, msg+16); + else + /* + * Allow replies to come back once data is cached in the controller + * This allows us to handle writes quickly thus giving more of the + * queue to reads. + */ + __raw_writel(16, msg+16); + } + else + { + /* Large write, don't cache */ + if(size>8192) + __raw_writel(4, msg+16); + else + /* write through */ + __raw_writel(8, msg+16); + } + } + __raw_writel(I2O_MESSAGE_SIZE(mptr-msg)>>2 | SGL_OFFSET_8, msg); + + if(count != 0) + { + printk(KERN_ERR "Request count botched by %d.\n", count); + } + + i2o_post_message(c,m); + atomic_inc(&i2ob_queues[c->unit]->queue_depth); + + return 0; +} + +/* + * Remove a request from the _locked_ request list. We update both the + * list chain and if this is the last item the tail pointer. Caller + * must hold the lock. + */ + +static inline void i2ob_unhook_request(struct i2ob_request *ireq, + unsigned int iop) +{ + ireq->next = i2ob_queues[iop]->i2ob_qhead; + i2ob_queues[iop]->i2ob_qhead = ireq; +} + +/* + * Request completion handler + */ + +static inline void i2ob_end_request(struct request *req) +{ + /* + * Loop until all of the buffers that are linked + * to this request have been marked updated and + * unlocked. + */ + + while (end_that_request_first( req, !req->errors, "i2o block" )); + + /* + * It is now ok to complete the request. + */ + end_that_request_last( req ); +} + +/* + * Request merging functions + */ +static inline int i2ob_new_segment(request_queue_t *q, struct request *req, + int __max_segments) +{ + int max_segments = i2ob_dev[MINOR(req->rq_dev)].max_segments; + + if (__max_segments < max_segments) + max_segments = __max_segments; + + if (req->nr_segments < max_segments) { + req->nr_segments++; + return 1; + } + return 0; +} + +static int i2ob_back_merge(request_queue_t *q, struct request *req, + struct buffer_head *bh, int __max_segments) +{ + if (req->bhtail->b_data + req->bhtail->b_size == bh->b_data) + return 1; + return i2ob_new_segment(q, req, __max_segments); +} + +static int i2ob_front_merge(request_queue_t *q, struct request *req, + struct buffer_head *bh, int __max_segments) +{ + if (bh->b_data + bh->b_size == req->bh->b_data) + return 1; + return i2ob_new_segment(q, req, __max_segments); +} + +static int i2ob_merge_requests(request_queue_t *q, + struct request *req, + struct request *next, + int __max_segments) +{ + int max_segments = i2ob_dev[MINOR(req->rq_dev)].max_segments; + int total_segments = req->nr_segments + next->nr_segments; + + if (__max_segments < max_segments) + max_segments = __max_segments; + + if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) + total_segments--; + + if (total_segments > max_segments) + return 0; + + req->nr_segments = total_segments; + return 1; +} + +static int i2ob_flush(struct i2o_controller *c, struct i2ob_device *d, int unit) +{ + unsigned long msg; + u32 m = i2ob_get(d); + + if(m == 0xFFFFFFFF) + return -1; + + msg = c->mem_offset + m; + + /* + * Ask the controller to write the cache back. This sorts out + * the supertrak firmware flaw and also does roughly the right + * thing for other cases too. + */ + + __raw_writel(FIVE_WORD_MSG_SIZE|SGL_OFFSET_0, msg); + __raw_writel(I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|d->tid, msg+4); + __raw_writel(i2ob_context|(unit<<8), msg+8); + __raw_writel(0, msg+12); + __raw_writel(60<<16, msg+16); + + i2o_post_message(c,m); + return 0; +} + +/* + * OSM reply handler. This gets all the message replies + */ + +static void i2o_block_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *msg) +{ + unsigned long flags; + struct i2ob_request *ireq = NULL; + u8 st; + u32 *m = (u32 *)msg; + u8 unit = (m[2]>>8)&0xF0; /* low 4 bits are partition */ + struct i2ob_device *dev = &i2ob_dev[(unit&0xF0)]; + + /* + * Pull the lock over ready + */ + + spin_lock_prefetch(&io_request_lock); + + /* + * FAILed message + */ + if(m[0] & (1<<13)) + { + /* + * FAILed message from controller + * We increment the error count and abort it + * + * In theory this will never happen. The I2O block class + * speficiation states that block devices never return + * FAILs but instead use the REQ status field...but + * better be on the safe side since no one really follows + * the spec to the book :) + */ + ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; + ireq->req->errors++; + + spin_lock_irqsave(&io_request_lock, flags); + i2ob_unhook_request(ireq, c->unit); + i2ob_end_request(ireq->req); + spin_unlock_irqrestore(&io_request_lock, flags); + + /* Now flush the message by making it a NOP */ + m[0]&=0x00FFFFFF; + m[0]|=(I2O_CMD_UTIL_NOP)<<24; + i2o_post_message(c,virt_to_bus(m)); + + return; + } + + if(msg->function == I2O_CMD_UTIL_EVT_REGISTER) + { + spin_lock(&i2ob_evt_lock); + memcpy(evt_msg, msg, (m[0]>>16)<<2); + spin_unlock(&i2ob_evt_lock); + up(&i2ob_evt_sem); + return; + } + + if(msg->function == I2O_CMD_BLOCK_CFLUSH) + { + spin_lock_irqsave(&io_request_lock, flags); + dev->constipated=0; + DEBUG(("unconstipated\n")); + if(i2ob_backlog_request(c, dev)==0) + i2ob_request(dev->req_queue); + spin_unlock_irqrestore(&io_request_lock, flags); + return; + } + + if(!dev->i2odev) + { + /* + * This is HACK, but Intel Integrated RAID allows user + * to delete a volume that is claimed, locked, and in use + * by the OS. We have to check for a reply from a + * non-existent device and flag it as an error or the system + * goes kaput... + */ + ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; + ireq->req->errors++; + printk(KERN_WARNING "I2O Block: Data transfer to deleted device!\n"); + spin_lock_irqsave(&io_request_lock, flags); + i2ob_unhook_request(ireq, c->unit); + i2ob_end_request(ireq->req); + spin_unlock_irqrestore(&io_request_lock, flags); + return; + } + + /* + * Lets see what is cooking. We stuffed the + * request in the context. + */ + + ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; + st=m[4]>>24; + + if(st!=0) + { + int err; + char *bsa_errors[] = + { + "Success", + "Media Error", + "Failure communicating to device", + "Device Failure", + "Device is not ready", + "Media not present", + "Media is locked by another user", + "Media has failed", + "Failure communicating to device", + "Device bus failure", + "Device is locked by another user", + "Device is write protected", + "Device has reset", + "Volume has changed, waiting for acknowledgement" + }; + + err = m[4]&0xFFFF; + + /* + * Device not ready means two things. One is that the + * the thing went offline (but not a removal media) + * + * The second is that you have a SuperTrak 100 and the + * firmware got constipated. Unlike standard i2o card + * setups the supertrak returns an error rather than + * blocking for the timeout in these cases. + */ + + + spin_lock_irqsave(&io_request_lock, flags); + if(err==4) + { + /* + * Time to uncork stuff + */ + + if(!dev->constipated) + { + dev->constipated = 1; + DEBUG(("constipated\n")); + /* Now pull the chain */ + if(i2ob_flush(c, dev, unit)<0) + { + DEBUG(("i2ob: Unable to queue flush. Retrying I/O immediately.\n")); + dev->constipated=0; + } + DEBUG(("flushing\n")); + } + + /* + * Recycle the request + */ + +// i2ob_unhook_request(ireq, c->unit); + + /* + * Place it on the recycle queue + */ + + ireq->next = NULL; + if(i2ob_backlog_tail[c->unit]!=NULL) + i2ob_backlog_tail[c->unit]->next = ireq; + else + i2ob_backlog[c->unit] = ireq; + i2ob_backlog_tail[c->unit] = ireq; + + atomic_dec(&i2ob_queues[c->unit]->queue_depth); + + /* + * If the constipator flush failed we want to + * poke the queue again. + */ + + i2ob_request(dev->req_queue); + spin_unlock_irqrestore(&io_request_lock, flags); + + /* + * and out + */ + + return; + } + spin_unlock_irqrestore(&io_request_lock, flags); + printk(KERN_ERR "\n/dev/%s error: %s", dev->i2odev->dev_name, + bsa_errors[m[4]&0XFFFF]); + if(m[4]&0x00FF0000) + printk(" - DDM attempted %d retries", (m[4]>>16)&0x00FF ); + printk(".\n"); + ireq->req->errors++; + } + else + ireq->req->errors = 0; + + /* + * Dequeue the request. We use irqsave locks as one day we + * may be running polled controllers from a BH... + */ + + spin_lock_irqsave(&io_request_lock, flags); + i2ob_unhook_request(ireq, c->unit); + i2ob_end_request(ireq->req); + atomic_dec(&i2ob_queues[c->unit]->queue_depth); + + /* + * We may be able to do more I/O + */ + + if(i2ob_backlog_request(c, dev)==0) + i2ob_request(dev->req_queue); + + spin_unlock_irqrestore(&io_request_lock, flags); +} + +/* + * Event handler. Needs to be a separate thread b/c we may have + * to do things like scan a partition table, or query parameters + * which cannot be done from an interrupt or from a bottom half. + */ +static int i2ob_evt(void *dummy) +{ + unsigned int evt; + unsigned long flags; + int unit; + int i; + //The only event that has data is the SCSI_SMART event. + struct i2o_reply { + u32 header[4]; + u32 evt_indicator; + u8 ASC; + u8 ASCQ; + u8 data[16]; + } *evt_local; + + lock_kernel(); + daemonize(); + unlock_kernel(); + + strcpy(current->comm, "i2oblock"); + evt_running = 1; + + while(1) + { + if(down_interruptible(&i2ob_evt_sem)) + { + evt_running = 0; + printk("exiting..."); + break; + } + + /* + * Keep another CPU/interrupt from overwriting the + * message while we're reading it + * + * We stuffed the unit in the TxContext and grab the event mask + * None of the BSA we care about events have EventData + */ + spin_lock_irqsave(&i2ob_evt_lock, flags); + evt_local = (struct i2o_reply *)evt_msg; + spin_unlock_irqrestore(&i2ob_evt_lock, flags); + + unit = evt_local->header[3]; + evt = evt_local->evt_indicator; + + switch(evt) + { + /* + * New volume loaded on same TID, so we just re-install. + * The TID/controller don't change as it is the same + * I2O device. It's just new media that we have to + * rescan. + */ + case I2O_EVT_IND_BSA_VOLUME_LOAD: + { + i2ob_install_device(i2ob_dev[unit].i2odev->controller, + i2ob_dev[unit].i2odev, unit); + break; + } + + /* + * No media, so set all parameters to 0 and set the media + * change flag. The I2O device is still valid, just doesn't + * have media, so we don't want to clear the controller or + * device pointer. + */ + case I2O_EVT_IND_BSA_VOLUME_UNLOAD: + { + for(i = unit; i <= unit+15; i++) + { + i2ob_sizes[i] = 0; + i2ob_hardsizes[i] = 0; + i2ob_max_sectors[i] = 0; + i2ob[i].nr_sects = 0; + i2ob_gendisk.part[i].nr_sects = 0; + } + i2ob_media_change_flag[unit] = 1; + break; + } + + case I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ: + printk(KERN_WARNING "%s: Attempt to eject locked media\n", + i2ob_dev[unit].i2odev->dev_name); + break; + + /* + * The capacity has changed and we are going to be + * updating the max_sectors and other information + * about this disk. We try a revalidate first. If + * the block device is in use, we don't want to + * do that as there may be I/Os bound for the disk + * at the moment. In that case we read the size + * from the device and update the information ourselves + * and the user can later force a partition table + * update through an ioctl. + */ + case I2O_EVT_IND_BSA_CAPACITY_CHANGE: + { + u64 size; + + if(do_i2ob_revalidate(MKDEV(MAJOR_NR, unit),0) != -EBUSY) + continue; + + if(i2ob_query_device(&i2ob_dev[unit], 0x0004, 0, &size, 8) !=0 ) + i2ob_query_device(&i2ob_dev[unit], 0x0000, 4, &size, 8); + + spin_lock_irqsave(&io_request_lock, flags); + i2ob_sizes[unit] = (int)(size>>10); + i2ob_gendisk.part[unit].nr_sects = size>>9; + i2ob[unit].nr_sects = (int)(size>>9); + spin_unlock_irqrestore(&io_request_lock, flags); + break; + } + + /* + * We got a SCSI SMART event, we just log the relevant + * information and let the user decide what they want + * to do with the information. + */ + case I2O_EVT_IND_BSA_SCSI_SMART: + { + char buf[16]; + printk(KERN_INFO "I2O Block: %s received a SCSI SMART Event\n",i2ob_dev[unit].i2odev->dev_name); + evt_local->data[16]='\0'; + sprintf(buf,"%s",&evt_local->data[0]); + printk(KERN_INFO " Disk Serial#:%s\n",buf); + printk(KERN_INFO " ASC 0x%02x \n",evt_local->ASC); + printk(KERN_INFO " ASCQ 0x%02x \n",evt_local->ASCQ); + break; + } + + /* + * Non event + */ + + case 0: + break; + + /* + * An event we didn't ask for. Call the card manufacturer + * and tell them to fix their firmware :) + */ + default: + printk(KERN_INFO "%s: Received event %d we didn't register for\n" + KERN_INFO " Blame the I2O card manufacturer 8)\n", + i2ob_dev[unit].i2odev->dev_name, evt); + break; + } + }; + + complete_and_exit(&i2ob_thread_dead,0); + return 0; +} + +/* + * The timer handler will attempt to restart requests + * that are queued to the driver. This handler + * currently only gets called if the controller + * had no more room in its inbound fifo. + */ + +static void i2ob_timer_handler(unsigned long q) +{ + unsigned long flags; + + /* + * We cannot touch the request queue or the timer + * flag without holding the io_request_lock. + */ + spin_lock_irqsave(&io_request_lock,flags); + + /* + * Clear the timer started flag so that + * the timer can be queued again. + */ + i2ob_timer_started = 0; + + /* + * Restart any requests. + */ + i2ob_request((request_queue_t*)q); + + /* + * Free the lock. + */ + spin_unlock_irqrestore(&io_request_lock,flags); +} + +static int i2ob_backlog_request(struct i2o_controller *c, struct i2ob_device *dev) +{ + u32 m; + struct i2ob_request *ireq; + + while((ireq=i2ob_backlog[c->unit])!=NULL) + { + int unit; + + if(atomic_read(&i2ob_queues[c->unit]->queue_depth) > dev->depth/4) + break; + + m = i2ob_get(dev); + if(m == 0xFFFFFFFF) + break; + + i2ob_backlog[c->unit] = ireq->next; + if(i2ob_backlog[c->unit] == NULL) + i2ob_backlog_tail[c->unit] = NULL; + + unit = MINOR(ireq->req->rq_dev); + i2ob_send(m, dev, ireq, i2ob[unit].start_sect, unit); + } + if(i2ob_backlog[c->unit]) + return 1; + return 0; +} + +/* + * The I2O block driver is listed as one of those that pulls the + * front entry off the queue before processing it. This is important + * to remember here. If we drop the io lock then CURRENT will change + * on us. We must unlink CURRENT in this routine before we return, if + * we use it. + */ + +static void i2ob_request(request_queue_t *q) +{ + struct request *req; + struct i2ob_request *ireq; + int unit; + struct i2ob_device *dev; + u32 m; + + + while (!list_empty(&q->queue_head)) { + /* + * On an IRQ completion if there is an inactive + * request on the queue head it means it isnt yet + * ready to dispatch. + */ + req = blkdev_entry_next_request(&q->queue_head); + + if(req->rq_status == RQ_INACTIVE) + return; + + unit = MINOR(req->rq_dev); + dev = &i2ob_dev[(unit&0xF0)]; + + /* + * Queue depths probably belong with some kind of + * generic IOP commit control. Certainly its not right + * its global! + */ + if(atomic_read(&i2ob_queues[dev->unit]->queue_depth) >= dev->depth) + break; + + /* + * Is the channel constipated ? + */ + + if(i2ob_backlog[dev->unit]!=NULL) + break; + + /* Get a message */ + m = i2ob_get(dev); + + if(m==0xFFFFFFFF) + { + /* + * See if the timer has already been queued. + */ + if (!i2ob_timer_started) + { + DEBUG((KERN_ERR "i2ob: starting timer\n")); + + /* + * Set the timer_started flag to insure + * that the timer is only queued once. + * Queing it more than once will corrupt + * the timer queue. + */ + i2ob_timer_started = 1; + + /* + * Set up the timer to expire in + * 500ms. + */ + i2ob_timer.expires = jiffies + (HZ >> 1); + i2ob_timer.data = (unsigned int)q; + + /* + * Start it. + */ + + add_timer(&i2ob_timer); + return; + } + } + + /* + * Everything ok, so pull from kernel queue onto our queue + */ + req->errors = 0; + blkdev_dequeue_request(req); + req->waiting = NULL; + + ireq = i2ob_queues[dev->unit]->i2ob_qhead; + i2ob_queues[dev->unit]->i2ob_qhead = ireq->next; + ireq->req = req; + + i2ob_send(m, dev, ireq, i2ob[unit].start_sect, (unit&0xF0)); + } +} + + +/* + * SCSI-CAM for ioctl geometry mapping + * Duplicated with SCSI - this should be moved into somewhere common + * perhaps genhd ? + * + * LBA -> CHS mapping table taken from: + * + * "Incorporating the I2O Architecture into BIOS for Intel Architecture + * Platforms" + * + * This is an I2O document that is only available to I2O members, + * not developers. + * + * From my understanding, this is how all the I2O cards do this + * + * Disk Size | Sectors | Heads | Cylinders + * ---------------+---------+-------+------------------- + * 1 < X <= 528M | 63 | 16 | X/(63 * 16 * 512) + * 528M < X <= 1G | 63 | 32 | X/(63 * 32 * 512) + * 1 < X <528M | 63 | 16 | X/(63 * 16 * 512) + * 1 < X <528M | 63 | 16 | X/(63 * 16 * 512) + * + */ +#define BLOCK_SIZE_528M 1081344 +#define BLOCK_SIZE_1G 2097152 +#define BLOCK_SIZE_21G 4403200 +#define BLOCK_SIZE_42G 8806400 +#define BLOCK_SIZE_84G 17612800 + +static void i2o_block_biosparam( + unsigned long capacity, + unsigned short *cyls, + unsigned char *hds, + unsigned char *secs) +{ + unsigned long heads, sectors, cylinders; + + sectors = 63L; /* Maximize sectors per track */ + if(capacity <= BLOCK_SIZE_528M) + heads = 16; + else if(capacity <= BLOCK_SIZE_1G) + heads = 32; + else if(capacity <= BLOCK_SIZE_21G) + heads = 64; + else if(capacity <= BLOCK_SIZE_42G) + heads = 128; + else + heads = 255; + + cylinders = capacity / (heads * sectors); + + *cyls = (unsigned short) cylinders; /* Stuff return values */ + *secs = (unsigned char) sectors; + *hds = (unsigned char) heads; +} + + +/* + * Rescan the partition tables + */ + +static int do_i2ob_revalidate(kdev_t dev, int maxu) +{ + int minor=MINOR(dev); + int i; + + minor&=0xF0; + + i2ob_dev[minor].refcnt++; + if(i2ob_dev[minor].refcnt>maxu+1) + { + i2ob_dev[minor].refcnt--; + return -EBUSY; + } + + for( i = 15; i>=0 ; i--) + { + int m = minor+i; + invalidate_device(MKDEV(MAJOR_NR, m), 1); + i2ob_gendisk.part[m].start_sect = 0; + i2ob_gendisk.part[m].nr_sects = 0; + } + + /* + * Do a physical check and then reconfigure + */ + + i2ob_install_device(i2ob_dev[minor].controller, i2ob_dev[minor].i2odev, + minor); + i2ob_dev[minor].refcnt--; + return 0; +} + +/* + * Issue device specific ioctl calls. + */ + +static int i2ob_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct i2ob_device *dev; + int minor; + + /* Anyone capable of this syscall can do *real bad* things */ + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (!inode) + return -EINVAL; + minor = MINOR(inode->i_rdev); + if (minor >= (MAX_I2OB<<4)) + return -ENODEV; + + dev = &i2ob_dev[minor]; + switch (cmd) { + case BLKGETSIZE: + return put_user(i2ob[minor].nr_sects, (long *) arg); + case BLKGETSIZE64: + return put_user((u64)i2ob[minor].nr_sects << 9, (u64 *)arg); + + case HDIO_GETGEO: + { + struct hd_geometry g; + int u=minor&0xF0; + i2o_block_biosparam(i2ob_sizes[u]<<1, + &g.cylinders, &g.heads, &g.sectors); + g.start = i2ob[minor].start_sect; + return copy_to_user((void *)arg,&g, sizeof(g))?-EFAULT:0; + } + + case BLKRRPART: + if(!capable(CAP_SYS_ADMIN)) + return -EACCES; + return do_i2ob_revalidate(inode->i_rdev,1); + + case BLKFLSBUF: + case BLKROSET: + case BLKROGET: + case BLKRASET: + case BLKRAGET: + case BLKPG: + return blk_ioctl(inode->i_rdev, cmd, arg); + + default: + return -EINVAL; + } +} + +/* + * Close the block device down + */ + +static int i2ob_release(struct inode *inode, struct file *file) +{ + struct i2ob_device *dev; + int minor; + + minor = MINOR(inode->i_rdev); + if (minor >= (MAX_I2OB<<4)) + return -ENODEV; + dev = &i2ob_dev[(minor&0xF0)]; + + /* + * This is to deail with the case of an application + * opening a device and then the device dissapears while + * it's in use, and then the application tries to release + * it. ex: Unmounting a deleted RAID volume at reboot. + * If we send messages, it will just cause FAILs since + * the TID no longer exists. + */ + if(!dev->i2odev) + return 0; + + /* Sync the device so we don't get errors */ + fsync_dev(inode->i_rdev); + + if (dev->refcnt <= 0) + printk(KERN_ALERT "i2ob_release: refcount(%d) <= 0\n", dev->refcnt); + dev->refcnt--; + if(dev->refcnt==0) + { + /* + * Flush the onboard cache on unmount + */ + u32 msg[5]; + int *query_done = &dev->done_flag; + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid; + msg[2] = i2ob_context|0x40000000; + msg[3] = (u32)query_done; + msg[4] = 60<<16; + DEBUG("Flushing..."); + i2o_post_wait(dev->controller, msg, 20, 60); + + /* + * Unlock the media + */ + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid; + msg[2] = i2ob_context|0x40000000; + msg[3] = (u32)query_done; + msg[4] = -1; + DEBUG("Unlocking..."); + i2o_post_wait(dev->controller, msg, 20, 2); + DEBUG("Unlocked.\n"); + + /* + * Now unclaim the device. + */ + + if (i2o_release_device(dev->i2odev, &i2o_block_handler)) + printk(KERN_ERR "i2ob_release: controller rejected unclaim.\n"); + + DEBUG("Unclaim\n"); + } + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Open the block device. + */ + +static int i2ob_open(struct inode *inode, struct file *file) +{ + int minor; + struct i2ob_device *dev; + + if (!inode) + return -EINVAL; + minor = MINOR(inode->i_rdev); + if (minor >= MAX_I2OB<<4) + return -ENODEV; + dev=&i2ob_dev[(minor&0xF0)]; + + if(!dev->i2odev) + return -ENODEV; + + if(dev->refcnt++==0) + { + u32 msg[6]; + + DEBUG("Claim "); + if(i2o_claim_device(dev->i2odev, &i2o_block_handler)) + { + dev->refcnt--; + printk(KERN_INFO "I2O Block: Could not open device\n"); + return -EBUSY; + } + DEBUG("Claimed "); + + /* + * Mount the media if needed. Note that we don't use + * the lock bit. Since we have to issue a lock if it + * refuses a mount (quite possible) then we might as + * well just send two messages out. + */ + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_BLOCK_MMOUNT<<24|HOST_TID<<12|dev->tid; + msg[4] = -1; + msg[5] = 0; + DEBUG("Mount "); + i2o_post_wait(dev->controller, msg, 24, 2); + + /* + * Lock the media + */ + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_BLOCK_MLOCK<<24|HOST_TID<<12|dev->tid; + msg[4] = -1; + DEBUG("Lock "); + i2o_post_wait(dev->controller, msg, 20, 2); + DEBUG("Ready.\n"); + } + MOD_INC_USE_COUNT; + return 0; +} + +/* + * Issue a device query + */ + +static int i2ob_query_device(struct i2ob_device *dev, int table, + int field, void *buf, int buflen) +{ + return i2o_query_scalar(dev->controller, dev->tid, + table, field, buf, buflen); +} + + +/* + * Install the I2O block device we found. + */ + +static int i2ob_install_device(struct i2o_controller *c, struct i2o_device *d, int unit) +{ + u64 size; + u32 blocksize; + u32 limit; + u8 type; + u32 flags, status; + struct i2ob_device *dev=&i2ob_dev[unit]; + int i; + + /* + * For logging purposes... + */ + printk(KERN_INFO "i2ob: Installing tid %d device at unit %d\n", + d->lct_data.tid, unit); + + /* + * Ask for the current media data. If that isn't supported + * then we ask for the device capacity data + */ + if(i2ob_query_device(dev, 0x0004, 1, &blocksize, 4) != 0 + || i2ob_query_device(dev, 0x0004, 0, &size, 8) !=0 ) + { + i2ob_query_device(dev, 0x0000, 3, &blocksize, 4); + i2ob_query_device(dev, 0x0000, 4, &size, 8); + } + + i2ob_query_device(dev, 0x0000, 5, &flags, 4); + i2ob_query_device(dev, 0x0000, 6, &status, 4); + i2ob_sizes[unit] = (int)(size>>10); + for(i=unit; i <= unit+15 ; i++) + i2ob_hardsizes[i] = blocksize; + i2ob_gendisk.part[unit].nr_sects = size>>9; + i2ob[unit].nr_sects = (int)(size>>9); + + /* Set limit based on inbound frame size */ + limit = (d->controller->status_block->inbound_frame_size - 8)/2; + limit = limit<<9; + + /* + * Max number of Scatter-Gather Elements + */ + + for(i=unit;i<=unit+15;i++) + { + if(d->controller->type == I2O_TYPE_PCI && d->controller->bus.pci.queue_buggy) + { + i2ob_max_sectors[i] = 32; + i2ob_dev[i].max_segments = 8; + i2ob_dev[i].depth = 4; + } + else if(d->controller->type == I2O_TYPE_PCI && d->controller->bus.pci.short_req) + { + i2ob_max_sectors[i] = 8; + i2ob_dev[i].max_segments = 8; + } + else + { + /* MAX_SECTORS was used but 255 is a dumb number for + striped RAID */ + i2ob_max_sectors[i]=256; + i2ob_dev[i].max_segments = (d->controller->status_block->inbound_frame_size - 8)/2; + } + } + + printk(KERN_INFO "Max segments set to %d\n", + i2ob_dev[unit].max_segments); + printk(KERN_INFO "Byte limit is %d.\n", limit); + + i2ob_query_device(dev, 0x0000, 0, &type, 1); + + sprintf(d->dev_name, "%s%c", i2ob_gendisk.major_name, 'a' + (unit>>4)); + + printk(KERN_INFO "%s: ", d->dev_name); + switch(type) + { + case 0: printk("Disk Storage");break; + case 4: printk("WORM");break; + case 5: printk("CD-ROM");break; + case 7: printk("Optical device");break; + default: + printk("Type %d", type); + } + if(status&(1<<10)) + printk("(RAID)"); + if(((flags & (1<<3)) && !(status & (1<<3))) || + ((flags & (1<<4)) && !(status & (1<<4)))) + { + printk(KERN_INFO " Not loaded.\n"); + return 1; + } + printk("- %dMb, %d byte sectors", + (int)(size>>20), blocksize); + if(status&(1<<0)) + { + u32 cachesize; + i2ob_query_device(dev, 0x0003, 0, &cachesize, 4); + cachesize>>=10; + if(cachesize>4095) + printk(", %dMb cache", cachesize>>10); + else + printk(", %dKb cache", cachesize); + + } + printk(".\n"); + printk(KERN_INFO "%s: Maximum sectors/read set to %d.\n", + d->dev_name, i2ob_max_sectors[unit]); + + /* + * If this is the first I2O block device found on this IOP, + * we need to initialize all the queue data structures + * before any I/O can be performed. If it fails, this + * device is useless. + */ + if(!i2ob_queues[c->unit]) { + if(i2ob_init_iop(c->unit)) + return 1; + } + + /* + * This will save one level of lookup/indirection in critical + * code so that we can directly get the queue ptr from the + * device instead of having to go the IOP data structure. + */ + dev->req_queue = &i2ob_queues[c->unit]->req_queue; + + grok_partitions(&i2ob_gendisk, unit>>4, 1<<4, (long)(size>>9)); + + /* + * Register for the events we're interested in and that the + * device actually supports. + */ + i2o_event_register(c, d->lct_data.tid, i2ob_context, unit, + (I2OB_EVENT_MASK & d->lct_data.event_capabilities)); + + return 0; +} + +/* + * Initialize IOP specific queue structures. This is called + * once for each IOP that has a block device sitting behind it. + */ +static int i2ob_init_iop(unsigned int unit) +{ + int i; + + i2ob_queues[unit] = (struct i2ob_iop_queue*) + kmalloc(sizeof(struct i2ob_iop_queue), GFP_ATOMIC); + if(!i2ob_queues[unit]) + { + printk(KERN_WARNING + "Could not allocate request queue for I2O block device!\n"); + return -1; + } + + for(i = 0; i< MAX_I2OB_DEPTH; i++) + { + i2ob_queues[unit]->request_queue[i].next = + &i2ob_queues[unit]->request_queue[i+1]; + i2ob_queues[unit]->request_queue[i].num = i; + } + + /* Queue is MAX_I2OB + 1... */ + i2ob_queues[unit]->request_queue[i].next = NULL; + i2ob_queues[unit]->i2ob_qhead = &i2ob_queues[unit]->request_queue[0]; + atomic_set(&i2ob_queues[unit]->queue_depth, 0); + + blk_init_queue(&i2ob_queues[unit]->req_queue, i2ob_request); + blk_queue_headactive(&i2ob_queues[unit]->req_queue, 0); + i2ob_queues[unit]->req_queue.back_merge_fn = i2ob_back_merge; + i2ob_queues[unit]->req_queue.front_merge_fn = i2ob_front_merge; + i2ob_queues[unit]->req_queue.merge_requests_fn = i2ob_merge_requests; + i2ob_queues[unit]->req_queue.queuedata = &i2ob_queues[unit]; + + return 0; +} + +/* + * Get the request queue for the given device. + */ +static request_queue_t* i2ob_get_queue(kdev_t dev) +{ + int unit = MINOR(dev)&0xF0; + + return i2ob_dev[unit].req_queue; +} + +/* + * Probe the I2O subsytem for block class devices + */ +static void i2ob_scan(int bios) +{ + int i; + int warned = 0; + + struct i2o_device *d, *b=NULL; + struct i2o_controller *c; + struct i2ob_device *dev; + + for(i=0; i< MAX_I2O_CONTROLLERS; i++) + { + c=i2o_find_controller(i); + + if(c==NULL) + continue; + + /* + * The device list connected to the I2O Controller is doubly linked + * Here we traverse the end of the list , and start claiming devices + * from that end. This assures that within an I2O controller atleast + * the newly created volumes get claimed after the older ones, thus + * mapping to same major/minor (and hence device file name) after + * every reboot. + * The exception being: + * 1. If there was a TID reuse. + * 2. There was more than one I2O controller. + */ + + if(!bios) + { + for (d=c->devices;d!=NULL;d=d->next) + if(d->next == NULL) + b = d; + } + else + b = c->devices; + + while(b != NULL) + { + d=b; + if(bios) + b = b->next; + else + b = b->prev; + + if(d->lct_data.class_id!=I2O_CLASS_RANDOM_BLOCK_STORAGE) + continue; + + if(d->lct_data.user_tid != 0xFFF) + continue; + + if(bios) + { + if(d->lct_data.bios_info != 0x80) + continue; + printk(KERN_INFO "Claiming as Boot device: Controller %d, TID %d\n", c->unit, d->lct_data.tid); + } + else + { + if(d->lct_data.bios_info == 0x80) + continue; /*Already claimed on pass 1 */ + } + + if(i2o_claim_device(d, &i2o_block_handler)) + { + printk(KERN_WARNING "i2o_block: Controller %d, TID %d\n", c->unit, + d->lct_data.tid); + printk(KERN_WARNING "\t%sevice refused claim! Skipping installation\n", bios?"Boot d":"D"); + continue; + } + + if(scan_uniti2odev = d; + dev->controller = c; + dev->unit = c->unit; + dev->tid = d->lct_data.tid; + + if(i2ob_install_device(c,d,scan_unit)) + printk(KERN_WARNING "Could not install I2O block device\n"); + else + { + scan_unit+=16; + i2ob_dev_count++; + + /* We want to know when device goes away */ + i2o_device_notify_on(d, &i2o_block_handler); + } + } + else + { + if(!warned++) + printk(KERN_WARNING "i2o_block: too many device, registering only %d.\n", scan_unit>>4); + } + i2o_release_device(d, &i2o_block_handler); + } + i2o_unlock_controller(c); + } +} + +static void i2ob_probe(void) +{ + /* + * Some overhead/redundancy involved here, while trying to + * claim the first boot volume encountered as /dev/i2o/hda + * everytime. All the i2o_controllers are searched and the + * first i2o block device marked as bootable is claimed + * If an I2O block device was booted off , the bios sets + * its bios_info field to 0x80, this what we search for. + * Assuming that the bootable volume is /dev/i2o/hda + * everytime will prevent any kernel panic while mounting + * root partition + */ + + printk(KERN_INFO "i2o_block: Checking for Boot device...\n"); + i2ob_scan(1); + + /* + * Now the remainder. + */ + printk(KERN_INFO "i2o_block: Checking for I2O Block devices...\n"); + i2ob_scan(0); +} + + +/* + * New device notification handler. Called whenever a new + * I2O block storage device is added to the system. + * + * Should we spin lock around this to keep multiple devs from + * getting updated at the same time? + * + */ +void i2ob_new_device(struct i2o_controller *c, struct i2o_device *d) +{ + struct i2ob_device *dev; + int unit = 0; + + printk(KERN_INFO "i2o_block: New device detected\n"); + printk(KERN_INFO " Controller %d Tid %d\n",c->unit, d->lct_data.tid); + + /* Check for available space */ + if(i2ob_dev_count>=MAX_I2OB<<4) + { + printk(KERN_ERR "i2o_block: No more devices allowed!\n"); + return; + } + for(unit = 0; unit < (MAX_I2OB<<4); unit += 16) + { + if(!i2ob_dev[unit].i2odev) + break; + } + + if(i2o_claim_device(d, &i2o_block_handler)) + { + printk(KERN_INFO + "i2o_block: Unable to claim device. Installation aborted\n"); + return; + } + + dev = &i2ob_dev[unit]; + dev->i2odev = d; + dev->controller = c; + dev->tid = d->lct_data.tid; + + if(i2ob_install_device(c,d,unit)) + printk(KERN_ERR "i2o_block: Could not install new device\n"); + else + { + i2ob_dev_count++; + i2o_device_notify_on(d, &i2o_block_handler); + } + + i2o_release_device(d, &i2o_block_handler); + + return; +} + +/* + * Deleted device notification handler. Called when a device we + * are talking to has been deleted by the user or some other + * mysterious fource outside the kernel. + */ +void i2ob_del_device(struct i2o_controller *c, struct i2o_device *d) +{ + int unit = 0; + int i = 0; + unsigned long flags; + + spin_lock_irqsave(&io_request_lock, flags); + + /* + * Need to do this...we somtimes get two events from the IRTOS + * in a row and that causes lots of problems. + */ + i2o_device_notify_off(d, &i2o_block_handler); + + printk(KERN_INFO "I2O Block Device Deleted\n"); + + for(unit = 0; unit < MAX_I2OB<<4; unit += 16) + { + if(i2ob_dev[unit].i2odev == d) + { + printk(KERN_INFO " /dev/%s: Controller %d Tid %d\n", + d->dev_name, c->unit, d->lct_data.tid); + break; + } + } + if(unit >= MAX_I2OB<<4) + { + printk(KERN_ERR "i2ob_del_device called, but not in dev table!\n"); + spin_unlock_irqrestore(&io_request_lock, flags); + return; + } + + /* + * This will force errors when i2ob_get_queue() is called + * by the kenrel. + */ + i2ob_dev[unit].req_queue = NULL; + for(i = unit; i <= unit+15; i++) + { + i2ob_dev[i].i2odev = NULL; + i2ob_sizes[i] = 0; + i2ob_hardsizes[i] = 0; + i2ob_max_sectors[i] = 0; + i2ob[i].nr_sects = 0; + i2ob_gendisk.part[i].nr_sects = 0; + } + spin_unlock_irqrestore(&io_request_lock, flags); + + /* + * Decrease usage count for module + */ + + while(i2ob_dev[unit].refcnt--) + MOD_DEC_USE_COUNT; + + i2ob_dev[unit].refcnt = 0; + + i2ob_dev[i].tid = 0; + + /* + * Do we need this? + * The media didn't really change...the device is just gone + */ + i2ob_media_change_flag[unit] = 1; + + i2ob_dev_count--; +} + +/* + * Have we seen a media change ? + */ +static int i2ob_media_change(kdev_t dev) +{ + int i=MINOR(dev); + i>>=4; + if(i2ob_media_change_flag[i]) + { + i2ob_media_change_flag[i]=0; + return 1; + } + return 0; +} + +static int i2ob_revalidate(kdev_t dev) +{ + return do_i2ob_revalidate(dev, 0); +} + +/* + * Reboot notifier. This is called by i2o_core when the system + * shuts down. + */ +static void i2ob_reboot_event(void) +{ + int i; + + for(i=0;irefcnt!=0) + { + /* + * Flush the onboard cache + */ + u32 msg[5]; + int *query_done = &dev->done_flag; + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid; + msg[2] = i2ob_context|0x40000000; + msg[3] = (u32)query_done; + msg[4] = 60<<16; + + DEBUG("Flushing..."); + i2o_post_wait(dev->controller, msg, 20, 60); + + DEBUG("Unlocking..."); + /* + * Unlock the media + */ + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid; + msg[2] = i2ob_context|0x40000000; + msg[3] = (u32)query_done; + msg[4] = -1; + i2o_post_wait(dev->controller, msg, 20, 2); + + DEBUG("Unlocked.\n"); + } + } +} + +static struct block_device_operations i2ob_fops = +{ + open: i2ob_open, + release: i2ob_release, + ioctl: i2ob_ioctl, + check_media_change: i2ob_media_change, + revalidate: i2ob_revalidate, +}; + +static struct gendisk i2ob_gendisk = +{ + major: MAJOR_NR, + major_name: "i2o/hd", + minor_shift: 4, + max_p: 1<<4, + part: i2ob, + sizes: i2ob_sizes, + nr_real: MAX_I2OB, + fops: &i2ob_fops, +}; + + +/* + * And here should be modules and kernel interface + * (Just smiley confuses emacs :-) + */ + +#ifdef MODULE +#define i2o_block_init init_module +#endif + +int i2o_block_init(void) +{ + int i; + + printk(KERN_INFO "I2O Block Storage OSM v0.9\n"); + printk(KERN_INFO " (c) Copyright 1999-2001 Red Hat Software.\n"); + + /* + * Register the block device interfaces + */ + + if (register_blkdev(MAJOR_NR, "i2o_block", &i2ob_fops)) { + printk(KERN_ERR "Unable to get major number %d for i2o_block\n", + MAJOR_NR); + return -EIO; + } +#ifdef MODULE + printk(KERN_INFO "i2o_block: registered device at major %d\n", MAJOR_NR); +#endif + + /* + * Now fill in the boiler plate + */ + + blksize_size[MAJOR_NR] = i2ob_blksizes; + hardsect_size[MAJOR_NR] = i2ob_hardsizes; + blk_size[MAJOR_NR] = i2ob_sizes; + max_sectors[MAJOR_NR] = i2ob_max_sectors; + blk_dev[MAJOR_NR].queue = i2ob_get_queue; + + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), i2ob_request); + blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR), 0); + + for (i = 0; i < MAX_I2OB << 4; i++) { + i2ob_dev[i].refcnt = 0; + i2ob_dev[i].flags = 0; + i2ob_dev[i].controller = NULL; + i2ob_dev[i].i2odev = NULL; + i2ob_dev[i].tid = 0; + i2ob_dev[i].head = NULL; + i2ob_dev[i].tail = NULL; + i2ob_dev[i].depth = MAX_I2OB_DEPTH; + i2ob_blksizes[i] = 1024; + i2ob_max_sectors[i] = 2; + } + + /* + * Set up the queue + */ + for(i = 0; i < MAX_I2O_CONTROLLERS; i++) + { + i2ob_queues[i] = NULL; + } + + /* + * Timers + */ + + init_timer(&i2ob_timer); + i2ob_timer.function = i2ob_timer_handler; + i2ob_timer.data = 0; + + /* + * Register the OSM handler as we will need this to probe for + * drives, geometry and other goodies. + */ + + if(i2o_install_handler(&i2o_block_handler)<0) + { + unregister_blkdev(MAJOR_NR, "i2o_block"); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + printk(KERN_ERR "i2o_block: unable to register OSM.\n"); + return -EINVAL; + } + i2ob_context = i2o_block_handler.context; + + /* + * Initialize event handling thread + */ + init_MUTEX_LOCKED(&i2ob_evt_sem); + evt_pid = kernel_thread(i2ob_evt, NULL, CLONE_SIGHAND); + if(evt_pid < 0) + { + printk(KERN_ERR + "i2o_block: Could not initialize event thread. Aborting\n"); + i2o_remove_handler(&i2o_block_handler); + return 0; + } + + /* + * Finally see what is actually plugged in to our controllers + */ + for (i = 0; i < MAX_I2OB; i++) + register_disk(&i2ob_gendisk, MKDEV(MAJOR_NR,i<<4), 1<<4, + &i2ob_fops, 0); + i2ob_probe(); + + /* + * Adding i2ob_gendisk into the gendisk list. + */ + add_gendisk(&i2ob_gendisk); + + return 0; +} + +#ifdef MODULE + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Red Hat Software"); +MODULE_DESCRIPTION("I2O Block Device OSM"); +MODULE_LICENSE("GPL"); + + +void cleanup_module(void) +{ + int i; + + if(evt_running) { + printk(KERN_INFO "Killing I2O block threads..."); + i = kill_proc(evt_pid, SIGTERM, 1); + if(!i) { + printk("waiting..."); + } + /* Be sure it died */ + wait_for_completion(&i2ob_thread_dead); + printk("done.\n"); + } + + /* + * Unregister for updates from any devices..otherwise we still + * get them and the core jumps to random memory :O + */ + if(i2ob_dev_count) { + struct i2o_device *d; + for(i = 0; i < MAX_I2OB; i++) + if((d=i2ob_dev[i<<4].i2odev)) { + i2o_device_notify_off(d, &i2o_block_handler); + i2o_event_register(d->controller, d->lct_data.tid, + i2ob_context, i<<4, 0); + } + } + + /* + * We may get further callbacks for ourself. The i2o_core + * code handles this case reasonably sanely. The problem here + * is we shouldn't get them .. but a couple of cards feel + * obliged to tell us stuff we dont care about. + * + * This isnt ideal at all but will do for now. + */ + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ); + + /* + * Flush the OSM + */ + + i2o_remove_handler(&i2o_block_handler); + + /* + * Return the block device + */ + if (unregister_blkdev(MAJOR_NR, "i2o_block") != 0) + printk("i2o_block: cleanup_module failed\n"); + + /* + * free request queue + */ + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + + del_gendisk(&i2ob_gendisk); +} +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_config.c linux.ac/drivers/message/i2o/i2o_config.c --- linux.vanilla/drivers/message/i2o/i2o_config.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_config.c Wed Oct 10 01:47:56 2001 @@ -0,0 +1,966 @@ +/* + * I2O Configuration Interface Driver + * + * (C) Copyright 1999 Red Hat Software + * + * Written by Alan Cox, Building Number Three Ltd + * + * Modified 04/20/1999 by Deepak Saxena + * - Added basic ioctl() support + * Modified 06/07/1999 by Deepak Saxena + * - Added software download ioctl (still testing) + * Modified 09/10/1999 by Auvo Häkkinen + * - Changes to i2o_cfg_reply(), ioctl_parms() + * - Added ioct_validate() + * Modified 09/30/1999 by Taneli Vähäkangas + * - Fixed ioctl_swdl() + * Modified 10/04/1999 by Taneli Vähäkangas + * - Changed ioctl_swdl(), implemented ioctl_swul() and ioctl_swdel() + * Modified 11/18/199 by Deepak Saxena + * - Added event managmenet support + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static int i2o_cfg_context = -1; +static void *page_buf; +static spinlock_t i2o_config_lock = SPIN_LOCK_UNLOCKED; +struct wait_queue *i2o_wait_queue; + +#define MODINC(x,y) (x = x++ % y) + +struct i2o_cfg_info +{ + struct file* fp; + struct fasync_struct *fasync; + struct i2o_evt_info event_q[I2O_EVT_Q_LEN]; + u16 q_in; // Queue head index + u16 q_out; // Queue tail index + u16 q_len; // Queue length + u16 q_lost; // Number of lost events + u32 q_id; // Event queue ID...used as tx_context + struct i2o_cfg_info *next; +}; +static struct i2o_cfg_info *open_files = NULL; +static int i2o_cfg_info_id = 0; + +static int ioctl_getiops(unsigned long); +static int ioctl_gethrt(unsigned long); +static int ioctl_getlct(unsigned long); +static int ioctl_parms(unsigned long, unsigned int); +static int ioctl_html(unsigned long); +static int ioctl_swdl(unsigned long); +static int ioctl_swul(unsigned long); +static int ioctl_swdel(unsigned long); +static int ioctl_validate(unsigned long); +static int ioctl_evt_reg(unsigned long, struct file *); +static int ioctl_evt_get(unsigned long, struct file *); +static int cfg_fasync(int, struct file*, int); + +/* + * This is the callback for any message we have posted. The message itself + * will be returned to the message pool when we return from the IRQ + * + * This runs in irq context so be short and sweet. + */ +static void i2o_cfg_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *m) +{ + u32 *msg = (u32 *)m; + + if (msg[0] & MSG_FAIL) { + u32 *preserved_msg = (u32*)(c->mem_offset + msg[7]); + + printk(KERN_ERR "i2o_config: IOP failed to process the msg.\n"); + + /* Release the preserved msg frame by resubmitting it as a NOP */ + + preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; + preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0; + preserved_msg[2] = 0; + i2o_post_message(c, msg[7]); + } + + if (msg[4] >> 24) // ReqStatus != SUCCESS + i2o_report_status(KERN_INFO,"i2o_config", msg); + + if(m->function == I2O_CMD_UTIL_EVT_REGISTER) + { + struct i2o_cfg_info *inf; + + for(inf = open_files; inf; inf = inf->next) + if(inf->q_id == msg[3]) + break; + + // + // If this is the case, it means that we're getting + // events for a file descriptor that's been close()'d + // w/o the user unregistering for events first. + // The code currently assumes that the user will + // take care of unregistering for events before closing + // a file. + // + // TODO: + // Should we track event registartion and deregister + // for events when a file is close()'d so this doesn't + // happen? That would get rid of the search through + // the linked list since file->private_data could point + // directly to the i2o_config_info data structure...but + // it would mean having all sorts of tables to track + // what each file is registered for...I think the + // current method is simpler. - DS + // + if(!inf) + return; + + inf->event_q[inf->q_in].id.iop = c->unit; + inf->event_q[inf->q_in].id.tid = m->target_tid; + inf->event_q[inf->q_in].id.evt_mask = msg[4]; + + // + // Data size = msg size - reply header + // + inf->event_q[inf->q_in].data_size = (m->size - 5) * 4; + if(inf->event_q[inf->q_in].data_size) + memcpy(inf->event_q[inf->q_in].evt_data, + (unsigned char *)(msg + 5), + inf->event_q[inf->q_in].data_size); + + spin_lock(&i2o_config_lock); + MODINC(inf->q_in, I2O_EVT_Q_LEN); + if(inf->q_len == I2O_EVT_Q_LEN) + { + MODINC(inf->q_out, I2O_EVT_Q_LEN); + inf->q_lost++; + } + else + { + // Keep I2OEVTGET on another CPU from touching this + inf->q_len++; + } + spin_unlock(&i2o_config_lock); + + +// printk(KERN_INFO "File %p w/id %d has %d events\n", +// inf->fp, inf->q_id, inf->q_len); + + kill_fasync(&inf->fasync, SIGIO, POLL_IN); + } + + return; +} + +/* + * Each of these describes an i2o message handler. They are + * multiplexed by the i2o_core code + */ + +struct i2o_handler cfg_handler= +{ + i2o_cfg_reply, + NULL, + NULL, + NULL, + "Configuration", + 0, + 0xffffffff // All classes +}; + +static ssize_t cfg_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + printk(KERN_INFO "i2o_config write not yet supported\n"); + + return 0; +} + + +static ssize_t cfg_read(struct file *file, char *buf, size_t count, loff_t *ptr) +{ + return 0; +} + +/* + * IOCTL Handler + */ +static int cfg_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, + unsigned long arg) +{ + int ret; + + switch(cmd) + { + case I2OGETIOPS: + ret = ioctl_getiops(arg); + break; + + case I2OHRTGET: + ret = ioctl_gethrt(arg); + break; + + case I2OLCTGET: + ret = ioctl_getlct(arg); + break; + + case I2OPARMSET: + ret = ioctl_parms(arg, I2OPARMSET); + break; + + case I2OPARMGET: + ret = ioctl_parms(arg, I2OPARMGET); + break; + + case I2OSWDL: + ret = ioctl_swdl(arg); + break; + + case I2OSWUL: + ret = ioctl_swul(arg); + break; + + case I2OSWDEL: + ret = ioctl_swdel(arg); + break; + + case I2OVALIDATE: + ret = ioctl_validate(arg); + break; + + case I2OHTML: + ret = ioctl_html(arg); + break; + + case I2OEVTREG: + ret = ioctl_evt_reg(arg, fp); + break; + + case I2OEVTGET: + ret = ioctl_evt_get(arg, fp); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +int ioctl_getiops(unsigned long arg) +{ + u8 *user_iop_table = (u8*)arg; + struct i2o_controller *c = NULL; + int i; + u8 foo[MAX_I2O_CONTROLLERS]; + + if(!access_ok(VERIFY_WRITE, user_iop_table, MAX_I2O_CONTROLLERS)) + return -EFAULT; + + for(i = 0; i < MAX_I2O_CONTROLLERS; i++) + { + c = i2o_find_controller(i); + if(c) + { + foo[i] = 1; + i2o_unlock_controller(c); + } + else + { + foo[i] = 0; + } + } + + __copy_to_user(user_iop_table, foo, MAX_I2O_CONTROLLERS); + return 0; +} + +int ioctl_gethrt(unsigned long arg) +{ + struct i2o_controller *c; + struct i2o_cmd_hrtlct *cmd = (struct i2o_cmd_hrtlct*)arg; + struct i2o_cmd_hrtlct kcmd; + i2o_hrt *hrt; + int len; + u32 reslen; + int ret = 0; + + if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct))) + return -EFAULT; + + if(get_user(reslen, kcmd.reslen) < 0) + return -EFAULT; + + if(kcmd.resbuf == NULL) + return -EFAULT; + + c = i2o_find_controller(kcmd.iop); + if(!c) + return -ENXIO; + + hrt = (i2o_hrt *)c->hrt; + + i2o_unlock_controller(c); + + len = 8 + ((hrt->entry_len * hrt->num_entries) << 2); + + /* We did a get user...so assuming mem is ok...is this bad? */ + put_user(len, kcmd.reslen); + if(len > reslen) + ret = -ENOBUFS; + if(copy_to_user(kcmd.resbuf, (void*)hrt, len)) + ret = -EFAULT; + + return ret; +} + +int ioctl_getlct(unsigned long arg) +{ + struct i2o_controller *c; + struct i2o_cmd_hrtlct *cmd = (struct i2o_cmd_hrtlct*)arg; + struct i2o_cmd_hrtlct kcmd; + i2o_lct *lct; + int len; + int ret = 0; + u32 reslen; + + if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct))) + return -EFAULT; + + if(get_user(reslen, kcmd.reslen) < 0) + return -EFAULT; + + if(kcmd.resbuf == NULL) + return -EFAULT; + + c = i2o_find_controller(kcmd.iop); + if(!c) + return -ENXIO; + + lct = (i2o_lct *)c->lct; + i2o_unlock_controller(c); + + len = (unsigned int)lct->table_size << 2; + put_user(len, kcmd.reslen); + if(len > reslen) + ret = -ENOBUFS; + else if(copy_to_user(kcmd.resbuf, (void*)lct, len)) + ret = -EFAULT; + + return ret; +} + +static int ioctl_parms(unsigned long arg, unsigned int type) +{ + int ret = 0; + struct i2o_controller *c; + struct i2o_cmd_psetget *cmd = (struct i2o_cmd_psetget*)arg; + struct i2o_cmd_psetget kcmd; + u32 reslen; + u8 *ops; + u8 *res; + int len; + + u32 i2o_cmd = (type == I2OPARMGET ? + I2O_CMD_UTIL_PARAMS_GET : + I2O_CMD_UTIL_PARAMS_SET); + + if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_psetget))) + return -EFAULT; + + if(get_user(reslen, kcmd.reslen)) + return -EFAULT; + + c = i2o_find_controller(kcmd.iop); + if(!c) + return -ENXIO; + + ops = (u8*)kmalloc(kcmd.oplen, GFP_KERNEL); + if(!ops) + { + i2o_unlock_controller(c); + return -ENOMEM; + } + + if(copy_from_user(ops, kcmd.opbuf, kcmd.oplen)) + { + i2o_unlock_controller(c); + kfree(ops); + return -EFAULT; + } + + /* + * It's possible to have a _very_ large table + * and that the user asks for all of it at once... + */ + res = (u8*)kmalloc(65536, GFP_KERNEL); + if(!res) + { + i2o_unlock_controller(c); + kfree(ops); + return -ENOMEM; + } + + len = i2o_issue_params(i2o_cmd, c, kcmd.tid, + ops, kcmd.oplen, res, 65536); + i2o_unlock_controller(c); + kfree(ops); + + if (len < 0) { + kfree(res); + return -EAGAIN; + } + + put_user(len, kcmd.reslen); + if(len > reslen) + ret = -ENOBUFS; + else if(copy_to_user(cmd->resbuf, res, len)) + ret = -EFAULT; + + kfree(res); + + return ret; +} + +int ioctl_html(unsigned long arg) +{ + struct i2o_html *cmd = (struct i2o_html*)arg; + struct i2o_html kcmd; + struct i2o_controller *c; + u8 *res = NULL; + void *query = NULL; + int ret = 0; + int token; + u32 len; + u32 reslen; + u32 msg[MSG_FRAME_SIZE/4]; + + if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_html))) + { + printk(KERN_INFO "i2o_config: can't copy html cmd\n"); + return -EFAULT; + } + + if(get_user(reslen, kcmd.reslen) < 0) + { + printk(KERN_INFO "i2o_config: can't copy html reslen\n"); + return -EFAULT; + } + + if(!kcmd.resbuf) + { + printk(KERN_INFO "i2o_config: NULL html buffer\n"); + return -EFAULT; + } + + c = i2o_find_controller(kcmd.iop); + if(!c) + return -ENXIO; + + if(kcmd.qlen) /* Check for post data */ + { + query = kmalloc(kcmd.qlen, GFP_KERNEL); + if(!query) + { + i2o_unlock_controller(c); + return -ENOMEM; + } + if(copy_from_user(query, kcmd.qbuf, kcmd.qlen)) + { + i2o_unlock_controller(c); + printk(KERN_INFO "i2o_config: could not get query\n"); + kfree(query); + return -EFAULT; + } + } + + res = kmalloc(65536, GFP_KERNEL); + if(!res) + { + i2o_unlock_controller(c); + kfree(query); + return -ENOMEM; + } + + msg[1] = (I2O_CMD_UTIL_CONFIG_DIALOG << 24)|HOST_TID<<12|kcmd.tid; + msg[2] = i2o_cfg_context; + msg[3] = 0; + msg[4] = kcmd.page; + msg[5] = 0xD0000000|65536; + msg[6] = virt_to_bus(res); + if(!kcmd.qlen) /* Check for post data */ + msg[0] = SEVEN_WORD_MSG_SIZE|SGL_OFFSET_5; + else + { + msg[0] = NINE_WORD_MSG_SIZE|SGL_OFFSET_5; + msg[5] = 0x50000000|65536; + msg[7] = 0xD4000000|(kcmd.qlen); + msg[8] = virt_to_bus(query); + } + /* + Wait for a considerable time till the Controller + does its job before timing out. The controller might + take more time to process this request if there are + many devices connected to it. + */ + token = i2o_post_wait_mem(c, msg, 9*4, 400, query, res); + if(token < 0) + { + printk(KERN_DEBUG "token = %#10x\n", token); + i2o_unlock_controller(c); + + if(token != -ETIMEDOUT) + { + kfree(res); + if(kcmd.qlen) kfree(query); + } + + return token; + } + i2o_unlock_controller(c); + + len = strnlen(res, 65536); + put_user(len, kcmd.reslen); + if(len > reslen) + ret = -ENOMEM; + if(copy_to_user(kcmd.resbuf, res, len)) + ret = -EFAULT; + + kfree(res); + if(kcmd.qlen) + kfree(query); + + return ret; +} + +int ioctl_swdl(unsigned long arg) +{ + struct i2o_sw_xfer kxfer; + struct i2o_sw_xfer *pxfer = (struct i2o_sw_xfer *)arg; + unsigned char maxfrag = 0, curfrag = 1; + unsigned char *buffer; + u32 msg[9]; + unsigned int status = 0, swlen = 0, fragsize = 8192; + struct i2o_controller *c; + + if(copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) + return -EFAULT; + + if(get_user(swlen, kxfer.swlen) < 0) + return -EFAULT; + + if(get_user(maxfrag, kxfer.maxfrag) < 0) + return -EFAULT; + + if(get_user(curfrag, kxfer.curfrag) < 0) + return -EFAULT; + + if(curfrag==maxfrag) fragsize = swlen-(maxfrag-1)*8192; + + if(!kxfer.buf || !access_ok(VERIFY_READ, kxfer.buf, fragsize)) + return -EFAULT; + + c = i2o_find_controller(kxfer.iop); + if(!c) + return -ENXIO; + + buffer=kmalloc(fragsize, GFP_KERNEL); + if (buffer==NULL) + { + i2o_unlock_controller(c); + return -ENOMEM; + } + __copy_from_user(buffer, kxfer.buf, fragsize); + + msg[0]= NINE_WORD_MSG_SIZE | SGL_OFFSET_7; + msg[1]= I2O_CMD_SW_DOWNLOAD<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[2]= (u32)cfg_handler.context; + msg[3]= 0; + msg[4]= (((u32)kxfer.flags)<<24) | (((u32)kxfer.sw_type)<<16) | + (((u32)maxfrag)<<8) | (((u32)curfrag)); + msg[5]= swlen; + msg[6]= kxfer.sw_id; + msg[7]= (0xD0000000 | fragsize); + msg[8]= virt_to_bus(buffer); + +// printk("i2o_config: swdl frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); + status = i2o_post_wait_mem(c, msg, sizeof(msg), 60, buffer, NULL); + + i2o_unlock_controller(c); + if(status != -ETIMEDOUT) + kfree(buffer); + + if (status != I2O_POST_WAIT_OK) + { + // it fails if you try and send frags out of order + // and for some yet unknown reasons too + printk(KERN_INFO "i2o_config: swdl failed, DetailedStatus = %d\n", status); + return status; + } + + return 0; +} + +int ioctl_swul(unsigned long arg) +{ + struct i2o_sw_xfer kxfer; + struct i2o_sw_xfer *pxfer = (struct i2o_sw_xfer *)arg; + unsigned char maxfrag = 0, curfrag = 1; + unsigned char *buffer; + u32 msg[9]; + unsigned int status = 0, swlen = 0, fragsize = 8192; + struct i2o_controller *c; + + if(copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) + return -EFAULT; + + if(get_user(swlen, kxfer.swlen) < 0) + return -EFAULT; + + if(get_user(maxfrag, kxfer.maxfrag) < 0) + return -EFAULT; + + if(get_user(curfrag, kxfer.curfrag) < 0) + return -EFAULT; + + if(curfrag==maxfrag) fragsize = swlen-(maxfrag-1)*8192; + + if(!kxfer.buf || !access_ok(VERIFY_WRITE, kxfer.buf, fragsize)) + return -EFAULT; + + c = i2o_find_controller(kxfer.iop); + if(!c) + return -ENXIO; + + buffer=kmalloc(fragsize, GFP_KERNEL); + if (buffer==NULL) + { + i2o_unlock_controller(c); + return -ENOMEM; + } + + msg[0]= NINE_WORD_MSG_SIZE | SGL_OFFSET_7; + msg[1]= I2O_CMD_SW_UPLOAD<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[2]= (u32)cfg_handler.context; + msg[3]= 0; + msg[4]= (u32)kxfer.flags<<24|(u32)kxfer.sw_type<<16|(u32)maxfrag<<8|(u32)curfrag; + msg[5]= swlen; + msg[6]= kxfer.sw_id; + msg[7]= (0xD0000000 | fragsize); + msg[8]= virt_to_bus(buffer); + +// printk("i2o_config: swul frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); + status = i2o_post_wait_mem(c, msg, sizeof(msg), 60, buffer, NULL); + i2o_unlock_controller(c); + + if (status != I2O_POST_WAIT_OK) + { + if(status != -ETIMEDOUT) + kfree(buffer); + printk(KERN_INFO "i2o_config: swul failed, DetailedStatus = %d\n", status); + return status; + } + + __copy_to_user(kxfer.buf, buffer, fragsize); + kfree(buffer); + + return 0; +} + +int ioctl_swdel(unsigned long arg) +{ + struct i2o_controller *c; + struct i2o_sw_xfer kxfer, *pxfer = (struct i2o_sw_xfer *)arg; + u32 msg[7]; + unsigned int swlen; + int token; + + if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) + return -EFAULT; + + if (get_user(swlen, kxfer.swlen) < 0) + return -EFAULT; + + c = i2o_find_controller(kxfer.iop); + if (!c) + return -ENXIO; + + msg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0; + msg[1] = I2O_CMD_SW_REMOVE<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[2] = (u32)i2o_cfg_context; + msg[3] = 0; + msg[4] = (u32)kxfer.flags<<24 | (u32)kxfer.sw_type<<16; + msg[5] = swlen; + msg[6] = kxfer.sw_id; + + token = i2o_post_wait(c, msg, sizeof(msg), 10); + i2o_unlock_controller(c); + + if (token != I2O_POST_WAIT_OK) + { + printk(KERN_INFO "i2o_config: swdel failed, DetailedStatus = %d\n", token); + return -ETIMEDOUT; + } + + return 0; +} + +int ioctl_validate(unsigned long arg) +{ + int token; + int iop = (int)arg; + u32 msg[4]; + struct i2o_controller *c; + + c=i2o_find_controller(iop); + if (!c) + return -ENXIO; + + msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_CONFIG_VALIDATE<<24 | HOST_TID<<12 | iop; + msg[2] = (u32)i2o_cfg_context; + msg[3] = 0; + + token = i2o_post_wait(c, msg, sizeof(msg), 10); + i2o_unlock_controller(c); + + if (token != I2O_POST_WAIT_OK) + { + printk(KERN_INFO "Can't validate configuration, ErrorStatus = %d\n", + token); + return -ETIMEDOUT; + } + + return 0; +} + +static int ioctl_evt_reg(unsigned long arg, struct file *fp) +{ + u32 msg[5]; + struct i2o_evt_id *pdesc = (struct i2o_evt_id *)arg; + struct i2o_evt_id kdesc; + struct i2o_controller *iop; + struct i2o_device *d; + + if (copy_from_user(&kdesc, pdesc, sizeof(struct i2o_evt_id))) + return -EFAULT; + + /* IOP exists? */ + iop = i2o_find_controller(kdesc.iop); + if(!iop) + return -ENXIO; + i2o_unlock_controller(iop); + + /* Device exists? */ + for(d = iop->devices; d; d = d->next) + if(d->lct_data.tid == kdesc.tid) + break; + + if(!d) + return -ENODEV; + + msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_UTIL_EVT_REGISTER<<24 | HOST_TID<<12 | kdesc.tid; + msg[2] = (u32)i2o_cfg_context; + msg[3] = (u32)fp->private_data; + msg[4] = kdesc.evt_mask; + + i2o_post_this(iop, msg, 20); + + return 0; +} + +static int ioctl_evt_get(unsigned long arg, struct file *fp) +{ + u32 id = (u32)fp->private_data; + struct i2o_cfg_info *p = NULL; + struct i2o_evt_get *uget = (struct i2o_evt_get*)arg; + struct i2o_evt_get kget; + unsigned long flags; + + for(p = open_files; p; p = p->next) + if(p->q_id == id) + break; + + if(!p->q_len) + { + return -ENOENT; + return 0; + } + + memcpy(&kget.info, &p->event_q[p->q_out], sizeof(struct i2o_evt_info)); + MODINC(p->q_out, I2O_EVT_Q_LEN); + spin_lock_irqsave(&i2o_config_lock, flags); + p->q_len--; + kget.pending = p->q_len; + kget.lost = p->q_lost; + spin_unlock_irqrestore(&i2o_config_lock, flags); + + if(copy_to_user(uget, &kget, sizeof(struct i2o_evt_get))) + return -EFAULT; + return 0; +} + +static int cfg_open(struct inode *inode, struct file *file) +{ + struct i2o_cfg_info *tmp = + (struct i2o_cfg_info *)kmalloc(sizeof(struct i2o_cfg_info), GFP_KERNEL); + unsigned long flags; + + if(!tmp) + return -ENOMEM; + + file->private_data = (void*)(i2o_cfg_info_id++); + tmp->fp = file; + tmp->fasync = NULL; + tmp->q_id = (u32)file->private_data; + tmp->q_len = 0; + tmp->q_in = 0; + tmp->q_out = 0; + tmp->q_lost = 0; + tmp->next = open_files; + + spin_lock_irqsave(&i2o_config_lock, flags); + open_files = tmp; + spin_unlock_irqrestore(&i2o_config_lock, flags); + + return 0; +} + +static int cfg_release(struct inode *inode, struct file *file) +{ + u32 id = (u32)file->private_data; + struct i2o_cfg_info *p1, *p2; + unsigned long flags; + + lock_kernel(); + p1 = p2 = NULL; + + spin_lock_irqsave(&i2o_config_lock, flags); + for(p1 = open_files; p1; ) + { + if(p1->q_id == id) + { + + if(p1->fasync) + cfg_fasync(-1, file, 0); + if(p2) + p2->next = p1->next; + else + open_files = p1->next; + + kfree(p1); + break; + } + p2 = p1; + p1 = p1->next; + } + spin_unlock_irqrestore(&i2o_config_lock, flags); + unlock_kernel(); + + return 0; +} + +static int cfg_fasync(int fd, struct file *fp, int on) +{ + u32 id = (u32)fp->private_data; + struct i2o_cfg_info *p; + + for(p = open_files; p; p = p->next) + if(p->q_id == id) + break; + + if(!p) + return -EBADF; + + return fasync_helper(fd, fp, on, &p->fasync); +} + +static struct file_operations config_fops = +{ + owner: THIS_MODULE, + llseek: no_llseek, + read: cfg_read, + write: cfg_write, + ioctl: cfg_ioctl, + open: cfg_open, + release: cfg_release, + fasync: cfg_fasync, +}; + +static struct miscdevice i2o_miscdev = { + I2O_MINOR, + "i2octl", + &config_fops +}; + +#ifdef MODULE +int init_module(void) +#else +int __init i2o_config_init(void) +#endif +{ + printk(KERN_INFO "I2O configuration manager v 0.04.\n"); + printk(KERN_INFO " (C) Copyright 1999 Red Hat Software\n"); + + if((page_buf = kmalloc(4096, GFP_KERNEL))==NULL) + { + printk(KERN_ERR "i2o_config: no memory for page buffer.\n"); + return -ENOBUFS; + } + if(misc_register(&i2o_miscdev)==-1) + { + printk(KERN_ERR "i2o_config: can't register device.\n"); + kfree(page_buf); + return -EBUSY; + } + /* + * Install our handler + */ + if(i2o_install_handler(&cfg_handler)<0) + { + kfree(page_buf); + printk(KERN_ERR "i2o_config: handler register failed.\n"); + misc_deregister(&i2o_miscdev); + return -EBUSY; + } + /* + * The low 16bits of the transaction context must match this + * for everything we post. Otherwise someone else gets our mail + */ + i2o_cfg_context = cfg_handler.context; + return 0; +} + +#ifdef MODULE + +void cleanup_module(void) +{ + misc_deregister(&i2o_miscdev); + + if(page_buf) + kfree(page_buf); + if(i2o_cfg_context != -1) + i2o_remove_handler(&cfg_handler); +} + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Red Hat Software"); +MODULE_DESCRIPTION("I2O Configuration"); +MODULE_LICENSE("GPL"); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_core.c linux.ac/drivers/message/i2o/i2o_core.c --- linux.vanilla/drivers/message/i2o/i2o_core.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_core.c Wed Oct 10 01:47:56 2001 @@ -0,0 +1,3591 @@ +/* + * Core I2O structure management + * + * (C) Copyright 1999 Red Hat Software + * + * Written by Alan Cox, Building Number Three Ltd + * + * 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. + * + * A lot of the I2O message side code from this is taken from the + * Red Creek RCPCI45 adapter driver by Red Creek Communications + * + * Fixes by: + * Philipp Rumpf + * Juha Sievänen + * Auvo Häkkinen + * Deepak Saxena + * Boji T Kannanthanam + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "i2o_lan.h" + +//#define DRIVERDEBUG + +#ifdef DRIVERDEBUG +#define dprintk(s, args...) printk(s, ## args) +#else +#define dprintk(s, args...) +#endif + +/* OSM table */ +static struct i2o_handler *i2o_handlers[MAX_I2O_MODULES]; + +/* Controller list */ +static struct i2o_controller *i2o_controllers[MAX_I2O_CONTROLLERS]; +struct i2o_controller *i2o_controller_chain; +int i2o_num_controllers; + +/* Initiator Context for Core message */ +static int core_context; + +/* Initialization && shutdown functions */ +static void i2o_sys_init(void); +static void i2o_sys_shutdown(void); +static int i2o_reset_controller(struct i2o_controller *); +static int i2o_reboot_event(struct notifier_block *, unsigned long , void *); +static int i2o_online_controller(struct i2o_controller *); +static int i2o_init_outbound_q(struct i2o_controller *); +static int i2o_post_outbound_messages(struct i2o_controller *); + +/* Reply handler */ +static void i2o_core_reply(struct i2o_handler *, struct i2o_controller *, + struct i2o_message *); + +/* Various helper functions */ +static int i2o_lct_get(struct i2o_controller *); +static int i2o_lct_notify(struct i2o_controller *); +static int i2o_hrt_get(struct i2o_controller *); + +static int i2o_build_sys_table(void); +static int i2o_systab_send(struct i2o_controller *c); + +/* I2O core event handler */ +static int i2o_core_evt(void *); +static int evt_pid; +static int evt_running; + +/* Dynamic LCT update handler */ +static int i2o_dyn_lct(void *); + +void i2o_report_controller_unit(struct i2o_controller *, struct i2o_device *); + +/* + * I2O System Table. Contains information about + * all the IOPs in the system. Used to inform IOPs + * about each other's existence. + * + * sys_tbl_ver is the CurrentChangeIndicator that is + * used by IOPs to track changes. + */ +static struct i2o_sys_tbl *sys_tbl; +static int sys_tbl_ind; +static int sys_tbl_len; + +/* + * This spin lock is used to keep a device from being + * added and deleted concurrently across CPUs or interrupts. + * This can occur when a user creates a device and immediatelly + * deletes it before the new_dev_notify() handler is called. + */ +static spinlock_t i2o_dev_lock = SPIN_LOCK_UNLOCKED; + +#ifdef MODULE +/* + * Function table to send to bus specific layers + * See for explanation of this + */ +static struct i2o_core_func_table i2o_core_functions = +{ + i2o_install_controller, + i2o_activate_controller, + i2o_find_controller, + i2o_unlock_controller, + i2o_run_queue, + i2o_delete_controller +}; + +#ifdef CONFIG_I2O_PCI_MODULE +extern int i2o_pci_core_attach(struct i2o_core_func_table *); +extern void i2o_pci_core_detach(void); +#endif /* CONFIG_I2O_PCI_MODULE */ + +#endif /* MODULE */ + +/* + * Structures and definitions for synchronous message posting. + * See i2o_post_wait() for description. + */ +struct i2o_post_wait_data +{ + int *status; /* Pointer to status block on caller stack */ + int *complete; /* Pointer to completion flag on caller stack */ + u32 id; /* Unique identifier */ + wait_queue_head_t *wq; /* Wake up for caller (NULL for dead) */ + struct i2o_post_wait_data *next; /* Chain */ + void *mem[2]; /* Memory blocks to recover on failure path */ +}; +static struct i2o_post_wait_data *post_wait_queue; +static u32 post_wait_id; // Unique ID for each post_wait +static spinlock_t post_wait_lock = SPIN_LOCK_UNLOCKED; +static void i2o_post_wait_complete(u32, int); + +/* OSM descriptor handler */ +static struct i2o_handler i2o_core_handler = +{ + (void *)i2o_core_reply, + NULL, + NULL, + NULL, + "I2O core layer", + 0, + I2O_CLASS_EXECUTIVE +}; + +/* + * Used when queueing a reply to be handled later + */ + +struct reply_info +{ + struct i2o_controller *iop; + u32 msg[MSG_FRAME_SIZE]; +}; +static struct reply_info evt_reply; +static struct reply_info events[I2O_EVT_Q_LEN]; +static int evt_in; +static int evt_out; +static int evt_q_len; +#define MODINC(x,y) ((x) = ((x) + 1) % (y)) + +/* + * I2O configuration spinlock. This isnt a big deal for contention + * so we have one only + */ + +static DECLARE_MUTEX(i2o_configuration_lock); + +/* + * Event spinlock. Used to keep event queue sane and from + * handling multiple events simultaneously. + */ +static spinlock_t i2o_evt_lock = SPIN_LOCK_UNLOCKED; + +/* + * Semaphore used to synchronize event handling thread with + * interrupt handler. + */ + +static DECLARE_MUTEX(evt_sem); +static DECLARE_COMPLETION(evt_dead); +DECLARE_WAIT_QUEUE_HEAD(evt_wait); + +static struct notifier_block i2o_reboot_notifier = +{ + i2o_reboot_event, + NULL, + 0 +}; + +/* + * Config options + */ + +static int verbose; +MODULE_PARM(verbose, "i"); + +/* + * I2O Core reply handler + */ +static void i2o_core_reply(struct i2o_handler *h, struct i2o_controller *c, + struct i2o_message *m) +{ + u32 *msg=(u32 *)m; + u32 status; + u32 context = msg[2]; + + if (msg[0] & MSG_FAIL) // Fail bit is set + { + u32 *preserved_msg = (u32*)(c->mem_offset + msg[7]); + + i2o_report_status(KERN_INFO, "i2o_core", msg); + i2o_dump_message(preserved_msg); + + /* If the failed request needs special treatment, + * it should be done here. */ + + /* Release the preserved msg by resubmitting it as a NOP */ + + preserved_msg[0] = cpu_to_le32(THREE_WORD_MSG_SIZE | SGL_OFFSET_0); + preserved_msg[1] = cpu_to_le32(I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0); + preserved_msg[2] = 0; + i2o_post_message(c, msg[7]); + + /* If reply to i2o_post_wait failed, return causes a timeout */ + + return; + } + +#ifdef DRIVERDEBUG + i2o_report_status(KERN_INFO, "i2o_core", msg); +#endif + + if(msg[2]&0x80000000) // Post wait message + { + if (msg[4] >> 24) + status = (msg[4] & 0xFFFF); + else + status = I2O_POST_WAIT_OK; + + i2o_post_wait_complete(context, status); + return; + } + + if(m->function == I2O_CMD_UTIL_EVT_REGISTER) + { + memcpy(events[evt_in].msg, msg, (msg[0]>>16)<<2); + events[evt_in].iop = c; + + spin_lock(&i2o_evt_lock); + MODINC(evt_in, I2O_EVT_Q_LEN); + if(evt_q_len == I2O_EVT_Q_LEN) + MODINC(evt_out, I2O_EVT_Q_LEN); + else + evt_q_len++; + spin_unlock(&i2o_evt_lock); + + up(&evt_sem); + wake_up_interruptible(&evt_wait); + return; + } + + if(m->function == I2O_CMD_LCT_NOTIFY) + { + up(&c->lct_sem); + return; + } + + /* + * If this happens, we want to dump the message to the syslog so + * it can be sent back to the card manufacturer by the end user + * to aid in debugging. + * + */ + printk(KERN_WARNING "%s: Unsolicited message reply sent to core!" + "Message dumped to syslog\n", + c->name); + i2o_dump_message(msg); + + return; +} + +/** + * i2o_install_handler - install a message handler + * @h: Handler structure + * + * Install an I2O handler - these handle the asynchronous messaging + * from the card once it has initialised. If the table of handlers is + * full then -ENOSPC is returned. On a success 0 is returned and the + * context field is set by the function. The structure is part of the + * system from this time onwards. It must not be freed until it has + * been uninstalled + */ + +int i2o_install_handler(struct i2o_handler *h) +{ + int i; + down(&i2o_configuration_lock); + for(i=0;icontext = i; + i2o_handlers[i]=h; + up(&i2o_configuration_lock); + return 0; + } + } + up(&i2o_configuration_lock); + return -ENOSPC; +} + +/** + * i2o_remove_handler - remove an i2o message handler + * @h: handler + * + * Remove a message handler previously installed with i2o_install_handler. + * After this function returns the handler object can be freed or re-used + */ + +int i2o_remove_handler(struct i2o_handler *h) +{ + i2o_handlers[h->context]=NULL; + return 0; +} + + +/* + * Each I2O controller has a chain of devices on it. + * Each device has a pointer to it's LCT entry to be used + * for fun purposes. + */ + +/** + * i2o_install_device - attach a device to a controller + * @c: controller + * @d: device + * + * Add a new device to an i2o controller. This can be called from + * non interrupt contexts only. It adds the device and marks it as + * unclaimed. The device memory becomes part of the kernel and must + * be uninstalled before being freed or reused. Zero is returned + * on success. + */ + +int i2o_install_device(struct i2o_controller *c, struct i2o_device *d) +{ + int i; + + down(&i2o_configuration_lock); + d->controller=c; + d->owner=NULL; + d->next=c->devices; + d->prev=NULL; + if (c->devices != NULL) + c->devices->prev=d; + c->devices=d; + *d->dev_name = 0; + + for(i = 0; i < I2O_MAX_MANAGERS; i++) + d->managers[i] = NULL; + + up(&i2o_configuration_lock); + return 0; +} + +/* we need this version to call out of i2o_delete_controller */ + +int __i2o_delete_device(struct i2o_device *d) +{ + struct i2o_device **p; + int i; + + p=&(d->controller->devices); + + /* + * Hey we have a driver! + * Check to see if the driver wants us to notify it of + * device deletion. If it doesn't we assume that it + * is unsafe to delete a device with an owner and + * fail. + */ + if(d->owner) + { + if(d->owner->dev_del_notify) + { + dprintk(KERN_INFO "Device has owner, notifying\n"); + d->owner->dev_del_notify(d->controller, d); + if(d->owner) + { + printk(KERN_WARNING + "Driver \"%s\" did not release device!\n", d->owner->name); + return -EBUSY; + } + } + else + return -EBUSY; + } + + /* + * Tell any other users who are talking to this device + * that it's going away. We assume that everything works. + */ + for(i=0; i < I2O_MAX_MANAGERS; i++) + { + if(d->managers[i] && d->managers[i]->dev_del_notify) + d->managers[i]->dev_del_notify(d->controller, d); + } + + while(*p!=NULL) + { + if(*p==d) + { + /* + * Destroy + */ + *p=d->next; + kfree(d); + return 0; + } + p=&((*p)->next); + } + printk(KERN_ERR "i2o_delete_device: passed invalid device.\n"); + return -EINVAL; +} + +/** + * i2o_delete_device - remove an i2o device + * @d: device to remove + * + * This function unhooks a device from a controller. The device + * will not be unhooked if it has an owner who does not wish to free + * it, or if the owner lacks a dev_del_notify function. In that case + * -EBUSY is returned. On success 0 is returned. Other errors cause + * negative errno values to be returned + */ + +int i2o_delete_device(struct i2o_device *d) +{ + int ret; + + down(&i2o_configuration_lock); + + /* + * Seek, locate + */ + + ret = __i2o_delete_device(d); + + up(&i2o_configuration_lock); + + return ret; +} + +/** + * i2o_install_controller - attach a controller + * @c: controller + * + * Add a new controller to the i2o layer. This can be called from + * non interrupt contexts only. It adds the controller and marks it as + * unused with no devices. If the tables are full or memory allocations + * fail then a negative errno code is returned. On success zero is + * returned and the controller is bound to the system. The structure + * must not be freed or reused until being uninstalled. + */ + +int i2o_install_controller(struct i2o_controller *c) +{ + int i; + down(&i2o_configuration_lock); + for(i=0;idlct = (i2o_lct*)kmalloc(8192, GFP_KERNEL); + if(c->dlct==NULL) + { + up(&i2o_configuration_lock); + return -ENOMEM; + } + i2o_controllers[i]=c; + c->devices = NULL; + c->next=i2o_controller_chain; + i2o_controller_chain=c; + c->unit = i; + c->page_frame = NULL; + c->hrt = NULL; + c->lct = NULL; + c->status_block = NULL; + sprintf(c->name, "i2o/iop%d", i); + i2o_num_controllers++; + init_MUTEX_LOCKED(&c->lct_sem); + up(&i2o_configuration_lock); + return 0; + } + } + printk(KERN_ERR "No free i2o controller slots.\n"); + up(&i2o_configuration_lock); + return -EBUSY; +} + +/** + * i2o_delete_controller - delete a controller + * @c: controller + * + * Remove an i2o controller from the system. If the controller or its + * devices are busy then -EBUSY is returned. On a failure a negative + * errno code is returned. On success zero is returned. + */ + +int i2o_delete_controller(struct i2o_controller *c) +{ + struct i2o_controller **p; + int users; + char name[16]; + int stat; + + dprintk(KERN_INFO "Deleting controller %s\n", c->name); + + /* + * Clear event registration as this can cause weird behavior + */ + if(c->status_block->iop_state == ADAPTER_STATE_OPERATIONAL) + i2o_event_register(c, core_context, 0, 0, 0); + + down(&i2o_configuration_lock); + if((users=atomic_read(&c->users))) + { + dprintk(KERN_INFO "I2O: %d users for controller %s\n", users, + c->name); + up(&i2o_configuration_lock); + return -EBUSY; + } + while(c->devices) + { + if(__i2o_delete_device(c->devices)<0) + { + /* Shouldnt happen */ + c->bus_disable(c); + up(&i2o_configuration_lock); + return -EBUSY; + } + } + + /* + * If this is shutdown time, the thread's already been killed + */ + if(c->lct_running) { + stat = kill_proc(c->lct_pid, SIGTERM, 1); + if(!stat) { + int count = 10 * 100; + while(c->lct_running && --count) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + if(!count) + printk(KERN_ERR + "%s: LCT thread still running!\n", + c->name); + } + } + + p=&i2o_controller_chain; + + while(*p) + { + if(*p==c) + { + /* Ask the IOP to switch to RESET state */ + i2o_reset_controller(c); + + /* Release IRQ */ + c->destructor(c); + + *p=c->next; + up(&i2o_configuration_lock); + + if(c->page_frame) + { + pci_unmap_single(c->pdev, c->page_frame_map, MSG_POOL_SIZE, PCI_DMA_FROMDEVICE); + kfree(c->page_frame); + } + if(c->hrt) + kfree(c->hrt); + if(c->lct) + kfree(c->lct); + if(c->status_block) + kfree(c->status_block); + if(c->dlct) + kfree(c->dlct); + + i2o_controllers[c->unit]=NULL; + memcpy(name, c->name, strlen(c->name)+1); + kfree(c); + dprintk(KERN_INFO "%s: Deleted from controller chain.\n", name); + + i2o_num_controllers--; + return 0; + } + p=&((*p)->next); + } + up(&i2o_configuration_lock); + printk(KERN_ERR "i2o_delete_controller: bad pointer!\n"); + return -ENOENT; +} + +/** + * i2o_unlock_controller - unlock a controller + * @c: controller to unlock + * + * Take a lock on an i2o controller. This prevents it being deleted. + * i2o controllers are not refcounted so a deletion of an in use device + * will fail, not take affect on the last dereference. + */ + +void i2o_unlock_controller(struct i2o_controller *c) +{ + atomic_dec(&c->users); +} + +/** + * i2o_find_controller - return a locked controller + * @n: controller number + * + * Returns a pointer to the controller object. The controller is locked + * on return. NULL is returned if the controller is not found. + */ + +struct i2o_controller *i2o_find_controller(int n) +{ + struct i2o_controller *c; + + if(n<0 || n>=MAX_I2O_CONTROLLERS) + return NULL; + + down(&i2o_configuration_lock); + c=i2o_controllers[n]; + if(c!=NULL) + atomic_inc(&c->users); + up(&i2o_configuration_lock); + return c; +} + +/** + * i2o_issue_claim - claim or release a device + * @cmd: command + * @c: controller to claim for + * @tid: i2o task id + * @type: type of claim + * + * Issue I2O UTIL_CLAIM and UTIL_RELEASE messages. The message to be sent + * is set by cmd. The tid is the task id of the object to claim and the + * type is the claim type (see the i2o standard) + * + * Zero is returned on success. + */ + +static int i2o_issue_claim(u32 cmd, struct i2o_controller *c, int tid, u32 type) +{ + u32 msg[5]; + + msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; + msg[1] = cmd << 24 | HOST_TID<<12 | tid; + msg[3] = 0; + msg[4] = type; + + return i2o_post_wait(c, msg, sizeof(msg), 60); +} + +/* + * i2o_claim_device - claim a device for use by an OSM + * @d: device to claim + * @h: handler for this device + * + * Do the leg work to assign a device to a given OSM on Linux. The + * kernel updates the internal handler data for the device and then + * performs an I2O claim for the device, attempting to claim the + * device as primary. If the attempt fails a negative errno code + * is returned. On success zero is returned. + */ + +int i2o_claim_device(struct i2o_device *d, struct i2o_handler *h) +{ + down(&i2o_configuration_lock); + if (d->owner) { + printk(KERN_INFO "Device claim called, but dev already owned by %s!", + h->name); + up(&i2o_configuration_lock); + return -EBUSY; + } + d->owner=h; + + if(i2o_issue_claim(I2O_CMD_UTIL_CLAIM ,d->controller,d->lct_data.tid, + I2O_CLAIM_PRIMARY)) + { + d->owner = NULL; + return -EBUSY; + } + up(&i2o_configuration_lock); + return 0; +} + +/** + * i2o_release_device - release a device that the OSM is using + * @d: device to claim + * @h: handler for this device + * + * Drop a claim by an OSM on a given I2O device. The handler is cleared + * and 0 is returned on success. + * + * AC - some devices seem to want to refuse an unclaim until they have + * finished internal processing. It makes sense since you don't want a + * new device to go reconfiguring the entire system until you are done. + * Thus we are prepared to wait briefly. + */ + +int i2o_release_device(struct i2o_device *d, struct i2o_handler *h) +{ + int err = 0; + int tries; + + down(&i2o_configuration_lock); + if (d->owner != h) { + printk(KERN_INFO "Claim release called, but not owned by %s!\n", + h->name); + up(&i2o_configuration_lock); + return -ENOENT; + } + + for(tries=0;tries<10;tries++) + { + d->owner = NULL; + + /* + * If the controller takes a nonblocking approach to + * releases we have to sleep/poll for a few times. + */ + + if((err=i2o_issue_claim(I2O_CMD_UTIL_RELEASE, d->controller, d->lct_data.tid, I2O_CLAIM_PRIMARY)) ) + { + err = -ENXIO; + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ); + } + else + { + err=0; + break; + } + } + up(&i2o_configuration_lock); + return err; +} + +/** + * i2o_device_notify_on - Enable deletion notifiers + * @d: device for notification + * @h: handler to install + * + * Called by OSMs to let the core know that they want to be + * notified if the given device is deleted from the system. + */ + +int i2o_device_notify_on(struct i2o_device *d, struct i2o_handler *h) +{ + int i; + + if(d->num_managers == I2O_MAX_MANAGERS) + return -ENOSPC; + + for(i = 0; i < I2O_MAX_MANAGERS; i++) + { + if(!d->managers[i]) + { + d->managers[i] = h; + break; + } + } + + d->num_managers++; + + return 0; +} + +/** + * i2o_device_notify_off - Remove deletion notifiers + * @d: device for notification + * @h: handler to remove + * + * Called by OSMs to let the core know that they no longer + * are interested in the fate of the given device. + */ +int i2o_device_notify_off(struct i2o_device *d, struct i2o_handler *h) +{ + int i; + + for(i=0; i < I2O_MAX_MANAGERS; i++) + { + if(d->managers[i] == h) + { + d->managers[i] = NULL; + d->num_managers--; + return 0; + } + } + + return -ENOENT; +} + +/** + * i2o_event_register - register interest in an event + * @c: Controller to register interest with + * @tid: I2O task id + * @init_context: initiator context to use with this notifier + * @tr_context: transaction context to use with this notifier + * @evt_mask: mask of events + * + * Create and posts an event registration message to the task. No reply + * is waited for, or expected. Errors in posting will be reported. + */ + +int i2o_event_register(struct i2o_controller *c, u32 tid, + u32 init_context, u32 tr_context, u32 evt_mask) +{ + u32 msg[5]; // Not performance critical, so we just + // i2o_post_this it instead of building it + // in IOP memory + + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_UTIL_EVT_REGISTER<<24 | HOST_TID<<12 | tid; + msg[2] = init_context; + msg[3] = tr_context; + msg[4] = evt_mask; + + return i2o_post_this(c, msg, sizeof(msg)); +} + +/* + * i2o_event_ack - acknowledge an event + * @c: controller + * @msg: pointer to the UTIL_EVENT_REGISTER reply we received + * + * We just take a pointer to the original UTIL_EVENT_REGISTER reply + * message and change the function code since that's what spec + * describes an EventAck message looking like. + */ + +int i2o_event_ack(struct i2o_controller *c, u32 *msg) +{ + struct i2o_message *m = (struct i2o_message *)msg; + + m->function = I2O_CMD_UTIL_EVT_ACK; + + return i2o_post_wait(c, msg, m->size * 4, 2); +} + +/* + * Core event handler. Runs as a separate thread and is woken + * up whenever there is an Executive class event. + */ +static int i2o_core_evt(void *reply_data) +{ + struct reply_info *reply = (struct reply_info *) reply_data; + u32 *msg = reply->msg; + struct i2o_controller *c = NULL; + unsigned long flags; + + lock_kernel(); + daemonize(); + unlock_kernel(); + + strcpy(current->comm, "i2oevtd"); + evt_running = 1; + + while(1) + { + if(down_interruptible(&evt_sem)) + { + dprintk(KERN_INFO "I2O event thread dead\n"); + printk("exiting..."); + evt_running = 0; + complete_and_exit(&evt_dead, 0); + } + + /* + * Copy the data out of the queue so that we don't have to lock + * around the whole function and just around the qlen update + */ + spin_lock_irqsave(&i2o_evt_lock, flags); + memcpy(reply, &events[evt_out], sizeof(struct reply_info)); + MODINC(evt_out, I2O_EVT_Q_LEN); + evt_q_len--; + spin_unlock_irqrestore(&i2o_evt_lock, flags); + + c = reply->iop; + dprintk(KERN_INFO "I2O IRTOS EVENT: iop%d, event %#10x\n", c->unit, msg[4]); + + /* + * We do not attempt to delete/quiesce/etc. the controller if + * some sort of error indidication occurs. We may want to do + * so in the future, but for now we just let the user deal with + * it. One reason for this is that what to do with an error + * or when to send what ćrror is not really agreed on, so + * we get errors that may not be fatal but just look like they + * are...so let the user deal with it. + */ + switch(msg[4]) + { + case I2O_EVT_IND_EXEC_RESOURCE_LIMITS: + printk(KERN_ERR "%s: Out of resources\n", c->name); + break; + + case I2O_EVT_IND_EXEC_POWER_FAIL: + printk(KERN_ERR "%s: Power failure\n", c->name); + break; + + case I2O_EVT_IND_EXEC_HW_FAIL: + { + char *fail[] = + { + "Unknown Error", + "Power Lost", + "Code Violation", + "Parity Error", + "Code Execution Exception", + "Watchdog Timer Expired" + }; + + if(msg[5] <= 6) + printk(KERN_ERR "%s: Hardware Failure: %s\n", + c->name, fail[msg[5]]); + else + printk(KERN_ERR "%s: Unknown Hardware Failure\n", c->name); + + break; + } + + /* + * New device created + * - Create a new i2o_device entry + * - Inform all interested drivers about this device's existence + */ + case I2O_EVT_IND_EXEC_NEW_LCT_ENTRY: + { + struct i2o_device *d = (struct i2o_device *) + kmalloc(sizeof(struct i2o_device), GFP_KERNEL); + int i; + + if (d == NULL) { + printk(KERN_EMERG "i2oevtd: out of memory\n"); + break; + } + memcpy(&d->lct_data, &msg[5], sizeof(i2o_lct_entry)); + + d->next = NULL; + d->controller = c; + d->flags = 0; + + i2o_report_controller_unit(c, d); + i2o_install_device(c,d); + + for(i = 0; i < MAX_I2O_MODULES; i++) + { + if(i2o_handlers[i] && + i2o_handlers[i]->new_dev_notify && + (i2o_handlers[i]->class&d->lct_data.class_id)) + { + spin_lock(&i2o_dev_lock); + i2o_handlers[i]->new_dev_notify(c,d); + spin_unlock(&i2o_dev_lock); + } + } + + break; + } + + /* + * LCT entry for a device has been modified, so update it + * internally. + */ + case I2O_EVT_IND_EXEC_MODIFIED_LCT: + { + struct i2o_device *d; + i2o_lct_entry *new_lct = (i2o_lct_entry *)&msg[5]; + + for(d = c->devices; d; d = d->next) + { + if(d->lct_data.tid == new_lct->tid) + { + memcpy(&d->lct_data, new_lct, sizeof(i2o_lct_entry)); + break; + } + } + break; + } + + case I2O_EVT_IND_CONFIGURATION_FLAG: + printk(KERN_WARNING "%s requires user configuration\n", c->name); + break; + + case I2O_EVT_IND_GENERAL_WARNING: + printk(KERN_WARNING "%s: Warning notification received!" + "Check configuration for errors!\n", c->name); + break; + + case I2O_EVT_IND_EVT_MASK_MODIFIED: + /* Well I guess that was us hey .. */ + break; + + default: + printk(KERN_WARNING "%s: No handler for event (0x%08x)\n", c->name, msg[4]); + break; + } + } + + return 0; +} + +/* + * Dynamic LCT update. This compares the LCT with the currently + * installed devices to check for device deletions..this needed b/c there + * is no DELETED_LCT_ENTRY EventIndicator for the Executive class so + * we can't just have the event handler do this...annoying + * + * This is a hole in the spec that will hopefully be fixed someday. + */ +static int i2o_dyn_lct(void *foo) +{ + struct i2o_controller *c = (struct i2o_controller *)foo; + struct i2o_device *d = NULL; + struct i2o_device *d1 = NULL; + int i = 0; + int found = 0; + int entries; + void *tmp; + char name[16]; + + lock_kernel(); + daemonize(); + unlock_kernel(); + + sprintf(name, "iop%d_lctd", c->unit); + strcpy(current->comm, name); + + c->lct_running = 1; + + while(1) + { + down_interruptible(&c->lct_sem); + if(signal_pending(current)) + { + dprintk(KERN_ERR "%s: LCT thread dead\n", c->name); + c->lct_running = 0; + return 0; + } + + entries = c->dlct->table_size; + entries -= 3; + entries /= 9; + + dprintk(KERN_INFO "%s: Dynamic LCT Update\n",c->name); + dprintk(KERN_INFO "%s: Dynamic LCT contains %d entries\n", c->name, entries); + + if(!entries) + { + printk(KERN_INFO "%s: Empty LCT???\n", c->name); + continue; + } + + /* + * Loop through all the devices on the IOP looking for their + * LCT data in the LCT. We assume that TIDs are not repeated. + * as that is the only way to really tell. It's been confirmed + * by the IRTOS vendor(s?) that TIDs are not reused until they + * wrap arround(4096), and I doubt a system will up long enough + * to create/delete that many devices. + */ + for(d = c->devices; d; ) + { + found = 0; + d1 = d->next; + + for(i = 0; i < entries; i++) + { + if(d->lct_data.tid == c->dlct->lct_entry[i].tid) + { + found = 1; + break; + } + } + if(!found) + { + dprintk(KERN_INFO "i2o_core: Deleted device!\n"); + spin_lock(&i2o_dev_lock); + i2o_delete_device(d); + spin_unlock(&i2o_dev_lock); + } + d = d1; + } + + /* + * Tell LCT to renotify us next time there is a change + */ + i2o_lct_notify(c); + + /* + * Copy new LCT into public LCT + * + * Possible race if someone is reading LCT while we are copying + * over it. If this happens, we'll fix it then. but I doubt that + * the LCT will get updated often enough or will get read by + * a user often enough to worry. + */ + if(c->lct->table_size < c->dlct->table_size) + { + tmp = c->lct; + c->lct = kmalloc(c->dlct->table_size<<2, GFP_KERNEL); + if(!c->lct) + { + printk(KERN_ERR "%s: No memory for LCT!\n", c->name); + c->lct = tmp; + continue; + } + kfree(tmp); + } + memcpy(c->lct, c->dlct, c->dlct->table_size<<2); + } + + return 0; +} + +/** + * i2o_run_queue - process pending events on a controller + * @c: controller to process + * + * This is called by the bus specific driver layer when an interrupt + * or poll of this card interface is desired. + */ + +void i2o_run_queue(struct i2o_controller *c) +{ + struct i2o_message *m; + u32 mv; + u32 *msg; + + /* + * Old 960 steppings had a bug in the I2O unit that caused + * the queue to appear empty when it wasn't. + */ + if((mv=I2O_REPLY_READ32(c))==0xFFFFFFFF) + mv=I2O_REPLY_READ32(c); + + while(mv!=0xFFFFFFFF) + { + struct i2o_handler *i; + /* Map the message from the page frame map to kernel virtual */ + m=(struct i2o_message *)(mv - (unsigned long)c->page_frame_map + (unsigned long)c->page_frame); + msg=(u32*)m; + + /* + * Ensure this message is seen coherently but cachably by + * the processor + */ + + pci_dma_sync_single(c->pdev, c->page_frame_map, MSG_FRAME_SIZE, PCI_DMA_FROMDEVICE); + + /* + * Despatch it + */ + + i=i2o_handlers[m->initiator_context&(MAX_I2O_MODULES-1)]; + if(i && i->reply) + i->reply(i,c,m); + else + { + printk(KERN_WARNING "I2O: Spurious reply to handler %d\n", + m->initiator_context&(MAX_I2O_MODULES-1)); + } + i2o_flush_reply(c,mv); + mb(); + + /* That 960 bug again... */ + if((mv=I2O_REPLY_READ32(c))==0xFFFFFFFF) + mv=I2O_REPLY_READ32(c); + } +} + + +/** + * i2o_get_class_name - do i2o class name lookup + * @class: class number + * + * Return a descriptive string for an i2o class + */ + +const char *i2o_get_class_name(int class) +{ + int idx = 16; + static char *i2o_class_name[] = { + "Executive", + "Device Driver Module", + "Block Device", + "Tape Device", + "LAN Interface", + "WAN Interface", + "Fibre Channel Port", + "Fibre Channel Device", + "SCSI Device", + "ATE Port", + "ATE Device", + "Floppy Controller", + "Floppy Device", + "Secondary Bus Port", + "Peer Transport Agent", + "Peer Transport", + "Unknown" + }; + + switch(class&0xFFF) + { + case I2O_CLASS_EXECUTIVE: + idx = 0; break; + case I2O_CLASS_DDM: + idx = 1; break; + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + idx = 2; break; + case I2O_CLASS_SEQUENTIAL_STORAGE: + idx = 3; break; + case I2O_CLASS_LAN: + idx = 4; break; + case I2O_CLASS_WAN: + idx = 5; break; + case I2O_CLASS_FIBRE_CHANNEL_PORT: + idx = 6; break; + case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL: + idx = 7; break; + case I2O_CLASS_SCSI_PERIPHERAL: + idx = 8; break; + case I2O_CLASS_ATE_PORT: + idx = 9; break; + case I2O_CLASS_ATE_PERIPHERAL: + idx = 10; break; + case I2O_CLASS_FLOPPY_CONTROLLER: + idx = 11; break; + case I2O_CLASS_FLOPPY_DEVICE: + idx = 12; break; + case I2O_CLASS_BUS_ADAPTER_PORT: + idx = 13; break; + case I2O_CLASS_PEER_TRANSPORT_AGENT: + idx = 14; break; + case I2O_CLASS_PEER_TRANSPORT: + idx = 15; break; + } + + return i2o_class_name[idx]; +} + + +/** + * i2o_wait_message - obtain an i2o message from the IOP + * @c: controller + * @why: explanation + * + * This function waits up to 5 seconds for a message slot to be + * available. If no message is available it prints an error message + * that is expected to be what the message will be used for (eg + * "get_status"). 0xFFFFFFFF is returned on a failure. + * + * On a success the message is returned. This is the physical page + * frame offset address from the read port. (See the i2o spec) + */ + +u32 i2o_wait_message(struct i2o_controller *c, char *why) +{ + long time=jiffies; + u32 m; + while((m=I2O_POST_READ32(c))==0xFFFFFFFF) + { + if((jiffies-time)>=5*HZ) + { + dprintk(KERN_ERR "%s: Timeout waiting for message frame to send %s.\n", + c->name, why); + return 0xFFFFFFFF; + } + schedule(); + barrier(); + } + return m; +} + +/** + * i2o_report_controller_unit - print information about a tid + * @c: controller + * @d: device + * + * Dump an information block associated with a given unit (TID). The + * tables are read and a block of text is output to printk that is + * formatted intended for the user. + */ + +void i2o_report_controller_unit(struct i2o_controller *c, struct i2o_device *d) +{ + char buf[64]; + char str[22]; + int ret; + int unit = d->lct_data.tid; + + if(verbose==0) + return; + + printk(KERN_INFO "Target ID %d.\n", unit); + if((ret=i2o_query_scalar(c, unit, 0xF100, 3, buf, 16))>=0) + { + buf[16]=0; + printk(KERN_INFO " Vendor: %s\n", buf); + } + if((ret=i2o_query_scalar(c, unit, 0xF100, 4, buf, 16))>=0) + { + buf[16]=0; + printk(KERN_INFO " Device: %s\n", buf); + } + if(i2o_query_scalar(c, unit, 0xF100, 5, buf, 16)>=0) + { + buf[16]=0; + printk(KERN_INFO " Description: %s\n", buf); + } + if((ret=i2o_query_scalar(c, unit, 0xF100, 6, buf, 8))>=0) + { + buf[8]=0; + printk(KERN_INFO " Rev: %s\n", buf); + } + + printk(KERN_INFO " Class: "); + sprintf(str, "%-21s", i2o_get_class_name(d->lct_data.class_id)); + printk("%s\n", str); + + printk(KERN_INFO " Subclass: 0x%04X\n", d->lct_data.sub_class); + printk(KERN_INFO " Flags: "); + + if(d->lct_data.device_flags&(1<<0)) + printk("C"); // ConfigDialog requested + if(d->lct_data.device_flags&(1<<1)) + printk("U"); // Multi-user capable + if(!(d->lct_data.device_flags&(1<<4))) + printk("P"); // Peer service enabled! + if(!(d->lct_data.device_flags&(1<<5))) + printk("M"); // Mgmt service enabled! + printk("\n"); + +} + + +/* + * Parse the hardware resource table. Right now we print it out + * and don't do a lot with it. We should collate these and then + * interact with the Linux resource allocation block. + * + * Lets prove we can read it first eh ? + * + * This is full of endianisms! + */ + +static int i2o_parse_hrt(struct i2o_controller *c) +{ +#ifdef DRIVERDEBUG + u32 *rows=(u32*)c->hrt; + u8 *p=(u8 *)c->hrt; + u8 *d; + int count; + int length; + int i; + int state; + + if(p[3]!=0) + { + printk(KERN_ERR "%s: HRT table for controller is too new a version.\n", + c->name); + return -1; + } + + count=p[0]|(p[1]<<8); + length = p[2]; + + printk(KERN_INFO "%s: HRT has %d entries of %d bytes each.\n", + c->name, count, length<<2); + + rows+=2; + + for(i=0;i>=12; + if(state&(1<<0)) + printk("H"); /* Hidden */ + if(state&(1<<2)) + { + printk("P"); /* Present */ + if(state&(1<<1)) + printk("C"); /* Controlled */ + } + if(state>9) + printk("*"); /* Hard */ + + printk("]:"); + + switch(p[3]&0xFFFF) + { + case 0: + /* Adapter private bus - easy */ + printk("Local bus %d: I/O at 0x%04X Mem 0x%08X", + p[2], d[1]<<8|d[0], *(u32 *)(d+4)); + break; + case 1: + /* ISA bus */ + printk("ISA %d: CSN %d I/O at 0x%04X Mem 0x%08X", + p[2], d[2], d[1]<<8|d[0], *(u32 *)(d+4)); + break; + + case 2: /* EISA bus */ + printk("EISA %d: Slot %d I/O at 0x%04X Mem 0x%08X", + p[2], d[3], d[1]<<8|d[0], *(u32 *)(d+4)); + break; + + case 3: /* MCA bus */ + printk("MCA %d: Slot %d I/O at 0x%04X Mem 0x%08X", + p[2], d[3], d[1]<<8|d[0], *(u32 *)(d+4)); + break; + + case 4: /* PCI bus */ + printk("PCI %d: Bus %d Device %d Function %d", + p[2], d[2], d[1], d[0]); + break; + + case 0x80: /* Other */ + default: + printk("Unsupported bus type."); + break; + } + printk("\n"); + rows+=length; + } +#endif + return 0; +} + +/* + * The logical configuration table tells us what we can talk to + * on the board. Most of the stuff isn't interesting to us. + */ + +static int i2o_parse_lct(struct i2o_controller *c) +{ + int i; + int max; + int tid; + struct i2o_device *d; + i2o_lct *lct = c->lct; + + if (lct == NULL) { + printk(KERN_ERR "%s: LCT is empty???\n", c->name); + return -1; + } + + max = lct->table_size; + max -= 3; + max /= 9; + + printk(KERN_INFO "%s: LCT has %d entries.\n", c->name, max); + + if(lct->iop_flags&(1<<0)) + printk(KERN_WARNING "%s: Configuration dialog desired.\n", c->name); + + for(i=0;icontroller = c; + d->next = NULL; + + memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry)); + + d->flags = 0; + tid = d->lct_data.tid; + + i2o_report_controller_unit(c, d); + + i2o_install_device(c, d); + } + return 0; +} + + +/** + * i2o_quiesce_controller - quiesce controller + * @c: controller + * + * Quiesce an IOP. Causes IOP to make external operation quiescent + * (i2o 'READY' state). Internal operation of the IOP continues normally. + */ + +int i2o_quiesce_controller(struct i2o_controller *c) +{ + u32 msg[4]; + int ret; + + i2o_status_get(c); + + /* SysQuiesce discarded if IOP not in READY or OPERATIONAL state */ + + if ((c->status_block->iop_state != ADAPTER_STATE_READY) && + (c->status_block->iop_state != ADAPTER_STATE_OPERATIONAL)) + { + return 0; + } + + msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID; + msg[3] = 0; + + /* Long timeout needed for quiesce if lots of devices */ + + if ((ret = i2o_post_wait(c, msg, sizeof(msg), 240))) + printk(KERN_INFO "%s: Unable to quiesce (status=%#x).\n", + c->name, -ret); + else + dprintk(KERN_INFO "%s: Quiesced.\n", c->name); + + i2o_status_get(c); // Entered READY state + return ret; +} + +/** + * i2o_enable_controller - move controller from ready to operational + * @c: controller + * + * Enable IOP. This allows the IOP to resume external operations and + * reverses the effect of a quiesce. In the event of an error a negative + * errno code is returned. + */ + +int i2o_enable_controller(struct i2o_controller *c) +{ + u32 msg[4]; + int ret; + + i2o_status_get(c); + + /* Enable only allowed on READY state */ + if(c->status_block->iop_state != ADAPTER_STATE_READY) + return -EINVAL; + + msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1]=I2O_CMD_SYS_ENABLE<<24|HOST_TID<<12|ADAPTER_TID; + + /* How long of a timeout do we need? */ + + if ((ret = i2o_post_wait(c, msg, sizeof(msg), 240))) + printk(KERN_ERR "%s: Could not enable (status=%#x).\n", + c->name, -ret); + else + dprintk(KERN_INFO "%s: Enabled.\n", c->name); + + i2o_status_get(c); // entered OPERATIONAL state + + return ret; +} + +/** + * i2o_clear_controller - clear a controller + * @c: controller + * + * Clear an IOP to HOLD state, ie. terminate external operations, clear all + * input queues and prepare for a system restart. IOP's internal operation + * continues normally and the outbound queue is alive. + * The IOP is not expected to rebuild its LCT. + */ + +int i2o_clear_controller(struct i2o_controller *c) +{ + struct i2o_controller *iop; + u32 msg[4]; + int ret; + + /* Quiesce all IOPs first */ + + for (iop = i2o_controller_chain; iop; iop = iop->next) + i2o_quiesce_controller(iop); + + msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1]=I2O_CMD_ADAPTER_CLEAR<<24|HOST_TID<<12|ADAPTER_TID; + msg[3]=0; + + if ((ret=i2o_post_wait(c, msg, sizeof(msg), 30))) + printk(KERN_INFO "%s: Unable to clear (status=%#x).\n", + c->name, -ret); + else + dprintk(KERN_INFO "%s: Cleared.\n",c->name); + + i2o_status_get(c); + + /* Enable other IOPs */ + + for (iop = i2o_controller_chain; iop; iop = iop->next) + if (iop != c) + i2o_enable_controller(iop); + + return ret; +} + + +/** + * i2o_reset_controller - reset an IOP + * @c: controller to reset + * + * Reset the IOP into INIT state and wait until IOP gets into RESET state. + * Terminate all external operations, clear IOP's inbound and outbound + * queues, terminate all DDMs, and reload the IOP's operating environment + * and all local DDMs. The IOP rebuilds its LCT. + */ + +static int i2o_reset_controller(struct i2o_controller *c) +{ + struct i2o_controller *iop; + u32 m; + u8 *status; + u32 *msg; + long time; + + /* Quiesce all IOPs first */ + + for (iop = i2o_controller_chain; iop; iop = iop->next) + { + if(iop->type != I2O_TYPE_PCI || !iop->bus.pci.dpt) + i2o_quiesce_controller(iop); + } + + m=i2o_wait_message(c, "AdapterReset"); + if(m==0xFFFFFFFF) + return -ETIMEDOUT; + msg=(u32 *)(c->mem_offset+m); + + status=(void *)kmalloc(4, GFP_KERNEL); + if(status==NULL) { + printk(KERN_ERR "IOP reset failed - no free memory.\n"); + return -ENOMEM; + } + memset(status, 0, 4); + + msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID; + msg[2]=core_context; + msg[3]=0; + msg[4]=0; + msg[5]=0; + msg[6]=virt_to_bus(status); + msg[7]=0; /* 64bit host FIXME */ + + i2o_post_message(c,m); + + /* Wait for a reply */ + time=jiffies; + while(*status==0) + { + if((jiffies-time)>=20*HZ) + { + printk(KERN_ERR "IOP reset timeout.\n"); + // Better to leak this for safety: kfree(status); + return -ETIMEDOUT; + } + schedule(); + barrier(); + } + + if (*status==I2O_CMD_IN_PROGRESS) + { + /* + * Once the reset is sent, the IOP goes into the INIT state + * which is indeterminate. We need to wait until the IOP + * has rebooted before we can let the system talk to + * it. We read the inbound Free_List until a message is + * available. If we can't read one in the given ammount of + * time, we assume the IOP could not reboot properly. + */ + + dprintk(KERN_INFO "%s: Reset in progress, waiting for reboot...\n", + c->name); + + time = jiffies; + m = I2O_POST_READ32(c); + while(m == 0XFFFFFFFF) + { + if((jiffies-time) >= 30*HZ) + { + printk(KERN_ERR "%s: Timeout waiting for IOP reset.\n", + c->name); + return -ETIMEDOUT; + } + schedule(); + barrier(); + m = I2O_POST_READ32(c); + } + i2o_flush_reply(c,m); + } + + /* If IopReset was rejected or didn't perform reset, try IopClear */ + + i2o_status_get(c); + if (status[0] == I2O_CMD_REJECTED || + c->status_block->iop_state != ADAPTER_STATE_RESET) + { + printk(KERN_WARNING "%s: Reset rejected, trying to clear\n",c->name); + i2o_clear_controller(c); + } + else + dprintk(KERN_INFO "%s: Reset completed.\n", c->name); + + /* Enable other IOPs */ + + for (iop = i2o_controller_chain; iop; iop = iop->next) + if (iop != c) + i2o_enable_controller(iop); + + kfree(status); + return 0; +} + + +/** + * i2o_status_get - get the status block for the IOP + * @c: controller + * + * Issue a status query on the controller. This updates the + * attached status_block. If the controller fails to reply or an + * error occurs then a negative errno code is returned. On success + * zero is returned and the status_blok is updated. + */ + +int i2o_status_get(struct i2o_controller *c) +{ + long time; + u32 m; + u32 *msg; + u8 *status_block; + + if (c->status_block == NULL) + { + c->status_block = (i2o_status_block *) + kmalloc(sizeof(i2o_status_block),GFP_KERNEL); + if (c->status_block == NULL) + { + printk(KERN_CRIT "%s: Get Status Block failed; Out of memory.\n", + c->name); + return -ENOMEM; + } + } + + status_block = (u8*)c->status_block; + memset(c->status_block,0,sizeof(i2o_status_block)); + + m=i2o_wait_message(c, "StatusGet"); + if(m==0xFFFFFFFF) + return -ETIMEDOUT; + msg=(u32 *)(c->mem_offset+m); + + msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1]=I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID; + msg[2]=core_context; + msg[3]=0; + msg[4]=0; + msg[5]=0; + msg[6]=virt_to_bus(c->status_block); + msg[7]=0; /* 64bit host FIXME */ + msg[8]=sizeof(i2o_status_block); /* always 88 bytes */ + + i2o_post_message(c,m); + + /* Wait for a reply */ + + time=jiffies; + while(status_block[87]!=0xFF) + { + if((jiffies-time)>=5*HZ) + { + printk(KERN_ERR "%s: Get status timeout.\n",c->name); + return -ETIMEDOUT; + } + schedule(); + barrier(); + } + +#ifdef DRIVERDEBUG + printk(KERN_INFO "%s: State = ", c->name); + switch (c->status_block->iop_state) { + case 0x01: + printk("INIT\n"); + break; + case 0x02: + printk("RESET\n"); + break; + case 0x04: + printk("HOLD\n"); + break; + case 0x05: + printk("READY\n"); + break; + case 0x08: + printk("OPERATIONAL\n"); + break; + case 0x10: + printk("FAILED\n"); + break; + case 0x11: + printk("FAULTED\n"); + break; + default: + printk("%x (unknown !!)\n",c->status_block->iop_state); +} +#endif + + return 0; +} + +/* + * Get the Hardware Resource Table for the device. + * The HRT contains information about possible hidden devices + * but is mostly useless to us + */ +int i2o_hrt_get(struct i2o_controller *c) +{ + u32 msg[6]; + int ret, size = sizeof(i2o_hrt); + + /* First read just the header to figure out the real size */ + + do { + if (c->hrt == NULL) { + c->hrt=kmalloc(size, GFP_KERNEL); + if (c->hrt == NULL) { + printk(KERN_CRIT "%s: Hrt Get failed; Out of memory.\n", c->name); + return -ENOMEM; + } + } + + msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4; + msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[3]= 0; + msg[4]= (0xD0000000 | size); /* Simple transaction */ + msg[5]= virt_to_bus(c->hrt); /* Dump it here */ + + ret = i2o_post_wait_mem(c, msg, sizeof(msg), 20, c->hrt, NULL); + + if(ret == -ETIMEDOUT) + { + /* The HRT block we used is in limbo somewhere. When the iop wakes up + we will recover it */ + c->hrt = NULL; + return ret; + } + + if(ret<0) + { + printk(KERN_ERR "%s: Unable to get HRT (status=%#x)\n", + c->name, -ret); + return ret; + } + + if (c->hrt->num_entries * c->hrt->entry_len << 2 > size) { + size = c->hrt->num_entries * c->hrt->entry_len << 2; + kfree(c->hrt); + c->hrt = NULL; + } + } while (c->hrt == NULL); + + i2o_parse_hrt(c); // just for debugging + + return 0; +} + +/* + * Send the I2O System Table to the specified IOP + * + * The system table contains information about all the IOPs in the + * system. It is build and then sent to each IOP so that IOPs can + * establish connections between each other. + * + */ +static int i2o_systab_send(struct i2o_controller *iop) +{ + u32 msg[12]; + int ret; + u32 *privbuf = kmalloc(16, GFP_KERNEL); + if(privbuf == NULL) + return -ENOMEM; + + if(iop->type == I2O_TYPE_PCI) + { + struct resource *root; + + if(iop->status_block->current_mem_size < iop->status_block->desired_mem_size) + { + struct resource *res = &iop->mem_resource; + res->name = iop->pdev->bus->name; + res->flags = IORESOURCE_MEM; + res->start = 0; + res->end = 0; + printk("%s: requires private memory resources.\n", iop->name); + root = pci_find_parent_resource(iop->pdev, res); + if(root==NULL) + printk("Can't find parent resource!\n"); + if(root && allocate_resource(root, res, + iop->status_block->desired_mem_size, + iop->status_block->desired_mem_size, + iop->status_block->desired_mem_size, + 1<<20, /* Unspecified, so use 1Mb and play safe */ + NULL, + NULL)>=0) + { + iop->mem_alloc = 1; + iop->status_block->current_mem_size = 1 + res->end - res->start; + iop->status_block->current_mem_base = res->start; + printk(KERN_INFO "%s: allocated %ld bytes of PCI memory at 0x%08lX.\n", + iop->name, 1+res->end-res->start, res->start); + } + } + if(iop->status_block->current_io_size < iop->status_block->desired_io_size) + { + struct resource *res = &iop->io_resource; + res->name = iop->pdev->bus->name; + res->flags = IORESOURCE_IO; + res->start = 0; + res->end = 0; + printk("%s: requires private memory resources.\n", iop->name); + root = pci_find_parent_resource(iop->pdev, res); + if(root==NULL) + printk("Can't find parent resource!\n"); + if(root && allocate_resource(root, res, + iop->status_block->desired_io_size, + iop->status_block->desired_io_size, + iop->status_block->desired_io_size, + 1<<20, /* Unspecified, so use 1Mb and play safe */ + NULL, + NULL)>=0) + { + iop->io_alloc = 1; + iop->status_block->current_io_size = 1 + res->end - res->start; + iop->status_block->current_mem_base = res->start; + printk(KERN_INFO "%s: allocated %ld bytes of PCI I/O at 0x%08lX.\n", + iop->name, 1+res->end-res->start, res->start); + } + } + } + else + { + privbuf[0] = iop->status_block->current_mem_base; + privbuf[1] = iop->status_block->current_mem_size; + privbuf[2] = iop->status_block->current_io_base; + privbuf[3] = iop->status_block->current_io_size; + } + + msg[0] = I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6; + msg[1] = I2O_CMD_SYS_TAB_SET<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[3] = 0; + msg[4] = (0<<16) | ((iop->unit+2) ); /* Host 0 IOP ID (unit + 2) */ + msg[5] = 0; /* Segment 0 */ + + /* + * Provide three SGL-elements: + * System table (SysTab), Private memory space declaration and + * Private i/o space declaration + * + * FIXME: provide these for controllers needing them + */ + msg[6] = 0x54000000 | sys_tbl_len; + msg[7] = virt_to_bus(sys_tbl); + msg[8] = 0x54000000 | 8; + msg[9] = virt_to_bus(privbuf); + msg[10] = 0xD4000000 | 8; + msg[11] = virt_to_bus(privbuf+2); + + ret=i2o_post_wait_mem(iop, msg, sizeof(msg), 120, privbuf, NULL); + + if(ret==-ETIMEDOUT) + { + printk(KERN_ERR "%s: SysTab setup timed out.\n", iop->name); + } + else if(ret<0) + { + printk(KERN_ERR "%s: Unable to set SysTab (status=%#x).\n", + iop->name, -ret); + kfree(privbuf); + } + else + { + dprintk(KERN_INFO "%s: SysTab set.\n", iop->name); + kfree(privbuf); + } + i2o_status_get(iop); // Entered READY state + + return ret; + + } + +/* + * Initialize I2O subsystem. + */ +static void __init i2o_sys_init(void) +{ + struct i2o_controller *iop, *niop = NULL; + + printk(KERN_INFO "Activating I2O controllers...\n"); + printk(KERN_INFO "This may take a few minutes if there are many devices\n"); + + /* In INIT state, Activate IOPs */ + for (iop = i2o_controller_chain; iop; iop = niop) { + dprintk(KERN_INFO "Calling i2o_activate_controller for %s...\n", + iop->name); + niop = iop->next; + if (i2o_activate_controller(iop) < 0) + i2o_delete_controller(iop); + } + + /* Active IOPs in HOLD state */ + +rebuild_sys_tab: + if (i2o_controller_chain == NULL) + return; + + /* + * If build_sys_table fails, we kill everything and bail + * as we can't init the IOPs w/o a system table + */ + dprintk(KERN_INFO "i2o_core: Calling i2o_build_sys_table...\n"); + if (i2o_build_sys_table() < 0) { + i2o_sys_shutdown(); + return; + } + + /* If IOP don't get online, we need to rebuild the System table */ + for (iop = i2o_controller_chain; iop; iop = niop) { + niop = iop->next; + dprintk(KERN_INFO "Calling i2o_online_controller for %s...\n", iop->name); + if (i2o_online_controller(iop) < 0) { + i2o_delete_controller(iop); + goto rebuild_sys_tab; + } + } + + /* Active IOPs now in OPERATIONAL state */ + + /* + * Register for status updates from all IOPs + */ + for(iop = i2o_controller_chain; iop; iop=iop->next) { + + /* Create a kernel thread to deal with dynamic LCT updates */ + iop->lct_pid = kernel_thread(i2o_dyn_lct, iop, CLONE_SIGHAND); + + /* Update change ind on DLCT */ + iop->dlct->change_ind = iop->lct->change_ind; + + /* Start dynamic LCT updates */ + i2o_lct_notify(iop); + + /* Register for all events from IRTOS */ + i2o_event_register(iop, core_context, 0, 0, 0xFFFFFFFF); + } +} + +/** + * i2o_sys_shutdown - shutdown I2O system + * + * Bring down each i2o controller and then return. Each controller + * is taken through an orderly shutdown + */ + +static void i2o_sys_shutdown(void) +{ + struct i2o_controller *iop, *niop; + + /* Delete all IOPs from the controller chain */ + /* that will reset all IOPs too */ + + for (iop = i2o_controller_chain; iop; iop = niop) { + niop = iop->next; + i2o_delete_controller(iop); + } +} + +/** + * i2o_activate_controller - bring controller up to HOLD + * @iop: controller + * + * This function brings an I2O controller into HOLD state. The adapter + * is reset if neccessary and then the queues and resource table + * are read. -1 is returned on a failure, 0 on success. + * + */ + +int i2o_activate_controller(struct i2o_controller *iop) +{ + /* In INIT state, Wait Inbound Q to initialize (in i2o_status_get) */ + /* In READY state, Get status */ + + if (i2o_status_get(iop) < 0) { + printk(KERN_INFO "Unable to obtain status of %s, " + "attempting a reset.\n", iop->name); + if (i2o_reset_controller(iop) < 0) + return -1; + } + + if(iop->status_block->iop_state == ADAPTER_STATE_FAULTED) { + printk(KERN_CRIT "%s: hardware fault\n", iop->name); + return -1; + } + + if (iop->status_block->i2o_version > I2OVER15) { + printk(KERN_ERR "%s: Not running vrs. 1.5. of the I2O Specification.\n", + iop->name); + return -1; + } + + if (iop->status_block->iop_state == ADAPTER_STATE_READY || + iop->status_block->iop_state == ADAPTER_STATE_OPERATIONAL || + iop->status_block->iop_state == ADAPTER_STATE_HOLD || + iop->status_block->iop_state == ADAPTER_STATE_FAILED) + { + dprintk(KERN_INFO "%s: Already running, trying to reset...\n", + iop->name); + if (i2o_reset_controller(iop) < 0) + return -1; + } + + if (i2o_init_outbound_q(iop) < 0) + return -1; + + if (i2o_post_outbound_messages(iop)) + return -1; + + /* In HOLD state */ + + if (i2o_hrt_get(iop) < 0) + return -1; + + return 0; +} + + +/** + * i2o_init_outbound_queue - setup the outbound queue + * @c: controller + * + * Clear and (re)initialize IOP's outbound queue. Returns 0 on + * success or a negative errno code on a failure. + */ + +int i2o_init_outbound_q(struct i2o_controller *c) +{ + u8 *status; + u32 m; + u32 *msg; + u32 time; + + dprintk(KERN_INFO "%s: Initializing Outbound Queue...\n", c->name); + m=i2o_wait_message(c, "OutboundInit"); + if(m==0xFFFFFFFF) + return -ETIMEDOUT; + msg=(u32 *)(c->mem_offset+m); + + status = kmalloc(4,GFP_KERNEL); + if (status==NULL) { + printk(KERN_ERR "%s: Outbound Queue initialization failed - no free memory.\n", + c->name); + return -ENOMEM; + } + memset(status, 0, 4); + + msg[0]= EIGHT_WORD_MSG_SIZE| TRL_OFFSET_6; + msg[1]= I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[2]= core_context; + msg[3]= 0x0106; /* Transaction context */ + msg[4]= 4096; /* Host page frame size */ + /* Frame size is in words. Pick 128, its what everyone elses uses and + other sizes break some adapters. */ + msg[5]= MSG_FRAME_SIZE<<16|0x80; /* Outbound msg frame size and Initcode */ + msg[6]= 0xD0000004; /* Simple SG LE, EOB */ + msg[7]= virt_to_bus(status); + + i2o_post_message(c,m); + + barrier(); + time=jiffies; + while(status[0] < I2O_CMD_REJECTED) + { + if((jiffies-time)>=30*HZ) + { + if(status[0]==0x00) + printk(KERN_ERR "%s: Ignored queue initialize request.\n", + c->name); + else + printk(KERN_ERR "%s: Outbound queue initialize timeout.\n", + c->name); + kfree(status); + return -ETIMEDOUT; + } + schedule(); + barrier(); + } + + if(status[0] != I2O_CMD_COMPLETED) + { + printk(KERN_ERR "%s: IOP outbound initialise failed.\n", c->name); + kfree(status); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * i2o_post_outbound_messages - fill message queue + * @c: controller + * + * Allocate a message frame and load the messages into the IOP. The + * function returns zero on success or a negative errno code on + * failure. + */ + +int i2o_post_outbound_messages(struct i2o_controller *c) +{ + int i; + u32 m; + /* Alloc space for IOP's outbound queue message frames */ + + c->page_frame = kmalloc(MSG_POOL_SIZE, GFP_KERNEL); + if(c->page_frame==NULL) { + printk(KERN_ERR "%s: Outbound Q initialize failed; out of memory.\n", + c->name); + return -ENOMEM; + } + + c->page_frame_map = pci_map_single(c->pdev, c->page_frame, MSG_POOL_SIZE, PCI_DMA_FROMDEVICE); + + if(c->page_frame_map == 0) + { + kfree(c->page_frame); + printk(KERN_ERR "%s: Unable to map outbound queue.\n", c->name); + return -ENOMEM; + } + + m = c->page_frame_map; + + /* Post frames */ + + for(i=0; i< NMBR_MSG_FRAMES; i++) { + I2O_REPLY_WRITE32(c,m); + mb(); + m += MSG_FRAME_SIZE; + } + + return 0; +} + +/* + * Get the IOP's Logical Configuration Table + */ +int i2o_lct_get(struct i2o_controller *c) +{ + u32 msg[8]; + int ret, size = c->status_block->expected_lct_size; + + do { + if (c->lct == NULL) { + c->lct = kmalloc(size, GFP_KERNEL); + if(c->lct == NULL) { + printk(KERN_CRIT "%s: Lct Get failed. Out of memory.\n", + c->name); + return -ENOMEM; + } + } + memset(c->lct, 0, size); + + msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6; + msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID; + /* msg[2] filled in i2o_post_wait */ + msg[3] = 0; + msg[4] = 0xFFFFFFFF; /* All devices */ + msg[5] = 0x00000000; /* Report now */ + msg[6] = 0xD0000000|size; + msg[7] = virt_to_bus(c->lct); + + ret=i2o_post_wait_mem(c, msg, sizeof(msg), 120, c->lct, NULL); + + if(ret == -ETIMEDOUT) + { + c->lct = NULL; + return ret; + } + + if(ret<0) + { + printk(KERN_ERR "%s: LCT Get failed (status=%#x.\n", + c->name, -ret); + return ret; + } + + if (c->lct->table_size << 2 > size) { + size = c->lct->table_size << 2; + kfree(c->lct); + c->lct = NULL; + } + } while (c->lct == NULL); + + if ((ret=i2o_parse_lct(c)) < 0) + return ret; + + return 0; +} + +/* + * Like above, but used for async notification. The main + * difference is that we keep track of the CurrentChangeIndiicator + * so that we only get updates when it actually changes. + * + */ +int i2o_lct_notify(struct i2o_controller *c) +{ + u32 msg[8]; + + msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6; + msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[2] = core_context; + msg[3] = 0xDEADBEEF; + msg[4] = 0xFFFFFFFF; /* All devices */ + msg[5] = c->dlct->change_ind+1; /* Next change */ + msg[6] = 0xD0000000|8192; + msg[7] = virt_to_bus(c->dlct); + + return i2o_post_this(c, msg, sizeof(msg)); +} + +/* + * Bring a controller online into OPERATIONAL state. + */ + +int i2o_online_controller(struct i2o_controller *iop) +{ + u32 v; + + if (i2o_systab_send(iop) < 0) + return -1; + + /* In READY state */ + + dprintk(KERN_INFO "%s: Attempting to enable...\n", iop->name); + if (i2o_enable_controller(iop) < 0) + return -1; + + /* In OPERATIONAL state */ + + dprintk(KERN_INFO "%s: Attempting to get/parse lct...\n", iop->name); + if (i2o_lct_get(iop) < 0) + return -1; + + /* Check battery status */ + + iop->battery = 0; + if(i2o_query_scalar(iop, ADAPTER_TID, 0x0000, 4, &v, 4)>=0) + { + if(v&16) + iop->battery = 1; + } + + return 0; +} + +/* + * Build system table + * + * The system table contains information about all the IOPs in the + * system (duh) and is used by the Executives on the IOPs to establish + * peer2peer connections. We're not supporting peer2peer at the moment, + * but this will be needed down the road for things like lan2lan forwarding. + */ +static int i2o_build_sys_table(void) +{ + struct i2o_controller *iop = NULL; + struct i2o_controller *niop = NULL; + int count = 0; + + sys_tbl_len = sizeof(struct i2o_sys_tbl) + // Header + IOPs + (i2o_num_controllers) * + sizeof(struct i2o_sys_tbl_entry); + + if(sys_tbl) + kfree(sys_tbl); + + sys_tbl = kmalloc(sys_tbl_len, GFP_KERNEL); + if(!sys_tbl) { + printk(KERN_CRIT "SysTab Set failed. Out of memory.\n"); + return -ENOMEM; + } + memset((void*)sys_tbl, 0, sys_tbl_len); + + sys_tbl->num_entries = i2o_num_controllers; + sys_tbl->version = I2OVERSION; /* TODO: Version 2.0 */ + sys_tbl->change_ind = sys_tbl_ind++; + + for(iop = i2o_controller_chain; iop; iop = niop) + { + niop = iop->next; + + /* + * Get updated IOP state so we have the latest information + * + * We should delete the controller at this point if it + * doesn't respond since if it's not on the system table + * it is techninically not part of the I2O subsyßtem... + */ + if(i2o_status_get(iop)) { + printk(KERN_ERR "%s: Deleting b/c could not get status while" + "attempting to build system table\n", iop->name); + i2o_delete_controller(iop); + sys_tbl->num_entries--; + continue; // try the next one + } + + sys_tbl->iops[count].org_id = iop->status_block->org_id; + sys_tbl->iops[count].iop_id = iop->unit + 2; + sys_tbl->iops[count].seg_num = 0; + sys_tbl->iops[count].i2o_version = + iop->status_block->i2o_version; + sys_tbl->iops[count].iop_state = + iop->status_block->iop_state; + sys_tbl->iops[count].msg_type = + iop->status_block->msg_type; + sys_tbl->iops[count].frame_size = + iop->status_block->inbound_frame_size; + sys_tbl->iops[count].last_changed = sys_tbl_ind - 1; // ?? + sys_tbl->iops[count].iop_capabilities = + iop->status_block->iop_capabilities; + sys_tbl->iops[count].inbound_low = + (u32)virt_to_bus(iop->post_port); + sys_tbl->iops[count].inbound_high = 0; // TODO: 64-bit support + + count++; + } + +#ifdef DRIVERDEBUG +{ + u32 *table; + table = (u32*)sys_tbl; + for(count = 0; count < (sys_tbl_len >>2); count++) + printk(KERN_INFO "sys_tbl[%d] = %0#10x\n", count, table[count]); +} +#endif + + return 0; +} + + +/* + * Run time support routines + */ + +/* + * Generic "post and forget" helpers. This is less efficient - we do + * a memcpy for example that isnt strictly needed, but for most uses + * this is simply not worth optimising + */ + +int i2o_post_this(struct i2o_controller *c, u32 *data, int len) +{ + u32 m; + u32 *msg; + unsigned long t=jiffies; + + do + { + mb(); + m = I2O_POST_READ32(c); + } + while(m==0xFFFFFFFF && (jiffies-t)name); + return -ETIMEDOUT; + } + msg = (u32 *)(c->mem_offset + m); + memcpy_toio(msg, data, len); + i2o_post_message(c,m); + return 0; +} + +/** + * i2o_post_wait_mem - I2O query/reply with DMA buffers + * @c: controller + * @msg: message to send + * @len: length of message + * @timeout: time in seconds to wait + * @mem1: attached memory buffer 1 + * @mem2: attached memory buffer 2 + * + * This core API allows an OSM to post a message and then be told whether + * or not the system received a successful reply. + * + * If the message times out then the value '-ETIMEDOUT' is returned. This + * is a special case. In this situation the message may (should) complete + * at an indefinite time in the future. When it completes it will use the + * memory buffers attached to the request. If -ETIMEDOUT is returned then + * the memory buffers must not be freed. Instead the event completion will + * free them for you. In all other cases the buffers are your problem. + * + * Pass NULL for unneeded buffers. + */ + +int i2o_post_wait_mem(struct i2o_controller *c, u32 *msg, int len, int timeout, void *mem1, void *mem2) +{ + DECLARE_WAIT_QUEUE_HEAD(wq_i2o_post); + int complete = 0; + int status; + unsigned long flags = 0; + struct i2o_post_wait_data *wait_data = + kmalloc(sizeof(struct i2o_post_wait_data), GFP_KERNEL); + + if(!wait_data) + return -ENOMEM; + + /* + * Create a new notification object + */ + wait_data->status = &status; + wait_data->complete = &complete; + wait_data->mem[0] = mem1; + wait_data->mem[1] = mem2; + /* + * Queue the event with its unique id + */ + spin_lock_irqsave(&post_wait_lock, flags); + + wait_data->next = post_wait_queue; + post_wait_queue = wait_data; + wait_data->id = (++post_wait_id) & 0x7fff; + wait_data->wq = &wq_i2o_post; + + spin_unlock_irqrestore(&post_wait_lock, flags); + + /* + * Fill in the message id + */ + + msg[2] = 0x80000000|(u32)core_context|((u32)wait_data->id<<16); + + /* + * Post the message to the controller. At some point later it + * will return. If we time out before it returns then + * complete will be zero. From the point post_this returns + * the wait_data may have been deleted. + */ + if ((status = i2o_post_this(c, msg, len))==0) { + sleep_on_timeout(&wq_i2o_post, HZ * timeout); + } + else + return -EIO; + + if(signal_pending(current)) + status = -EINTR; + + spin_lock_irqsave(&post_wait_lock, flags); + barrier(); /* Be sure we see complete as it is locked */ + if(!complete) + { + /* + * Mark the entry dead. We cannot remove it. This is important. + * When it does terminate (which it must do if the controller hasnt + * died..) then it will otherwise scribble on stuff. + * !complete lets us safely check if the entry is still + * allocated and thus we can write into it + */ + wait_data->wq = NULL; + status = -ETIMEDOUT; + } + else + { + /* Debugging check - remove me soon */ + if(status == -ETIMEDOUT) + { + printk("TIMEDOUT BUG!\n"); + status = -EIO; + } + } + /* And the wait_data is not leaked either! */ + spin_unlock_irqrestore(&post_wait_lock, flags); + return status; +} + +/** + * i2o_post_wait - I2O query/reply + * @c: controller + * @msg: message to send + * @len: length of message + * @timeout: time in seconds to wait + * + * This core API allows an OSM to post a message and then be told whether + * or not the system received a successful reply. + */ + +int i2o_post_wait(struct i2o_controller *c, u32 *msg, int len, int timeout) +{ + return i2o_post_wait_mem(c, msg, len, timeout, NULL, NULL); +} + +/* + * i2o_post_wait is completed and we want to wake up the + * sleeping proccess. Called by core's reply handler. + */ + +static void i2o_post_wait_complete(u32 context, int status) +{ + struct i2o_post_wait_data **p1, *q; + unsigned long flags; + + /* + * We need to search through the post_wait + * queue to see if the given message is still + * outstanding. If not, it means that the IOP + * took longer to respond to the message than we + * had allowed and timer has already expired. + * Not much we can do about that except log + * it for debug purposes, increase timeout, and recompile + * + * Lock needed to keep anyone from moving queue pointers + * around while we're looking through them. + */ + + spin_lock_irqsave(&post_wait_lock, flags); + + for(p1 = &post_wait_queue; *p1!=NULL; p1 = &((*p1)->next)) + { + q = (*p1); + if(q->id == ((context >> 16) & 0x7fff)) { + /* + * Delete it + */ + + *p1 = q->next; + + /* + * Live or dead ? + */ + + if(q->wq) + { + /* Live entry - wakeup and set status */ + *q->status = status; + *q->complete = 1; + wake_up(q->wq); + } + else + { + /* + * Free resources. Caller is dead + */ + if(q->mem[0]) + kfree(q->mem[0]); + if(q->mem[1]) + kfree(q->mem[1]); + printk(KERN_WARNING "i2o_post_wait event completed after timeout.\n"); + } + kfree(q); + spin_unlock(&post_wait_lock); + return; + } + } + spin_unlock(&post_wait_lock); + + printk(KERN_DEBUG "i2o_post_wait: Bogus reply!\n"); +} + +/* Issue UTIL_PARAMS_GET or UTIL_PARAMS_SET + * + * This function can be used for all UtilParamsGet/Set operations. + * The OperationList is given in oplist-buffer, + * and results are returned in reslist-buffer. + * Note that the minimum sized reslist is 8 bytes and contains + * ResultCount, ErrorInfoSize, BlockStatus and BlockSize. + */ +int i2o_issue_params(int cmd, struct i2o_controller *iop, int tid, + void *oplist, int oplen, void *reslist, int reslen) +{ + u32 msg[9]; + u32 *res32 = (u32*)reslist; + u32 *restmp = (u32*)reslist; + int len = 0; + int i = 0; + int wait_status; + u32 *opmem, *resmem; + + /* Get DMAable memory */ + opmem = kmalloc(oplen, GFP_KERNEL); + if(opmem == NULL) + return -ENOMEM; + memcpy(opmem, oplist, oplen); + + resmem = kmalloc(reslen, GFP_KERNEL); + if(resmem == NULL) + { + kfree(opmem); + return -ENOMEM; + } + + msg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_5; + msg[1] = cmd << 24 | HOST_TID << 12 | tid; + msg[3] = 0; + msg[4] = 0; + msg[5] = 0x54000000 | oplen; /* OperationList */ + msg[6] = virt_to_bus(opmem); + msg[7] = 0xD0000000 | reslen; /* ResultList */ + msg[8] = virt_to_bus(resmem); + + wait_status = i2o_post_wait_mem(iop, msg, sizeof(msg), 10, opmem, resmem); + + /* + * This only looks like a memory leak - don't "fix" it. + */ + if(wait_status == -ETIMEDOUT) + return wait_status; + + /* Query failed */ + if(wait_status != 0) + { + kfree(resmem); + kfree(opmem); + return wait_status; + } + + memcpy(reslist, resmem, reslen); + /* + * Calculate number of bytes of Result LIST + * We need to loop through each Result BLOCK and grab the length + */ + restmp = res32 + 1; + len = 1; + for(i = 0; i < (res32[0]&0X0000FFFF); i++) + { + if(restmp[0]&0x00FF0000) /* BlockStatus != SUCCESS */ + { + printk(KERN_WARNING "%s - Error:\n ErrorInfoSize = 0x%02x, " + "BlockStatus = 0x%02x, BlockSize = 0x%04x\n", + (cmd == I2O_CMD_UTIL_PARAMS_SET) ? "PARAMS_SET" + : "PARAMS_GET", + res32[1]>>24, (res32[1]>>16)&0xFF, res32[1]&0xFFFF); + + /* + * If this is the only request,than we return an error + */ + if((res32[0]&0x0000FFFF) == 1) + { + return -((res32[1] >> 16) & 0xFF); /* -BlockStatus */ + } + } + len += restmp[0] & 0x0000FFFF; /* Length of res BLOCK */ + restmp += restmp[0] & 0x0000FFFF; /* Skip to next BLOCK */ + } + return (len << 2); /* bytes used by result list */ +} + +/* + * Query one scalar group value or a whole scalar group. + */ +int i2o_query_scalar(struct i2o_controller *iop, int tid, + int group, int field, void *buf, int buflen) +{ + u16 opblk[] = { 1, 0, I2O_PARAMS_FIELD_GET, group, 1, field }; + u8 resblk[8+buflen]; /* 8 bytes for header */ + int size; + + if (field == -1) /* whole group */ + opblk[4] = -1; + + size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_GET, iop, tid, + opblk, sizeof(opblk), resblk, sizeof(resblk)); + + memcpy(buf, resblk+8, buflen); /* cut off header */ + + if(size>buflen) + return buflen; + return size; +} + +/* + * Set a scalar group value or a whole group. + */ +int i2o_set_scalar(struct i2o_controller *iop, int tid, + int group, int field, void *buf, int buflen) +{ + u16 *opblk; + u8 resblk[8+buflen]; /* 8 bytes for header */ + int size; + + opblk = kmalloc(buflen+64, GFP_KERNEL); + if (opblk == NULL) + { + printk(KERN_ERR "i2o: no memory for operation buffer.\n"); + return -ENOMEM; + } + + opblk[0] = 1; /* operation count */ + opblk[1] = 0; /* pad */ + opblk[2] = I2O_PARAMS_FIELD_SET; + opblk[3] = group; + + if(field == -1) { /* whole group */ + opblk[4] = -1; + memcpy(opblk+5, buf, buflen); + } + else /* single field */ + { + opblk[4] = 1; + opblk[5] = field; + memcpy(opblk+6, buf, buflen); + } + + size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_SET, iop, tid, + opblk, 12+buflen, resblk, sizeof(resblk)); + + kfree(opblk); + if(size>buflen) + return buflen; + return size; +} + +/* + * if oper == I2O_PARAMS_TABLE_GET, get from all rows + * if fieldcount == -1 return all fields + * ibuf and ibuflen are unused (use NULL, 0) + * else return specific fields + * ibuf contains fieldindexes + * + * if oper == I2O_PARAMS_LIST_GET, get from specific rows + * if fieldcount == -1 return all fields + * ibuf contains rowcount, keyvalues + * else return specific fields + * fieldcount is # of fieldindexes + * ibuf contains fieldindexes, rowcount, keyvalues + * + * You could also use directly function i2o_issue_params(). + */ +int i2o_query_table(int oper, struct i2o_controller *iop, int tid, int group, + int fieldcount, void *ibuf, int ibuflen, + void *resblk, int reslen) +{ + u16 *opblk; + int size; + + opblk = kmalloc(10 + ibuflen, GFP_KERNEL); + if (opblk == NULL) + { + printk(KERN_ERR "i2o: no memory for query buffer.\n"); + return -ENOMEM; + } + + opblk[0] = 1; /* operation count */ + opblk[1] = 0; /* pad */ + opblk[2] = oper; + opblk[3] = group; + opblk[4] = fieldcount; + memcpy(opblk+5, ibuf, ibuflen); /* other params */ + + size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_GET,iop, tid, + opblk, 10+ibuflen, resblk, reslen); + + kfree(opblk); + if(size>reslen) + return reslen; + return size; +} + +/* + * Clear table group, i.e. delete all rows. + */ +int i2o_clear_table(struct i2o_controller *iop, int tid, int group) +{ + u16 opblk[] = { 1, 0, I2O_PARAMS_TABLE_CLEAR, group }; + u8 resblk[32]; /* min 8 bytes for result header */ + + return i2o_issue_params(I2O_CMD_UTIL_PARAMS_SET, iop, tid, + opblk, sizeof(opblk), resblk, sizeof(resblk)); +} + +/* + * Add a new row into a table group. + * + * if fieldcount==-1 then we add whole rows + * buf contains rowcount, keyvalues + * else just specific fields are given, rest use defaults + * buf contains fieldindexes, rowcount, keyvalues + */ +int i2o_row_add_table(struct i2o_controller *iop, int tid, + int group, int fieldcount, void *buf, int buflen) +{ + u16 *opblk; + u8 resblk[32]; /* min 8 bytes for header */ + int size; + + opblk = kmalloc(buflen+64, GFP_KERNEL); + if (opblk == NULL) + { + printk(KERN_ERR "i2o: no memory for operation buffer.\n"); + return -ENOMEM; + } + + opblk[0] = 1; /* operation count */ + opblk[1] = 0; /* pad */ + opblk[2] = I2O_PARAMS_ROW_ADD; + opblk[3] = group; + opblk[4] = fieldcount; + memcpy(opblk+5, buf, buflen); + + size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_SET, iop, tid, + opblk, 10+buflen, resblk, sizeof(resblk)); + + kfree(opblk); + if(size>buflen) + return buflen; + return size; +} + + +/* + * Used for error reporting/debugging purposes. + * Following fail status are common to all classes. + * The preserved message must be handled in the reply handler. + */ +void i2o_report_fail_status(u8 req_status, u32* msg) +{ + static char *FAIL_STATUS[] = { + "0x80", /* not used */ + "SERVICE_SUSPENDED", /* 0x81 */ + "SERVICE_TERMINATED", /* 0x82 */ + "CONGESTION", + "FAILURE", + "STATE_ERROR", + "TIME_OUT", + "ROUTING_FAILURE", + "INVALID_VERSION", + "INVALID_OFFSET", + "INVALID_MSG_FLAGS", + "FRAME_TOO_SMALL", + "FRAME_TOO_LARGE", + "INVALID_TARGET_ID", + "INVALID_INITIATOR_ID", + "INVALID_INITIATOR_CONTEX", /* 0x8F */ + "UNKNOWN_FAILURE" /* 0xFF */ + }; + + if (req_status == I2O_FSC_TRANSPORT_UNKNOWN_FAILURE) + printk("TRANSPORT_UNKNOWN_FAILURE (%0#2x)\n.", req_status); + else + printk("TRANSPORT_%s.\n", FAIL_STATUS[req_status & 0x0F]); + + /* Dump some details */ + + printk(KERN_ERR " InitiatorId = %d, TargetId = %d\n", + (msg[1] >> 12) & 0xFFF, msg[1] & 0xFFF); + printk(KERN_ERR " LowestVersion = 0x%02X, HighestVersion = 0x%02X\n", + (msg[4] >> 8) & 0xFF, msg[4] & 0xFF); + printk(KERN_ERR " FailingHostUnit = 0x%04X, FailingIOP = 0x%03X\n", + msg[5] >> 16, msg[5] & 0xFFF); + + printk(KERN_ERR " Severity: 0x%02X ", (msg[4] >> 16) & 0xFF); + if (msg[4] & (1<<16)) + printk("(FormatError), " + "this msg can never be delivered/processed.\n"); + if (msg[4] & (1<<17)) + printk("(PathError), " + "this msg can no longer be delivered/processed.\n"); + if (msg[4] & (1<<18)) + printk("(PathState), " + "the system state does not allow delivery.\n"); + if (msg[4] & (1<<19)) + printk("(Congestion), resources temporarily not available;" + "do not retry immediately.\n"); +} + +/* + * Used for error reporting/debugging purposes. + * Following reply status are common to all classes. + */ +void i2o_report_common_status(u8 req_status) +{ + static char *REPLY_STATUS[] = { + "SUCCESS", + "ABORT_DIRTY", + "ABORT_NO_DATA_TRANSFER", + "ABORT_PARTIAL_TRANSFER", + "ERROR_DIRTY", + "ERROR_NO_DATA_TRANSFER", + "ERROR_PARTIAL_TRANSFER", + "PROCESS_ABORT_DIRTY", + "PROCESS_ABORT_NO_DATA_TRANSFER", + "PROCESS_ABORT_PARTIAL_TRANSFER", + "TRANSACTION_ERROR", + "PROGRESS_REPORT" + }; + + if (req_status > I2O_REPLY_STATUS_PROGRESS_REPORT) + printk("RequestStatus = %0#2x", req_status); + else + printk("%s", REPLY_STATUS[req_status]); +} + +/* + * Used for error reporting/debugging purposes. + * Following detailed status are valid for executive class, + * utility class, DDM class and for transaction error replies. + */ +static void i2o_report_common_dsc(u16 detailed_status) +{ + static char *COMMON_DSC[] = { + "SUCCESS", + "0x01", // not used + "BAD_KEY", + "TCL_ERROR", + "REPLY_BUFFER_FULL", + "NO_SUCH_PAGE", + "INSUFFICIENT_RESOURCE_SOFT", + "INSUFFICIENT_RESOURCE_HARD", + "0x08", // not used + "CHAIN_BUFFER_TOO_LARGE", + "UNSUPPORTED_FUNCTION", + "DEVICE_LOCKED", + "DEVICE_RESET", + "INAPPROPRIATE_FUNCTION", + "INVALID_INITIATOR_ADDRESS", + "INVALID_MESSAGE_FLAGS", + "INVALID_OFFSET", + "INVALID_PARAMETER", + "INVALID_REQUEST", + "INVALID_TARGET_ADDRESS", + "MESSAGE_TOO_LARGE", + "MESSAGE_TOO_SMALL", + "MISSING_PARAMETER", + "TIMEOUT", + "UNKNOWN_ERROR", + "UNKNOWN_FUNCTION", + "UNSUPPORTED_VERSION", + "DEVICE_BUSY", + "DEVICE_NOT_AVAILABLE" + }; + + if (detailed_status > I2O_DSC_DEVICE_NOT_AVAILABLE) + printk(" / DetailedStatus = %0#4x.\n", detailed_status); + else + printk(" / %s.\n", COMMON_DSC[detailed_status]); +} + +/* + * Used for error reporting/debugging purposes + */ +static void i2o_report_lan_dsc(u16 detailed_status) +{ + static char *LAN_DSC[] = { // Lan detailed status code strings + "SUCCESS", + "DEVICE_FAILURE", + "DESTINATION_NOT_FOUND", + "TRANSMIT_ERROR", + "TRANSMIT_ABORTED", + "RECEIVE_ERROR", + "RECEIVE_ABORTED", + "DMA_ERROR", + "BAD_PACKET_DETECTED", + "OUT_OF_MEMORY", + "BUCKET_OVERRUN", + "IOP_INTERNAL_ERROR", + "CANCELED", + "INVALID_TRANSACTION_CONTEXT", + "DEST_ADDRESS_DETECTED", + "DEST_ADDRESS_OMITTED", + "PARTIAL_PACKET_RETURNED", + "TEMP_SUSPENDED_STATE", // last Lan detailed status code + "INVALID_REQUEST" // general detailed status code + }; + + if (detailed_status > I2O_DSC_INVALID_REQUEST) + printk(" / %0#4x.\n", detailed_status); + else + printk(" / %s.\n", LAN_DSC[detailed_status]); +} + +/* + * Used for error reporting/debugging purposes + */ +static void i2o_report_util_cmd(u8 cmd) +{ + switch (cmd) { + case I2O_CMD_UTIL_NOP: + printk("UTIL_NOP, "); + break; + case I2O_CMD_UTIL_ABORT: + printk("UTIL_ABORT, "); + break; + case I2O_CMD_UTIL_CLAIM: + printk("UTIL_CLAIM, "); + break; + case I2O_CMD_UTIL_RELEASE: + printk("UTIL_CLAIM_RELEASE, "); + break; + case I2O_CMD_UTIL_CONFIG_DIALOG: + printk("UTIL_CONFIG_DIALOG, "); + break; + case I2O_CMD_UTIL_DEVICE_RESERVE: + printk("UTIL_DEVICE_RESERVE, "); + break; + case I2O_CMD_UTIL_DEVICE_RELEASE: + printk("UTIL_DEVICE_RELEASE, "); + break; + case I2O_CMD_UTIL_EVT_ACK: + printk("UTIL_EVENT_ACKNOWLEDGE, "); + break; + case I2O_CMD_UTIL_EVT_REGISTER: + printk("UTIL_EVENT_REGISTER, "); + break; + case I2O_CMD_UTIL_LOCK: + printk("UTIL_LOCK, "); + break; + case I2O_CMD_UTIL_LOCK_RELEASE: + printk("UTIL_LOCK_RELEASE, "); + break; + case I2O_CMD_UTIL_PARAMS_GET: + printk("UTIL_PARAMS_GET, "); + break; + case I2O_CMD_UTIL_PARAMS_SET: + printk("UTIL_PARAMS_SET, "); + break; + case I2O_CMD_UTIL_REPLY_FAULT_NOTIFY: + printk("UTIL_REPLY_FAULT_NOTIFY, "); + break; + default: + printk("Cmd = %0#2x, ",cmd); + } +} + +/* + * Used for error reporting/debugging purposes + */ +static void i2o_report_exec_cmd(u8 cmd) +{ + switch (cmd) { + case I2O_CMD_ADAPTER_ASSIGN: + printk("EXEC_ADAPTER_ASSIGN, "); + break; + case I2O_CMD_ADAPTER_READ: + printk("EXEC_ADAPTER_READ, "); + break; + case I2O_CMD_ADAPTER_RELEASE: + printk("EXEC_ADAPTER_RELEASE, "); + break; + case I2O_CMD_BIOS_INFO_SET: + printk("EXEC_BIOS_INFO_SET, "); + break; + case I2O_CMD_BOOT_DEVICE_SET: + printk("EXEC_BOOT_DEVICE_SET, "); + break; + case I2O_CMD_CONFIG_VALIDATE: + printk("EXEC_CONFIG_VALIDATE, "); + break; + case I2O_CMD_CONN_SETUP: + printk("EXEC_CONN_SETUP, "); + break; + case I2O_CMD_DDM_DESTROY: + printk("EXEC_DDM_DESTROY, "); + break; + case I2O_CMD_DDM_ENABLE: + printk("EXEC_DDM_ENABLE, "); + break; + case I2O_CMD_DDM_QUIESCE: + printk("EXEC_DDM_QUIESCE, "); + break; + case I2O_CMD_DDM_RESET: + printk("EXEC_DDM_RESET, "); + break; + case I2O_CMD_DDM_SUSPEND: + printk("EXEC_DDM_SUSPEND, "); + break; + case I2O_CMD_DEVICE_ASSIGN: + printk("EXEC_DEVICE_ASSIGN, "); + break; + case I2O_CMD_DEVICE_RELEASE: + printk("EXEC_DEVICE_RELEASE, "); + break; + case I2O_CMD_HRT_GET: + printk("EXEC_HRT_GET, "); + break; + case I2O_CMD_ADAPTER_CLEAR: + printk("EXEC_IOP_CLEAR, "); + break; + case I2O_CMD_ADAPTER_CONNECT: + printk("EXEC_IOP_CONNECT, "); + break; + case I2O_CMD_ADAPTER_RESET: + printk("EXEC_IOP_RESET, "); + break; + case I2O_CMD_LCT_NOTIFY: + printk("EXEC_LCT_NOTIFY, "); + break; + case I2O_CMD_OUTBOUND_INIT: + printk("EXEC_OUTBOUND_INIT, "); + break; + case I2O_CMD_PATH_ENABLE: + printk("EXEC_PATH_ENABLE, "); + break; + case I2O_CMD_PATH_QUIESCE: + printk("EXEC_PATH_QUIESCE, "); + break; + case I2O_CMD_PATH_RESET: + printk("EXEC_PATH_RESET, "); + break; + case I2O_CMD_STATIC_MF_CREATE: + printk("EXEC_STATIC_MF_CREATE, "); + break; + case I2O_CMD_STATIC_MF_RELEASE: + printk("EXEC_STATIC_MF_RELEASE, "); + break; + case I2O_CMD_STATUS_GET: + printk("EXEC_STATUS_GET, "); + break; + case I2O_CMD_SW_DOWNLOAD: + printk("EXEC_SW_DOWNLOAD, "); + break; + case I2O_CMD_SW_UPLOAD: + printk("EXEC_SW_UPLOAD, "); + break; + case I2O_CMD_SW_REMOVE: + printk("EXEC_SW_REMOVE, "); + break; + case I2O_CMD_SYS_ENABLE: + printk("EXEC_SYS_ENABLE, "); + break; + case I2O_CMD_SYS_MODIFY: + printk("EXEC_SYS_MODIFY, "); + break; + case I2O_CMD_SYS_QUIESCE: + printk("EXEC_SYS_QUIESCE, "); + break; + case I2O_CMD_SYS_TAB_SET: + printk("EXEC_SYS_TAB_SET, "); + break; + default: + printk("Cmd = %#02x, ",cmd); + } +} + +/* + * Used for error reporting/debugging purposes + */ +static void i2o_report_lan_cmd(u8 cmd) +{ + switch (cmd) { + case LAN_PACKET_SEND: + printk("LAN_PACKET_SEND, "); + break; + case LAN_SDU_SEND: + printk("LAN_SDU_SEND, "); + break; + case LAN_RECEIVE_POST: + printk("LAN_RECEIVE_POST, "); + break; + case LAN_RESET: + printk("LAN_RESET, "); + break; + case LAN_SUSPEND: + printk("LAN_SUSPEND, "); + break; + default: + printk("Cmd = %0#2x, ",cmd); + } +} + +/* + * Used for error reporting/debugging purposes. + * Report Cmd name, Request status, Detailed Status. + */ +void i2o_report_status(const char *severity, const char *str, u32 *msg) +{ + u8 cmd = (msg[1]>>24)&0xFF; + u8 req_status = (msg[4]>>24)&0xFF; + u16 detailed_status = msg[4]&0xFFFF; + struct i2o_handler *h = i2o_handlers[msg[2] & (MAX_I2O_MODULES-1)]; + + printk("%s%s: ", severity, str); + + if (cmd < 0x1F) // Utility cmd + i2o_report_util_cmd(cmd); + + else if (cmd >= 0xA0 && cmd <= 0xEF) // Executive cmd + i2o_report_exec_cmd(cmd); + + else if (h->class == I2O_CLASS_LAN && cmd >= 0x30 && cmd <= 0x3F) + i2o_report_lan_cmd(cmd); // LAN cmd + else + printk("Cmd = %0#2x, ", cmd); // Other cmds + + if (msg[0] & MSG_FAIL) { + i2o_report_fail_status(req_status, msg); + return; + } + + i2o_report_common_status(req_status); + + if (cmd < 0x1F || (cmd >= 0xA0 && cmd <= 0xEF)) + i2o_report_common_dsc(detailed_status); + else if (h->class == I2O_CLASS_LAN && cmd >= 0x30 && cmd <= 0x3F) + i2o_report_lan_dsc(detailed_status); + else + printk(" / DetailedStatus = %0#4x.\n", detailed_status); +} + +/* Used to dump a message to syslog during debugging */ +void i2o_dump_message(u32 *msg) +{ +#ifdef DRIVERDEBUG + int i; + printk(KERN_INFO "Dumping I2O message size %d @ %p\n", + msg[0]>>16&0xffff, msg); + for(i = 0; i < ((msg[0]>>16)&0xffff); i++) + printk(KERN_INFO " msg[%d] = %0#10x\n", i, msg[i]); +#endif +} + +/* + * I2O reboot/shutdown notification. + * + * - Call each OSM's reboot notifier (if one exists) + * - Quiesce each IOP in the system + * + * Each IOP has to be quiesced before we can ensure that the system + * can be properly shutdown as a transaction that has already been + * acknowledged still needs to be placed in permanent store on the IOP. + * The SysQuiesce causes the IOP to force all HDMs to complete their + * transactions before returning, so only at that point is it safe + * + */ +static int i2o_reboot_event(struct notifier_block *n, unsigned long code, void +*p) +{ + int i = 0; + struct i2o_controller *c = NULL; + + if(code != SYS_RESTART && code != SYS_HALT && code != SYS_POWER_OFF) + return NOTIFY_DONE; + + printk(KERN_INFO "Shutting down I2O system.\n"); + printk(KERN_INFO + " This could take a few minutes if there are many devices attached\n"); + + for(i = 0; i < MAX_I2O_MODULES; i++) + { + if(i2o_handlers[i] && i2o_handlers[i]->reboot_notify) + i2o_handlers[i]->reboot_notify(); + } + + for(c = i2o_controller_chain; c; c = c->next) + { + if(i2o_quiesce_controller(c)) + { + printk(KERN_WARNING "i2o: Could not quiesce %s." " + Verify setup on next system power up.\n", c->name); + } + } + + printk(KERN_INFO "I2O system down.\n"); + return NOTIFY_DONE; +} + + +EXPORT_SYMBOL(i2o_controller_chain); +EXPORT_SYMBOL(i2o_num_controllers); +EXPORT_SYMBOL(i2o_find_controller); +EXPORT_SYMBOL(i2o_unlock_controller); +EXPORT_SYMBOL(i2o_status_get); + +EXPORT_SYMBOL(i2o_install_handler); +EXPORT_SYMBOL(i2o_remove_handler); + +EXPORT_SYMBOL(i2o_claim_device); +EXPORT_SYMBOL(i2o_release_device); +EXPORT_SYMBOL(i2o_device_notify_on); +EXPORT_SYMBOL(i2o_device_notify_off); + +EXPORT_SYMBOL(i2o_post_this); +EXPORT_SYMBOL(i2o_post_wait); +EXPORT_SYMBOL(i2o_post_wait_mem); + +EXPORT_SYMBOL(i2o_query_scalar); +EXPORT_SYMBOL(i2o_set_scalar); +EXPORT_SYMBOL(i2o_query_table); +EXPORT_SYMBOL(i2o_clear_table); +EXPORT_SYMBOL(i2o_row_add_table); +EXPORT_SYMBOL(i2o_issue_params); + +EXPORT_SYMBOL(i2o_event_register); +EXPORT_SYMBOL(i2o_event_ack); + +EXPORT_SYMBOL(i2o_report_status); +EXPORT_SYMBOL(i2o_dump_message); + +EXPORT_SYMBOL(i2o_get_class_name); + +#ifdef MODULE + +MODULE_AUTHOR("Red Hat Software"); +MODULE_DESCRIPTION("I2O Core"); +MODULE_LICENSE("GPL"); + + + +int init_module(void) +{ + printk(KERN_INFO "I2O Core - (C) Copyright 1999 Red Hat Software\n"); + if (i2o_install_handler(&i2o_core_handler) < 0) + { + printk(KERN_ERR + "i2o_core: Unable to install core handler.\nI2O stack not loaded!"); + return 0; + } + + core_context = i2o_core_handler.context; + + /* + * Attach core to I2O PCI transport (and others as they are developed) + */ +#ifdef CONFIG_I2O_PCI_MODULE + if(i2o_pci_core_attach(&i2o_core_functions) < 0) + printk(KERN_INFO "i2o: No PCI I2O controllers found\n"); +#endif + + /* + * Initialize event handling thread + */ + init_MUTEX_LOCKED(&evt_sem); + evt_pid = kernel_thread(i2o_core_evt, &evt_reply, CLONE_SIGHAND); + if(evt_pid < 0) + { + printk(KERN_ERR "I2O: Could not create event handler kernel thread\n"); + i2o_remove_handler(&i2o_core_handler); + return 0; + } + else + printk(KERN_INFO "I2O: Event thread created as pid %d\n", evt_pid); + + if(i2o_num_controllers) + i2o_sys_init(); + + register_reboot_notifier(&i2o_reboot_notifier); + + return 0; +} + +void cleanup_module(void) +{ + int stat; + + unregister_reboot_notifier(&i2o_reboot_notifier); + + if(i2o_num_controllers) + i2o_sys_shutdown(); + + /* + * If this is shutdown time, the thread has already been killed + */ + if(evt_running) { + printk("Terminating i2o threads..."); + stat = kill_proc(evt_pid, SIGTERM, 1); + if(!stat) { + printk("waiting..."); + wait_for_completion(&evt_dead); + } + printk("done.\n"); + } + +#ifdef CONFIG_I2O_PCI_MODULE + i2o_pci_core_detach(); +#endif + + i2o_remove_handler(&i2o_core_handler); + + unregister_reboot_notifier(&i2o_reboot_notifier); +} + +#else + +extern int i2o_block_init(void); +extern int i2o_config_init(void); +extern int i2o_lan_init(void); +extern int i2o_pci_init(void); +extern int i2o_proc_init(void); +extern int i2o_scsi_init(void); + +int __init i2o_init(void) +{ + printk(KERN_INFO "Loading I2O Core - (c) Copyright 1999 Red Hat Software\n"); + + if (i2o_install_handler(&i2o_core_handler) < 0) + { + printk(KERN_ERR + "i2o_core: Unable to install core handler.\nI2O stack not loaded!"); + return 0; + } + + core_context = i2o_core_handler.context; + + /* + * Initialize event handling thread + * We may not find any controllers, but still want this as + * down the road we may have hot pluggable controllers that + * need to be dealt with. + */ + init_MUTEX_LOCKED(&evt_sem); + if((evt_pid = kernel_thread(i2o_core_evt, &evt_reply, CLONE_SIGHAND)) < 0) + { + printk(KERN_ERR "I2O: Could not create event handler kernel thread\n"); + i2o_remove_handler(&i2o_core_handler); + return 0; + } + + +#ifdef CONFIG_I2O_PCI + i2o_pci_init(); +#endif + + if(i2o_num_controllers) + i2o_sys_init(); + + register_reboot_notifier(&i2o_reboot_notifier); + + i2o_config_init(); +#ifdef CONFIG_I2O_BLOCK + i2o_block_init(); +#endif +#ifdef CONFIG_I2O_LAN + i2o_lan_init(); +#endif +#ifdef CONFIG_I2O_PROC + i2o_proc_init(); +#endif + return 0; +} + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_lan.c linux.ac/drivers/message/i2o/i2o_lan.c --- linux.vanilla/drivers/message/i2o/i2o_lan.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_lan.c Wed Oct 10 01:47:56 2001 @@ -0,0 +1,1579 @@ +/* + * drivers/i2o/i2o_lan.c + * + * I2O LAN CLASS OSM May 26th 2000 + * + * (C) Copyright 1999, 2000 University of Helsinki, + * Department of Computer Science + * + * This code is still under development / test. + * + * 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. + * + * Authors: Auvo Häkkinen + * Fixes: Juha Sievänen + * Taneli Vähäkangas + * Deepak Saxena + * + * Tested: in FDDI environment (using SysKonnect's DDM) + * in Gigabit Eth environment (using SysKonnect's DDM) + * in Fast Ethernet environment (using Intel 82558 DDM) + * + * TODO: tests for other LAN classes (Token Ring, Fibre Channel) + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "i2o_lan.h" + +//#define DRIVERDEBUG +#ifdef DRIVERDEBUG +#define dprintk(s, args...) printk(s, ## args) +#else +#define dprintk(s, args...) +#endif + +/* The following module parameters are used as default values + * for per interface values located in the net_device private area. + * Private values are changed via /proc filesystem. + */ +static u32 max_buckets_out = I2O_LAN_MAX_BUCKETS_OUT; +static u32 bucket_thresh = I2O_LAN_BUCKET_THRESH; +static u32 rx_copybreak = I2O_LAN_RX_COPYBREAK; +static u8 tx_batch_mode = I2O_LAN_TX_BATCH_MODE; +static u32 i2o_event_mask = I2O_LAN_EVENT_MASK; + +#define MAX_LAN_CARDS 16 +static struct net_device *i2o_landevs[MAX_LAN_CARDS+1]; +static int unit = -1; /* device unit number */ + +static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); +static void i2o_lan_send_post_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); +static int i2o_lan_receive_post(struct net_device *dev); +static void i2o_lan_receive_post_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); +static void i2o_lan_release_buckets(struct net_device *dev, u32 *msg); + +static int i2o_lan_reset(struct net_device *dev); +static void i2o_lan_handle_event(struct net_device *dev, u32 *msg); + +/* Structures to register handlers for the incoming replies. */ + +static struct i2o_handler i2o_lan_send_handler = { + i2o_lan_send_post_reply, // For send replies + NULL, + NULL, + NULL, + "I2O LAN OSM send", + -1, + I2O_CLASS_LAN +}; +static int lan_send_context; + +static struct i2o_handler i2o_lan_receive_handler = { + i2o_lan_receive_post_reply, // For receive replies + NULL, + NULL, + NULL, + "I2O LAN OSM receive", + -1, + I2O_CLASS_LAN +}; +static int lan_receive_context; + +static struct i2o_handler i2o_lan_handler = { + i2o_lan_reply, // For other replies + NULL, + NULL, + NULL, + "I2O LAN OSM", + -1, + I2O_CLASS_LAN +}; +static int lan_context; + +DECLARE_TASK_QUEUE(i2o_post_buckets_task); +struct tq_struct run_i2o_post_buckets_task = { + routine: (void (*)(void *)) run_task_queue, + data: (void *) 0 +}; + +/* Functions to handle message failures and transaction errors: +==============================================================*/ + +/* + * i2o_lan_handle_failure(): Fail bit has been set since IOP's message + * layer cannot deliver the request to the target, or the target cannot + * process the request. + */ +static void i2o_lan_handle_failure(struct net_device *dev, u32 *msg) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + + u32 *preserved_msg = (u32*)(iop->mem_offset + msg[7]); + u32 *sgl_elem = &preserved_msg[4]; + struct sk_buff *skb = NULL; + u8 le_flag; + + i2o_report_status(KERN_INFO, dev->name, msg); + + /* If PacketSend failed, free sk_buffs reserved by upper layers */ + + if (msg[1] >> 24 == LAN_PACKET_SEND) { + do { + skb = (struct sk_buff *)(sgl_elem[1]); + dev_kfree_skb_irq(skb); + + atomic_dec(&priv->tx_out); + + le_flag = *sgl_elem >> 31; + sgl_elem +=3; + } while (le_flag == 0); /* Last element flag not set */ + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + } + + /* If ReceivePost failed, free sk_buffs we have reserved */ + + if (msg[1] >> 24 == LAN_RECEIVE_POST) { + do { + skb = (struct sk_buff *)(sgl_elem[1]); + dev_kfree_skb_irq(skb); + + atomic_dec(&priv->buckets_out); + + le_flag = *sgl_elem >> 31; + sgl_elem +=3; + } while (le_flag == 0); /* Last element flag not set */ + } + + /* Release the preserved msg frame by resubmitting it as a NOP */ + + preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; + preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0; + preserved_msg[2] = 0; + i2o_post_message(iop, msg[7]); +} +/* + * i2o_lan_handle_transaction_error(): IOP or DDM has rejected the request + * for general cause (format error, bad function code, insufficient resources, + * etc.). We get one transaction_error for each failed transaction. + */ +static void i2o_lan_handle_transaction_error(struct net_device *dev, u32 *msg) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct sk_buff *skb; + + i2o_report_status(KERN_INFO, dev->name, msg); + + /* If PacketSend was rejected, free sk_buff reserved by upper layers */ + + if (msg[1] >> 24 == LAN_PACKET_SEND) { + skb = (struct sk_buff *)(msg[3]); // TransactionContext + dev_kfree_skb_irq(skb); + atomic_dec(&priv->tx_out); + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + } + + /* If ReceivePost was rejected, free sk_buff we have reserved */ + + if (msg[1] >> 24 == LAN_RECEIVE_POST) { + skb = (struct sk_buff *)(msg[3]); + dev_kfree_skb_irq(skb); + atomic_dec(&priv->buckets_out); + } +} + +/* + * i2o_lan_handle_status(): Common parts of handling a not succeeded request + * (status != SUCCESS). + */ +static int i2o_lan_handle_status(struct net_device *dev, u32 *msg) +{ + /* Fail bit set? */ + + if (msg[0] & MSG_FAIL) { + i2o_lan_handle_failure(dev, msg); + return -1; + } + + /* Message rejected for general cause? */ + + if ((msg[4]>>24) == I2O_REPLY_STATUS_TRANSACTION_ERROR) { + i2o_lan_handle_transaction_error(dev, msg); + return -1; + } + + /* Else have to handle it in the callback function */ + + return 0; +} + +/* Callback functions called from the interrupt routine: +=======================================================*/ + +/* + * i2o_lan_send_post_reply(): Callback function to handle PostSend replies. + */ +static void i2o_lan_send_post_reply(struct i2o_handler *h, + struct i2o_controller *iop, struct i2o_message *m) +{ + u32 *msg = (u32 *)m; + u8 unit = (u8)(msg[2]>>16); // InitiatorContext + struct net_device *dev = i2o_landevs[unit]; + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + u8 trl_count = msg[3] & 0x000000FF; + + if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { + if (i2o_lan_handle_status(dev, msg)) + return; + } + +#ifdef DRIVERDEBUG + i2o_report_status(KERN_INFO, dev->name, msg); +#endif + + /* DDM has handled transmit request(s), free sk_buffs. + * We get similar single transaction reply also in error cases + * (except if msg failure or transaction error). + */ + while (trl_count) { + dev_kfree_skb_irq((struct sk_buff *)msg[4 + trl_count]); + dprintk(KERN_INFO "%s: tx skb freed (trl_count=%d).\n", + dev->name, trl_count); + atomic_dec(&priv->tx_out); + trl_count--; + } + + /* If priv->tx_out had reached tx_max_out, the queue was stopped */ + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); +} + +/* + * i2o_lan_receive_post_reply(): Callback function to process incoming packets. + */ +static void i2o_lan_receive_post_reply(struct i2o_handler *h, + struct i2o_controller *iop, struct i2o_message *m) +{ + u32 *msg = (u32 *)m; + u8 unit = (u8)(msg[2]>>16); // InitiatorContext + struct net_device *dev = i2o_landevs[unit]; + + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_bucket_descriptor *bucket = (struct i2o_bucket_descriptor *)&msg[6]; + struct i2o_packet_info *packet; + u8 trl_count = msg[3] & 0x000000FF; + struct sk_buff *skb, *old_skb; + unsigned long flags = 0; + + if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { + if (i2o_lan_handle_status(dev, msg)) + return; + + i2o_lan_release_buckets(dev, msg); + return; + } + +#ifdef DRIVERDEBUG + i2o_report_status(KERN_INFO, dev->name, msg); +#endif + + /* Else we are receiving incoming post. */ + + while (trl_count--) { + skb = (struct sk_buff *)bucket->context; + packet = (struct i2o_packet_info *)bucket->packet_info; + atomic_dec(&priv->buckets_out); + + /* Sanity checks: Any weird characteristics in bucket? */ + + if (packet->flags & 0x0f || ! packet->flags & 0x40) { + if (packet->flags & 0x01) + printk(KERN_WARNING "%s: packet with errors, error code=0x%02x.\n", + dev->name, packet->status & 0xff); + + /* The following shouldn't happen, unless parameters in + * LAN_OPERATION group are changed during the run time. + */ + if (packet->flags & 0x0c) + printk(KERN_DEBUG "%s: multi-bucket packets not supported!\n", + dev->name); + + if (! packet->flags & 0x40) + printk(KERN_DEBUG "%s: multiple packets in a bucket not supported!\n", + dev->name); + + dev_kfree_skb_irq(skb); + + bucket++; + continue; + } + + /* Copy short packet to a new skb */ + + if (packet->len < priv->rx_copybreak) { + old_skb = skb; + skb = (struct sk_buff *)dev_alloc_skb(packet->len+2); + if (skb == NULL) { + printk(KERN_ERR "%s: Can't allocate skb.\n", dev->name); + return; + } + skb_reserve(skb, 2); + memcpy(skb_put(skb, packet->len), old_skb->data, packet->len); + + spin_lock_irqsave(&priv->fbl_lock, flags); + if (priv->i2o_fbl_tail < I2O_LAN_MAX_BUCKETS_OUT) + priv->i2o_fbl[++priv->i2o_fbl_tail] = old_skb; + else + dev_kfree_skb_irq(old_skb); + + spin_unlock_irqrestore(&priv->fbl_lock, flags); + } else + skb_put(skb, packet->len); + + /* Deliver to upper layers */ + + skb->dev = dev; + skb->protocol = priv->type_trans(skb, dev); + netif_rx(skb); + + dev->last_rx = jiffies; + + dprintk(KERN_INFO "%s: Incoming packet (%d bytes) delivered " + "to upper level.\n", dev->name, packet->len); + + bucket++; // to next Packet Descriptor Block + } + +#ifdef DRIVERDEBUG + if (msg[5] == 0) + printk(KERN_INFO "%s: DDM out of buckets (priv->count = %d)!\n", + dev->name, atomic_read(&priv->buckets_out)); +#endif + + /* If DDM has already consumed bucket_thresh buckets, post new ones */ + + if (atomic_read(&priv->buckets_out) <= priv->max_buckets_out - priv->bucket_thresh) { + run_i2o_post_buckets_task.data = (void *)dev; + queue_task(&run_i2o_post_buckets_task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + + return; +} + +/* + * i2o_lan_reply(): Callback function to handle other incoming messages + * except SendPost and ReceivePost. + */ +static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, + struct i2o_message *m) +{ + u32 *msg = (u32 *)m; + u8 unit = (u8)(msg[2]>>16); // InitiatorContext + struct net_device *dev = i2o_landevs[unit]; + + if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { + if (i2o_lan_handle_status(dev, msg)) + return; + + /* In other error cases just report and continue */ + + i2o_report_status(KERN_INFO, dev->name, msg); + } + +#ifdef DRIVERDEBUG + i2o_report_status(KERN_INFO, dev->name, msg); +#endif + switch (msg[1] >> 24) { + case LAN_RESET: + case LAN_SUSPEND: + /* default reply without payload */ + break; + + case I2O_CMD_UTIL_EVT_REGISTER: + case I2O_CMD_UTIL_EVT_ACK: + i2o_lan_handle_event(dev, msg); + break; + + case I2O_CMD_UTIL_PARAMS_SET: + /* default reply, results in ReplyPayload (not examined) */ + switch (msg[3] >> 16) { + case 1: dprintk(KERN_INFO "%s: Reply to set MAC filter mask.\n", + dev->name); + break; + case 2: dprintk(KERN_INFO "%s: Reply to set MAC table.\n", + dev->name); + break; + default: printk(KERN_WARNING "%s: Bad group 0x%04X\n", + dev->name,msg[3] >> 16); + } + break; + + default: + printk(KERN_ERR "%s: No handler for the reply.\n", + dev->name); + i2o_report_status(KERN_INFO, dev->name, msg); + } +} + +/* Functions used by the above callback functions: +=================================================*/ +/* + * i2o_lan_release_buckets(): Free unused buckets (sk_buffs). + */ +static void i2o_lan_release_buckets(struct net_device *dev, u32 *msg) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + u8 trl_elem_size = (u8)(msg[3]>>8 & 0x000000FF); + u8 trl_count = (u8)(msg[3] & 0x000000FF); + u32 *pskb = &msg[6]; + + while (trl_count--) { + dprintk(KERN_DEBUG "%s: Releasing unused rx skb %p (trl_count=%d).\n", + dev->name, (struct sk_buff*)(*pskb),trl_count+1); + dev_kfree_skb_irq((struct sk_buff *)(*pskb)); + pskb += 1 + trl_elem_size; + atomic_dec(&priv->buckets_out); + } +} + +/* + * i2o_lan_event_reply(): Handle events. + */ +static void i2o_lan_handle_event(struct net_device *dev, u32 *msg) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u32 max_evt_data_size =iop->status_block->inbound_frame_size-5; + struct i2o_reply { + u32 header[4]; + u32 evt_indicator; + u32 data[max_evt_data_size]; + } *evt = (struct i2o_reply *)msg; + int evt_data_len = ((msg[0]>>16) - 5) * 4; /* real size*/ + + printk(KERN_INFO "%s: I2O event - ", dev->name); + + if (msg[1]>>24 == I2O_CMD_UTIL_EVT_ACK) { + printk("Event acknowledgement reply.\n"); + return; + } + + /* Else evt->function == I2O_CMD_UTIL_EVT_REGISTER) */ + + switch (evt->evt_indicator) { + case I2O_EVT_IND_STATE_CHANGE: { + struct state_data { + u16 status; + u8 state; + u8 data; + } *evt_data = (struct state_data *)(evt->data[0]); + + printk("State chance 0x%08x.\n", evt->data[0]); + + /* If the DDM is in error state, recovery may be + * possible if status = Transmit or Receive Control + * Unit Inoperable. + */ + if (evt_data->state==0x05 && evt_data->status==0x0003) + i2o_lan_reset(dev); + break; + } + + case I2O_EVT_IND_FIELD_MODIFIED: { + u16 *work16 = (u16 *)evt->data; + printk("Group 0x%04x, field %d changed.\n", work16[0], work16[1]); + break; + } + + case I2O_EVT_IND_VENDOR_EVT: { + int i; + printk("Vendor event:\n"); + for (i = 0; i < evt_data_len / 4; i++) + printk(" 0x%08x\n", evt->data[i]); + break; + } + + case I2O_EVT_IND_DEVICE_RESET: + /* Spec 2.0 p. 6-121: + * The event of _DEVICE_RESET should also be responded + */ + printk("Device reset.\n"); + if (i2o_event_ack(iop, msg) < 0) + printk("%s: Event Acknowledge timeout.\n", dev->name); + break; + +#if 0 + case I2O_EVT_IND_EVT_MASK_MODIFIED: + printk("Event mask modified, 0x%08x.\n", evt->data[0]); + break; + + case I2O_EVT_IND_GENERAL_WARNING: + printk("General warning 0x%04x.\n", evt->data[0]); + break; + + case I2O_EVT_IND_CONFIGURATION_FLAG: + printk("Configuration requested.\n"); + break; + + case I2O_EVT_IND_CAPABILITY_CHANGE: + printk("Capability change 0x%04x.\n", evt->data[0]); + break; + + case I2O_EVT_IND_DEVICE_STATE: + printk("Device state changed 0x%08x.\n", evt->data[0]); + break; +#endif + case I2O_LAN_EVT_LINK_DOWN: + netif_carrier_off(dev); + printk("Link to the physical device is lost.\n"); + break; + + case I2O_LAN_EVT_LINK_UP: + netif_carrier_on(dev); + printk("Link to the physical device is (re)established.\n"); + break; + + case I2O_LAN_EVT_MEDIA_CHANGE: + printk("Media change.\n"); + break; + default: + printk("0x%08x. No handler.\n", evt->evt_indicator); + } +} + +/* + * i2o_lan_receive_post(): Post buckets to receive packets. + */ +static int i2o_lan_receive_post(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + struct sk_buff *skb; + u32 m, *msg; + u32 bucket_len = (dev->mtu + dev->hard_header_len); + u32 total = priv->max_buckets_out - atomic_read(&priv->buckets_out); + u32 bucket_count; + u32 *sgl_elem; + unsigned long flags; + + /* Send (total/bucket_count) separate I2O requests */ + + while (total) { + m = I2O_POST_READ32(iop); + if (m == 0xFFFFFFFF) + return -ETIMEDOUT; + msg = (u32 *)(iop->mem_offset + m); + + bucket_count = (total >= priv->sgl_max) ? priv->sgl_max : total; + total -= bucket_count; + atomic_add(bucket_count, &priv->buckets_out); + + dprintk(KERN_INFO "%s: Sending %d buckets (size %d) to LAN DDM.\n", + dev->name, bucket_count, bucket_len); + + /* Fill in the header */ + + __raw_writel(I2O_MESSAGE_SIZE(4 + 3 * bucket_count) | SGL_OFFSET_4, msg); + __raw_writel(LAN_RECEIVE_POST<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); + __raw_writel(priv->unit << 16 | lan_receive_context, msg+2); + __raw_writel(bucket_count, msg+3); + sgl_elem = &msg[4]; + + /* Fill in the payload - contains bucket_count SGL elements */ + + while (bucket_count--) { + spin_lock_irqsave(&priv->fbl_lock, flags); + if (priv->i2o_fbl_tail >= 0) + skb = priv->i2o_fbl[priv->i2o_fbl_tail--]; + else { + skb = dev_alloc_skb(bucket_len + 2); + if (skb == NULL) { + spin_unlock_irqrestore(&priv->fbl_lock, flags); + return -ENOMEM; + } + skb_reserve(skb, 2); + } + spin_unlock_irqrestore(&priv->fbl_lock, flags); + + __raw_writel(0x51000000 | bucket_len, sgl_elem); + __raw_writel((u32)skb, sgl_elem+1); + __raw_writel(virt_to_bus(skb->data), sgl_elem+2); + sgl_elem += 3; + } + + /* set LE flag and post */ + __raw_writel(__raw_readl(sgl_elem-3) | 0x80000000, (sgl_elem-3)); + i2o_post_message(iop, m); + } + + return 0; +} + +/* Functions called from the network stack, and functions called by them: +========================================================================*/ + +/* + * i2o_lan_reset(): Reset the LAN adapter into the operational state and + * restore it to full operation. + */ +static int i2o_lan_reset(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u32 msg[5]; + + dprintk(KERN_INFO "%s: LAN RESET MESSAGE.\n", dev->name); + msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; + msg[1] = LAN_RESET<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid; + msg[2] = priv->unit << 16 | lan_context; // InitiatorContext + msg[3] = 0; // TransactionContext + msg[4] = 0; // Keep posted buckets + + if (i2o_post_this(iop, msg, sizeof(msg)) < 0) + return -ETIMEDOUT; + + return 0; +} + +/* + * i2o_lan_suspend(): Put LAN adapter into a safe, non-active state. + * IOP replies to any LAN class message with status error_no_data_transfer + * / suspended. + */ +static int i2o_lan_suspend(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u32 msg[5]; + + dprintk(KERN_INFO "%s: LAN SUSPEND MESSAGE.\n", dev->name); + msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; + msg[1] = LAN_SUSPEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid; + msg[2] = priv->unit << 16 | lan_context; // InitiatorContext + msg[3] = 0; // TransactionContext + msg[4] = 1 << 16; // return posted buckets + + if (i2o_post_this(iop, msg, sizeof(msg)) < 0) + return -ETIMEDOUT; + + return 0; +} + +/* + * i2o_set_ddm_parameters: + * These settings are done to ensure proper initial values for DDM. + * They can be changed via proc file system or vai configuration utility. + */ +static void i2o_set_ddm_parameters(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u32 val; + + /* + * When PacketOrphanlimit is set to the maximum packet length, + * the packets will never be split into two separate buckets + */ + val = dev->mtu + dev->hard_header_len; + if (i2o_set_scalar(iop, i2o_dev->lct_data.tid, 0x0004, 2, &val, sizeof(val)) < 0) + printk(KERN_WARNING "%s: Unable to set PacketOrphanLimit.\n", + dev->name); + else + dprintk(KERN_INFO "%s: PacketOrphanLimit set to %d.\n", + dev->name, val); + + /* When RxMaxPacketsBucket = 1, DDM puts only one packet into bucket */ + + val = 1; + if (i2o_set_scalar(iop, i2o_dev->lct_data.tid, 0x0008, 4, &val, sizeof(val)) <0) + printk(KERN_WARNING "%s: Unable to set RxMaxPacketsBucket.\n", + dev->name); + else + dprintk(KERN_INFO "%s: RxMaxPacketsBucket set to %d.\n", + dev->name, val); + return; +} + +/* Functions called from the network stack: +==========================================*/ + +/* + * i2o_lan_open(): Open the device to send/receive packets via + * the network device. + */ +static int i2o_lan_open(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u32 mc_addr_group[64]; + + MOD_INC_USE_COUNT; + + if (i2o_claim_device(i2o_dev, &i2o_lan_handler)) { + printk(KERN_WARNING "%s: Unable to claim the I2O LAN device.\n", dev->name); + MOD_DEC_USE_COUNT; + return -EAGAIN; + } + dprintk(KERN_INFO "%s: I2O LAN device (tid=%d) claimed by LAN OSM.\n", + dev->name, i2o_dev->lct_data.tid); + + if (i2o_event_register(iop, i2o_dev->lct_data.tid, + priv->unit << 16 | lan_context, 0, priv->i2o_event_mask) < 0) + printk(KERN_WARNING "%s: Unable to set the event mask.\n", dev->name); + + i2o_lan_reset(dev); + + /* Get the max number of multicast addresses */ + + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0001, -1, + &mc_addr_group, sizeof(mc_addr_group)) < 0 ) { + printk(KERN_WARNING "%s: Unable to query LAN_MAC_ADDRESS group.\n", dev->name); + MOD_DEC_USE_COUNT; + return -EAGAIN; + } + priv->max_size_mc_table = mc_addr_group[8]; + + /* Malloc space for free bucket list to resuse reveive post buckets */ + + priv->i2o_fbl = kmalloc(priv->max_buckets_out * sizeof(struct sk_buff *), + GFP_KERNEL); + if (priv->i2o_fbl == NULL) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + priv->i2o_fbl_tail = -1; + priv->send_active = 0; + + i2o_set_ddm_parameters(dev); + i2o_lan_receive_post(dev); + + netif_start_queue(dev); + + return 0; +} + +/* + * i2o_lan_close(): End the transfering. + */ +static int i2o_lan_close(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + int ret = 0; + + netif_stop_queue(dev); + i2o_lan_suspend(dev); + + if (i2o_event_register(iop, i2o_dev->lct_data.tid, + priv->unit << 16 | lan_context, 0, 0) < 0) + printk(KERN_WARNING "%s: Unable to clear the event mask.\n", + dev->name); + + while (priv->i2o_fbl_tail >= 0) + dev_kfree_skb(priv->i2o_fbl[priv->i2o_fbl_tail--]); + + kfree(priv->i2o_fbl); + + if (i2o_release_device(i2o_dev, &i2o_lan_handler)) { + printk(KERN_WARNING "%s: Unable to unclaim I2O LAN device " + "(tid=%d).\n", dev->name, i2o_dev->lct_data.tid); + ret = -EBUSY; + } + + MOD_DEC_USE_COUNT; + + return ret; +} + +/* + * i2o_lan_tx_timeout(): Tx timeout handler. + */ +static void i2o_lan_tx_timeout(struct net_device *dev) +{ + if (!netif_queue_stopped(dev)) + netif_start_queue(dev); +} + +/* + * i2o_lan_batch_send(): Send packets in batch. + * Both i2o_lan_sdu_send and i2o_lan_packet_send use this. + */ +static void i2o_lan_batch_send(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_controller *iop = priv->i2o_dev->controller; + + spin_lock_irq(&priv->tx_lock); + if (priv->tx_count != 0) { + dev->trans_start = jiffies; + i2o_post_message(iop, priv->m); + dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count); + priv->tx_count = 0; + } + priv->send_active = 0; + spin_unlock_irq(&priv->tx_lock); + MOD_DEC_USE_COUNT; +} + +#ifdef CONFIG_NET_FC +/* + * i2o_lan_sdu_send(): Send a packet, MAC header added by the DDM. + * Must be supported by Fibre Channel, optional for Ethernet/802.3, + * Token Ring, FDDI + */ +static int i2o_lan_sdu_send(struct sk_buff *skb, struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + int tickssofar = jiffies - dev->trans_start; + u32 m, *msg; + u32 *sgl_elem; + + spin_lock_irq(&priv->tx_lock); + + priv->tx_count++; + atomic_inc(&priv->tx_out); + + /* + * If tx_batch_mode = 0x00 forced to immediate mode + * If tx_batch_mode = 0x01 forced to batch mode + * If tx_batch_mode = 0x10 switch automatically, current mode immediate + * If tx_batch_mode = 0x11 switch automatically, current mode batch + * If gap between two packets is > 0 ticks, switch to immediate + */ + if (priv->tx_batch_mode >> 1) // switch automatically + priv->tx_batch_mode = tickssofar ? 0x02 : 0x03; + + if (priv->tx_count == 1) { + m = I2O_POST_READ32(iop); + if (m == 0xFFFFFFFF) { + spin_unlock_irq(&priv->tx_lock); + return 1; + } + msg = (u32 *)(iop->mem_offset + m); + priv->m = m; + + __raw_writel(NINE_WORD_MSG_SIZE | 1<<12 | SGL_OFFSET_4, msg); + __raw_writel(LAN_PACKET_SEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); + __raw_writel(priv->unit << 16 | lan_send_context, msg+2); // InitiatorContext + __raw_writel(1 << 30 | 1 << 3, msg+3); // TransmitControlWord + + __raw_writel(0xD7000000 | skb->len, msg+4); // MAC hdr included + __raw_writel((u32)skb, msg+5); // TransactionContext + __raw_writel(virt_to_bus(skb->data), msg+6); + __raw_writel((u32)skb->mac.raw, msg+7); + __raw_writel((u32)skb->mac.raw+4, msg+8); + + if ((priv->tx_batch_mode & 0x01) && !priv->send_active) { + priv->send_active = 1; + MOD_INC_USE_COUNT; + if (schedule_task(&priv->i2o_batch_send_task) == 0) + MOD_DEC_USE_COUNT; + } + } else { /* Add new SGL element to the previous message frame */ + + msg = (u32 *)(iop->mem_offset + priv->m); + sgl_elem = &msg[priv->tx_count * 5 + 1]; + + __raw_writel(I2O_MESSAGE_SIZE((__raw_readl(msg)>>16) + 5) | 1<<12 | SGL_OFFSET_4, msg); + __raw_writel(__raw_readl(sgl_elem-5) & 0x7FFFFFFF, sgl_elem-5); /* clear LE flag */ + __raw_writel(0xD5000000 | skb->len, sgl_elem); + __raw_writel((u32)skb, sgl_elem+1); + __raw_writel(virt_to_bus(skb->data), sgl_elem+2); + __raw_writel((u32)(skb->mac.raw), sgl_elem+3); + __raw_writel((u32)(skb->mac.raw)+1, sgl_elem+4); + } + + /* If tx not in batch mode or frame is full, send immediatelly */ + + if (!(priv->tx_batch_mode & 0x01) || priv->tx_count == priv->sgl_max) { + dev->trans_start = jiffies; + i2o_post_message(iop, priv->m); + dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count); + priv->tx_count = 0; + } + + /* If DDMs TxMaxPktOut reached, stop queueing layer to send more */ + + if (atomic_read(&priv->tx_out) >= priv->tx_max_out) + netif_stop_queue(dev); + + spin_unlock_irq(&priv->tx_lock); + return 0; +} +#endif /* CONFIG_NET_FC */ + +/* + * i2o_lan_packet_send(): Send a packet as is, including the MAC header. + * + * Must be supported by Ethernet/802.3, Token Ring, FDDI, optional for + * Fibre Channel + */ +static int i2o_lan_packet_send(struct sk_buff *skb, struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + int tickssofar = jiffies - dev->trans_start; + u32 m, *msg; + u32 *sgl_elem; + + spin_lock_irq(&priv->tx_lock); + + priv->tx_count++; + atomic_inc(&priv->tx_out); + + /* + * If tx_batch_mode = 0x00 forced to immediate mode + * If tx_batch_mode = 0x01 forced to batch mode + * If tx_batch_mode = 0x10 switch automatically, current mode immediate + * If tx_batch_mode = 0x11 switch automatically, current mode batch + * If gap between two packets is > 0 ticks, switch to immediate + */ + if (priv->tx_batch_mode >> 1) // switch automatically + priv->tx_batch_mode = tickssofar ? 0x02 : 0x03; + + if (priv->tx_count == 1) { + m = I2O_POST_READ32(iop); + if (m == 0xFFFFFFFF) { + spin_unlock_irq(&priv->tx_lock); + return 1; + } + msg = (u32 *)(iop->mem_offset + m); + priv->m = m; + + __raw_writel(SEVEN_WORD_MSG_SIZE | 1<<12 | SGL_OFFSET_4, msg); + __raw_writel(LAN_PACKET_SEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); + __raw_writel(priv->unit << 16 | lan_send_context, msg+2); // InitiatorContext + __raw_writel(1 << 30 | 1 << 3, msg+3); // TransmitControlWord + // bit 30: reply as soon as transmission attempt is complete + // bit 3: Suppress CRC generation + __raw_writel(0xD5000000 | skb->len, msg+4); // MAC hdr included + __raw_writel((u32)skb, msg+5); // TransactionContext + __raw_writel(virt_to_bus(skb->data), msg+6); + + if ((priv->tx_batch_mode & 0x01) && !priv->send_active) { + priv->send_active = 1; + MOD_INC_USE_COUNT; + if (schedule_task(&priv->i2o_batch_send_task) == 0) + MOD_DEC_USE_COUNT; + } + } else { /* Add new SGL element to the previous message frame */ + + msg = (u32 *)(iop->mem_offset + priv->m); + sgl_elem = &msg[priv->tx_count * 3 + 1]; + + __raw_writel(I2O_MESSAGE_SIZE((__raw_readl(msg)>>16) + 3) | 1<<12 | SGL_OFFSET_4, msg); + __raw_writel(__raw_readl(sgl_elem-3) & 0x7FFFFFFF, sgl_elem-3); /* clear LE flag */ + __raw_writel(0xD5000000 | skb->len, sgl_elem); + __raw_writel((u32)skb, sgl_elem+1); + __raw_writel(virt_to_bus(skb->data), sgl_elem+2); + } + + /* If tx is in immediate mode or frame is full, send now */ + + if (!(priv->tx_batch_mode & 0x01) || priv->tx_count == priv->sgl_max) { + dev->trans_start = jiffies; + i2o_post_message(iop, priv->m); + dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count); + priv->tx_count = 0; + } + + /* If DDMs TxMaxPktOut reached, stop queueing layer to send more */ + + if (atomic_read(&priv->tx_out) >= priv->tx_max_out) + netif_stop_queue(dev); + + spin_unlock_irq(&priv->tx_lock); + return 0; +} + +/* + * i2o_lan_get_stats(): Fill in the statistics. + */ +static struct net_device_stats *i2o_lan_get_stats(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u64 val64[16]; + u64 supported_group[4] = { 0, 0, 0, 0 }; + + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0100, -1, val64, + sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_HISTORICAL_STATS.\n", dev->name); + else { + dprintk(KERN_DEBUG "%s: LAN_HISTORICAL_STATS queried.\n", dev->name); + priv->stats.tx_packets = val64[0]; + priv->stats.tx_bytes = val64[1]; + priv->stats.rx_packets = val64[2]; + priv->stats.rx_bytes = val64[3]; + priv->stats.tx_errors = val64[4]; + priv->stats.rx_errors = val64[5]; + priv->stats.rx_dropped = val64[6]; + } + + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0180, -1, + &supported_group, sizeof(supported_group)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_SUPPORTED_OPTIONAL_HISTORICAL_STATS.\n", dev->name); + + if (supported_group[2]) { + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0183, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_OPTIONAL_RX_HISTORICAL_STATS.\n", dev->name); + else { + dprintk(KERN_DEBUG "%s: LAN_OPTIONAL_RX_HISTORICAL_STATS queried.\n", dev->name); + priv->stats.multicast = val64[4]; + priv->stats.rx_length_errors = val64[10]; + priv->stats.rx_crc_errors = val64[0]; + } + } + + if (i2o_dev->lct_data.sub_class == I2O_LAN_ETHERNET) { + u64 supported_stats = 0; + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0200, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_802_3_HISTORICAL_STATS.\n", dev->name); + else { + dprintk(KERN_DEBUG "%s: LAN_802_3_HISTORICAL_STATS queried.\n", dev->name); + priv->stats.transmit_collision = val64[1] + val64[2]; + priv->stats.rx_frame_errors = val64[0]; + priv->stats.tx_carrier_errors = val64[6]; + } + + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0280, -1, + &supported_stats, sizeof(supported_stats)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_SUPPORTED_802_3_HISTORICAL_STATS.\n", dev->name); + + if (supported_stats != 0) { + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0281, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_OPTIONAL_802_3_HISTORICAL_STATS.\n", dev->name); + else { + dprintk(KERN_DEBUG "%s: LAN_OPTIONAL_802_3_HISTORICAL_STATS queried.\n", dev->name); + if (supported_stats & 0x1) + priv->stats.rx_over_errors = val64[0]; + if (supported_stats & 0x4) + priv->stats.tx_heartbeat_errors = val64[2]; + } + } + } + +#ifdef CONFIG_TR + if (i2o_dev->lct_data.sub_class == I2O_LAN_TR) { + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0300, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_802_5_HISTORICAL_STATS.\n", dev->name); + else { + struct tr_statistics *stats = + (struct tr_statistics *)&priv->stats; + dprintk(KERN_DEBUG "%s: LAN_802_5_HISTORICAL_STATS queried.\n", dev->name); + + stats->line_errors = val64[0]; + stats->internal_errors = val64[7]; + stats->burst_errors = val64[4]; + stats->A_C_errors = val64[2]; + stats->abort_delimiters = val64[3]; + stats->lost_frames = val64[1]; + /* stats->recv_congest_count = ?; FIXME ??*/ + stats->frame_copied_errors = val64[5]; + stats->frequency_errors = val64[6]; + stats->token_errors = val64[9]; + } + /* Token Ring optional stats not yet defined */ + } +#endif + +#ifdef CONFIG_FDDI + if (i2o_dev->lct_data.sub_class == I2O_LAN_FDDI) { + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0400, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_FDDI_HISTORICAL_STATS.\n", dev->name); + else { + dprintk(KERN_DEBUG "%s: LAN_FDDI_HISTORICAL_STATS queried.\n", dev->name); + priv->stats.smt_cf_state = val64[0]; + memcpy(priv->stats.mac_upstream_nbr, &val64[1], FDDI_K_ALEN); + memcpy(priv->stats.mac_downstream_nbr, &val64[2], FDDI_K_ALEN); + priv->stats.mac_error_cts = val64[3]; + priv->stats.mac_lost_cts = val64[4]; + priv->stats.mac_rmt_state = val64[5]; + memcpy(priv->stats.port_lct_fail_cts, &val64[6], 8); + memcpy(priv->stats.port_lem_reject_cts, &val64[7], 8); + memcpy(priv->stats.port_lem_cts, &val64[8], 8); + memcpy(priv->stats.port_pcm_state, &val64[9], 8); + } + /* FDDI optional stats not yet defined */ + } +#endif + +#ifdef CONFIG_NET_FC + /* Fibre Channel Statistics not yet defined in 1.53 nor 2.0 */ +#endif + + return (struct net_device_stats *)&priv->stats; +} + +/* + * i2o_lan_set_mc_filter(): Post a request to set multicast filter. + */ +int i2o_lan_set_mc_filter(struct net_device *dev, u32 filter_mask) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u32 msg[10]; + + msg[0] = TEN_WORD_MSG_SIZE | SGL_OFFSET_5; + msg[1] = I2O_CMD_UTIL_PARAMS_SET << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid; + msg[2] = priv->unit << 16 | lan_context; + msg[3] = 0x0001 << 16 | 3 ; // TransactionContext: group&field + msg[4] = 0; + msg[5] = 0xCC000000 | 16; // Immediate data SGL + msg[6] = 1; // OperationCount + msg[7] = 0x0001<<16 | I2O_PARAMS_FIELD_SET; // Group, Operation + msg[8] = 3 << 16 | 1; // FieldIndex, FieldCount + msg[9] = filter_mask; // Value + + return i2o_post_this(iop, msg, sizeof(msg)); +} + +/* + * i2o_lan_set_mc_table(): Post a request to set LAN_MULTICAST_MAC_ADDRESS table. + */ +int i2o_lan_set_mc_table(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + struct dev_mc_list *mc; + u32 msg[10 + 2 * dev->mc_count]; + u8 *work8 = (u8 *)(msg + 10); + + msg[0] = I2O_MESSAGE_SIZE(10 + 2 * dev->mc_count) | SGL_OFFSET_5; + msg[1] = I2O_CMD_UTIL_PARAMS_SET << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid; + msg[2] = priv->unit << 16 | lan_context; // InitiatorContext + msg[3] = 0x0002 << 16 | (u16)-1; // TransactionContext + msg[4] = 0; // OperationFlags + msg[5] = 0xCC000000 | (16 + 8 * dev->mc_count); // Immediate data SGL + msg[6] = 2; // OperationCount + msg[7] = 0x0002 << 16 | I2O_PARAMS_TABLE_CLEAR; // Group, Operation + msg[8] = 0x0002 << 16 | I2O_PARAMS_ROW_ADD; // Group, Operation + msg[9] = dev->mc_count << 16 | (u16)-1; // RowCount, FieldCount + + for (mc = dev->mc_list; mc ; mc = mc->next, work8 += 8) { + memset(work8, 0, 8); + memcpy(work8, mc->dmi_addr, mc->dmi_addrlen); // Values + } + + return i2o_post_this(iop, msg, sizeof(msg)); +} + +/* + * i2o_lan_set_multicast_list(): Enable a network device to receive packets + * not send to the protocol address. + */ +static void i2o_lan_set_multicast_list(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + u32 filter_mask; + + if (dev->flags & IFF_PROMISC) { + filter_mask = 0x00000002; + dprintk(KERN_INFO "%s: Enabling promiscuous mode...\n", dev->name); + } else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > priv->max_size_mc_table) { + filter_mask = 0x00000004; + dprintk(KERN_INFO "%s: Enabling all multicast mode...\n", dev->name); + } else if (dev->mc_count) { + filter_mask = 0x00000000; + dprintk(KERN_INFO "%s: Enabling multicast mode...\n", dev->name); + if (i2o_lan_set_mc_table(dev) < 0) + printk(KERN_WARNING "%s: Unable to send MAC table.\n", dev->name); + } else { + filter_mask = 0x00000300; // Broadcast, Multicast disabled + dprintk(KERN_INFO "%s: Enabling unicast mode...\n", dev->name); + } + + /* Finally copy new FilterMask to DDM */ + + if (i2o_lan_set_mc_filter(dev, filter_mask) < 0) + printk(KERN_WARNING "%s: Unable to send MAC FilterMask.\n", dev->name); +} + +/* + * i2o_lan_change_mtu(): Change maximum transfer unit size. + */ +static int i2o_lan_change_mtu(struct net_device *dev, int new_mtu) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + u32 max_pkt_size; + + if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, + 0x0000, 6, &max_pkt_size, 4) < 0) + return -EFAULT; + + if (new_mtu < 68 || new_mtu > 9000 || new_mtu > max_pkt_size) + return -EINVAL; + + dev->mtu = new_mtu; + + i2o_lan_suspend(dev); // to SUSPENDED state, return buckets + + while (priv->i2o_fbl_tail >= 0) // free buffered buckets + dev_kfree_skb(priv->i2o_fbl[priv->i2o_fbl_tail--]); + + i2o_lan_reset(dev); // to OPERATIONAL state + i2o_set_ddm_parameters(dev); // reset some parameters + i2o_lan_receive_post(dev); // post new buckets (new size) + + return 0; +} + +/* Functions to initialize I2O LAN OSM: +======================================*/ + +/* + * i2o_lan_register_device(): Register LAN class device to kernel. + */ +struct net_device *i2o_lan_register_device(struct i2o_device *i2o_dev) +{ + struct net_device *dev = NULL; + struct i2o_lan_local *priv = NULL; + u8 hw_addr[8]; + u32 tx_max_out = 0; + unsigned short (*type_trans)(struct sk_buff *, struct net_device *); + void (*unregister_dev)(struct net_device *dev); + + switch (i2o_dev->lct_data.sub_class) { + case I2O_LAN_ETHERNET: + dev = init_etherdev(NULL, sizeof(struct i2o_lan_local)); + if (dev == NULL) + return NULL; + type_trans = eth_type_trans; + unregister_dev = unregister_netdev; + break; + +#ifdef CONFIG_ANYLAN + case I2O_LAN_100VG: + printk(KERN_ERR "i2o_lan: 100base VG not yet supported.\n"); + return NULL; + break; +#endif + +#ifdef CONFIG_TR + case I2O_LAN_TR: + dev = init_trdev(NULL, sizeof(struct i2o_lan_local)); + if (dev==NULL) + return NULL; + type_trans = tr_type_trans; + unregister_dev = unregister_trdev; + break; +#endif + +#ifdef CONFIG_FDDI + case I2O_LAN_FDDI: + { + int size = sizeof(struct net_device) + sizeof(struct i2o_lan_local); + + dev = (struct net_device *) kmalloc(size, GFP_KERNEL); + if (dev == NULL) + return NULL; + memset((char *)dev, 0, size); + dev->priv = (void *)(dev + 1); + + if (dev_alloc_name(dev, "fddi%d") < 0) { + printk(KERN_WARNING "i2o_lan: Too many FDDI devices.\n"); + kfree(dev); + return NULL; + } + type_trans = fddi_type_trans; + unregister_dev = (void *)unregister_netdevice; + + fddi_setup(dev); + register_netdev(dev); + } + break; +#endif + +#ifdef CONFIG_NET_FC + case I2O_LAN_FIBRE_CHANNEL: + dev = init_fcdev(NULL, sizeof(struct i2o_lan_local)); + if (dev == NULL) + return NULL; + type_trans = NULL; +/* FIXME: Move fc_type_trans() from drivers/net/fc/iph5526.c to net/802/fc.c + * and export it in include/linux/fcdevice.h + * type_trans = fc_type_trans; + */ + unregister_dev = (void *)unregister_fcdev; + break; +#endif + + case I2O_LAN_UNKNOWN: + default: + printk(KERN_ERR "i2o_lan: LAN type 0x%04x not supported.\n", + i2o_dev->lct_data.sub_class); + return NULL; + } + + priv = (struct i2o_lan_local *)dev->priv; + priv->i2o_dev = i2o_dev; + priv->type_trans = type_trans; + priv->sgl_max = (i2o_dev->controller->status_block->inbound_frame_size - 4) / 3; + atomic_set(&priv->buckets_out, 0); + + /* Set default values for user configurable parameters */ + /* Private values are changed via /proc file system */ + + priv->max_buckets_out = max_buckets_out; + priv->bucket_thresh = bucket_thresh; + priv->rx_copybreak = rx_copybreak; + priv->tx_batch_mode = tx_batch_mode & 0x03; + priv->i2o_event_mask = i2o_event_mask; + + priv->tx_lock = SPIN_LOCK_UNLOCKED; + priv->fbl_lock = SPIN_LOCK_UNLOCKED; + + unit++; + i2o_landevs[unit] = dev; + priv->unit = unit; + + if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, + 0x0001, 0, &hw_addr, sizeof(hw_addr)) < 0) { + printk(KERN_ERR "%s: Unable to query hardware address.\n", dev->name); + unit--; + unregister_dev(dev); + kfree(dev); + return NULL; + } + dprintk(KERN_DEBUG "%s: hwaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + dev->name, hw_addr[0], hw_addr[1], hw_addr[2], hw_addr[3], + hw_addr[4], hw_addr[5]); + + dev->addr_len = 6; + memcpy(dev->dev_addr, hw_addr, 6); + + if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, + 0x0007, 2, &tx_max_out, sizeof(tx_max_out)) < 0) { + printk(KERN_ERR "%s: Unable to query max TX queue.\n", dev->name); + unit--; + unregister_dev(dev); + kfree(dev); + return NULL; + } + dprintk(KERN_INFO "%s: Max TX Outstanding = %d.\n", dev->name, tx_max_out); + priv->tx_max_out = tx_max_out; + atomic_set(&priv->tx_out, 0); + priv->tx_count = 0; + + INIT_LIST_HEAD(&priv->i2o_batch_send_task.list); + priv->i2o_batch_send_task.sync = 0; + priv->i2o_batch_send_task.routine = (void *)i2o_lan_batch_send; + priv->i2o_batch_send_task.data = (void *)dev; + + dev->open = i2o_lan_open; + dev->stop = i2o_lan_close; + dev->get_stats = i2o_lan_get_stats; + dev->set_multicast_list = i2o_lan_set_multicast_list; + dev->tx_timeout = i2o_lan_tx_timeout; + dev->watchdog_timeo = I2O_LAN_TX_TIMEOUT; + +#ifdef CONFIG_NET_FC + if (i2o_dev->lct_data.sub_class == I2O_LAN_FIBRE_CHANNEL) + dev->hard_start_xmit = i2o_lan_sdu_send; + else +#endif + dev->hard_start_xmit = i2o_lan_packet_send; + + if (i2o_dev->lct_data.sub_class == I2O_LAN_ETHERNET) + dev->change_mtu = i2o_lan_change_mtu; + + return dev; +} + +#ifdef MODULE +#define i2o_lan_init init_module +#endif + +int __init i2o_lan_init(void) +{ + struct net_device *dev; + int i; + + printk(KERN_INFO "I2O LAN OSM (C) 1999 University of Helsinki.\n"); + + /* Module params are used as global defaults for private values */ + + if (max_buckets_out > I2O_LAN_MAX_BUCKETS_OUT) + max_buckets_out = I2O_LAN_MAX_BUCKETS_OUT; + if (bucket_thresh > max_buckets_out) + bucket_thresh = max_buckets_out; + + /* Install handlers for incoming replies */ + + if (i2o_install_handler(&i2o_lan_send_handler) < 0) { + printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); + return -EINVAL; + } + lan_send_context = i2o_lan_send_handler.context; + + if (i2o_install_handler(&i2o_lan_receive_handler) < 0) { + printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); + return -EINVAL; + } + lan_receive_context = i2o_lan_receive_handler.context; + + if (i2o_install_handler(&i2o_lan_handler) < 0) { + printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); + return -EINVAL; + } + lan_context = i2o_lan_handler.context; + + for(i=0; i <= MAX_LAN_CARDS; i++) + i2o_landevs[i] = NULL; + + for (i=0; i < MAX_I2O_CONTROLLERS; i++) { + struct i2o_controller *iop = i2o_find_controller(i); + struct i2o_device *i2o_dev; + + if (iop==NULL) + continue; + + for (i2o_dev=iop->devices;i2o_dev != NULL;i2o_dev=i2o_dev->next) { + + if (i2o_dev->lct_data.class_id != I2O_CLASS_LAN) + continue; + + /* Make sure device not already claimed by an ISM */ + if (i2o_dev->lct_data.user_tid != 0xFFF) + continue; + + if (unit == MAX_LAN_CARDS) { + i2o_unlock_controller(iop); + printk(KERN_WARNING "i2o_lan: Too many I2O LAN devices.\n"); + return -EINVAL; + } + + dev = i2o_lan_register_device(i2o_dev); + if (dev == NULL) { + printk(KERN_ERR "i2o_lan: Unable to register I2O LAN device 0x%04x.\n", + i2o_dev->lct_data.sub_class); + continue; + } + + printk(KERN_INFO "%s: I2O LAN device registered, " + "subclass = 0x%04x, unit = %d, tid = %d.\n", + dev->name, i2o_dev->lct_data.sub_class, + ((struct i2o_lan_local *)dev->priv)->unit, + i2o_dev->lct_data.tid); + } + + i2o_unlock_controller(iop); + } + + dprintk(KERN_INFO "%d I2O LAN devices found and registered.\n", unit+1); + + return 0; +} + +#ifdef MODULE + +void cleanup_module(void) +{ + int i; + + for (i = 0; i <= unit; i++) { + struct net_device *dev = i2o_landevs[i]; + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + + switch (i2o_dev->lct_data.sub_class) { + case I2O_LAN_ETHERNET: + unregister_netdev(dev); + break; +#ifdef CONFIG_FDDI + case I2O_LAN_FDDI: + unregister_netdevice(dev); + break; +#endif +#ifdef CONFIG_TR + case I2O_LAN_TR: + unregister_trdev(dev); + break; +#endif +#ifdef CONFIG_NET_FC + case I2O_LAN_FIBRE_CHANNEL: + unregister_fcdev(dev); + break; +#endif + default: + printk(KERN_WARNING "%s: Spurious I2O LAN subclass 0x%08x.\n", + dev->name, i2o_dev->lct_data.sub_class); + } + + dprintk(KERN_INFO "%s: I2O LAN device unregistered.\n", + dev->name); + kfree(dev); + } + + i2o_remove_handler(&i2o_lan_handler); + i2o_remove_handler(&i2o_lan_send_handler); + i2o_remove_handler(&i2o_lan_receive_handler); +} + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("University of Helsinki, Department of Computer Science"); +MODULE_DESCRIPTION("I2O Lan OSM"); +MODULE_LICENSE("GPL"); + + +MODULE_PARM(max_buckets_out, "1-" __MODULE_STRING(I2O_LAN_MAX_BUCKETS_OUT) "i"); +MODULE_PARM_DESC(max_buckets_out, "Total number of buckets to post (1-)"); +MODULE_PARM(bucket_thresh, "1-" __MODULE_STRING(I2O_LAN_MAX_BUCKETS_OUT) "i"); +MODULE_PARM_DESC(bucket_thresh, "Bucket post threshold (1-)"); +MODULE_PARM(rx_copybreak, "1-" "i"); +MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy only small frames (1-)"); +MODULE_PARM(tx_batch_mode, "0-2" "i"); +MODULE_PARM_DESC(tx_batch_mode, "0=Send immediatelly, 1=Send in batches, 2=Switch automatically"); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_lan.h linux.ac/drivers/message/i2o/i2o_lan.h --- linux.vanilla/drivers/message/i2o/i2o_lan.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_lan.h Fri Oct 12 12:52:06 2001 @@ -0,0 +1,159 @@ +/* + * i2o_lan.h I2O LAN Class definitions + * + * I2O LAN CLASS OSM May 26th 2000 + * + * (C) Copyright 1999, 2000 University of Helsinki, + * Department of Computer Science + * + * This code is still under development / test. + * + * Author: Auvo Häkkinen + * Juha Sievänen + * Taneli Vähäkangas + */ + +#ifndef _I2O_LAN_H +#define _I2O_LAN_H + +/* Default values for tunable parameters first */ + +#define I2O_LAN_MAX_BUCKETS_OUT 96 +#define I2O_LAN_BUCKET_THRESH 18 /* 9 buckets in one message */ +#define I2O_LAN_RX_COPYBREAK 200 +#define I2O_LAN_TX_TIMEOUT (1*HZ) +#define I2O_LAN_TX_BATCH_MODE 2 /* 2=automatic, 1=on, 0=off */ +#define I2O_LAN_EVENT_MASK 0 /* 0=None, 0xFFC00002=All */ + +/* LAN types */ +#define I2O_LAN_ETHERNET 0x0030 +#define I2O_LAN_100VG 0x0040 +#define I2O_LAN_TR 0x0050 +#define I2O_LAN_FDDI 0x0060 +#define I2O_LAN_FIBRE_CHANNEL 0x0070 +#define I2O_LAN_UNKNOWN 0x00000000 + +/* Connector types */ + +/* Ethernet */ +#define I2O_LAN_AUI (I2O_LAN_ETHERNET << 4) + 0x00000001 +#define I2O_LAN_10BASE5 (I2O_LAN_ETHERNET << 4) + 0x00000002 +#define I2O_LAN_FIORL (I2O_LAN_ETHERNET << 4) + 0x00000003 +#define I2O_LAN_10BASE2 (I2O_LAN_ETHERNET << 4) + 0x00000004 +#define I2O_LAN_10BROAD36 (I2O_LAN_ETHERNET << 4) + 0x00000005 +#define I2O_LAN_10BASE_T (I2O_LAN_ETHERNET << 4) + 0x00000006 +#define I2O_LAN_10BASE_FP (I2O_LAN_ETHERNET << 4) + 0x00000007 +#define I2O_LAN_10BASE_FB (I2O_LAN_ETHERNET << 4) + 0x00000008 +#define I2O_LAN_10BASE_FL (I2O_LAN_ETHERNET << 4) + 0x00000009 +#define I2O_LAN_100BASE_TX (I2O_LAN_ETHERNET << 4) + 0x0000000A +#define I2O_LAN_100BASE_FX (I2O_LAN_ETHERNET << 4) + 0x0000000B +#define I2O_LAN_100BASE_T4 (I2O_LAN_ETHERNET << 4) + 0x0000000C +#define I2O_LAN_1000BASE_SX (I2O_LAN_ETHERNET << 4) + 0x0000000D +#define I2O_LAN_1000BASE_LX (I2O_LAN_ETHERNET << 4) + 0x0000000E +#define I2O_LAN_1000BASE_CX (I2O_LAN_ETHERNET << 4) + 0x0000000F +#define I2O_LAN_1000BASE_T (I2O_LAN_ETHERNET << 4) + 0x00000010 + +/* AnyLAN */ +#define I2O_LAN_100VG_ETHERNET (I2O_LAN_100VG << 4) + 0x00000001 +#define I2O_LAN_100VG_TR (I2O_LAN_100VG << 4) + 0x00000002 + +/* Token Ring */ +#define I2O_LAN_4MBIT (I2O_LAN_TR << 4) + 0x00000001 +#define I2O_LAN_16MBIT (I2O_LAN_TR << 4) + 0x00000002 + +/* FDDI */ +#define I2O_LAN_125MBAUD (I2O_LAN_FDDI << 4) + 0x00000001 + +/* Fibre Channel */ +#define I2O_LAN_POINT_POINT (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000001 +#define I2O_LAN_ARB_LOOP (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000002 +#define I2O_LAN_PUBLIC_LOOP (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000003 +#define I2O_LAN_FABRIC (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000004 + +#define I2O_LAN_EMULATION 0x00000F00 +#define I2O_LAN_OTHER 0x00000F01 +#define I2O_LAN_DEFAULT 0xFFFFFFFF + +/* LAN class functions */ + +#define LAN_PACKET_SEND 0x3B +#define LAN_SDU_SEND 0x3D +#define LAN_RECEIVE_POST 0x3E +#define LAN_RESET 0x35 +#define LAN_SUSPEND 0x37 + +/* LAN DetailedStatusCode defines */ +#define I2O_LAN_DSC_SUCCESS 0x00 +#define I2O_LAN_DSC_DEVICE_FAILURE 0x01 +#define I2O_LAN_DSC_DESTINATION_NOT_FOUND 0x02 +#define I2O_LAN_DSC_TRANSMIT_ERROR 0x03 +#define I2O_LAN_DSC_TRANSMIT_ABORTED 0x04 +#define I2O_LAN_DSC_RECEIVE_ERROR 0x05 +#define I2O_LAN_DSC_RECEIVE_ABORTED 0x06 +#define I2O_LAN_DSC_DMA_ERROR 0x07 +#define I2O_LAN_DSC_BAD_PACKET_DETECTED 0x08 +#define I2O_LAN_DSC_OUT_OF_MEMORY 0x09 +#define I2O_LAN_DSC_BUCKET_OVERRUN 0x0A +#define I2O_LAN_DSC_IOP_INTERNAL_ERROR 0x0B +#define I2O_LAN_DSC_CANCELED 0x0C +#define I2O_LAN_DSC_INVALID_TRANSACTION_CONTEXT 0x0D +#define I2O_LAN_DSC_DEST_ADDRESS_DETECTED 0x0E +#define I2O_LAN_DSC_DEST_ADDRESS_OMITTED 0x0F +#define I2O_LAN_DSC_PARTIAL_PACKET_RETURNED 0x10 +#define I2O_LAN_DSC_SUSPENDED 0x11 + +struct i2o_packet_info { + u32 offset : 24; + u32 flags : 8; + u32 len : 24; + u32 status : 8; +}; + +struct i2o_bucket_descriptor { + u32 context; /* FIXME: 64bit support */ + struct i2o_packet_info packet_info[1]; +}; + +/* Event Indicator Mask Flags for LAN OSM */ + +#define I2O_LAN_EVT_LINK_DOWN 0x01 +#define I2O_LAN_EVT_LINK_UP 0x02 +#define I2O_LAN_EVT_MEDIA_CHANGE 0x04 + +#include +#include + +struct i2o_lan_local { + u8 unit; + struct i2o_device *i2o_dev; + + struct fddi_statistics stats; /* see also struct net_device_stats */ + unsigned short (*type_trans)(struct sk_buff *, struct net_device *); + atomic_t buckets_out; /* nbr of unused buckets on DDM */ + atomic_t tx_out; /* outstanding TXes */ + u8 tx_count; /* packets in one TX message frame */ + u16 tx_max_out; /* DDM's Tx queue len */ + u8 sgl_max; /* max SGLs in one message frame */ + u32 m; /* IOP address of the batch msg frame */ + + struct tq_struct i2o_batch_send_task; + int send_active; + struct sk_buff **i2o_fbl; /* Free bucket list (to reuse skbs) */ + int i2o_fbl_tail; + spinlock_t fbl_lock; + + spinlock_t tx_lock; + + u32 max_size_mc_table; /* max number of multicast addresses */ + + /* LAN OSM configurable parameters are here: */ + + u16 max_buckets_out; /* max nbr of buckets to send to DDM */ + u16 bucket_thresh; /* send more when this many used */ + u16 rx_copybreak; + + u8 tx_batch_mode; /* Set when using batch mode sends */ + u32 i2o_event_mask; /* To turn on interesting event flags */ +}; + +#endif /* _I2O_LAN_H */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_pci.c linux.ac/drivers/message/i2o/i2o_pci.c --- linux.vanilla/drivers/message/i2o/i2o_pci.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_pci.c Wed Oct 10 01:47:56 2001 @@ -0,0 +1,391 @@ +/* + * Find I2O capable controllers on the PCI bus, and register/install + * them with the I2O layer + * + * (C) Copyright 1999 Red Hat Software + * + * Written by Alan Cox, Building Number Three Ltd + * Modified by Deepak Saxena + * Modified by Boji T Kannanthanam + * + * 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. + * + * TODO: + * Support polled I2O PCI controllers. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTRR +#include +#endif // CONFIG_MTRR + +#ifdef MODULE +/* + * Core function table + * See for an explanation + */ +static struct i2o_core_func_table *core; + +/* Core attach function */ +extern int i2o_pci_core_attach(struct i2o_core_func_table *); +extern void i2o_pci_core_detach(void); +#endif /* MODULE */ + +/* + * Free bus specific resources + */ +static void i2o_pci_dispose(struct i2o_controller *c) +{ + I2O_IRQ_WRITE32(c,0xFFFFFFFF); + if(c->bus.pci.irq > 0) + free_irq(c->bus.pci.irq, c); + iounmap(((u8 *)c->post_port)-0x40); + +#ifdef CONFIG_MTRR + if(c->bus.pci.mtrr_reg0 > 0) + mtrr_del(c->bus.pci.mtrr_reg0, 0, 0); + if(c->bus.pci.mtrr_reg1 > 0) + mtrr_del(c->bus.pci.mtrr_reg1, 0, 0); +#endif +} + +/* + * No real bus specific handling yet (note that later we will + * need to 'steal' PCI devices on i960 mainboards) + */ + +static int i2o_pci_bind(struct i2o_controller *c, struct i2o_device *dev) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static int i2o_pci_unbind(struct i2o_controller *c, struct i2o_device *dev) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Bus specific enable/disable functions + */ +static void i2o_pci_enable(struct i2o_controller *c) +{ + I2O_IRQ_WRITE32(c, 0); + c->enabled = 1; +} + +static void i2o_pci_disable(struct i2o_controller *c) +{ + I2O_IRQ_WRITE32(c, 0xFFFFFFFF); + c->enabled = 0; +} + +/* + * Bus specific interrupt handler + */ + +static void i2o_pci_interrupt(int irq, void *dev_id, struct pt_regs *r) +{ + struct i2o_controller *c = dev_id; +#ifdef MODULE + core->run_queue(c); +#else + i2o_run_queue(c); +#endif /* MODULE */ +} + +/* + * Install a PCI (or in theory AGP) i2o controller + * + * TODO: Add support for polled controllers + */ +int __init i2o_pci_install(struct pci_dev *dev) +{ + struct i2o_controller *c=kmalloc(sizeof(struct i2o_controller), + GFP_KERNEL); + u8 *mem; + u32 memptr = 0; + u32 size; + + int i; + + if(c==NULL) + { + printk(KERN_ERR "i2o: Insufficient memory to add controller.\n"); + return -ENOMEM; + } + memset(c, 0, sizeof(*c)); + + for(i=0; i<6; i++) + { + /* Skip I/O spaces */ + if(!(pci_resource_flags(dev, i) & IORESOURCE_IO)) + { + memptr = pci_resource_start(dev, i); + break; + } + } + + if(i==6) + { + printk(KERN_ERR "i2o: I2O controller has no memory regions defined.\n"); + kfree(c); + return -EINVAL; + } + + size = dev->resource[i].end-dev->resource[i].start+1; + /* Map the I2O controller */ + + printk(KERN_INFO "i2o: PCI I2O controller at 0x%08X size=%d\n", memptr, size); + mem = ioremap(memptr, size); + if(mem==NULL) + { + printk(KERN_ERR "i2o: Unable to map controller.\n"); + kfree(c); + return -EINVAL; + } + + c->bus.pci.irq = -1; + c->bus.pci.queue_buggy = 0; + c->bus.pci.dpt = 0; + c->bus.pci.short_req = 0; + c->pdev = dev; + + c->irq_mask = (volatile u32 *)(mem+0x34); + c->post_port = (volatile u32 *)(mem+0x40); + c->reply_port = (volatile u32 *)(mem+0x44); + + c->mem_phys = memptr; + c->mem_offset = (u32)mem; + c->destructor = i2o_pci_dispose; + + c->bind = i2o_pci_bind; + c->unbind = i2o_pci_unbind; + c->bus_enable = i2o_pci_enable; + c->bus_disable = i2o_pci_disable; + + c->type = I2O_TYPE_PCI; + + /* + * Cards that fall apart if you hit them with large I/O + * loads... + */ + + if(dev->vendor == PCI_VENDOR_ID_NCR && dev->device == 0x0630) + { + c->bus.pci.short_req=1; + printk(KERN_INFO "I2O: Symbios FC920 workarounds activated.\n"); + } + if(dev->subsystem_vendor == PCI_VENDOR_ID_PROMISE) + { + c->bus.pci.queue_buggy=1; + printk(KERN_INFO "I2O: Promise workarounds activated.\n"); + } + + /* + * Cards that go bananas if you quiesce them before you reset + * them + */ + + if(dev->vendor == PCI_VENDOR_ID_DPT) + c->bus.pci.dpt=1; + + /* + * Enable Write Combining MTRR for IOP's memory region + */ +#ifdef CONFIG_MTRR + c->bus.pci.mtrr_reg0 = + mtrr_add(c->mem_phys, size, MTRR_TYPE_WRCOMB, 1); +/* +* If it is an INTEL i960 I/O processor then set the first 64K to Uncacheable +* since the region contains the Messaging unit which shouldn't be cached. +*/ + c->bus.pci.mtrr_reg1 = -1; + if(dev->vendor == PCI_VENDOR_ID_INTEL || dev->vendor == PCI_VENDOR_ID_DPT) + { + printk(KERN_INFO "I2O: MTRR workaround for Intel i960 processor\n"); + c->bus.pci.mtrr_reg1 = mtrr_add(c->mem_phys, 65536, MTRR_TYPE_UNCACHABLE, 1); + if(c->bus.pci.mtrr_reg1< 0) + { + printk(KERN_INFO "i2o_pci: Error in setting MTRR_TYPE_UNCACHABLE\n"); + mtrr_del(c->bus.pci.mtrr_reg0, c->mem_phys, size); + c->bus.pci.mtrr_reg0 = -1; + } + } + +#endif + + I2O_IRQ_WRITE32(c,0xFFFFFFFF); + +#ifdef MODULE + i = core->install(c); +#else + i = i2o_install_controller(c); +#endif /* MODULE */ + + if(i<0) + { + printk(KERN_ERR "i2o: Unable to install controller.\n"); + kfree(c); + iounmap(mem); + return i; + } + + c->bus.pci.irq = dev->irq; + if(c->bus.pci.irq) + { + i=request_irq(dev->irq, i2o_pci_interrupt, SA_SHIRQ, + c->name, c); + if(i<0) + { + printk(KERN_ERR "%s: unable to allocate interrupt %d.\n", + c->name, dev->irq); + c->bus.pci.irq = -1; +#ifdef MODULE + core->delete(c); +#else + i2o_delete_controller(c); +#endif /* MODULE */ + iounmap(mem); + return -EBUSY; + } + } + + printk(KERN_INFO "%s: Installed at IRQ%d\n", c->name, dev->irq); + I2O_IRQ_WRITE32(c,0x0); + c->enabled = 1; + return 0; +} + +int __init i2o_pci_scan(void) +{ + struct pci_dev *dev; + int count=0; + + printk(KERN_INFO "i2o: Checking for PCI I2O controllers...\n"); + + pci_for_each_dev(dev) + { + if((dev->class>>8)!=PCI_CLASS_INTELLIGENT_I2O) + continue; + if(dev->vendor == PCI_VENDOR_ID_DPT) + { + if(dev->device == 0xA501 || dev->device == 0xA511) + { + printk(KERN_INFO "i2o: Skipping Adaptec/DPT I2O raid with preferred native driver.\n"); + continue; + } + } + if((dev->class&0xFF)>1) + { + printk(KERN_INFO "i2o: I2O Controller found but does not support I2O 1.5 (skipping).\n"); + continue; + } + if (pci_enable_device(dev)) + continue; + printk(KERN_INFO "i2o: I2O controller on bus %d at %d.\n", + dev->bus->number, dev->devfn); + pci_set_master(dev); + if(i2o_pci_install(dev)==0) + count++; + } + if(count) + printk(KERN_INFO "i2o: %d I2O controller%s found and installed.\n", count, + count==1?"":"s"); + return count?count:-ENODEV; +} + +#ifdef I2O_HOTPLUG_SUPPORT +/* + * Activate a newly found PCI I2O controller + * Not used now, but will be needed in future for + * hot plug PCI support + */ +static void i2o_pci_activate(i2o_controller * c) +{ + int i=0; + struct i2o_controller *c; + + if(c->type == I2O_TYPE_PCI) + { + I2O_IRQ_WRITE32(c,0); +#ifdef MODULE + if(core->activate(c)) +#else + if(i2o_activate_controller(c)) +#endif /* MODULE */ + { + printk("%s: Failed to initialize.\n", c->name); +#ifdef MODULE + core->unlock(c); + core->delete(c); +#else + i2o_unlock_controller(c); + i2o_delete_controller(c); +#endif + continue; + } + } +} +#endif // I2O_HOTPLUG_SUPPORT + +#ifdef MODULE + +int i2o_pci_core_attach(struct i2o_core_func_table *table) +{ + MOD_INC_USE_COUNT; + + core = table; + + return i2o_pci_scan(); +} + +void i2o_pci_core_detach(void) +{ + core = NULL; + + MOD_DEC_USE_COUNT; +} + +int init_module(void) +{ + printk(KERN_INFO "Linux I2O PCI support (c) 1999 Red Hat Software.\n"); + + core = NULL; + + return 0; + +} + +void cleanup_module(void) +{ +} + +EXPORT_SYMBOL(i2o_pci_core_attach); +EXPORT_SYMBOL(i2o_pci_core_detach); + +MODULE_AUTHOR("Red Hat Software"); +MODULE_DESCRIPTION("I2O PCI Interface"); +MODULE_LICENSE("GPL"); + + +#else +void __init i2o_pci_init(void) +{ + printk(KERN_INFO "Linux I2O PCI support (c) 1999 Red Hat Software.\n"); + i2o_pci_scan(); +} +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_proc.c linux.ac/drivers/message/i2o/i2o_proc.c --- linux.vanilla/drivers/message/i2o/i2o_proc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_proc.c Thu Oct 11 09:46:30 2001 @@ -0,0 +1,3377 @@ +/* + * procfs handler for Linux I2O subsystem + * + * (c) Copyright 1999 Deepak Saxena + * + * Originally written by Deepak Saxena(deepak@plexity.net) + * + * 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 is an initial test release. The code is based on the design + * of the ide procfs system (drivers/block/ide-proc.c). Some code + * taken from i2o-core module by Alan Cox. + * + * DISCLAIMER: This code is still under development/test and may cause + * your system to behave unpredictably. Use at your own discretion. + * + * LAN entries by Juha Sievänen (Juha.Sievanen@cs.Helsinki.FI), + * Auvo Häkkinen (Auvo.Hakkinen@cs.Helsinki.FI) + * University of Helsinki, Department of Computer Science + */ + +/* + * set tabstop=3 + */ + +/* + * TODO List + * + * - Add support for any version 2.0 spec changes once 2.0 IRTOS is + * is available to test with + * - Clean up code to use official structure definitions + */ + +// FIXME! +#define FMT_U64_HEX "0x%08x%08x" +#define U64_VAL(pu64) *((u32*)(pu64)+1), *((u32*)(pu64)) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "i2o_lan.h" + +/* + * Structure used to define /proc entries + */ +typedef struct _i2o_proc_entry_t +{ + char *name; /* entry name */ + mode_t mode; /* mode */ + read_proc_t *read_proc; /* read func */ + write_proc_t *write_proc; /* write func */ +} i2o_proc_entry; + +// #define DRIVERDEBUG + +static int i2o_proc_read_lct(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_hrt(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_status(char *, char **, off_t, int, int *, void *); + +static int i2o_proc_read_hw(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_ddm_table(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_driver_store(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_drivers_stored(char *, char **, off_t, int, int *, void *); + +static int i2o_proc_read_groups(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_phys_device(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_claimed(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_users(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_priv_msgs(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_authorized_users(char *, char **, off_t, int, int *, void *); + +static int i2o_proc_read_dev_name(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_dev_identity(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_ddm_identity(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_uinfo(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_sgl_limits(char *, char **, off_t, int, int *, void *); + +static int i2o_proc_read_sensors(char *, char **, off_t, int, int *, void *); + +static int print_serial_number(char *, int, u8 *, int); + +static int i2o_proc_create_entries(void *, i2o_proc_entry *, + struct proc_dir_entry *); +static void i2o_proc_remove_entries(i2o_proc_entry *, struct proc_dir_entry *); +static int i2o_proc_add_controller(struct i2o_controller *, + struct proc_dir_entry * ); +static void i2o_proc_remove_controller(struct i2o_controller *, + struct proc_dir_entry * ); +static void i2o_proc_add_device(struct i2o_device *, struct proc_dir_entry *); +static void i2o_proc_remove_device(struct i2o_device *); +static int create_i2o_procfs(void); +static int destroy_i2o_procfs(void); +static void i2o_proc_new_dev(struct i2o_controller *, struct i2o_device *); +static void i2o_proc_dev_del(struct i2o_controller *, struct i2o_device *); + +static int i2o_proc_read_lan_dev_info(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_mac_addr(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_mcast_addr(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_batch_control(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_operation(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_media_operation(char *, char **, off_t, int, + int *, void *); +static int i2o_proc_read_lan_alt_addr(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_tx_info(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_rx_info(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_hist_stats(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_eth_stats(char *, char **, off_t, int, + int *, void *); +static int i2o_proc_read_lan_tr_stats(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_fddi_stats(char *, char **, off_t, int, int *, + void *); + +static struct proc_dir_entry *i2o_proc_dir_root; + +/* + * I2O OSM descriptor + */ +static struct i2o_handler i2o_proc_handler = +{ + NULL, + i2o_proc_new_dev, + i2o_proc_dev_del, + NULL, + "I2O procfs Layer", + 0, + 0xffffffff // All classes +}; + +/* + * IOP specific entries...write field just in case someone + * ever wants one. + */ +static i2o_proc_entry generic_iop_entries[] = +{ + {"hrt", S_IFREG|S_IRUGO, i2o_proc_read_hrt, NULL}, + {"lct", S_IFREG|S_IRUGO, i2o_proc_read_lct, NULL}, + {"status", S_IFREG|S_IRUGO, i2o_proc_read_status, NULL}, + {"hw", S_IFREG|S_IRUGO, i2o_proc_read_hw, NULL}, + {"ddm_table", S_IFREG|S_IRUGO, i2o_proc_read_ddm_table, NULL}, + {"driver_store", S_IFREG|S_IRUGO, i2o_proc_read_driver_store, NULL}, + {"drivers_stored", S_IFREG|S_IRUGO, i2o_proc_read_drivers_stored, NULL}, + {NULL, 0, NULL, NULL} +}; + +/* + * Device specific entries + */ +static i2o_proc_entry generic_dev_entries[] = +{ + {"groups", S_IFREG|S_IRUGO, i2o_proc_read_groups, NULL}, + {"phys_dev", S_IFREG|S_IRUGO, i2o_proc_read_phys_device, NULL}, + {"claimed", S_IFREG|S_IRUGO, i2o_proc_read_claimed, NULL}, + {"users", S_IFREG|S_IRUGO, i2o_proc_read_users, NULL}, + {"priv_msgs", S_IFREG|S_IRUGO, i2o_proc_read_priv_msgs, NULL}, + {"authorized_users", S_IFREG|S_IRUGO, i2o_proc_read_authorized_users, NULL}, + {"dev_identity", S_IFREG|S_IRUGO, i2o_proc_read_dev_identity, NULL}, + {"ddm_identity", S_IFREG|S_IRUGO, i2o_proc_read_ddm_identity, NULL}, + {"user_info", S_IFREG|S_IRUGO, i2o_proc_read_uinfo, NULL}, + {"sgl_limits", S_IFREG|S_IRUGO, i2o_proc_read_sgl_limits, NULL}, + {"sensors", S_IFREG|S_IRUGO, i2o_proc_read_sensors, NULL}, + {NULL, 0, NULL, NULL} +}; + +/* + * Storage unit specific entries (SCSI Periph, BS) with device names + */ +static i2o_proc_entry rbs_dev_entries[] = +{ + {"dev_name", S_IFREG|S_IRUGO, i2o_proc_read_dev_name, NULL}, + {NULL, 0, NULL, NULL} +}; + +#define SCSI_TABLE_SIZE 13 +static char *scsi_devices[] = +{ + "Direct-Access Read/Write", + "Sequential-Access Storage", + "Printer", + "Processor", + "WORM Device", + "CD-ROM Device", + "Scanner Device", + "Optical Memory Device", + "Medium Changer Device", + "Communications Device", + "Graphics Art Pre-Press Device", + "Graphics Art Pre-Press Device", + "Array Controller Device" +}; + +/* private */ + +/* + * Generic LAN specific entries + * + * Should groups with r/w entries have their own subdirectory? + * + */ +static i2o_proc_entry lan_entries[] = +{ + {"lan_dev_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_dev_info, NULL}, + {"lan_mac_addr", S_IFREG|S_IRUGO, i2o_proc_read_lan_mac_addr, NULL}, + {"lan_mcast_addr", S_IFREG|S_IRUGO|S_IWUSR, + i2o_proc_read_lan_mcast_addr, NULL}, + {"lan_batch_ctrl", S_IFREG|S_IRUGO|S_IWUSR, + i2o_proc_read_lan_batch_control, NULL}, + {"lan_operation", S_IFREG|S_IRUGO, i2o_proc_read_lan_operation, NULL}, + {"lan_media_operation", S_IFREG|S_IRUGO, + i2o_proc_read_lan_media_operation, NULL}, + {"lan_alt_addr", S_IFREG|S_IRUGO, i2o_proc_read_lan_alt_addr, NULL}, + {"lan_tx_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_tx_info, NULL}, + {"lan_rx_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_rx_info, NULL}, + + {"lan_hist_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_hist_stats, NULL}, + {NULL, 0, NULL, NULL} +}; + +/* + * Port specific LAN entries + * + */ +static i2o_proc_entry lan_eth_entries[] = +{ + {"lan_eth_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_eth_stats, NULL}, + {NULL, 0, NULL, NULL} +}; + +static i2o_proc_entry lan_tr_entries[] = +{ + {"lan_tr_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_tr_stats, NULL}, + {NULL, 0, NULL, NULL} +}; + +static i2o_proc_entry lan_fddi_entries[] = +{ + {"lan_fddi_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_fddi_stats, NULL}, + {NULL, 0, NULL, NULL} +}; + + +static char *chtostr(u8 *chars, int n) +{ + char tmp[256]; + tmp[0] = 0; + return strncat(tmp, (char *)chars, n); +} + +static int i2o_report_query_status(char *buf, int block_status, char *group) +{ + switch (block_status) + { + case -ETIMEDOUT: + return sprintf(buf, "Timeout reading group %s.\n",group); + case -ENOMEM: + return sprintf(buf, "No free memory to read the table.\n"); + case -I2O_PARAMS_STATUS_INVALID_GROUP_ID: + return sprintf(buf, "Group %s not supported.\n", group); + default: + return sprintf(buf, "Error reading group %s. BlockStatus 0x%02X\n", + group, -block_status); + } +} + +static char* bus_strings[] = +{ + "Local Bus", + "ISA", + "EISA", + "MCA", + "PCI", + "PCMCIA", + "NUBUS", + "CARDBUS" +}; + +static spinlock_t i2o_proc_lock = SPIN_LOCK_UNLOCKED; + +int i2o_proc_read_hrt(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + struct i2o_controller *c = (struct i2o_controller *)data; + i2o_hrt *hrt = (i2o_hrt *)c->hrt; + u32 bus; + int len, i; + + spin_lock(&i2o_proc_lock); + + len = 0; + + if(hrt->hrt_version) + { + len += sprintf(buf+len, + "HRT table for controller is too new a version.\n"); + spin_unlock(&i2o_proc_lock); + return len; + } + + if((hrt->num_entries * hrt->entry_len + 8) > 2048) { + printk(KERN_WARNING "i2o_proc: HRT does not fit into buffer\n"); + len += sprintf(buf+len, + "HRT table too big to fit in buffer.\n"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "HRT has %d entries of %d bytes each.\n", + hrt->num_entries, hrt->entry_len << 2); + + for(i = 0; i < hrt->num_entries && len < count; i++) + { + len += sprintf(buf+len, "Entry %d:\n", i); + len += sprintf(buf+len, " Adapter ID: %0#10x\n", + hrt->hrt_entry[i].adapter_id); + len += sprintf(buf+len, " Controlling tid: %0#6x\n", + hrt->hrt_entry[i].parent_tid); + + if(hrt->hrt_entry[i].bus_type != 0x80) + { + bus = hrt->hrt_entry[i].bus_type; + len += sprintf(buf+len, " %s Information\n", bus_strings[bus]); + + switch(bus) + { + case I2O_BUS_LOCAL: + len += sprintf(buf+len, " IOBase: %0#6x,", + hrt->hrt_entry[i].bus.local_bus.LbBaseIOPort); + len += sprintf(buf+len, " MemoryBase: %0#10x\n", + hrt->hrt_entry[i].bus.local_bus.LbBaseMemoryAddress); + break; + + case I2O_BUS_ISA: + len += sprintf(buf+len, " IOBase: %0#6x,", + hrt->hrt_entry[i].bus.isa_bus.IsaBaseIOPort); + len += sprintf(buf+len, " MemoryBase: %0#10x,", + hrt->hrt_entry[i].bus.isa_bus.IsaBaseMemoryAddress); + len += sprintf(buf+len, " CSN: %0#4x,", + hrt->hrt_entry[i].bus.isa_bus.CSN); + break; + + case I2O_BUS_EISA: + len += sprintf(buf+len, " IOBase: %0#6x,", + hrt->hrt_entry[i].bus.eisa_bus.EisaBaseIOPort); + len += sprintf(buf+len, " MemoryBase: %0#10x,", + hrt->hrt_entry[i].bus.eisa_bus.EisaBaseMemoryAddress); + len += sprintf(buf+len, " Slot: %0#4x,", + hrt->hrt_entry[i].bus.eisa_bus.EisaSlotNumber); + break; + + case I2O_BUS_MCA: + len += sprintf(buf+len, " IOBase: %0#6x,", + hrt->hrt_entry[i].bus.mca_bus.McaBaseIOPort); + len += sprintf(buf+len, " MemoryBase: %0#10x,", + hrt->hrt_entry[i].bus.mca_bus.McaBaseMemoryAddress); + len += sprintf(buf+len, " Slot: %0#4x,", + hrt->hrt_entry[i].bus.mca_bus.McaSlotNumber); + break; + + case I2O_BUS_PCI: + len += sprintf(buf+len, " Bus: %0#4x", + hrt->hrt_entry[i].bus.pci_bus.PciBusNumber); + len += sprintf(buf+len, " Dev: %0#4x", + hrt->hrt_entry[i].bus.pci_bus.PciDeviceNumber); + len += sprintf(buf+len, " Func: %0#4x", + hrt->hrt_entry[i].bus.pci_bus.PciFunctionNumber); + len += sprintf(buf+len, " Vendor: %0#6x", + hrt->hrt_entry[i].bus.pci_bus.PciVendorID); + len += sprintf(buf+len, " Device: %0#6x\n", + hrt->hrt_entry[i].bus.pci_bus.PciDeviceID); + break; + + default: + len += sprintf(buf+len, " Unsupported Bus Type\n"); + } + } + else + len += sprintf(buf+len, " Unknown Bus Type\n"); + } + + spin_unlock(&i2o_proc_lock); + + return len; +} + +int i2o_proc_read_lct(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_controller *c = (struct i2o_controller*)data; + i2o_lct *lct = (i2o_lct *)c->lct; + int entries; + int i; + +#define BUS_TABLE_SIZE 3 + static char *bus_ports[] = + { + "Generic Bus", + "SCSI Bus", + "Fibre Channel Bus" + }; + + spin_lock(&i2o_proc_lock); + len = 0; + + entries = (lct->table_size - 3)/9; + + len += sprintf(buf, "LCT contains %d %s\n", entries, + entries == 1 ? "entry" : "entries"); + if(lct->boot_tid) + len += sprintf(buf+len, "Boot Device @ ID %d\n", lct->boot_tid); + + len += + sprintf(buf+len, "Current Change Indicator: %#10x\n", lct->change_ind); + + for(i = 0; i < entries; i++) + { + len += sprintf(buf+len, "Entry %d\n", i); + len += sprintf(buf+len, " Class, SubClass : %s", i2o_get_class_name(lct->lct_entry[i].class_id)); + + /* + * Classes which we'll print subclass info for + */ + switch(lct->lct_entry[i].class_id & 0xFFF) + { + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + switch(lct->lct_entry[i].sub_class) + { + case 0x00: + len += sprintf(buf+len, ", Direct-Access Read/Write"); + break; + + case 0x04: + len += sprintf(buf+len, ", WORM Drive"); + break; + + case 0x05: + len += sprintf(buf+len, ", CD-ROM Drive"); + break; + + case 0x07: + len += sprintf(buf+len, ", Optical Memory Device"); + break; + + default: + len += sprintf(buf+len, ", Unknown (0x%02x)", + lct->lct_entry[i].sub_class); + break; + } + break; + + case I2O_CLASS_LAN: + switch(lct->lct_entry[i].sub_class & 0xFF) + { + case 0x30: + len += sprintf(buf+len, ", Ethernet"); + break; + + case 0x40: + len += sprintf(buf+len, ", 100base VG"); + break; + + case 0x50: + len += sprintf(buf+len, ", IEEE 802.5/Token-Ring"); + break; + + case 0x60: + len += sprintf(buf+len, ", ANSI X3T9.5 FDDI"); + break; + + case 0x70: + len += sprintf(buf+len, ", Fibre Channel"); + break; + + default: + len += sprintf(buf+len, ", Unknown Sub-Class (0x%02x)", + lct->lct_entry[i].sub_class & 0xFF); + break; + } + break; + + case I2O_CLASS_SCSI_PERIPHERAL: + if(lct->lct_entry[i].sub_class < SCSI_TABLE_SIZE) + len += sprintf(buf+len, ", %s", + scsi_devices[lct->lct_entry[i].sub_class]); + else + len += sprintf(buf+len, ", Unknown Device Type"); + break; + + case I2O_CLASS_BUS_ADAPTER_PORT: + if(lct->lct_entry[i].sub_class < BUS_TABLE_SIZE) + len += sprintf(buf+len, ", %s", + bus_ports[lct->lct_entry[i].sub_class]); + else + len += sprintf(buf+len, ", Unknown Bus Type"); + break; + } + len += sprintf(buf+len, "\n"); + + len += sprintf(buf+len, " Local TID : 0x%03x\n", lct->lct_entry[i].tid); + len += sprintf(buf+len, " User TID : 0x%03x\n", lct->lct_entry[i].user_tid); + len += sprintf(buf+len, " Parent TID : 0x%03x\n", + lct->lct_entry[i].parent_tid); + len += sprintf(buf+len, " Identity Tag : 0x%x%x%x%x%x%x%x%x\n", + lct->lct_entry[i].identity_tag[0], + lct->lct_entry[i].identity_tag[1], + lct->lct_entry[i].identity_tag[2], + lct->lct_entry[i].identity_tag[3], + lct->lct_entry[i].identity_tag[4], + lct->lct_entry[i].identity_tag[5], + lct->lct_entry[i].identity_tag[6], + lct->lct_entry[i].identity_tag[7]); + len += sprintf(buf+len, " Change Indicator : %0#10x\n", + lct->lct_entry[i].change_ind); + len += sprintf(buf+len, " Event Capab Mask : %0#10x\n", + lct->lct_entry[i].device_flags); + } + + spin_unlock(&i2o_proc_lock); + return len; +} + +int i2o_proc_read_status(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_controller *c = (struct i2o_controller*)data; + char prodstr[25]; + int version; + + spin_lock(&i2o_proc_lock); + len = 0; + + i2o_status_get(c); // reread the status block + + len += sprintf(buf+len,"Organization ID : %0#6x\n", + c->status_block->org_id); + + version = c->status_block->i2o_version; + +/* FIXME for Spec 2.0 + if (version == 0x02) { + len += sprintf(buf+len,"Lowest I2O version supported: "); + switch(workspace[2]) { + case 0x00: + len += sprintf(buf+len,"1.0\n"); + break; + case 0x01: + len += sprintf(buf+len,"1.5\n"); + break; + case 0x02: + len += sprintf(buf+len,"2.0\n"); + break; + } + + len += sprintf(buf+len, "Highest I2O version supported: "); + switch(workspace[3]) { + case 0x00: + len += sprintf(buf+len,"1.0\n"); + break; + case 0x01: + len += sprintf(buf+len,"1.5\n"); + break; + case 0x02: + len += sprintf(buf+len,"2.0\n"); + break; + } + } +*/ + len += sprintf(buf+len,"IOP ID : %0#5x\n", + c->status_block->iop_id); + len += sprintf(buf+len,"Host Unit ID : %0#6x\n", + c->status_block->host_unit_id); + len += sprintf(buf+len,"Segment Number : %0#5x\n", + c->status_block->segment_number); + + len += sprintf(buf+len, "I2O version : "); + switch (version) { + case 0x00: + len += sprintf(buf+len,"1.0\n"); + break; + case 0x01: + len += sprintf(buf+len,"1.5\n"); + break; + case 0x02: + len += sprintf(buf+len,"2.0\n"); + break; + default: + len += sprintf(buf+len,"Unknown version\n"); + } + + len += sprintf(buf+len, "IOP State : "); + switch (c->status_block->iop_state) { + case 0x01: + len += sprintf(buf+len,"INIT\n"); + break; + + case 0x02: + len += sprintf(buf+len,"RESET\n"); + break; + + case 0x04: + len += sprintf(buf+len,"HOLD\n"); + break; + + case 0x05: + len += sprintf(buf+len,"READY\n"); + break; + + case 0x08: + len += sprintf(buf+len,"OPERATIONAL\n"); + break; + + case 0x10: + len += sprintf(buf+len,"FAILED\n"); + break; + + case 0x11: + len += sprintf(buf+len,"FAULTED\n"); + break; + + default: + len += sprintf(buf+len,"Unknown\n"); + break; + } + + len += sprintf(buf+len,"Messenger Type : "); + switch (c->status_block->msg_type) { + case 0x00: + len += sprintf(buf+len,"Memory mapped\n"); + break; + case 0x01: + len += sprintf(buf+len,"Memory mapped only\n"); + break; + case 0x02: + len += sprintf(buf+len,"Remote only\n"); + break; + case 0x03: + len += sprintf(buf+len,"Memory mapped and remote\n"); + break; + default: + len += sprintf(buf+len,"Unknown\n"); + } + + len += sprintf(buf+len,"Inbound Frame Size : %d bytes\n", + c->status_block->inbound_frame_size<<2); + len += sprintf(buf+len,"Max Inbound Frames : %d\n", + c->status_block->max_inbound_frames); + len += sprintf(buf+len,"Current Inbound Frames : %d\n", + c->status_block->cur_inbound_frames); + len += sprintf(buf+len,"Max Outbound Frames : %d\n", + c->status_block->max_outbound_frames); + + /* Spec doesn't say if NULL terminated or not... */ + memcpy(prodstr, c->status_block->product_id, 24); + prodstr[24] = '\0'; + len += sprintf(buf+len,"Product ID : %s\n", prodstr); + len += sprintf(buf+len,"Expected LCT Size : %d bytes\n", + c->status_block->expected_lct_size); + + len += sprintf(buf+len,"IOP Capabilities\n"); + len += sprintf(buf+len," Context Field Size Support : "); + switch (c->status_block->iop_capabilities & 0x0000003) { + case 0: + len += sprintf(buf+len,"Supports only 32-bit context fields\n"); + break; + case 1: + len += sprintf(buf+len,"Supports only 64-bit context fields\n"); + break; + case 2: + len += sprintf(buf+len,"Supports 32-bit and 64-bit context fields, " + "but not concurrently\n"); + break; + case 3: + len += sprintf(buf+len,"Supports 32-bit and 64-bit context fields " + "concurrently\n"); + break; + default: + len += sprintf(buf+len,"0x%08x\n",c->status_block->iop_capabilities); + } + len += sprintf(buf+len," Current Context Field Size : "); + switch (c->status_block->iop_capabilities & 0x0000000C) { + case 0: + len += sprintf(buf+len,"not configured\n"); + break; + case 4: + len += sprintf(buf+len,"Supports only 32-bit context fields\n"); + break; + case 8: + len += sprintf(buf+len,"Supports only 64-bit context fields\n"); + break; + case 12: + len += sprintf(buf+len,"Supports both 32-bit or 64-bit context fields " + "concurrently\n"); + break; + default: + len += sprintf(buf+len,"\n"); + } + len += sprintf(buf+len," Inbound Peer Support : %s\n", + (c->status_block->iop_capabilities & 0x00000010) ? "Supported" : "Not supported"); + len += sprintf(buf+len," Outbound Peer Support : %s\n", + (c->status_block->iop_capabilities & 0x00000020) ? "Supported" : "Not supported"); + len += sprintf(buf+len," Peer to Peer Support : %s\n", + (c->status_block->iop_capabilities & 0x00000040) ? "Supported" : "Not supported"); + + len += sprintf(buf+len, "Desired private memory size : %d kB\n", + c->status_block->desired_mem_size>>10); + len += sprintf(buf+len, "Allocated private memory size : %d kB\n", + c->status_block->current_mem_size>>10); + len += sprintf(buf+len, "Private memory base address : %0#10x\n", + c->status_block->current_mem_base); + len += sprintf(buf+len, "Desired private I/O size : %d kB\n", + c->status_block->desired_io_size>>10); + len += sprintf(buf+len, "Allocated private I/O size : %d kB\n", + c->status_block->current_io_size>>10); + len += sprintf(buf+len, "Private I/O base address : %0#10x\n", + c->status_block->current_io_base); + + spin_unlock(&i2o_proc_lock); + + return len; +} + +int i2o_proc_read_hw(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_controller *c = (struct i2o_controller*)data; + static u32 work32[5]; + static u8 *work8 = (u8*)work32; + static u16 *work16 = (u16*)work32; + int token; + u32 hwcap; + + static char *cpu_table[] = + { + "Intel 80960 series", + "AMD2900 series", + "Motorola 68000 series", + "ARM series", + "MIPS series", + "Sparc series", + "PowerPC series", + "Intel x86 series" + }; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_scalar(c, ADAPTER_TID, 0x0000, -1, &work32, sizeof(work32)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0000 IOP Hardware"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "I2O Vendor ID : %0#6x\n", work16[0]); + len += sprintf(buf+len, "Product ID : %0#6x\n", work16[1]); + len += sprintf(buf+len, "CPU : "); + if(work8[16] > 8) + len += sprintf(buf+len, "Unknown\n"); + else + len += sprintf(buf+len, "%s\n", cpu_table[work8[16]]); + /* Anyone using ProcessorVersion? */ + + len += sprintf(buf+len, "RAM : %dkB\n", work32[1]>>10); + len += sprintf(buf+len, "Non-Volatile Mem : %dkB\n", work32[2]>>10); + + hwcap = work32[3]; + len += sprintf(buf+len, "Capabilities : 0x%08x\n", hwcap); + len += sprintf(buf+len, " [%s] Self booting\n", + (hwcap&0x00000001) ? "+" : "-"); + len += sprintf(buf+len, " [%s] Upgradable IRTOS\n", + (hwcap&0x00000002) ? "+" : "-"); + len += sprintf(buf+len, " [%s] Supports downloading DDMs\n", + (hwcap&0x00000004) ? "+" : "-"); + len += sprintf(buf+len, " [%s] Supports installing DDMs\n", + (hwcap&0x00000008) ? "+" : "-"); + len += sprintf(buf+len, " [%s] Battery-backed RAM\n", + (hwcap&0x00000010) ? "+" : "-"); + + spin_unlock(&i2o_proc_lock); + + return len; +} + + +/* Executive group 0003h - Executing DDM List (table) */ +int i2o_proc_read_ddm_table(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_controller *c = (struct i2o_controller*)data; + int token; + int i; + + typedef struct _i2o_exec_execute_ddm_table { + u16 ddm_tid; + u8 module_type; + u8 reserved; + u16 i2o_vendor_id; + u16 module_id; + u8 module_name_version[28]; + u32 data_size; + u32 code_size; + } i2o_exec_execute_ddm_table; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + i2o_exec_execute_ddm_table ddm_table[MAX_I2O_MODULES]; + } result; + + i2o_exec_execute_ddm_table ddm_table; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + c, ADAPTER_TID, + 0x0003, -1, + NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0003 Executing DDM List"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "Tid Module_type Vendor Mod_id Module_name Vrs Data_size Code_size\n"); + ddm_table=result.ddm_table[0]; + + for(i=0; i < result.row_count; ddm_table=result.ddm_table[++i]) + { + len += sprintf(buf+len, "0x%03x ", ddm_table.ddm_tid & 0xFFF); + + switch(ddm_table.module_type) + { + case 0x01: + len += sprintf(buf+len, "Downloaded DDM "); + break; + case 0x22: + len += sprintf(buf+len, "Embedded DDM "); + break; + default: + len += sprintf(buf+len, " "); + } + + len += sprintf(buf+len, "%-#7x", ddm_table.i2o_vendor_id); + len += sprintf(buf+len, "%-#8x", ddm_table.module_id); + len += sprintf(buf+len, "%-29s", chtostr(ddm_table.module_name_version, 28)); + len += sprintf(buf+len, "%9d ", ddm_table.data_size); + len += sprintf(buf+len, "%8d", ddm_table.code_size); + + len += sprintf(buf+len, "\n"); + } + + spin_unlock(&i2o_proc_lock); + + return len; +} + + +/* Executive group 0004h - Driver Store (scalar) */ +int i2o_proc_read_driver_store(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_controller *c = (struct i2o_controller*)data; + u32 work32[8]; + int token; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_scalar(c, ADAPTER_TID, 0x0004, -1, &work32, sizeof(work32)); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0004 Driver Store"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "Module limit : %d\n" + "Module count : %d\n" + "Current space : %d kB\n" + "Free space : %d kB\n", + work32[0], work32[1], work32[2]>>10, work32[3]>>10); + + spin_unlock(&i2o_proc_lock); + + return len; +} + + +/* Executive group 0005h - Driver Store Table (table) */ +int i2o_proc_read_drivers_stored(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + typedef struct _i2o_driver_store { + u16 stored_ddm_index; + u8 module_type; + u8 reserved; + u16 i2o_vendor_id; + u16 module_id; + u8 module_name_version[28]; + u8 date[8]; + u32 module_size; + u32 mpb_size; + u32 module_flags; + } i2o_driver_store_table; + + struct i2o_controller *c = (struct i2o_controller*)data; + int token; + int i; + + typedef struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + i2o_driver_store_table dst[MAX_I2O_MODULES]; + } i2o_driver_result_table; + + i2o_driver_result_table *result; + i2o_driver_store_table *dst; + + spin_lock(&i2o_proc_lock); + + len = 0; + + result = kmalloc(sizeof(i2o_driver_result_table), GFP_KERNEL); + if(result == NULL) + return -ENOMEM; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + c, ADAPTER_TID, 0x0005, -1, NULL, 0, + result, sizeof(*result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0005 DRIVER STORE TABLE"); + spin_unlock(&i2o_proc_lock); + kfree(result); + return len; + } + + len += sprintf(buf+len, "# Module_type Vendor Mod_id Module_name Vrs" + "Date Mod_size Par_size Flags\n"); + for(i=0, dst=&result->dst[0]; i < result->row_count; dst=&result->dst[++i]) + { + len += sprintf(buf+len, "%-3d", dst->stored_ddm_index); + switch(dst->module_type) + { + case 0x01: + len += sprintf(buf+len, "Downloaded DDM "); + break; + case 0x22: + len += sprintf(buf+len, "Embedded DDM "); + break; + default: + len += sprintf(buf+len, " "); + } + +#if 0 + if(c->i2oversion == 0x02) + len += sprintf(buf+len, "%-d", dst->module_state); +#endif + + len += sprintf(buf+len, "%-#7x", dst->i2o_vendor_id); + len += sprintf(buf+len, "%-#8x", dst->module_id); + len += sprintf(buf+len, "%-29s", chtostr(dst->module_name_version,28)); + len += sprintf(buf+len, "%-9s", chtostr(dst->date,8)); + len += sprintf(buf+len, "%8d ", dst->module_size); + len += sprintf(buf+len, "%8d ", dst->mpb_size); + len += sprintf(buf+len, "0x%04x", dst->module_flags); +#if 0 + if(c->i2oversion == 0x02) + len += sprintf(buf+len, "%d", + dst->notification_level); +#endif + len += sprintf(buf+len, "\n"); + } + + spin_unlock(&i2o_proc_lock); + kfree(result); + return len; +} + + +/* Generic group F000h - Params Descriptor (table) */ +int i2o_proc_read_groups(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + u8 properties; + + typedef struct _i2o_group_info + { + u16 group_number; + u16 field_count; + u16 row_count; + u8 properties; + u8 reserved; + } i2o_group_info; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + i2o_group_info group[256]; + } result; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, 0xF000, -1, NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len = i2o_report_query_status(buf+len, token, "0xF000 Params Descriptor"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "# Group FieldCount RowCount Type Add Del Clear\n"); + + for (i=0; i < result.row_count; i++) + { + len += sprintf(buf+len, "%-3d", i); + len += sprintf(buf+len, "0x%04X ", result.group[i].group_number); + len += sprintf(buf+len, "%10d ", result.group[i].field_count); + len += sprintf(buf+len, "%8d ", result.group[i].row_count); + + properties = result.group[i].properties; + if (properties & 0x1) len += sprintf(buf+len, "Table "); + else len += sprintf(buf+len, "Scalar "); + if (properties & 0x2) len += sprintf(buf+len, " + "); + else len += sprintf(buf+len, " - "); + if (properties & 0x4) len += sprintf(buf+len, " + "); + else len += sprintf(buf+len, " - "); + if (properties & 0x8) len += sprintf(buf+len, " + "); + else len += sprintf(buf+len, " - "); + + len += sprintf(buf+len, "\n"); + } + + if (result.more_flag) + len += sprintf(buf+len, "There is more...\n"); + + spin_unlock(&i2o_proc_lock); + + return len; +} + + +/* Generic group F001h - Physical Device Table (table) */ +int i2o_proc_read_phys_device(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + u32 adapter_id[64]; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, + 0xF001, -1, NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF001 Physical Device Table"); + spin_unlock(&i2o_proc_lock); + return len; + } + + if (result.row_count) + len += sprintf(buf+len, "# AdapterId\n"); + + for (i=0; i < result.row_count; i++) + { + len += sprintf(buf+len, "%-2d", i); + len += sprintf(buf+len, "%#7x\n", result.adapter_id[i]); + } + + if (result.more_flag) + len += sprintf(buf+len, "There is more...\n"); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* Generic group F002h - Claimed Table (table) */ +int i2o_proc_read_claimed(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + + struct { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + u16 claimed_tid[64]; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, + 0xF002, -1, NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF002 Claimed Table"); + spin_unlock(&i2o_proc_lock); + return len; + } + + if (result.row_count) + len += sprintf(buf+len, "# ClaimedTid\n"); + + for (i=0; i < result.row_count; i++) + { + len += sprintf(buf+len, "%-2d", i); + len += sprintf(buf+len, "%#7x\n", result.claimed_tid[i]); + } + + if (result.more_flag) + len += sprintf(buf+len, "There is more...\n"); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* Generic group F003h - User Table (table) */ +int i2o_proc_read_users(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + + typedef struct _i2o_user_table + { + u16 instance; + u16 user_tid; + u8 claim_type; + u8 reserved1; + u16 reserved2; + } i2o_user_table; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + i2o_user_table user[64]; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, + 0xF003, -1, NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF003 User Table"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "# Instance UserTid ClaimType\n"); + + for(i=0; i < result.row_count; i++) + { + len += sprintf(buf+len, "%-3d", i); + len += sprintf(buf+len, "%#8x ", result.user[i].instance); + len += sprintf(buf+len, "%#7x ", result.user[i].user_tid); + len += sprintf(buf+len, "%#9x\n", result.user[i].claim_type); + } + + if (result.more_flag) + len += sprintf(buf+len, "There is more...\n"); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* Generic group F005h - Private message extensions (table) (optional) */ +int i2o_proc_read_priv_msgs(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + + typedef struct _i2o_private + { + u16 ext_instance; + u16 organization_id; + u16 x_function_code; + } i2o_private; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + i2o_private extension[64]; + } result; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, + 0xF000, -1, + NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF005 Private Message Extensions (optional)"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "Instance# OrgId FunctionCode\n"); + + for(i=0; i < result.row_count; i++) + { + len += sprintf(buf+len, "%0#9x ", result.extension[i].ext_instance); + len += sprintf(buf+len, "%0#6x ", result.extension[i].organization_id); + len += sprintf(buf+len, "%0#6x", result.extension[i].x_function_code); + + len += sprintf(buf+len, "\n"); + } + + if(result.more_flag) + len += sprintf(buf+len, "There is more...\n"); + + spin_unlock(&i2o_proc_lock); + + return len; +} + + +/* Generic group F006h - Authorized User Table (table) */ +int i2o_proc_read_authorized_users(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + u32 alternate_tid[64]; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, + 0xF006, -1, + NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF006 Autohorized User Table"); + spin_unlock(&i2o_proc_lock); + return len; + } + + if (result.row_count) + len += sprintf(buf+len, "# AlternateTid\n"); + + for(i=0; i < result.row_count; i++) + { + len += sprintf(buf+len, "%-2d", i); + len += sprintf(buf+len, "%#7x ", result.alternate_tid[i]); + } + + if (result.more_flag) + len += sprintf(buf+len, "There is more...\n"); + + spin_unlock(&i2o_proc_lock); + return len; +} + + +/* Generic group F100h - Device Identity (scalar) */ +int i2o_proc_read_dev_identity(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[128]; // allow for "stuff" + up to 256 byte (max) serial number + // == (allow) 512d bytes (max) + static u16 *work16 = (u16*)work32; + int token; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0xF100, -1, + &work32, sizeof(work32)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token ,"0xF100 Device Identity"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Device Class : %s\n", i2o_get_class_name(work16[0])); + len += sprintf(buf+len, "Owner TID : %0#5x\n", work16[2]); + len += sprintf(buf+len, "Parent TID : %0#5x\n", work16[3]); + len += sprintf(buf+len, "Vendor info : %s\n", chtostr((u8 *)(work32+2), 16)); + len += sprintf(buf+len, "Product info : %s\n", chtostr((u8 *)(work32+6), 16)); + len += sprintf(buf+len, "Description : %s\n", chtostr((u8 *)(work32+10), 16)); + len += sprintf(buf+len, "Product rev. : %s\n", chtostr((u8 *)(work32+14), 8)); + + len += sprintf(buf+len, "Serial number : "); + len = print_serial_number(buf, len, + (u8*)(work32+16), + /* allow for SNLen plus + * possible trailing '\0' + */ + sizeof(work32)-(16*sizeof(u32))-2 + ); + len += sprintf(buf+len, "\n"); + + spin_unlock(&i2o_proc_lock); + + return len; +} + + +int i2o_proc_read_dev_name(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + + if ( d->dev_name[0] == '\0' ) + return 0; + + len = sprintf(buf, "%s\n", d->dev_name); + + return len; +} + + +/* Generic group F101h - DDM Identity (scalar) */ +int i2o_proc_read_ddm_identity(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + + struct + { + u16 ddm_tid; + u8 module_name[24]; + u8 module_rev[8]; + u8 sn_format; + u8 serial_number[12]; + u8 pad[256]; // allow up to 256 byte (max) serial number + } result; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0xF101, -1, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF101 DDM Identity"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Registering DDM TID : 0x%03x\n", result.ddm_tid); + len += sprintf(buf+len, "Module name : %s\n", chtostr(result.module_name, 24)); + len += sprintf(buf+len, "Module revision : %s\n", chtostr(result.module_rev, 8)); + + len += sprintf(buf+len, "Serial number : "); + len = print_serial_number(buf, len, result.serial_number, sizeof(result)-36); + /* allow for SNLen plus possible trailing '\0' */ + + len += sprintf(buf+len, "\n"); + + spin_unlock(&i2o_proc_lock); + + return len; +} + +/* Generic group F102h - User Information (scalar) */ +int i2o_proc_read_uinfo(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + + struct + { + u8 device_name[64]; + u8 service_name[64]; + u8 physical_location[64]; + u8 instance_number[4]; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0xF102, -1, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF102 User Information"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Device name : %s\n", chtostr(result.device_name, 64)); + len += sprintf(buf+len, "Service name : %s\n", chtostr(result.service_name, 64)); + len += sprintf(buf+len, "Physical name : %s\n", chtostr(result.physical_location, 64)); + len += sprintf(buf+len, "Instance number : %s\n", chtostr(result.instance_number, 4)); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* Generic group F103h - SGL Operating Limits (scalar) */ +int i2o_proc_read_sgl_limits(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[12]; + static u16 *work16 = (u16 *)work32; + static u8 *work8 = (u8 *)work32; + int token; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0xF103, -1, + &work32, sizeof(work32)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF103 SGL Operating Limits"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "SGL chain size : %d\n", work32[0]); + len += sprintf(buf+len, "Max SGL chain size : %d\n", work32[1]); + len += sprintf(buf+len, "SGL chain size target : %d\n", work32[2]); + len += sprintf(buf+len, "SGL frag count : %d\n", work16[6]); + len += sprintf(buf+len, "Max SGL frag count : %d\n", work16[7]); + len += sprintf(buf+len, "SGL frag count target : %d\n", work16[8]); + + if (d->i2oversion == 0x02) + { + len += sprintf(buf+len, "SGL data alignment : %d\n", work16[8]); + len += sprintf(buf+len, "SGL addr limit : %d\n", work8[20]); + len += sprintf(buf+len, "SGL addr sizes supported : "); + if (work8[21] & 0x01) + len += sprintf(buf+len, "32 bit "); + if (work8[21] & 0x02) + len += sprintf(buf+len, "64 bit "); + if (work8[21] & 0x04) + len += sprintf(buf+len, "96 bit "); + if (work8[21] & 0x08) + len += sprintf(buf+len, "128 bit "); + len += sprintf(buf+len, "\n"); + } + + spin_unlock(&i2o_proc_lock); + + return len; +} + +/* Generic group F200h - Sensors (scalar) */ +int i2o_proc_read_sensors(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + + struct + { + u16 sensor_instance; + u8 component; + u16 component_instance; + u8 sensor_class; + u8 sensor_type; + u8 scaling_exponent; + u32 actual_reading; + u32 minimum_reading; + u32 low2lowcat_treshold; + u32 lowcat2low_treshold; + u32 lowwarn2low_treshold; + u32 low2lowwarn_treshold; + u32 norm2lowwarn_treshold; + u32 lowwarn2norm_treshold; + u32 nominal_reading; + u32 hiwarn2norm_treshold; + u32 norm2hiwarn_treshold; + u32 high2hiwarn_treshold; + u32 hiwarn2high_treshold; + u32 hicat2high_treshold; + u32 hi2hicat_treshold; + u32 maximum_reading; + u8 sensor_state; + u16 event_enable; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0xF200, -1, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF200 Sensors (optional)"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "Sensor instance : %d\n", result.sensor_instance); + + len += sprintf(buf+len, "Component : %d = ", result.component); + switch (result.component) + { + case 0: len += sprintf(buf+len, "Other"); + break; + case 1: len += sprintf(buf+len, "Planar logic Board"); + break; + case 2: len += sprintf(buf+len, "CPU"); + break; + case 3: len += sprintf(buf+len, "Chassis"); + break; + case 4: len += sprintf(buf+len, "Power Supply"); + break; + case 5: len += sprintf(buf+len, "Storage"); + break; + case 6: len += sprintf(buf+len, "External"); + break; + } + len += sprintf(buf+len,"\n"); + + len += sprintf(buf+len, "Component instance : %d\n", result.component_instance); + len += sprintf(buf+len, "Sensor class : %s\n", + result.sensor_class ? "Analog" : "Digital"); + + len += sprintf(buf+len, "Sensor type : %d = ",result.sensor_type); + switch (result.sensor_type) + { + case 0: len += sprintf(buf+len, "Other\n"); + break; + case 1: len += sprintf(buf+len, "Thermal\n"); + break; + case 2: len += sprintf(buf+len, "DC voltage (DC volts)\n"); + break; + case 3: len += sprintf(buf+len, "AC voltage (AC volts)\n"); + break; + case 4: len += sprintf(buf+len, "DC current (DC amps)\n"); + break; + case 5: len += sprintf(buf+len, "AC current (AC volts)\n"); + break; + case 6: len += sprintf(buf+len, "Door open\n"); + break; + case 7: len += sprintf(buf+len, "Fan operational\n"); + break; + } + + len += sprintf(buf+len, "Scaling exponent : %d\n", result.scaling_exponent); + len += sprintf(buf+len, "Actual reading : %d\n", result.actual_reading); + len += sprintf(buf+len, "Minimum reading : %d\n", result.minimum_reading); + len += sprintf(buf+len, "Low2LowCat treshold : %d\n", result.low2lowcat_treshold); + len += sprintf(buf+len, "LowCat2Low treshold : %d\n", result.lowcat2low_treshold); + len += sprintf(buf+len, "LowWarn2Low treshold : %d\n", result.lowwarn2low_treshold); + len += sprintf(buf+len, "Low2LowWarn treshold : %d\n", result.low2lowwarn_treshold); + len += sprintf(buf+len, "Norm2LowWarn treshold : %d\n", result.norm2lowwarn_treshold); + len += sprintf(buf+len, "LowWarn2Norm treshold : %d\n", result.lowwarn2norm_treshold); + len += sprintf(buf+len, "Nominal reading : %d\n", result.nominal_reading); + len += sprintf(buf+len, "HiWarn2Norm treshold : %d\n", result.hiwarn2norm_treshold); + len += sprintf(buf+len, "Norm2HiWarn treshold : %d\n", result.norm2hiwarn_treshold); + len += sprintf(buf+len, "High2HiWarn treshold : %d\n", result.high2hiwarn_treshold); + len += sprintf(buf+len, "HiWarn2High treshold : %d\n", result.hiwarn2high_treshold); + len += sprintf(buf+len, "HiCat2High treshold : %d\n", result.hicat2high_treshold); + len += sprintf(buf+len, "High2HiCat treshold : %d\n", result.hi2hicat_treshold); + len += sprintf(buf+len, "Maximum reading : %d\n", result.maximum_reading); + + len += sprintf(buf+len, "Sensor state : %d = ", result.sensor_state); + switch (result.sensor_state) + { + case 0: len += sprintf(buf+len, "Normal\n"); + break; + case 1: len += sprintf(buf+len, "Abnormal\n"); + break; + case 2: len += sprintf(buf+len, "Unknown\n"); + break; + case 3: len += sprintf(buf+len, "Low Catastrophic (LoCat)\n"); + break; + case 4: len += sprintf(buf+len, "Low (Low)\n"); + break; + case 5: len += sprintf(buf+len, "Low Warning (LoWarn)\n"); + break; + case 6: len += sprintf(buf+len, "High Warning (HiWarn)\n"); + break; + case 7: len += sprintf(buf+len, "High (High)\n"); + break; + case 8: len += sprintf(buf+len, "High Catastrophic (HiCat)\n"); + break; + } + + len += sprintf(buf+len, "Event_enable : 0x%02X\n", result.event_enable); + len += sprintf(buf+len, " [%s] Operational state change. \n", + (result.event_enable & 0x01) ? "+" : "-" ); + len += sprintf(buf+len, " [%s] Low catastrophic. \n", + (result.event_enable & 0x02) ? "+" : "-" ); + len += sprintf(buf+len, " [%s] Low reading. \n", + (result.event_enable & 0x04) ? "+" : "-" ); + len += sprintf(buf+len, " [%s] Low warning. \n", + (result.event_enable & 0x08) ? "+" : "-" ); + len += sprintf(buf+len, " [%s] Change back to normal from out of range state. \n", + (result.event_enable & 0x10) ? "+" : "-" ); + len += sprintf(buf+len, " [%s] High warning. \n", + (result.event_enable & 0x20) ? "+" : "-" ); + len += sprintf(buf+len, " [%s] High reading. \n", + (result.event_enable & 0x40) ? "+" : "-" ); + len += sprintf(buf+len, " [%s] High catastrophic. \n", + (result.event_enable & 0x80) ? "+" : "-" ); + + spin_unlock(&i2o_proc_lock); + return len; +} + + +static int print_serial_number(char *buff, int pos, u8 *serialno, int max_len) +{ + int i; + + /* 19990419 -sralston + * The I2O v1.5 (and v2.0 so far) "official specification" + * got serial numbers WRONG! + * Apparently, and despite what Section 3.4.4 says and + * Figure 3-35 shows (pg 3-39 in the pdf doc), + * the convention / consensus seems to be: + * + First byte is SNFormat + * + Second byte is SNLen (but only if SNFormat==7 (?)) + * + (v2.0) SCSI+BS may use IEEE Registered (64 or 128 bit) format + */ + switch(serialno[0]) + { + case I2O_SNFORMAT_BINARY: /* Binary */ + pos += sprintf(buff+pos, "0x"); + for(i = 0; i < serialno[1]; i++) + { + pos += sprintf(buff+pos, "%02X", serialno[2+i]); + } + break; + + case I2O_SNFORMAT_ASCII: /* ASCII */ + if ( serialno[1] < ' ' ) /* printable or SNLen? */ + { + /* sanity */ + max_len = (max_len < serialno[1]) ? max_len : serialno[1]; + serialno[1+max_len] = '\0'; + + /* just print it */ + pos += sprintf(buff+pos, "%s", &serialno[2]); + } + else + { + /* print chars for specified length */ + for(i = 0; i < serialno[1]; i++) + { + pos += sprintf(buff+pos, "%c", serialno[2+i]); + } + } + break; + + case I2O_SNFORMAT_UNICODE: /* UNICODE */ + pos += sprintf(buff+pos, "UNICODE Format. Can't Display\n"); + break; + + case I2O_SNFORMAT_LAN48_MAC: /* LAN-48 MAC Address */ + pos += sprintf(buff+pos, + "LAN-48 MAC address @ %02X:%02X:%02X:%02X:%02X:%02X", + serialno[2], serialno[3], + serialno[4], serialno[5], + serialno[6], serialno[7]); + break; + + case I2O_SNFORMAT_WAN: /* WAN MAC Address */ + /* FIXME: Figure out what a WAN access address looks like?? */ + pos += sprintf(buff+pos, "WAN Access Address"); + break; + +/* plus new in v2.0 */ + case I2O_SNFORMAT_LAN64_MAC: /* LAN-64 MAC Address */ + /* FIXME: Figure out what a LAN-64 address really looks like?? */ + pos += sprintf(buff+pos, + "LAN-64 MAC address @ [?:%02X:%02X:?] %02X:%02X:%02X:%02X:%02X:%02X", + serialno[8], serialno[9], + serialno[2], serialno[3], + serialno[4], serialno[5], + serialno[6], serialno[7]); + break; + + + case I2O_SNFORMAT_DDM: /* I2O DDM */ + pos += sprintf(buff+pos, + "DDM: Tid=%03Xh, Rsvd=%04Xh, OrgId=%04Xh", + *(u16*)&serialno[2], + *(u16*)&serialno[4], + *(u16*)&serialno[6]); + break; + + case I2O_SNFORMAT_IEEE_REG64: /* IEEE Registered (64-bit) */ + case I2O_SNFORMAT_IEEE_REG128: /* IEEE Registered (128-bit) */ + /* FIXME: Figure if this is even close?? */ + pos += sprintf(buff+pos, + "IEEE NodeName(hi,lo)=(%08Xh:%08Xh), PortName(hi,lo)=(%08Xh:%08Xh)\n", + *(u32*)&serialno[2], + *(u32*)&serialno[6], + *(u32*)&serialno[10], + *(u32*)&serialno[14]); + break; + + + case I2O_SNFORMAT_UNKNOWN: /* Unknown 0 */ + case I2O_SNFORMAT_UNKNOWN2: /* Unknown 0xff */ + default: + pos += sprintf(buff+pos, "Unknown data format (0x%02x)", + serialno[0]); + break; + } + + return pos; +} + +const char * i2o_get_connector_type(int conn) +{ + int idx = 16; + static char *i2o_connector_type[] = { + "OTHER", + "UNKNOWN", + "AUI", + "UTP", + "BNC", + "RJ45", + "STP DB9", + "FIBER MIC", + "APPLE AUI", + "MII", + "DB9", + "HSSDC", + "DUPLEX SC FIBER", + "DUPLEX ST FIBER", + "TNC/BNC", + "HW DEFAULT" + }; + + switch(conn) + { + case 0x00000000: + idx = 0; + break; + case 0x00000001: + idx = 1; + break; + case 0x00000002: + idx = 2; + break; + case 0x00000003: + idx = 3; + break; + case 0x00000004: + idx = 4; + break; + case 0x00000005: + idx = 5; + break; + case 0x00000006: + idx = 6; + break; + case 0x00000007: + idx = 7; + break; + case 0x00000008: + idx = 8; + break; + case 0x00000009: + idx = 9; + break; + case 0x0000000A: + idx = 10; + break; + case 0x0000000B: + idx = 11; + break; + case 0x0000000C: + idx = 12; + break; + case 0x0000000D: + idx = 13; + break; + case 0x0000000E: + idx = 14; + break; + case 0xFFFFFFFF: + idx = 15; + break; + } + + return i2o_connector_type[idx]; +} + + +const char * i2o_get_connection_type(int conn) +{ + int idx = 0; + static char *i2o_connection_type[] = { + "Unknown", + "AUI", + "10BASE5", + "FIORL", + "10BASE2", + "10BROAD36", + "10BASE-T", + "10BASE-FP", + "10BASE-FB", + "10BASE-FL", + "100BASE-TX", + "100BASE-FX", + "100BASE-T4", + "1000BASE-SX", + "1000BASE-LX", + "1000BASE-CX", + "1000BASE-T", + "100VG-ETHERNET", + "100VG-TOKEN RING", + "4MBIT TOKEN RING", + "16 Mb Token Ring", + "125 MBAUD FDDI", + "Point-to-point", + "Arbitrated loop", + "Public loop", + "Fabric", + "Emulation", + "Other", + "HW default" + }; + + switch(conn) + { + case I2O_LAN_UNKNOWN: + idx = 0; + break; + case I2O_LAN_AUI: + idx = 1; + break; + case I2O_LAN_10BASE5: + idx = 2; + break; + case I2O_LAN_FIORL: + idx = 3; + break; + case I2O_LAN_10BASE2: + idx = 4; + break; + case I2O_LAN_10BROAD36: + idx = 5; + break; + case I2O_LAN_10BASE_T: + idx = 6; + break; + case I2O_LAN_10BASE_FP: + idx = 7; + break; + case I2O_LAN_10BASE_FB: + idx = 8; + break; + case I2O_LAN_10BASE_FL: + idx = 9; + break; + case I2O_LAN_100BASE_TX: + idx = 10; + break; + case I2O_LAN_100BASE_FX: + idx = 11; + break; + case I2O_LAN_100BASE_T4: + idx = 12; + break; + case I2O_LAN_1000BASE_SX: + idx = 13; + break; + case I2O_LAN_1000BASE_LX: + idx = 14; + break; + case I2O_LAN_1000BASE_CX: + idx = 15; + break; + case I2O_LAN_1000BASE_T: + idx = 16; + break; + case I2O_LAN_100VG_ETHERNET: + idx = 17; + break; + case I2O_LAN_100VG_TR: + idx = 18; + break; + case I2O_LAN_4MBIT: + idx = 19; + break; + case I2O_LAN_16MBIT: + idx = 20; + break; + case I2O_LAN_125MBAUD: + idx = 21; + break; + case I2O_LAN_POINT_POINT: + idx = 22; + break; + case I2O_LAN_ARB_LOOP: + idx = 23; + break; + case I2O_LAN_PUBLIC_LOOP: + idx = 24; + break; + case I2O_LAN_FABRIC: + idx = 25; + break; + case I2O_LAN_EMULATION: + idx = 26; + break; + case I2O_LAN_OTHER: + idx = 27; + break; + case I2O_LAN_DEFAULT: + idx = 28; + break; + } + + return i2o_connection_type[idx]; +} + + +/* LAN group 0000h - Device info (scalar) */ +int i2o_proc_read_lan_dev_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[56]; + static u8 *work8 = (u8*)work32; + static u16 *work16 = (u16*)work32; + static u64 *work64 = (u64*)work32; + int token; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0000, -1, &work32, 56*4); + if (token < 0) { + len += i2o_report_query_status(buf+len, token, "0x0000 LAN Device Info"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "LAN Type : "); + switch (work16[0]) + { + case 0x0030: + len += sprintf(buf+len, "Ethernet, "); + break; + case 0x0040: + len += sprintf(buf+len, "100Base VG, "); + break; + case 0x0050: + len += sprintf(buf+len, "Token Ring, "); + break; + case 0x0060: + len += sprintf(buf+len, "FDDI, "); + break; + case 0x0070: + len += sprintf(buf+len, "Fibre Channel, "); + break; + default: + len += sprintf(buf+len, "Unknown type (0x%04x), ", work16[0]); + break; + } + + if (work16[1]&0x00000001) + len += sprintf(buf+len, "emulated LAN, "); + else + len += sprintf(buf+len, "physical LAN port, "); + + if (work16[1]&0x00000002) + len += sprintf(buf+len, "full duplex\n"); + else + len += sprintf(buf+len, "simplex\n"); + + len += sprintf(buf+len, "Address format : "); + switch(work8[4]) { + case 0x00: + len += sprintf(buf+len, "IEEE 48bit\n"); + break; + case 0x01: + len += sprintf(buf+len, "FC IEEE\n"); + break; + default: + len += sprintf(buf+len, "Unknown (0x%02x)\n", work8[4]); + break; + } + + len += sprintf(buf+len, "State : "); + switch(work8[5]) + { + case 0x00: + len += sprintf(buf+len, "Unknown\n"); + break; + case 0x01: + len += sprintf(buf+len, "Unclaimed\n"); + break; + case 0x02: + len += sprintf(buf+len, "Operational\n"); + break; + case 0x03: + len += sprintf(buf+len, "Suspended\n"); + break; + case 0x04: + len += sprintf(buf+len, "Resetting\n"); + break; + case 0x05: + len += sprintf(buf+len, "ERROR: "); + if(work16[3]&0x0001) + len += sprintf(buf+len, "TxCU inoperative "); + if(work16[3]&0x0002) + len += sprintf(buf+len, "RxCU inoperative "); + if(work16[3]&0x0004) + len += sprintf(buf+len, "Local mem alloc "); + len += sprintf(buf+len, "\n"); + break; + case 0x06: + len += sprintf(buf+len, "Operational no Rx\n"); + break; + case 0x07: + len += sprintf(buf+len, "Suspended no Rx\n"); + break; + default: + len += sprintf(buf+len, "Unspecified\n"); + break; + } + + len += sprintf(buf+len, "Min packet size : %d\n", work32[2]); + len += sprintf(buf+len, "Max packet size : %d\n", work32[3]); + len += sprintf(buf+len, "HW address : " + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + work8[16],work8[17],work8[18],work8[19], + work8[20],work8[21],work8[22],work8[23]); + + len += sprintf(buf+len, "Max Tx wire speed : %d bps\n", (int)work64[3]); + len += sprintf(buf+len, "Max Rx wire speed : %d bps\n", (int)work64[4]); + + len += sprintf(buf+len, "Min SDU packet size : 0x%08x\n", work32[10]); + len += sprintf(buf+len, "Max SDU packet size : 0x%08x\n", work32[11]); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0001h - MAC address table (scalar) */ +int i2o_proc_read_lan_mac_addr(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[48]; + static u8 *work8 = (u8*)work32; + int token; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0001, -1, &work32, 48*4); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0001 LAN MAC Address"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Active address : " + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + work8[0],work8[1],work8[2],work8[3], + work8[4],work8[5],work8[6],work8[7]); + len += sprintf(buf+len, "Current address : " + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + work8[8],work8[9],work8[10],work8[11], + work8[12],work8[13],work8[14],work8[15]); + len += sprintf(buf+len, "Functional address mask : " + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + work8[16],work8[17],work8[18],work8[19], + work8[20],work8[21],work8[22],work8[23]); + + len += sprintf(buf+len,"HW/DDM capabilities : 0x%08x\n", work32[7]); + len += sprintf(buf+len," [%s] Unicast packets supported\n", + (work32[7]&0x00000001)?"+":"-"); + len += sprintf(buf+len," [%s] Promiscuous mode supported\n", + (work32[7]&0x00000002)?"+":"-"); + len += sprintf(buf+len," [%s] Promiscuous multicast mode supported\n", + (work32[7]&0x00000004)?"+":"-"); + len += sprintf(buf+len," [%s] Broadcast reception disabling supported\n", + (work32[7]&0x00000100)?"+":"-"); + len += sprintf(buf+len," [%s] Multicast reception disabling supported\n", + (work32[7]&0x00000200)?"+":"-"); + len += sprintf(buf+len," [%s] Functional address disabling supported\n", + (work32[7]&0x00000400)?"+":"-"); + len += sprintf(buf+len," [%s] MAC reporting supported\n", + (work32[7]&0x00000800)?"+":"-"); + + len += sprintf(buf+len,"Filter mask : 0x%08x\n", work32[6]); + len += sprintf(buf+len," [%s] Unicast packets disable\n", + (work32[6]&0x00000001)?"+":"-"); + len += sprintf(buf+len," [%s] Promiscuous mode enable\n", + (work32[6]&0x00000002)?"+":"-"); + len += sprintf(buf+len," [%s] Promiscuous multicast mode enable\n", + (work32[6]&0x00000004)?"+":"-"); + len += sprintf(buf+len," [%s] Broadcast packets disable\n", + (work32[6]&0x00000100)?"+":"-"); + len += sprintf(buf+len," [%s] Multicast packets disable\n", + (work32[6]&0x00000200)?"+":"-"); + len += sprintf(buf+len," [%s] Functional address disable\n", + (work32[6]&0x00000400)?"+":"-"); + + if (work32[7]&0x00000800) { + len += sprintf(buf+len, " MAC reporting mode : "); + if (work32[6]&0x00000800) + len += sprintf(buf+len, "Pass only priority MAC packets to user\n"); + else if (work32[6]&0x00001000) + len += sprintf(buf+len, "Pass all MAC packets to user\n"); + else if (work32[6]&0x00001800) + len += sprintf(buf+len, "Pass all MAC packets (promiscuous) to user\n"); + else + len += sprintf(buf+len, "Do not pass MAC packets to user\n"); + } + len += sprintf(buf+len, "Number of multicast addresses : %d\n", work32[8]); + len += sprintf(buf+len, "Perfect filtering for max %d multicast addresses\n", + work32[9]); + len += sprintf(buf+len, "Imperfect filtering for max %d multicast addresses\n", + work32[10]); + + spin_unlock(&i2o_proc_lock); + + return len; +} + +/* LAN group 0002h - Multicast MAC address table (table) */ +int i2o_proc_read_lan_mcast_addr(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + u8 mc_addr[8]; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + u8 mc_addr[256][8]; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, 0x0002, -1, + NULL, 0, &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x002 LAN Multicast MAC Address"); + spin_unlock(&i2o_proc_lock); + return len; + } + + for (i = 0; i < result.row_count; i++) + { + memcpy(mc_addr, result.mc_addr[i], 8); + + len += sprintf(buf+len, "MC MAC address[%d]: " + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + i, mc_addr[0], mc_addr[1], mc_addr[2], + mc_addr[3], mc_addr[4], mc_addr[5], + mc_addr[6], mc_addr[7]); + } + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0003h - Batch Control (scalar) */ +int i2o_proc_read_lan_batch_control(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[9]; + int token; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0003, -1, &work32, 9*4); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0003 LAN Batch Control"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Batch mode "); + if (work32[0]&0x00000001) + len += sprintf(buf+len, "disabled"); + else + len += sprintf(buf+len, "enabled"); + if (work32[0]&0x00000002) + len += sprintf(buf+len, " (current setting)"); + if (work32[0]&0x00000004) + len += sprintf(buf+len, ", forced"); + else + len += sprintf(buf+len, ", toggle"); + len += sprintf(buf+len, "\n"); + + len += sprintf(buf+len, "Max Rx batch count : %d\n", work32[5]); + len += sprintf(buf+len, "Max Rx batch delay : %d\n", work32[6]); + len += sprintf(buf+len, "Max Tx batch delay : %d\n", work32[7]); + len += sprintf(buf+len, "Max Tx batch count : %d\n", work32[8]); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0004h - LAN Operation (scalar) */ +int i2o_proc_read_lan_operation(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[5]; + int token; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0004, -1, &work32, 20); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0004 LAN Operation"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Packet prepadding (32b words) : %d\n", work32[0]); + len += sprintf(buf+len, "Transmission error reporting : %s\n", + (work32[1]&1)?"on":"off"); + len += sprintf(buf+len, "Bad packet handling : %s\n", + (work32[1]&0x2)?"by host":"by DDM"); + len += sprintf(buf+len, "Packet orphan limit : %d\n", work32[2]); + + len += sprintf(buf+len, "Tx modes : 0x%08x\n", work32[3]); + len += sprintf(buf+len, " [%s] HW CRC suppression\n", + (work32[3]&0x00000004) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW IPv4 checksum\n", + (work32[3]&0x00000100) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW TCP checksum\n", + (work32[3]&0x00000200) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW UDP checksum\n", + (work32[3]&0x00000400) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW RSVP checksum\n", + (work32[3]&0x00000800) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW ICMP checksum\n", + (work32[3]&0x00001000) ? "+" : "-"); + len += sprintf(buf+len, " [%s] Loopback suppression enable\n", + (work32[3]&0x00002000) ? "+" : "-"); + + len += sprintf(buf+len, "Rx modes : 0x%08x\n", work32[4]); + len += sprintf(buf+len, " [%s] FCS in payload\n", + (work32[4]&0x00000004) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW IPv4 checksum validation\n", + (work32[4]&0x00000100) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW TCP checksum validation\n", + (work32[4]&0x00000200) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW UDP checksum validation\n", + (work32[4]&0x00000400) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW RSVP checksum validation\n", + (work32[4]&0x00000800) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW ICMP checksum validation\n", + (work32[4]&0x00001000) ? "+" : "-"); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0005h - Media operation (scalar) */ +int i2o_proc_read_lan_media_operation(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + + struct + { + u32 connector_type; + u32 connection_type; + u64 current_tx_wire_speed; + u64 current_rx_wire_speed; + u8 duplex_mode; + u8 link_status; + u8 reserved; + u8 duplex_mode_target; + u32 connector_type_target; + u32 connection_type_target; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0005, -1, &result, sizeof(result)); + if (token < 0) { + len += i2o_report_query_status(buf+len, token, "0x0005 LAN Media Operation"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Connector type : %s\n", + i2o_get_connector_type(result.connector_type)); + len += sprintf(buf+len, "Connection type : %s\n", + i2o_get_connection_type(result.connection_type)); + + len += sprintf(buf+len, "Current Tx wire speed : %d bps\n", (int)result.current_tx_wire_speed); + len += sprintf(buf+len, "Current Rx wire speed : %d bps\n", (int)result.current_rx_wire_speed); + len += sprintf(buf+len, "Duplex mode : %s duplex\n", + (result.duplex_mode)?"Full":"Half"); + + len += sprintf(buf+len, "Link status : "); + switch (result.link_status) + { + case 0x00: + len += sprintf(buf+len, "Unknown\n"); + break; + case 0x01: + len += sprintf(buf+len, "Normal\n"); + break; + case 0x02: + len += sprintf(buf+len, "Failure\n"); + break; + case 0x03: + len += sprintf(buf+len, "Reset\n"); + break; + default: + len += sprintf(buf+len, "Unspecified\n"); + } + + len += sprintf(buf+len, "Duplex mode target : "); + switch (result.duplex_mode_target){ + case 0: + len += sprintf(buf+len, "Half duplex\n"); + break; + case 1: + len += sprintf(buf+len, "Full duplex\n"); + break; + default: + len += sprintf(buf+len, "\n"); + } + + len += sprintf(buf+len, "Connector type target : %s\n", + i2o_get_connector_type(result.connector_type_target)); + len += sprintf(buf+len, "Connection type target : %s\n", + i2o_get_connection_type(result.connection_type_target)); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0006h - Alternate address (table) (optional) */ +int i2o_proc_read_lan_alt_addr(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + u8 alt_addr[8]; + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + u8 alt_addr[256][8]; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, + 0x0006, -1, NULL, 0, &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token, "0x0006 LAN Alternate Address (optional)"); + spin_unlock(&i2o_proc_lock); + return len; + } + + for (i=0; i < result.row_count; i++) + { + memcpy(alt_addr,result.alt_addr[i],8); + len += sprintf(buf+len, "Alternate address[%d]: " + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + i, alt_addr[0], alt_addr[1], alt_addr[2], + alt_addr[3], alt_addr[4], alt_addr[5], + alt_addr[6], alt_addr[7]); + } + + spin_unlock(&i2o_proc_lock); + return len; +} + + +/* LAN group 0007h - Transmit info (scalar) */ +int i2o_proc_read_lan_tx_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[8]; + int token; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0007, -1, &work32, 8*4); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0007 LAN Transmit Info"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Tx Max SG elements per packet : %d\n", work32[0]); + len += sprintf(buf+len, "Tx Max SG elements per chain : %d\n", work32[1]); + len += sprintf(buf+len, "Tx Max outstanding packets : %d\n", work32[2]); + len += sprintf(buf+len, "Tx Max packets per request : %d\n", work32[3]); + + len += sprintf(buf+len, "Tx modes : 0x%08x\n", work32[4]); + len += sprintf(buf+len, " [%s] No DA in SGL\n", + (work32[4]&0x00000002) ? "+" : "-"); + len += sprintf(buf+len, " [%s] CRC suppression\n", + (work32[4]&0x00000004) ? "+" : "-"); + len += sprintf(buf+len, " [%s] MAC insertion\n", + (work32[4]&0x00000010) ? "+" : "-"); + len += sprintf(buf+len, " [%s] RIF insertion\n", + (work32[4]&0x00000020) ? "+" : "-"); + len += sprintf(buf+len, " [%s] IPv4 checksum generation\n", + (work32[4]&0x00000100) ? "+" : "-"); + len += sprintf(buf+len, " [%s] TCP checksum generation\n", + (work32[4]&0x00000200) ? "+" : "-"); + len += sprintf(buf+len, " [%s] UDP checksum generation\n", + (work32[4]&0x00000400) ? "+" : "-"); + len += sprintf(buf+len, " [%s] RSVP checksum generation\n", + (work32[4]&0x00000800) ? "+" : "-"); + len += sprintf(buf+len, " [%s] ICMP checksum generation\n", + (work32[4]&0x00001000) ? "+" : "-"); + len += sprintf(buf+len, " [%s] Loopback enabled\n", + (work32[4]&0x00010000) ? "+" : "-"); + len += sprintf(buf+len, " [%s] Loopback suppression enabled\n", + (work32[4]&0x00020000) ? "+" : "-"); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0008h - Receive info (scalar) */ +int i2o_proc_read_lan_rx_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[8]; + int token; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0008, -1, &work32, 8*4); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0008 LAN Receive Info"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf ,"Rx Max size of chain element : %d\n", work32[0]); + len += sprintf(buf+len, "Rx Max Buckets : %d\n", work32[1]); + len += sprintf(buf+len, "Rx Max Buckets in Reply : %d\n", work32[3]); + len += sprintf(buf+len, "Rx Max Packets in Bucket : %d\n", work32[4]); + len += sprintf(buf+len, "Rx Max Buckets in Post : %d\n", work32[5]); + + len += sprintf(buf+len, "Rx Modes : 0x%08x\n", work32[2]); + len += sprintf(buf+len, " [%s] FCS reception\n", + (work32[2]&0x00000004) ? "+" : "-"); + len += sprintf(buf+len, " [%s] IPv4 checksum validation \n", + (work32[2]&0x00000100) ? "+" : "-"); + len += sprintf(buf+len, " [%s] TCP checksum validation \n", + (work32[2]&0x00000200) ? "+" : "-"); + len += sprintf(buf+len, " [%s] UDP checksum validation \n", + (work32[2]&0x00000400) ? "+" : "-"); + len += sprintf(buf+len, " [%s] RSVP checksum validation \n", + (work32[2]&0x00000800) ? "+" : "-"); + len += sprintf(buf+len, " [%s] ICMP checksum validation \n", + (work32[2]&0x00001000) ? "+" : "-"); + + spin_unlock(&i2o_proc_lock); + return len; +} + +static int i2o_report_opt_field(char *buf, char *field_name, + int field_nbr, int supp_fields, u64 *value) +{ + if (supp_fields & (1 << field_nbr)) + return sprintf(buf, "%-24s : " FMT_U64_HEX "\n", field_name, U64_VAL(value)); + else + return sprintf(buf, "%-24s : Not supported\n", field_name); +} + +/* LAN group 0100h - LAN Historical statistics (scalar) */ +/* LAN group 0180h - Supported Optional Historical Statistics (scalar) */ +/* LAN group 0182h - Optional Non Media Specific Transmit Historical Statistics (scalar) */ +/* LAN group 0183h - Optional Non Media Specific Receive Historical Statistics (scalar) */ + +int i2o_proc_read_lan_hist_stats(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + + struct + { + u64 tx_packets; + u64 tx_bytes; + u64 rx_packets; + u64 rx_bytes; + u64 tx_errors; + u64 rx_errors; + u64 rx_dropped; + u64 adapter_resets; + u64 adapter_suspends; + } stats; // 0x0100 + + static u64 supp_groups[4]; // 0x0180 + + struct + { + u64 tx_retries; + u64 tx_directed_bytes; + u64 tx_directed_packets; + u64 tx_multicast_bytes; + u64 tx_multicast_packets; + u64 tx_broadcast_bytes; + u64 tx_broadcast_packets; + u64 tx_group_addr_packets; + u64 tx_short_packets; + } tx_stats; // 0x0182 + + struct + { + u64 rx_crc_errors; + u64 rx_directed_bytes; + u64 rx_directed_packets; + u64 rx_multicast_bytes; + u64 rx_multicast_packets; + u64 rx_broadcast_bytes; + u64 rx_broadcast_packets; + u64 rx_group_addr_packets; + u64 rx_short_packets; + u64 rx_long_packets; + u64 rx_runt_packets; + } rx_stats; // 0x0183 + + struct + { + u64 ipv4_generate; + u64 ipv4_validate_success; + u64 ipv4_validate_errors; + u64 tcp_generate; + u64 tcp_validate_success; + u64 tcp_validate_errors; + u64 udp_generate; + u64 udp_validate_success; + u64 udp_validate_errors; + u64 rsvp_generate; + u64 rsvp_validate_success; + u64 rsvp_validate_errors; + u64 icmp_generate; + u64 icmp_validate_success; + u64 icmp_validate_errors; + } chksum_stats; // 0x0184 + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0100, -1, &stats, sizeof(stats)); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x100 LAN Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "Tx packets : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_packets)); + len += sprintf(buf+len, "Tx bytes : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_bytes)); + len += sprintf(buf+len, "Rx packets : " FMT_U64_HEX "\n", + U64_VAL(&stats.rx_packets)); + len += sprintf(buf+len, "Rx bytes : " FMT_U64_HEX "\n", + U64_VAL(&stats.rx_bytes)); + len += sprintf(buf+len, "Tx errors : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_errors)); + len += sprintf(buf+len, "Rx errors : " FMT_U64_HEX "\n", + U64_VAL(&stats.rx_errors)); + len += sprintf(buf+len, "Rx dropped : " FMT_U64_HEX "\n", + U64_VAL(&stats.rx_dropped)); + len += sprintf(buf+len, "Adapter resets : " FMT_U64_HEX "\n", + U64_VAL(&stats.adapter_resets)); + len += sprintf(buf+len, "Adapter suspends : " FMT_U64_HEX "\n", + U64_VAL(&stats.adapter_suspends)); + + /* Optional statistics follows */ + /* Get 0x0180 to see which optional groups/fields are supported */ + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0180, -1, &supp_groups, sizeof(supp_groups)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token, "0x180 LAN Supported Optional Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + if (supp_groups[1]) /* 0x0182 */ + { + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0182, -1, &tx_stats, sizeof(tx_stats)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x182 LAN Optional Tx Historical Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "==== Optional TX statistics (group 0182h)\n"); + + len += i2o_report_opt_field(buf+len, "Tx RetryCount", + 0, supp_groups[1], &tx_stats.tx_retries); + len += i2o_report_opt_field(buf+len, "Tx DirectedBytes", + 1, supp_groups[1], &tx_stats.tx_directed_bytes); + len += i2o_report_opt_field(buf+len, "Tx DirectedPackets", + 2, supp_groups[1], &tx_stats.tx_directed_packets); + len += i2o_report_opt_field(buf+len, "Tx MulticastBytes", + 3, supp_groups[1], &tx_stats.tx_multicast_bytes); + len += i2o_report_opt_field(buf+len, "Tx MulticastPackets", + 4, supp_groups[1], &tx_stats.tx_multicast_packets); + len += i2o_report_opt_field(buf+len, "Tx BroadcastBytes", + 5, supp_groups[1], &tx_stats.tx_broadcast_bytes); + len += i2o_report_opt_field(buf+len, "Tx BroadcastPackets", + 6, supp_groups[1], &tx_stats.tx_broadcast_packets); + len += i2o_report_opt_field(buf+len, "Tx TotalGroupAddrPackets", + 7, supp_groups[1], &tx_stats.tx_group_addr_packets); + len += i2o_report_opt_field(buf+len, "Tx TotalPacketsTooShort", + 8, supp_groups[1], &tx_stats.tx_short_packets); + } + + if (supp_groups[2]) /* 0x0183 */ + { + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0183, -1, &rx_stats, sizeof(rx_stats)); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x183 LAN Optional Rx Historical Stats"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "==== Optional RX statistics (group 0183h)\n"); + + len += i2o_report_opt_field(buf+len, "Rx CRCErrorCount", + 0, supp_groups[2], &rx_stats.rx_crc_errors); + len += i2o_report_opt_field(buf+len, "Rx DirectedBytes", + 1, supp_groups[2], &rx_stats.rx_directed_bytes); + len += i2o_report_opt_field(buf+len, "Rx DirectedPackets", + 2, supp_groups[2], &rx_stats.rx_directed_packets); + len += i2o_report_opt_field(buf+len, "Rx MulticastBytes", + 3, supp_groups[2], &rx_stats.rx_multicast_bytes); + len += i2o_report_opt_field(buf+len, "Rx MulticastPackets", + 4, supp_groups[2], &rx_stats.rx_multicast_packets); + len += i2o_report_opt_field(buf+len, "Rx BroadcastBytes", + 5, supp_groups[2], &rx_stats.rx_broadcast_bytes); + len += i2o_report_opt_field(buf+len, "Rx BroadcastPackets", + 6, supp_groups[2], &rx_stats.rx_broadcast_packets); + len += i2o_report_opt_field(buf+len, "Rx TotalGroupAddrPackets", + 7, supp_groups[2], &rx_stats.rx_group_addr_packets); + len += i2o_report_opt_field(buf+len, "Rx TotalPacketsTooShort", + 8, supp_groups[2], &rx_stats.rx_short_packets); + len += i2o_report_opt_field(buf+len, "Rx TotalPacketsTooLong", + 9, supp_groups[2], &rx_stats.rx_long_packets); + len += i2o_report_opt_field(buf+len, "Rx TotalPacketsRunt", + 10, supp_groups[2], &rx_stats.rx_runt_packets); + } + + if (supp_groups[3]) /* 0x0184 */ + { + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0184, -1, &chksum_stats, sizeof(chksum_stats)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x184 LAN Optional Chksum Historical Stats"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "==== Optional CHKSUM statistics (group 0x0184)\n"); + + len += i2o_report_opt_field(buf+len, "IPv4 Generate", + 0, supp_groups[3], &chksum_stats.ipv4_generate); + len += i2o_report_opt_field(buf+len, "IPv4 ValidateSuccess", + 1, supp_groups[3], &chksum_stats.ipv4_validate_success); + len += i2o_report_opt_field(buf+len, "IPv4 ValidateError", + 2, supp_groups[3], &chksum_stats.ipv4_validate_errors); + len += i2o_report_opt_field(buf+len, "TCP Generate", + 3, supp_groups[3], &chksum_stats.tcp_generate); + len += i2o_report_opt_field(buf+len, "TCP ValidateSuccess", + 4, supp_groups[3], &chksum_stats.tcp_validate_success); + len += i2o_report_opt_field(buf+len, "TCP ValidateError", + 5, supp_groups[3], &chksum_stats.tcp_validate_errors); + len += i2o_report_opt_field(buf+len, "UDP Generate", + 6, supp_groups[3], &chksum_stats.udp_generate); + len += i2o_report_opt_field(buf+len, "UDP ValidateSuccess", + 7, supp_groups[3], &chksum_stats.udp_validate_success); + len += i2o_report_opt_field(buf+len, "UDP ValidateError", + 8, supp_groups[3], &chksum_stats.udp_validate_errors); + len += i2o_report_opt_field(buf+len, "RSVP Generate", + 9, supp_groups[3], &chksum_stats.rsvp_generate); + len += i2o_report_opt_field(buf+len, "RSVP ValidateSuccess", + 10, supp_groups[3], &chksum_stats.rsvp_validate_success); + len += i2o_report_opt_field(buf+len, "RSVP ValidateError", + 11, supp_groups[3], &chksum_stats.rsvp_validate_errors); + len += i2o_report_opt_field(buf+len, "ICMP Generate", + 12, supp_groups[3], &chksum_stats.icmp_generate); + len += i2o_report_opt_field(buf+len, "ICMP ValidateSuccess", + 13, supp_groups[3], &chksum_stats.icmp_validate_success); + len += i2o_report_opt_field(buf+len, "ICMP ValidateError", + 14, supp_groups[3], &chksum_stats.icmp_validate_errors); + } + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0200h - Required Ethernet Statistics (scalar) */ +/* LAN group 0280h - Optional Ethernet Statistics Supported (scalar) */ +/* LAN group 0281h - Optional Ethernet Historical Statistics (scalar) */ +int i2o_proc_read_lan_eth_stats(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + + struct + { + u64 rx_align_errors; + u64 tx_one_collisions; + u64 tx_multiple_collisions; + u64 tx_deferred; + u64 tx_late_collisions; + u64 tx_max_collisions; + u64 tx_carrier_lost; + u64 tx_excessive_deferrals; + } stats; + + static u64 supp_fields; + struct + { + u64 rx_overrun; + u64 tx_underrun; + u64 tx_heartbeat_failure; + } hist_stats; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0200, -1, &stats, sizeof(stats)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0200 LAN Ethernet Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "Rx alignment errors : " FMT_U64_HEX "\n", + U64_VAL(&stats.rx_align_errors)); + len += sprintf(buf+len, "Tx one collisions : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_one_collisions)); + len += sprintf(buf+len, "Tx multicollisions : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_multiple_collisions)); + len += sprintf(buf+len, "Tx deferred : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_deferred)); + len += sprintf(buf+len, "Tx late collisions : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_late_collisions)); + len += sprintf(buf+len, "Tx max collisions : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_max_collisions)); + len += sprintf(buf+len, "Tx carrier lost : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_carrier_lost)); + len += sprintf(buf+len, "Tx excessive deferrals : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_excessive_deferrals)); + + /* Optional Ethernet statistics follows */ + /* Get 0x0280 to see which optional fields are supported */ + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0280, -1, &supp_fields, sizeof(supp_fields)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0280 LAN Supported Optional Ethernet Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + if (supp_fields) /* 0x0281 */ + { + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0281, -1, &stats, sizeof(stats)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0281 LAN Optional Ethernet Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "==== Optional ETHERNET statistics (group 0x0281)\n"); + + len += i2o_report_opt_field(buf+len, "Rx Overrun", + 0, supp_fields, &hist_stats.rx_overrun); + len += i2o_report_opt_field(buf+len, "Tx Underrun", + 1, supp_fields, &hist_stats.tx_underrun); + len += i2o_report_opt_field(buf+len, "Tx HeartbeatFailure", + 2, supp_fields, &hist_stats.tx_heartbeat_failure); + } + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0300h - Required Token Ring Statistics (scalar) */ +/* LAN group 0380h, 0381h - Optional Statistics not yet defined (TODO) */ +int i2o_proc_read_lan_tr_stats(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u64 work64[13]; + int token; + + static char *ring_status[] = + { + "", + "", + "", + "", + "", + "Ring Recovery", + "Single Station", + "Counter Overflow", + "Remove Received", + "", + "Auto-Removal Error 1", + "Lobe Wire Fault", + "Transmit Beacon", + "Soft Error", + "Hard Error", + "Signal Loss" + }; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0300, -1, &work64, sizeof(work64)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0300 Token Ring Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "LineErrors : " FMT_U64_HEX "\n", + U64_VAL(&work64[0])); + len += sprintf(buf+len, "LostFrames : " FMT_U64_HEX "\n", + U64_VAL(&work64[1])); + len += sprintf(buf+len, "ACError : " FMT_U64_HEX "\n", + U64_VAL(&work64[2])); + len += sprintf(buf+len, "TxAbortDelimiter : " FMT_U64_HEX "\n", + U64_VAL(&work64[3])); + len += sprintf(buf+len, "BursErrors : " FMT_U64_HEX "\n", + U64_VAL(&work64[4])); + len += sprintf(buf+len, "FrameCopiedErrors : " FMT_U64_HEX "\n", + U64_VAL(&work64[5])); + len += sprintf(buf+len, "FrequencyErrors : " FMT_U64_HEX "\n", + U64_VAL(&work64[6])); + len += sprintf(buf+len, "InternalErrors : " FMT_U64_HEX "\n", + U64_VAL(&work64[7])); + len += sprintf(buf+len, "LastRingStatus : %s\n", ring_status[work64[8]]); + len += sprintf(buf+len, "TokenError : " FMT_U64_HEX "\n", + U64_VAL(&work64[9])); + len += sprintf(buf+len, "UpstreamNodeAddress : " FMT_U64_HEX "\n", + U64_VAL(&work64[10])); + len += sprintf(buf+len, "LastRingID : " FMT_U64_HEX "\n", + U64_VAL(&work64[11])); + len += sprintf(buf+len, "LastBeaconType : " FMT_U64_HEX "\n", + U64_VAL(&work64[12])); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0400h - Required FDDI Statistics (scalar) */ +/* LAN group 0480h, 0481h - Optional Statistics, not yet defined (TODO) */ +int i2o_proc_read_lan_fddi_stats(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u64 work64[11]; + int token; + + static char *conf_state[] = + { + "Isolated", + "Local a", + "Local b", + "Local ab", + "Local s", + "Wrap a", + "Wrap b", + "Wrap ab", + "Wrap s", + "C-Wrap a", + "C-Wrap b", + "C-Wrap s", + "Through", + }; + + static char *ring_state[] = + { + "Isolated", + "Non-op", + "Rind-op", + "Detect", + "Non-op-Dup", + "Ring-op-Dup", + "Directed", + "Trace" + }; + + static char *link_state[] = + { + "Off", + "Break", + "Trace", + "Connect", + "Next", + "Signal", + "Join", + "Verify", + "Active", + "Maintenance" + }; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0400, -1, &work64, sizeof(work64)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0400 FDDI Required Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "ConfigurationState : %s\n", conf_state[work64[0]]); + len += sprintf(buf+len, "UpstreamNode : " FMT_U64_HEX "\n", + U64_VAL(&work64[1])); + len += sprintf(buf+len, "DownStreamNode : " FMT_U64_HEX "\n", + U64_VAL(&work64[2])); + len += sprintf(buf+len, "FrameErrors : " FMT_U64_HEX "\n", + U64_VAL(&work64[3])); + len += sprintf(buf+len, "FramesLost : " FMT_U64_HEX "\n", + U64_VAL(&work64[4])); + len += sprintf(buf+len, "RingMgmtState : %s\n", ring_state[work64[5]]); + len += sprintf(buf+len, "LCTFailures : " FMT_U64_HEX "\n", + U64_VAL(&work64[6])); + len += sprintf(buf+len, "LEMRejects : " FMT_U64_HEX "\n", + U64_VAL(&work64[7])); + len += sprintf(buf+len, "LEMCount : " FMT_U64_HEX "\n", + U64_VAL(&work64[8])); + len += sprintf(buf+len, "LConnectionState : %s\n", + link_state[work64[9]]); + + spin_unlock(&i2o_proc_lock); + return len; +} + +static int i2o_proc_create_entries(void *data, i2o_proc_entry *pentry, + struct proc_dir_entry *parent) +{ + struct proc_dir_entry *ent; + + while(pentry->name != NULL) + { + ent = create_proc_entry(pentry->name, pentry->mode, parent); + if(!ent) return -1; + + ent->data = data; + ent->read_proc = pentry->read_proc; + ent->write_proc = pentry->write_proc; + ent->nlink = 1; + + pentry++; + } + + return 0; +} + +static void i2o_proc_remove_entries(i2o_proc_entry *pentry, + struct proc_dir_entry *parent) +{ + while(pentry->name != NULL) + { + remove_proc_entry(pentry->name, parent); + pentry++; + } +} + +static int i2o_proc_add_controller(struct i2o_controller *pctrl, + struct proc_dir_entry *root ) +{ + struct proc_dir_entry *dir, *dir1; + struct i2o_device *dev; + char buff[10]; + + sprintf(buff, "iop%d", pctrl->unit); + + dir = proc_mkdir(buff, root); + if(!dir) + return -1; + + pctrl->proc_entry = dir; + + i2o_proc_create_entries(pctrl, generic_iop_entries, dir); + + for(dev = pctrl->devices; dev; dev = dev->next) + { + sprintf(buff, "%0#5x", dev->lct_data.tid); + + dir1 = proc_mkdir(buff, dir); + dev->proc_entry = dir1; + + if(!dir1) + printk(KERN_INFO "i2o_proc: Could not allocate proc dir\n"); + + i2o_proc_add_device(dev, dir1); + } + + return 0; +} + +void i2o_proc_new_dev(struct i2o_controller *c, struct i2o_device *d) +{ + char buff[10]; + +#ifdef DRIVERDEBUG + printk(KERN_INFO "Adding new device to /proc/i2o/iop%d\n", c->unit); +#endif + sprintf(buff, "%0#5x", d->lct_data.tid); + + d->proc_entry = proc_mkdir(buff, c->proc_entry); + + if(!d->proc_entry) + { + printk(KERN_WARNING "i2o: Could not allocate procdir!\n"); + return; + } + + i2o_proc_add_device(d, d->proc_entry); +} + +void i2o_proc_add_device(struct i2o_device *dev, struct proc_dir_entry *dir) +{ + i2o_proc_create_entries(dev, generic_dev_entries, dir); + + /* Inform core that we want updates about this device's status */ + i2o_device_notify_on(dev, &i2o_proc_handler); + switch(dev->lct_data.class_id) + { + case I2O_CLASS_SCSI_PERIPHERAL: + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + i2o_proc_create_entries(dev, rbs_dev_entries, dir); + break; + case I2O_CLASS_LAN: + i2o_proc_create_entries(dev, lan_entries, dir); + switch(dev->lct_data.sub_class) + { + case I2O_LAN_ETHERNET: + i2o_proc_create_entries(dev, lan_eth_entries, dir); + break; + case I2O_LAN_FDDI: + i2o_proc_create_entries(dev, lan_fddi_entries, dir); + break; + case I2O_LAN_TR: + i2o_proc_create_entries(dev, lan_tr_entries, dir); + break; + default: + break; + } + break; + default: + break; + } +} + +static void i2o_proc_remove_controller(struct i2o_controller *pctrl, + struct proc_dir_entry *parent) +{ + char buff[10]; + struct i2o_device *dev; + + /* Remove unused device entries */ + for(dev=pctrl->devices; dev; dev=dev->next) + i2o_proc_remove_device(dev); + + if(!atomic_read(&pctrl->proc_entry->count)) + { + sprintf(buff, "iop%d", pctrl->unit); + + i2o_proc_remove_entries(generic_iop_entries, pctrl->proc_entry); + + remove_proc_entry(buff, parent); + pctrl->proc_entry = NULL; + } +} + +void i2o_proc_remove_device(struct i2o_device *dev) +{ + struct proc_dir_entry *de=dev->proc_entry; + char dev_id[10]; + + sprintf(dev_id, "%0#5x", dev->lct_data.tid); + + i2o_device_notify_off(dev, &i2o_proc_handler); + /* Would it be safe to remove _files_ even if they are in use? */ + if((de) && (!atomic_read(&de->count))) + { + i2o_proc_remove_entries(generic_dev_entries, de); + switch(dev->lct_data.class_id) + { + case I2O_CLASS_SCSI_PERIPHERAL: + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + i2o_proc_remove_entries(rbs_dev_entries, de); + break; + case I2O_CLASS_LAN: + { + i2o_proc_remove_entries(lan_entries, de); + switch(dev->lct_data.sub_class) + { + case I2O_LAN_ETHERNET: + i2o_proc_remove_entries(lan_eth_entries, de); + break; + case I2O_LAN_FDDI: + i2o_proc_remove_entries(lan_fddi_entries, de); + break; + case I2O_LAN_TR: + i2o_proc_remove_entries(lan_tr_entries, de); + break; + } + } + remove_proc_entry(dev_id, dev->controller->proc_entry); + } + } +} + +void i2o_proc_dev_del(struct i2o_controller *c, struct i2o_device *d) +{ +#ifdef DRIVERDEBUG + printk(KERN_INFO "Deleting device %d from iop%d\n", + d->lct_data.tid, c->unit); +#endif + + i2o_proc_remove_device(d); +} + +static int create_i2o_procfs(void) +{ + struct i2o_controller *pctrl = NULL; + int i; + + i2o_proc_dir_root = proc_mkdir("i2o", 0); + if(!i2o_proc_dir_root) + return -1; + + for(i = 0; i < MAX_I2O_CONTROLLERS; i++) + { + pctrl = i2o_find_controller(i); + if(pctrl) + { + i2o_proc_add_controller(pctrl, i2o_proc_dir_root); + i2o_unlock_controller(pctrl); + } + }; + + return 0; +} + +static int __exit destroy_i2o_procfs(void) +{ + struct i2o_controller *pctrl = NULL; + int i; + + for(i = 0; i < MAX_I2O_CONTROLLERS; i++) + { + pctrl = i2o_find_controller(i); + if(pctrl) + { + i2o_proc_remove_controller(pctrl, i2o_proc_dir_root); + i2o_unlock_controller(pctrl); + } + } + + if(!atomic_read(&i2o_proc_dir_root->count)) + remove_proc_entry("i2o", 0); + else + return -1; + + return 0; +} + +int __init i2o_proc_init(void) +{ + if (i2o_install_handler(&i2o_proc_handler) < 0) + { + printk(KERN_ERR "i2o_proc: Unable to install PROC handler.\n"); + return 0; + } + + if(create_i2o_procfs()) + return -EBUSY; + + return 0; +} + +MODULE_AUTHOR("Deepak Saxena"); +MODULE_DESCRIPTION("I2O procfs Handler"); +MODULE_LICENSE("GPL"); + +static void __exit i2o_proc_exit(void) +{ + destroy_i2o_procfs(); + i2o_remove_handler(&i2o_proc_handler); +} + +#ifdef MODULE +module_init(i2o_proc_init); +#endif +module_exit(i2o_proc_exit); + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_scsi.c linux.ac/drivers/message/i2o/i2o_scsi.c --- linux.vanilla/drivers/message/i2o/i2o_scsi.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_scsi.c Wed Oct 10 01:47:56 2001 @@ -0,0 +1,923 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Complications for I2O scsi + * + * o Each (bus,lun) is a logical device in I2O. We keep a map + * table. We spoof failed selection for unmapped units + * o Request sense buffers can come back for free. + * o Scatter gather is a bit dynamic. We have to investigate at + * setup time. + * o Some of our resources are dynamically shared. The i2o core + * needs a message reservation protocol to avoid swap v net + * deadlocking. We need to back off queue requests. + * + * In general the firmware wants to help. Where its help isn't performance + * useful we just ignore the aid. Its not worth the code in truth. + * + * Fixes: + * Steve Ralston : Scatter gather now works + * + * To Do + * 64bit cleanups + * Fix the resource management problems. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../scsi/scsi.h" +#include "../../scsi/hosts.h" +#include "../../scsi/sd.h" +#include "i2o_scsi.h" + +#define VERSION_STRING "Version 0.0.1" + +#define dprintk(x) + +#define MAXHOSTS 32 + +struct i2o_scsi_host +{ + struct i2o_controller *controller; + s16 task[16][8]; /* Allow 16 devices for now */ + unsigned long tagclock[16][8]; /* Tag clock for queueing */ + s16 bus_task; /* The adapter TID */ +}; + +static int scsi_context; +static int lun_done; +static int i2o_scsi_hosts; + +static u32 *retry[32]; +static struct i2o_controller *retry_ctrl[32]; +static struct timer_list retry_timer; +static int retry_ct = 0; + +static atomic_t queue_depth; + +/* + * SG Chain buffer support... + */ + +#define SG_MAX_FRAGS 64 + +/* + * FIXME: we should allocate one of these per bus we find as we + * locate them not in a lump at boot. + */ + +typedef struct _chain_buf +{ + u32 sg_flags_cnt[SG_MAX_FRAGS]; + u32 sg_buf[SG_MAX_FRAGS]; +} chain_buf; + +#define SG_CHAIN_BUF_SZ sizeof(chain_buf) + +#define SG_MAX_BUFS (i2o_num_controllers * I2O_SCSI_CAN_QUEUE) +#define SG_CHAIN_POOL_SZ (SG_MAX_BUFS * SG_CHAIN_BUF_SZ) + +static int max_sg_len = 0; +static chain_buf *sg_chain_pool = NULL; +static int sg_chain_tag = 0; +static int sg_max_frags = SG_MAX_FRAGS; + +/* + * Retry congested frames. This actually needs pushing down into + * i2o core. We should only bother the OSM with this when we can't + * queue and retry the frame. Or perhaps we should call the OSM + * and its default handler should be this in the core, and this + * call a 2nd "I give up" handler in the OSM ? + */ + +static void i2o_retry_run(unsigned long f) +{ + int i; + unsigned long flags; + + save_flags(flags); + cli(); + + for(i=0;i>12)&0xFFF, + m[1]&0xFFF, + m[1]>>24); + printk("Failure Code %d.\n", m[4]>>24); + if(m[4]&(1<<16)) + printk("Format error.\n"); + if(m[4]&(1<<17)) + printk("Path error.\n"); + if(m[4]&(1<<18)) + printk("Path State.\n"); + if(m[4]&(1<<18)) + printk("Congestion.\n"); + + m=(u32 *)bus_to_virt(m[7]); + printk("Failing message is %p.\n", m); + + if((m[4]&(1<<18)) && retry_ct < 32) + { + retry_ctrl[retry_ct]=c; + retry[retry_ct]=m; + if(!retry_ct++) + { + retry_timer.expires=jiffies+1; + add_timer(&retry_timer); + } + } + else + { + /* Create a scsi error for this */ + current_command = (Scsi_Cmnd *)m[3]; + printk("Aborted %ld\n", current_command->serial_number); + + spin_lock_irq(&io_request_lock); + current_command->result = DID_ERROR << 16; + current_command->scsi_done(current_command); + spin_unlock_irq(&io_request_lock); + + /* Now flush the message by making it a NOP */ + m[0]&=0x00FFFFFF; + m[0]|=(I2O_CMD_UTIL_NOP)<<24; + i2o_post_message(c,virt_to_bus(m)); + } + return; + } + + prefetchw(&queue_depth); + + + /* + * Low byte is device status, next is adapter status, + * (then one byte reserved), then request status. + */ + ds=(u8)m[4]; + as=(u8)(m[4]>>8); + st=(u8)(m[4]>>24); + + dprintk(("i2o got a scsi reply %08X: ", m[0])); + dprintk(("m[2]=%08X: ", m[2])); + dprintk(("m[4]=%08X\n", m[4])); + + if(m[2]&0x80000000) + { + if(m[2]&0x40000000) + { + dprintk(("Event.\n")); + lun_done=1; + return; + } + printk(KERN_ERR "i2o_scsi: bus reset reply.\n"); + return; + } + + current_command = (Scsi_Cmnd *)m[3]; + + /* + * Is this a control request coming back - eg an abort ? + */ + + if(current_command==NULL) + { + if(st) + dprintk(("SCSI abort: %08X", m[4])); + dprintk(("SCSI abort completed.\n")); + return; + } + + dprintk(("Completed %ld\n", current_command->serial_number)); + + atomic_dec(&queue_depth); + + if(st == 0x06) + { + if(m[5] < current_command->underflow) + { + int i; + printk(KERN_ERR "SCSI: underflow 0x%08X 0x%08X\n", + m[5], current_command->underflow); + printk("Cmd: "); + for(i=0;i<15;i++) + printk("%02X ", current_command->cmnd[i]); + printk(".\n"); + } + else st=0; + } + + if(st) + { + /* An error has occurred */ + + dprintk((KERN_DEBUG "SCSI error %08X", m[4])); + + if (as == 0x0E) + /* SCSI Reset */ + current_command->result = DID_RESET << 16; + else if (as == 0x0F) + current_command->result = DID_PARITY << 16; + else + current_command->result = DID_ERROR << 16; + } + else + /* + * It worked maybe ? + */ + current_command->result = DID_OK << 16 | ds; + spin_lock(&io_request_lock); + current_command->scsi_done(current_command); + spin_unlock(&io_request_lock); + return; +} + +struct i2o_handler i2o_scsi_handler= +{ + i2o_scsi_reply, + NULL, + NULL, + NULL, + "I2O SCSI OSM", + 0, + I2O_CLASS_SCSI_PERIPHERAL +}; + +static int i2o_find_lun(struct i2o_controller *c, struct i2o_device *d, int *target, int *lun) +{ + u8 reply[8]; + + if(i2o_query_scalar(c, d->lct_data.tid, 0, 3, reply, 4)<0) + return -1; + + *target=reply[0]; + + if(i2o_query_scalar(c, d->lct_data.tid, 0, 4, reply, 8)<0) + return -1; + + *lun=reply[1]; + + dprintk(("SCSI (%d,%d)\n", *target, *lun)); + return 0; +} + +void i2o_scsi_init(struct i2o_controller *c, struct i2o_device *d, struct Scsi_Host *shpnt) +{ + struct i2o_device *unit; + struct i2o_scsi_host *h =(struct i2o_scsi_host *)shpnt->hostdata; + int lun; + int target; + + h->controller=c; + h->bus_task=d->lct_data.tid; + + for(target=0;target<16;target++) + for(lun=0;lun<8;lun++) + h->task[target][lun] = -1; + + for(unit=c->devices;unit!=NULL;unit=unit->next) + { + dprintk(("Class %03X, parent %d, want %d.\n", + unit->lct_data.class_id, unit->lct_data.parent_tid, d->lct_data.tid)); + + /* Only look at scsi and fc devices */ + if ( (unit->lct_data.class_id != I2O_CLASS_SCSI_PERIPHERAL) + && (unit->lct_data.class_id != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL) + ) + continue; + + /* On our bus ? */ + dprintk(("Found a disk (%d).\n", unit->lct_data.tid)); + if ((unit->lct_data.parent_tid == d->lct_data.tid) + || (unit->lct_data.parent_tid == d->lct_data.parent_tid) + ) + { + u16 limit; + dprintk(("Its ours.\n")); + if(i2o_find_lun(c, unit, &target, &lun)==-1) + { + printk(KERN_ERR "i2o_scsi: Unable to get lun for tid %d.\n", unit->lct_data.tid); + continue; + } + dprintk(("Found disk %d %d.\n", target, lun)); + h->task[target][lun]=unit->lct_data.tid; + h->tagclock[target][lun]=jiffies; + + /* Get the max fragments/request */ + i2o_query_scalar(c, d->lct_data.tid, 0xF103, 3, &limit, 2); + + /* sanity */ + if ( limit == 0 ) + { + printk(KERN_WARNING "i2o_scsi: Ignoring unreasonable SG limit of 0 from IOP!\n"); + limit = 1; + } + + shpnt->sg_tablesize = limit; + + dprintk(("i2o_scsi: set scatter-gather to %d.\n", + shpnt->sg_tablesize)); + } + } +} + +int i2o_scsi_detect(Scsi_Host_Template * tpnt) +{ + unsigned long flags; + struct Scsi_Host *shpnt = NULL; + int i; + int count; + + printk("i2o_scsi.c: %s\n", VERSION_STRING); + + if(i2o_install_handler(&i2o_scsi_handler)<0) + { + printk(KERN_ERR "i2o_scsi: Unable to install OSM handler.\n"); + return 0; + } + scsi_context = i2o_scsi_handler.context; + + if((sg_chain_pool = kmalloc(SG_CHAIN_POOL_SZ, GFP_KERNEL)) == NULL) + { + printk("i2o_scsi: Unable to alloc %d byte SG chain buffer pool.\n", SG_CHAIN_POOL_SZ); + printk("i2o_scsi: SG chaining DISABLED!\n"); + sg_max_frags = 11; + } + else + { + printk(" chain_pool: %d bytes @ %p\n", SG_CHAIN_POOL_SZ, sg_chain_pool); + printk(" (%d byte buffers X %d can_queue X %d i2o controllers)\n", + SG_CHAIN_BUF_SZ, I2O_SCSI_CAN_QUEUE, i2o_num_controllers); + sg_max_frags = SG_MAX_FRAGS; // 64 + } + + init_timer(&retry_timer); + retry_timer.data = 0UL; + retry_timer.function = i2o_retry_run; + +// printk("SCSI OSM at %d.\n", scsi_context); + + for (count = 0, i = 0; i < MAX_I2O_CONTROLLERS; i++) + { + struct i2o_controller *c=i2o_find_controller(i); + struct i2o_device *d; + /* + * This controller doesn't exist. + */ + + if(c==NULL) + continue; + + /* + * Fixme - we need some altered device locking. This + * is racing with device addition in theory. Easy to fix. + */ + + for(d=c->devices;d!=NULL;d=d->next) + { + /* + * bus_adapter, SCSI (obsolete), or FibreChannel busses only + */ + if( (d->lct_data.class_id!=I2O_CLASS_BUS_ADAPTER_PORT) // bus_adapter +// && (d->lct_data.class_id!=I2O_CLASS_FIBRE_CHANNEL_PORT) // FC_PORT + ) + continue; + + shpnt = scsi_register(tpnt, sizeof(struct i2o_scsi_host)); + if(shpnt==NULL) + continue; + save_flags(flags); + cli(); + shpnt->unique_id = (u32)d; + shpnt->io_port = 0; + shpnt->n_io_port = 0; + shpnt->irq = 0; + shpnt->this_id = /* Good question */15; + restore_flags(flags); + i2o_scsi_init(c, d, shpnt); + count++; + } + } + i2o_scsi_hosts = count; + + if(count==0) + { + if(sg_chain_pool!=NULL) + { + kfree(sg_chain_pool); + sg_chain_pool = NULL; + } + flush_pending(); + del_timer(&retry_timer); + i2o_remove_handler(&i2o_scsi_handler); + } + + return count; +} + +int i2o_scsi_release(struct Scsi_Host *host) +{ + if(--i2o_scsi_hosts==0) + { + if(sg_chain_pool!=NULL) + { + kfree(sg_chain_pool); + sg_chain_pool = NULL; + } + flush_pending(); + del_timer(&retry_timer); + i2o_remove_handler(&i2o_scsi_handler); + } + return 0; +} + + +const char *i2o_scsi_info(struct Scsi_Host *SChost) +{ + struct i2o_scsi_host *hostdata; + + hostdata = (struct i2o_scsi_host *)SChost->hostdata; + + return(&hostdata->controller->name[0]); +} + + +/* + * From the wd93 driver: + * Returns true if there will be a DATA_OUT phase with this command, + * false otherwise. + * (Thanks to Joerg Dorchain for the research and suggestion.) + * + */ +static int is_dir_out(Scsi_Cmnd *cmd) +{ + switch (cmd->cmnd[0]) + { + case WRITE_6: case WRITE_10: case WRITE_12: + case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER: + case WRITE_VERIFY: case WRITE_VERIFY_12: + case COMPARE: case COPY: case COPY_VERIFY: + case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: + case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12: + case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE: + case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT: + case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK: + case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG: + case 0xea: + return 1; + default: + return 0; + } +} + +int i2o_scsi_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) +{ + int i; + int tid; + struct i2o_controller *c; + Scsi_Cmnd *current_command; + struct Scsi_Host *host; + struct i2o_scsi_host *hostdata; + u32 *msg, *mptr; + u32 m; + u32 *lenptr; + int direction; + int scsidir; + u32 len; + u32 reqlen; + u32 tag; + + static int max_qd = 1; + + /* + * Do the incoming paperwork + */ + + host = SCpnt->host; + hostdata = (struct i2o_scsi_host *)host->hostdata; + + c = hostdata->controller; + prefetch(c); + prefetchw(&queue_depth); + + SCpnt->scsi_done = done; + + if(SCpnt->target > 15) + { + printk(KERN_ERR "i2o_scsi: Wild target %d.\n", SCpnt->target); + return -1; + } + + tid = hostdata->task[SCpnt->target][SCpnt->lun]; + + dprintk(("qcmd: Tid = %d\n", tid)); + + current_command = SCpnt; /* set current command */ + current_command->scsi_done = done; /* set ptr to done function */ + + /* We don't have such a device. Pretend we did the command + and that selection timed out */ + + if(tid == -1) + { + SCpnt->result = DID_NO_CONNECT << 16; + done(SCpnt); + return 0; + } + + dprintk(("Real scsi messages.\n")); + + + /* + * Obtain an I2O message. Right now we _have_ to obtain one + * until the scsi layer stuff is cleaned up. + */ + + do + { + mb(); + m = I2O_POST_READ32(c); + } + while(m==0xFFFFFFFF); + msg = (u32 *)(c->mem_offset + m); + + /* + * Put together a scsi execscb message + */ + + len = SCpnt->request_bufflen; + direction = 0x00000000; // SGL IN (osm<--iop) + + /* + * The scsi layer should be handling this stuff + */ + + scsidir = 0x00000000; // DATA NO XFER + if(len) + { + if(is_dir_out(SCpnt)) + { + direction=0x04000000; // SGL OUT (osm-->iop) + scsidir =0x80000000; // DATA OUT (iop-->dev) + } + else + { + scsidir =0x40000000; // DATA IN (iop<--dev) + } + } + + __raw_writel(I2O_CMD_SCSI_EXEC<<24|HOST_TID<<12|tid, &msg[1]); + __raw_writel(scsi_context, &msg[2]); /* So the I2O layer passes to us */ + /* Sorry 64bit folks. FIXME */ + __raw_writel((u32)SCpnt, &msg[3]); /* We want the SCSI control block back */ + + /* LSI_920_PCI_QUIRK + * + * Intermittant observations of msg frame word data corruption + * observed on msg[4] after: + * WRITE, READ-MODIFY-WRITE + * operations. 19990606 -sralston + * + * (Hence we build this word via tag. Its good practice anyway + * we don't want fetches over PCI needlessly) + */ + + tag=0; + + /* + * Attach tags to the devices + */ + if(SCpnt->device->tagged_supported) + { + /* + * Some drives are too stupid to handle fairness issues + * with tagged queueing. We throw in the odd ordered + * tag to stop them starving themselves. + */ + if((jiffies - hostdata->tagclock[SCpnt->target][SCpnt->lun]) > (5*HZ)) + { + tag=0x01800000; /* ORDERED! */ + hostdata->tagclock[SCpnt->target][SCpnt->lun]=jiffies; + } + else + { + /* Hmmm... I always see value of 0 here, + * of which {HEAD_OF, ORDERED, SIMPLE} are NOT! -sralston + */ + if(SCpnt->tag == HEAD_OF_QUEUE_TAG) + tag=0x01000000; + else if(SCpnt->tag == ORDERED_QUEUE_TAG) + tag=0x01800000; + } + } + + /* Direction, disconnect ok, tag, CDBLen */ + __raw_writel(scsidir|0x20000000|SCpnt->cmd_len|tag, &msg[4]); + + mptr=msg+5; + + /* + * Write SCSI command into the message - always 16 byte block + */ + + memcpy_toio(mptr, SCpnt->cmnd, 16); + mptr+=4; + lenptr=mptr++; /* Remember me - fill in when we know */ + + reqlen = 12; // SINGLE SGE + + /* + * Now fill in the SGList and command + * + * FIXME: we need to set the sglist limits according to the + * message size of the I2O controller. We might only have room + * for 6 or so worst case + */ + + if(SCpnt->use_sg) + { + struct scatterlist *sg = (struct scatterlist *)SCpnt->request_buffer; + int chain = 0; + + len = 0; + + if((sg_max_frags > 11) && (SCpnt->use_sg > 11)) + { + chain = 1; + /* + * Need to chain! + */ + __raw_writel(direction|0xB0000000|(SCpnt->use_sg*2*4), mptr++); + __raw_writel(virt_to_bus(sg_chain_pool + sg_chain_tag), mptr); + mptr = (u32*)(sg_chain_pool + sg_chain_tag); + if (SCpnt->use_sg > max_sg_len) + { + max_sg_len = SCpnt->use_sg; + printk("i2o_scsi: Chain SG! SCpnt=%p, SG_FragCnt=%d, SG_idx=%d\n", + SCpnt, SCpnt->use_sg, sg_chain_tag); + } + if ( ++sg_chain_tag == SG_MAX_BUFS ) + sg_chain_tag = 0; + for(i = 0 ; i < SCpnt->use_sg; i++) + { + *mptr++=direction|0x10000000|sg->length; + len+=sg->length; + *mptr++=virt_to_bus(sg->address); + sg++; + } + mptr[-2]=direction|0xD0000000|(sg-1)->length; + } + else + { + for(i = 0 ; i < SCpnt->use_sg; i++) + { + __raw_writel(direction|0x10000000|sg->length, mptr++); + len+=sg->length; + __raw_writel(virt_to_bus(sg->address), mptr++); + sg++; + } + + /* Make this an end of list. Again evade the 920 bug and + unwanted PCI read traffic */ + + __raw_writel(direction|0xD0000000|(sg-1)->length, &mptr[-2]); + } + + if(!chain) + reqlen = mptr - msg; + + __raw_writel(len, lenptr); + + if(len != SCpnt->underflow) + printk("Cmd len %08X Cmd underflow %08X\n", + len, SCpnt->underflow); + } + else + { + dprintk(("non sg for %p, %d\n", SCpnt->request_buffer, + SCpnt->request_bufflen)); + __raw_writel(len = SCpnt->request_bufflen, lenptr); + if(len == 0) + { + reqlen = 9; + } + else + { + __raw_writel(0xD0000000|direction|SCpnt->request_bufflen, mptr++); + __raw_writel(virt_to_bus(SCpnt->request_buffer), mptr++); + } + } + + /* + * Stick the headers on + */ + + __raw_writel(reqlen<<16 | SGL_OFFSET_10, msg); + + /* Queue the message */ + i2o_post_message(c,m); + + atomic_inc(&queue_depth); + + if(atomic_read(&queue_depth)> max_qd) + { + max_qd=atomic_read(&queue_depth); + printk("Queue depth now %d.\n", max_qd); + } + + mb(); + dprintk(("Issued %ld\n", current_command->serial_number)); + + return 0; +} + +static void internal_done(Scsi_Cmnd * SCpnt) +{ + SCpnt->SCp.Status++; +} + +int i2o_scsi_command(Scsi_Cmnd * SCpnt) +{ + i2o_scsi_queuecommand(SCpnt, internal_done); + SCpnt->SCp.Status = 0; + while (!SCpnt->SCp.Status) + barrier(); + return SCpnt->result; +} + +int i2o_scsi_abort(Scsi_Cmnd * SCpnt) +{ + struct i2o_controller *c; + struct Scsi_Host *host; + struct i2o_scsi_host *hostdata; + u32 *msg; + u32 m; + int tid; + + printk("i2o_scsi: Aborting command block.\n"); + + host = SCpnt->host; + hostdata = (struct i2o_scsi_host *)host->hostdata; + tid = hostdata->task[SCpnt->target][SCpnt->lun]; + if(tid==-1) + { + printk(KERN_ERR "impossible command to abort.\n"); + return SCSI_ABORT_NOT_RUNNING; + } + c = hostdata->controller; + + /* + * Obtain an I2O message. Right now we _have_ to obtain one + * until the scsi layer stuff is cleaned up. + */ + + do + { + mb(); + m = I2O_POST_READ32(c); + } + while(m==0xFFFFFFFF); + msg = (u32 *)(c->mem_offset + m); + + __raw_writel(FIVE_WORD_MSG_SIZE, &msg[0]); + __raw_writel(I2O_CMD_SCSI_ABORT<<24|HOST_TID<<12|tid, &msg[1]); + __raw_writel(scsi_context, &msg[2]); + __raw_writel(0, &msg[3]); /* Not needed for an abort */ + __raw_writel((u32)SCpnt, &msg[4]); + wmb(); + i2o_post_message(c,m); + wmb(); + return SCSI_ABORT_PENDING; +} + +int i2o_scsi_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags) +{ + int tid; + struct i2o_controller *c; + struct Scsi_Host *host; + struct i2o_scsi_host *hostdata; + u32 m; + u32 *msg; + + /* + * Find the TID for the bus + */ + + printk("i2o_scsi: Attempting to reset the bus.\n"); + + host = SCpnt->host; + hostdata = (struct i2o_scsi_host *)host->hostdata; + tid = hostdata->bus_task; + c = hostdata->controller; + + /* + * Now send a SCSI reset request. Any remaining commands + * will be aborted by the IOP. We need to catch the reply + * possibly ? + */ + + m = I2O_POST_READ32(c); + + /* + * No free messages, try again next time - no big deal + */ + + if(m == 0xFFFFFFFF) + return SCSI_RESET_PUNT; + + msg = (u32 *)(c->mem_offset + m); + __raw_writel(FOUR_WORD_MSG_SIZE|SGL_OFFSET_0, &msg[0]); + __raw_writel(I2O_CMD_SCSI_BUSRESET<<24|HOST_TID<<12|tid, &msg[1]); + __raw_writel(scsi_context|0x80000000, &msg[2]); + /* We use the top bit to split controller and unit transactions */ + /* Now store unit,tid so we can tie the completion back to a specific device */ + __raw_writel(c->unit << 16 | tid, &msg[3]); + wmb(); + i2o_post_message(c,m); + return SCSI_RESET_PENDING; +} + +/* + * This is anyones guess quite frankly. + */ + +int i2o_scsi_bios_param(Disk * disk, kdev_t dev, int *ip) +{ + int size; + + size = disk->capacity; + ip[0] = 64; /* heads */ + ip[1] = 32; /* sectors */ + if ((ip[2] = size >> 11) > 1024) { /* cylinders, test for big disk */ + ip[0] = 255; /* heads */ + ip[1] = 63; /* sectors */ + ip[2] = size / (255 * 63); /* cylinders */ + } + return 0; +} + +MODULE_AUTHOR("Red Hat Software"); +MODULE_LICENSE("GPL"); + + +static Scsi_Host_Template driver_template = I2OSCSI; + +#include "../../scsi/scsi_module.c" diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_scsi.h linux.ac/drivers/message/i2o/i2o_scsi.h --- linux.vanilla/drivers/message/i2o/i2o_scsi.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_scsi.h Thu Oct 11 23:21:35 2001 @@ -0,0 +1,47 @@ +#ifndef _I2O_SCSI_H +#define _I2O_SCSI_H + +#if !defined(LINUX_VERSION_CODE) +#include +#endif + +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) + +#include +#include + +#define I2O_SCSI_ID 15 +#define I2O_SCSI_CAN_QUEUE 4 +#define I2O_SCSI_CMD_PER_LUN 6 + +extern int i2o_scsi_detect(Scsi_Host_Template *); +extern const char *i2o_scsi_info(struct Scsi_Host *); +extern int i2o_scsi_command(Scsi_Cmnd *); +extern int i2o_scsi_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +extern int i2o_scsi_abort(Scsi_Cmnd *); +extern int i2o_scsi_reset(Scsi_Cmnd *, unsigned int); +extern int i2o_scsi_bios_param(Disk *, kdev_t, int *); +extern void i2o_scsi_setup(char *str, int *ints); +extern int i2o_scsi_release(struct Scsi_Host *host); + +#define I2OSCSI { \ + next: NULL, \ + proc_name: "i2o_scsi", \ + name: "I2O SCSI Layer", \ + detect: i2o_scsi_detect, \ + release: i2o_scsi_release, \ + info: i2o_scsi_info, \ + command: i2o_scsi_command, \ + queuecommand: i2o_scsi_queuecommand, \ + abort: i2o_scsi_abort, \ + reset: i2o_scsi_reset, \ + bios_param: i2o_scsi_bios_param, \ + can_queue: I2O_SCSI_CAN_QUEUE, \ + this_id: I2O_SCSI_ID, \ + sg_tablesize: 8, \ + cmd_per_lun: I2O_SCSI_CMD_PER_LUN, \ + unchecked_isa_dma: 0, \ + use_clustering: ENABLE_CLUSTERING \ + } + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/mtd/Config.in linux.ac/drivers/mtd/Config.in --- linux.vanilla/drivers/mtd/Config.in Thu Oct 11 13:52:09 2001 +++ linux.ac/drivers/mtd/Config.in Wed Oct 10 01:47:56 2001 @@ -13,10 +13,10 @@ fi dep_tristate ' MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD dep_tristate ' RedBoot partition table parsing' CONFIG_MTD_REDBOOT_PARTS $CONFIG_MTD_PARTITIONS -if [ "$CONFIG_ARM" = "y" ]; then - dep_tristate ' Compaq bootldr partition table parsing' CONFIG_MTD_BOOTLDR_PARTS $CONFIG_MTD_PARTITIONS - dep_tristate ' ARM Firmware Suite partition parsing' CONFIG_MTD_AFS_PARTS $CONFIG_MTD_PARTITIONS -fi + if [ "$CONFIG_ARM" = "y" ]; then + dep_tristate ' Compaq bootldr partition table parsing' CONFIG_MTD_BOOTLDR_PARTS $CONFIG_MTD_PARTITIONS + dep_tristate ' ARM Firmware Suite partition parsing' CONFIG_MTD_AFS_PARTS $CONFIG_MTD_PARTITIONS + fi comment 'User Modules And Translation Layers' dep_tristate ' Direct char device access to MTD devices' CONFIG_MTD_CHAR $CONFIG_MTD diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/mtd/devices/blkmtd.c linux.ac/drivers/mtd/devices/blkmtd.c --- linux.vanilla/drivers/mtd/devices/blkmtd.c Thu Oct 11 13:52:09 2001 +++ linux.ac/drivers/mtd/devices/blkmtd.c Wed Oct 10 01:47:57 2001 @@ -1,5 +1,7 @@ /* * $Id: blkmtd.c,v 1.3 2001/10/02 15:33:20 dwmw2 Exp $ + * - With alloc_kiovec_sz patches to work in 2.4.x-ac kernels. + * * blkmtd.c - use a block device as a fake MTD * * Author: Simon Evans @@ -157,6 +159,7 @@ struct kiobuf *iobuf; mtd_raw_dev_data_t *rawdevice = (mtd_raw_dev_data_t *)file->private_data; kdev_t dev; + int nbhs = KIO_MAX_SECTORS; if(!rawdevice) { printk("blkmtd: readpage: PANIC file->private_data == NULL\n"); @@ -206,7 +209,7 @@ DEBUG(3, "blkmtd: readpage: getting kiovec\n"); - err = alloc_kiovec(1, &iobuf); + err = alloc_kiovec_sz(1, &iobuf, &nbhs); if (err) { return err; } @@ -226,7 +229,7 @@ err = brw_kiovec(READ, 1, &iobuf, dev, iobuf->blocks, rawdevice->sector_size); DEBUG(3, "blkmtd: readpage: finished, err = %d\n", err); iobuf->locked = 0; - free_kiovec(1, &iobuf); + free_kiovec_sz(1, &iobuf, &nbhs); if(err != PAGE_SIZE) { printk("blkmtd: readpage: error reading page %ld\n", page->index); memset(page_address(page), 0, PAGE_SIZE); @@ -256,6 +259,7 @@ int err; struct task_struct *tsk = current; struct kiobuf *iobuf; + int nbhs = KIO_MAX_SECTORS; DECLARE_WAITQUEUE(wait, tsk); DEBUG(1, "blkmtd: writetask: starting (pid = %d)\n", tsk->pid); @@ -268,7 +272,7 @@ spin_unlock_irq(&tsk->sigmask_lock); exit_sighand(tsk); - if(alloc_kiovec(1, &iobuf)) + if(alloc_kiovec_sz(1, &iobuf, &nbhs)) return 0; DEBUG(2, "blkmtd: writetask: entering main loop\n"); add_wait_queue(&thr_wq, &wait); @@ -360,7 +364,7 @@ } remove_wait_queue(&thr_wq, &wait); DEBUG(1, "blkmtd: writetask: exiting\n"); - free_kiovec(1, &iobuf); + free_kiovec_sz(1, &iobuf, &nbhs); /* Tell people we have exitd */ up(&thread_sem); return 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/Config.in linux.ac/drivers/net/Config.in --- linux.vanilla/drivers/net/Config.in Fri Sep 21 04:56:31 2001 +++ linux.ac/drivers/net/Config.in Wed Oct 10 01:47:59 2001 @@ -191,7 +191,7 @@ if [ "$CONFIG_OBSOLETE" = "y" ]; then dep_bool ' Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET $CONFIG_ISA fi - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_MIPS" = "y" ]; then bool ' Philips SAA9730 Ethernet support (EXPERIMENTAL)' CONFIG_LAN_SAA9730 fi fi @@ -272,6 +272,9 @@ dep_tristate ' PPP BSD-Compress compression' CONFIG_PPP_BSDCOMP $CONFIG_PPP if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' PPP over Ethernet (EXPERIMENTAL)' CONFIG_PPPOE $CONFIG_PPP + fi + if [ ! "$CONFIG_ATM" = "n" ]; then + dep_tristate ' PPP over ATM (EXPERIMENTAL)' CONFIG_PPPOATM $CONFIG_PPP fi fi diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/eepro100.c linux.ac/drivers/net/eepro100.c --- linux.vanilla/drivers/net/eepro100.c Thu Oct 11 13:52:10 2001 +++ linux.ac/drivers/net/eepro100.c Thu Oct 11 20:26:50 2001 @@ -142,16 +142,6 @@ #define RUN_AT(x) (jiffies + (x)) -/* ACPI power states don't universally work (yet) */ -#ifndef CONFIG_PM -#undef pci_set_power_state -#define pci_set_power_state null_set_power_state -static inline int null_set_power_state(struct pci_dev *dev, int state) -{ - return 0; -} -#endif /* CONFIG_PM */ - #define netdevice_start(dev) #define netdevice_stop(dev) #define netif_set_tx_timeout(dev, tf, tm) \ @@ -284,7 +274,7 @@ */ -static int speedo_found1(struct pci_dev *pdev, long ioaddr, int fnd_cnt, int acpi_idle_state); +static int speedo_found1(struct pci_dev *pdev, long ioaddr, int fnd_cnt); enum pci_flags_bit { PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, @@ -565,17 +555,22 @@ { unsigned long ioaddr; int irq; - int acpi_idle_state = 0, pm; static int cards_found /* = 0 */; static int did_version /* = 0 */; /* Already printed version info. */ if (speedo_debug > 0 && did_version++ == 0) printk(version); + if (pci_enable_device(pdev)) + goto err_out_none; + + pci_enable_wake(pdev, 0, 1); + pci_set_master(pdev); + if (!request_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1), "eepro100")) { printk (KERN_ERR "eepro100: cannot reserve I/O ports\n"); - goto err_out_none; + goto err_out_disable; } if (!request_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0), "eepro100")) { @@ -602,20 +597,7 @@ pci_resource_start(pdev, 0), irq); #endif - /* save power state b4 pci_enable_device overwrites it */ - pm = pci_find_capability(pdev, PCI_CAP_ID_PM); - if (pm) { - u16 pwr_command; - pci_read_config_word(pdev, pm + PCI_PM_CTRL, &pwr_command); - acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK; - } - - if (pci_enable_device(pdev)) - goto err_out_free_mmio_region; - - pci_set_master(pdev); - - if (speedo_found1(pdev, ioaddr, cards_found, acpi_idle_state) == 0) + if (speedo_found1(pdev, ioaddr, cards_found) == 0) cards_found++; else goto err_out_iounmap; @@ -630,12 +612,14 @@ release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); err_out_free_pio_region: release_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1)); +err_out_disable: + pci_disable_device(pdev); err_out_none: return -ENODEV; } static int speedo_found1(struct pci_dev *pdev, - long ioaddr, int card_idx, int acpi_idle_state) + long ioaddr, int card_idx) { struct net_device *dev; struct speedo_private *sp; @@ -801,8 +785,8 @@ inl(ioaddr + SCBPort); udelay(10); - /* Return the chip to its original power state. */ - pci_set_power_state(pdev, acpi_idle_state); + /* Put chip into power state D2 until we open() it. */ +// pci_set_power_state(pdev, 2); pci_set_drvdata (pdev, dev); @@ -811,11 +795,12 @@ sp = dev->priv; sp->pdev = pdev; - sp->acpi_pwr = acpi_idle_state; sp->tx_ring = tx_ring_space; sp->tx_ring_dma = tx_ring_dma; sp->lstats = (struct speedo_stats *)(sp->tx_ring + TX_RING_SIZE); sp->lstats_dma = TX_RING_ELEM_DMA(sp, TX_RING_SIZE); + if ((pdev->device == 0x2449) || ( (pdev->device > 0x1030) && (pdev->device < 0x1039) )) + sp->chip_id = 1; init_timer(&sp->timer); /* used in ioctl() */ sp->full_duplex = option >= 0 && (option & 0x10) ? 1 : 0; @@ -927,7 +912,7 @@ MOD_INC_USE_COUNT; - pci_set_power_state(sp->pdev, 0); +// pci_set_power_state(sp->pdev, 0); /* Set up the Tx queue early.. */ sp->cur_tx = 0; @@ -1865,7 +1850,7 @@ if (speedo_debug > 0) printk(KERN_DEBUG "%s: %d multicast blocks dropped.\n", dev->name, i); - pci_set_power_state(sp->pdev, 2); +// pci_set_power_state(sp->pdev, 2); MOD_DEC_USE_COUNT; @@ -1933,24 +1918,26 @@ access from the timeout handler. They are currently serialized only with MDIO access from the timer routine. 2000/05/09 SAW */ - saved_acpi = pci_set_power_state(sp->pdev, 0); + saved_acpi = sp->pdev->current_state; +// pci_set_power_state(sp->pdev, 0); t = del_timer_sync(&sp->timer); data->val_out = mdio_read(ioaddr, data->phy_id & 0x1f, data->reg_num & 0x1f); if (t) add_timer(&sp->timer); /* may be set to the past --SAW */ - pci_set_power_state(sp->pdev, saved_acpi); +// pci_set_power_state(sp->pdev, saved_acpi); return 0; case SIOCSMIIREG: /* Write MII PHY register. */ case SIOCDEVPRIVATE+2: /* for binary compat, remove in 2.5 */ if (!capable(CAP_NET_ADMIN)) return -EPERM; - saved_acpi = pci_set_power_state(sp->pdev, 0); + saved_acpi = sp->pdev->current_state; +// pci_set_power_state(sp->pdev, 0); t = del_timer_sync(&sp->timer); mdio_write(ioaddr, data->phy_id, data->reg_num, data->val_in); if (t) add_timer(&sp->timer); /* may be set to the past --SAW */ - pci_set_power_state(sp->pdev, saved_acpi); +// pci_set_power_state(sp->pdev, saved_acpi); return 0; default: return -EOPNOTSUPP; @@ -2165,7 +2152,6 @@ if (!netif_running(dev)) return 0; - netif_device_detach(dev); outl(PortPartialReset, ioaddr + SCBPort); /* XXX call pci_set_power_state ()? */ @@ -2190,7 +2176,6 @@ 2000/03/08 SAW */ outw(SCBMaskAll, ioaddr + SCBCmd); speedo_resume(dev); - netif_device_attach(dev); sp->rx_mode = -1; sp->flow_ctrl = sp->partner = 0; set_rx_mode(dev); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/epic100.c linux.ac/drivers/net/epic100.c --- linux.vanilla/drivers/net/epic100.c Thu Oct 11 13:52:10 2001 +++ linux.ac/drivers/net/epic100.c Wed Oct 10 01:48:00 2001 @@ -675,9 +675,7 @@ required by the details of which bits are reset and the transceiver wiring on the Ositech CardBus card. */ -#if 0 outl(dev->if_port == 1 ? 0x13 : 0x12, ioaddr + MIICfg); -#endif if (ep->chip_flags & MII_PWRDWN) outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/net_init.c linux.ac/drivers/net/net_init.c --- linux.vanilla/drivers/net/net_init.c Sat Sep 8 00:18:50 2001 +++ linux.ac/drivers/net/net_init.c Wed Oct 10 01:48:01 2001 @@ -244,11 +244,23 @@ static int eth_change_mtu(struct net_device *dev, int new_mtu) { - if ((new_mtu < 68) || (new_mtu > 1500)) + if (new_mtu > 1500) return -EINVAL; dev->mtu = new_mtu; return 0; } + +#if defined(CONFIG_VETH) || defined(CONFIG_VETH_MODULE) + +struct net_device *init_vethdev(struct net_device *dev, int sizeof_priv, int veth) +{ + char name[32]; + + sprintf(name, "veth%d", veth); + return init_netdev(dev, sizeof_priv, name, ether_setup); +} + +#endif #ifdef CONFIG_FDDI diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/pcmcia/netwave_cs.c linux.ac/drivers/net/pcmcia/netwave_cs.c --- linux.vanilla/drivers/net/pcmcia/netwave_cs.c Thu Oct 11 13:52:10 2001 +++ linux.ac/drivers/net/pcmcia/netwave_cs.c Wed Oct 10 01:48:01 2001 @@ -269,8 +269,15 @@ because they generally can't be allocated dynamically. */ -#define SIOCGIPSNAP SIOCDEVPRIVATE /* Site Survey Snapshot */ -/*#define SIOCGIPQTHR SIOCDEVPRIVATE + 1*/ +/* Wireless Extension Backward compatibility - Jean II + * If the new wireless device private ioctl range is not defined, + * default to standard device private ioctl range */ +#ifndef SIOCIWFIRSTPRIV +#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE +#endif /* SIOCIWFIRSTPRIV */ + +#define SIOCGIPSNAP SIOCIWFIRSTPRIV /* Site Survey Snapshot */ +/*#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1*/ #define MAX_ESA 10 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/pcmcia/ray_cs.c linux.ac/drivers/net/pcmcia/ray_cs.c --- linux.vanilla/drivers/net/pcmcia/ray_cs.c Thu Oct 11 13:52:10 2001 +++ linux.ac/drivers/net/pcmcia/ray_cs.c Wed Oct 10 01:48:01 2001 @@ -1451,9 +1451,12 @@ #endif /* WIRELESS_SPY */ /* ------------------ PRIVATE IOCTL ------------------ */ -#define SIOCSIPFRAMING SIOCDEVPRIVATE /* Set framing mode */ -#define SIOCGIPFRAMING SIOCDEVPRIVATE + 1 /* Get framing mode */ -#define SIOCGIPCOUNTRY SIOCDEVPRIVATE + 3 /* Get country code */ +#ifndef SIOCIWFIRSTPRIV +#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE +#endif /* SIOCIWFIRSTPRIV */ +#define SIOCSIPFRAMING SIOCIWFIRSTPRIV /* Set framing mode */ +#define SIOCGIPFRAMING SIOCIWFIRSTPRIV + 1 /* Get framing mode */ +#define SIOCGIPCOUNTRY SIOCIWFIRSTPRIV + 3 /* Get country code */ case SIOCSIPFRAMING: if(!capable(CAP_NET_ADMIN)) /* For private IOCTLs, we need to check permissions */ { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/pcmcia/wavelan_cs.c linux.ac/drivers/net/pcmcia/wavelan_cs.c --- linux.vanilla/drivers/net/pcmcia/wavelan_cs.c Thu Oct 11 13:52:10 2001 +++ linux.ac/drivers/net/pcmcia/wavelan_cs.c Wed Oct 10 01:48:02 2001 @@ -2269,6 +2269,12 @@ range.max_qual.qual = MMR_SGNL_QUAL; range.max_qual.level = MMR_SIGNAL_LVL; range.max_qual.noise = MMR_SILENCE_LVL; +#if WIRELESS_EXT > 11 + range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */ + /* Need to get better values for those two */ + range.avg_qual.level = 30; + range.avg_qual.noise = 8; +#endif /* WIRELESS_EXT > 11 */ #if WIRELESS_EXT > 7 range.num_bitrates = 1; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/pcmcia/wavelan_cs.h linux.ac/drivers/net/pcmcia/wavelan_cs.h --- linux.vanilla/drivers/net/pcmcia/wavelan_cs.h Wed Apr 18 22:40:05 2001 +++ linux.ac/drivers/net/pcmcia/wavelan_cs.h Fri Oct 12 12:56:01 2001 @@ -465,13 +465,20 @@ /* ------------------------ PRIVATE IOCTL ------------------------ */ -#define SIOCSIPQTHR SIOCDEVPRIVATE /* Set quality threshold */ -#define SIOCGIPQTHR SIOCDEVPRIVATE + 1 /* Get quality threshold */ -#define SIOCSIPROAM SIOCDEVPRIVATE + 2 /* Set roaming state */ -#define SIOCGIPROAM SIOCDEVPRIVATE + 3 /* Get roaming state */ +/* Wireless Extension Backward compatibility - Jean II + * If the new wireless device private ioctl range is not defined, + * default to standard device private ioctl range */ +#ifndef SIOCIWFIRSTPRIV +#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE +#endif /* SIOCIWFIRSTPRIV */ -#define SIOCSIPHISTO SIOCDEVPRIVATE + 6 /* Set histogram ranges */ -#define SIOCGIPHISTO SIOCDEVPRIVATE + 7 /* Get histogram values */ +#define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */ +#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */ +#define SIOCSIPROAM SIOCIWFIRSTPRIV + 2 /* Set roaming state */ +#define SIOCGIPROAM SIOCIWFIRSTPRIV + 3 /* Get roaming state */ + +#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 6 /* Set histogram ranges */ +#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 7 /* Get histogram values */ /*************************** WaveLAN Roaming **************************/ #ifdef WAVELAN_ROAMING /* Conditional compile, see above in options */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/pcnet32.c linux.ac/drivers/net/pcnet32.c --- linux.vanilla/drivers/net/pcnet32.c Thu Oct 11 13:52:10 2001 +++ linux.ac/drivers/net/pcnet32.c Wed Oct 10 01:48:02 2001 @@ -1374,6 +1374,13 @@ */ lp->a.write_bcr (ioaddr, 20, 4); + /* + * FIXME: What happens if the bcr write is posted, the buffers are + * freed and there is still incoming DMA traffic + */ + +#warning "PCI posting bug" + free_irq(dev->irq, dev); /* free all allocated skbuffs */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/ppp_generic.c linux.ac/drivers/net/ppp_generic.c --- linux.vanilla/drivers/net/ppp_generic.c Thu Oct 11 13:52:10 2001 +++ linux.ac/drivers/net/ppp_generic.c Wed Oct 10 01:48:02 2001 @@ -2481,3 +2481,4 @@ EXPORT_SYMBOL(ppp_unregister_compressor); EXPORT_SYMBOL(all_ppp_units); /* for debugging */ EXPORT_SYMBOL(all_channels); /* for debugging */ +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/ppp_synctty.c linux.ac/drivers/net/ppp_synctty.c --- linux.vanilla/drivers/net/ppp_synctty.c Wed Jul 4 19:50:39 2001 +++ linux.ac/drivers/net/ppp_synctty.c Wed Oct 10 01:48:02 2001 @@ -44,13 +44,6 @@ #include #include -#ifndef spin_trylock_bh -#define spin_trylock_bh(lock) ({ int __r; local_bh_disable(); \ - __r = spin_trylock(lock); \ - if (!__r) local_bh_enable(); \ - __r; }) -#endif - #define PPP_VERSION "2.4.1" /* Structure for storing local state. */ @@ -708,3 +701,4 @@ module_init(ppp_sync_init); module_exit(ppp_sync_cleanup); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/pppoe.c linux.ac/drivers/net/pppoe.c --- linux.vanilla/drivers/net/pppoe.c Thu Oct 11 13:52:10 2001 +++ linux.ac/drivers/net/pppoe.c Wed Oct 10 01:48:02 2001 @@ -5,7 +5,7 @@ * PPPoE --- PPP over Ethernet (RFC 2516) * * - * Version: 0.6.8 + * Version: 0.6.9 * * 030700 : Fixed connect logic to allow for disconnect. * 270700 : Fixed potential SMP problems; we must protect against @@ -29,6 +29,8 @@ * the original skb that was passed in on success, never on * failure. Delete the copy of the skb on failure to avoid * a memory leak. + * 081001 : Misc. cleanup (licence string, non-blocking, prevent + * reference of device on close). * * Author: Michal Ostrowski * Contributors: @@ -349,7 +351,7 @@ if (relay_po == NULL) goto abort_kfree; - + if ((relay_po->sk->state & PPPOX_CONNECTED) == 0) goto abort_put; @@ -543,13 +545,12 @@ po = sk->protinfo.pppox; if (po->pppoe_pa.sid) { delete_item(po->pppoe_pa.sid, po->pppoe_pa.remote); - po->pppoe_pa.sid = 0 ; } if (po->pppoe_dev) - dev_put(po->pppoe_dev); + dev_put(po->pppoe_dev); - po->pppoe_dev = NULL ; + po->pppoe_dev = NULL; sock_orphan(sk); sock->sk = NULL; @@ -944,7 +945,8 @@ goto end; } - skb = skb_recv_datagram(sk, flags, 0, &error); + skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, + flags & MSG_DONTWAIT, &error); if (error < 0) { goto end; @@ -1077,3 +1079,7 @@ module_init(pppoe_init); module_exit(pppoe_exit); + +MODULE_AUTHOR("Michal Ostrowski "); +MODULE_DESCRIPTION("PPP over Ethernet driver"); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/pppox.c linux.ac/drivers/net/pppox.c --- linux.vanilla/drivers/net/pppox.c Fri Jul 20 01:55:06 2001 +++ linux.ac/drivers/net/pppox.c Wed Oct 10 01:48:02 2001 @@ -158,3 +158,7 @@ module_init(pppox_init); module_exit(pppox_exit); + +MODULE_AUTHOR("Michal Ostrowski "); +MODULE_DESCRIPTION("PPP over Ethernet driver (generic socket layer)"); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/sgiseeq.c linux.ac/drivers/net/sgiseeq.c --- linux.vanilla/drivers/net/sgiseeq.c Thu Apr 12 20:15:25 2001 +++ linux.ac/drivers/net/sgiseeq.c Wed Oct 10 01:48:02 2001 @@ -1,5 +1,4 @@ -/* $Id: sgiseeq.c,v 1.17 2000/03/27 23:02:57 ralf Exp $ - * +/* * sgiseeq.c: Seeq8003 ethernet driver for SGI machines. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) @@ -451,13 +450,12 @@ unsigned long flags; int err; - save_flags(flags); cli(); + save_and_cli(flags); if (request_irq(dev->irq, sgiseeq_interrupt, 0, sgiseeqstr, (void *) dev)) { printk("Seeq8003: Can't get irq %d\n", dev->irq); restore_flags(flags); return -EAGAIN; } - err = init_seeq(dev, sp, sregs); if (err) return err; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/wan/z85230.c linux.ac/drivers/net/wan/z85230.c --- linux.vanilla/drivers/net/wan/z85230.c Sun Sep 9 18:50:48 2001 +++ linux.ac/drivers/net/wan/z85230.c Wed Oct 10 01:48:04 2001 @@ -4,8 +4,8 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * (c) Copyright 1998 Building Number Three Ltd - * (c) Copyright 2000 Red Hat Software + * (c) Copyright 1998 Alan Cox + * (c) Copyright 2000, 2001 Red Hat Inc * * Development of this driver was funded by Equiinet Ltd * http://www.equiinet.com @@ -18,6 +18,8 @@ * DMA now uses get_free_page as kmalloc buffers may span a 64K * boundary. * + * Modified for SMP safety and SMP locking by Alan Cox + * * Performance * * Z85230: @@ -53,8 +55,6 @@ #include "z85230.h" -static spinlock_t z8530_buffer_lock = SPIN_LOCK_UNLOCKED; - /** * z8530_read_port - Architecture specific interface function * @p: port to read @@ -116,21 +116,14 @@ * * Most of the Z8530 registers are indexed off the control registers. * A read is done by writing to the control register and reading the - * register back. We do the locking needed to protect this - * operation. + * register back. The caller must hold the lock */ static inline u8 read_zsreg(struct z8530_channel *c, u8 reg) { - u8 r; - unsigned long flags; - save_flags(flags); - cli(); if(reg) z8530_write_port(c->ctrlio, reg); - r=z8530_read_port(c->ctrlio); - restore_flags(flags); - return r; + return z8530_read_port(c->ctrlio); } /** @@ -154,7 +147,7 @@ * @reg: Register number * @val: Value to write * - * Write a value to an indexed register. Perform the locking needed + * Write a value to an indexed register. The caller must hold the lock * to honour the irritating delay rules. We know about register 0 * being fast to access. */ @@ -162,12 +155,14 @@ static inline void write_zsreg(struct z8530_channel *c, u8 reg, u8 val) { unsigned long flags; - save_flags(flags); - cli(); + + spin_lock_irqsave(c->lock, flags); + if(reg) z8530_write_port(c->ctrlio, reg); z8530_write_port(c->ctrlio, val); - restore_flags(flags); + + spin_unlock_irqrestore(c->lock, flags); } /** @@ -299,7 +294,7 @@ * @set: 1 to set, 0 to clear * * Sets or clears DTR/RTS on the requested line. All locking is handled - * for the caller. For now we assume all boards use the actual RTS/DTR + * by the caller. For now we assume all boards use the actual RTS/DTR * on the chip. Apparently one or two don't. We'll scream about them * later. */ @@ -333,12 +328,14 @@ * do it yourself but consider medical assistance first. This non DMA * synchronous mode is portable code. The DMA mode assumes PCI like * ISA DMA + * + * Called with the device lock held */ static void z8530_rx(struct z8530_channel *c) { u8 ch,stat; - + while(1) { /* FIFO empty ? */ @@ -382,6 +379,10 @@ } else { + /* + * Drop the lock for RX processing, or + * there are deadlocks + */ z8530_rx_done(c); write_zsctrl(c, RES_Rx_CRC); } @@ -407,8 +408,7 @@ static void z8530_tx(struct z8530_channel *c) { - while(c->txcount) - { + while(c->txcount) { /* FIFO full ? */ if(!(read_zsreg(c, R0)&4)) break; @@ -424,8 +424,8 @@ write_zsctrl(c, RES_EOM_L); write_zsreg(c, R10, c->regs[10]&~ABUNDER); } - return; } + /* * End of frame TX - fire another one @@ -434,7 +434,6 @@ write_zsctrl(c, RES_Tx_P); z8530_tx_done(c); -/* write_zsreg(c, R8, *c->tx_ptr++); */ write_zsctrl(c, RES_H_IUS); } @@ -451,8 +450,10 @@ static void z8530_status(struct z8530_channel *chan) { - u8 status=read_zsreg(chan, R0); - u8 altered=chan->status^status; + u8 status, altered; + + status=read_zsreg(chan, R0); + altered=chan->status^status; chan->status=status; @@ -512,11 +513,12 @@ { /* Special condition check only */ u8 status; - + read_zsreg(chan, R7); read_zsreg(chan, R6); status=read_zsreg(chan, R1); + if(status&END_FR) { z8530_rx_done(chan); /* Fire up the next one */ @@ -565,16 +567,20 @@ static void z8530_dma_status(struct z8530_channel *chan) { - unsigned long flags; - u8 status=read_zsreg(chan, R0); - u8 altered=chan->status^status; + u8 status, altered; + + status=read_zsreg(chan, R0); + altered=chan->status^status; chan->status=status; + if(chan->dma_tx) { if(status&TxEOM) { + unsigned long flags; + flags=claim_dma_lock(); disable_dma(chan->txdma); clear_dma_ff(chan->txdma); @@ -706,6 +712,10 @@ * the channel specific call backs for each channel that has events. * We have to use callback functions because the two channels can be * in different modes. + * + * Locking is done for the handlers. Note that locking is done + * at the chip level (the 5uS delay issue is per chip not per + * channel). c->lock for both channels points to dev->lock */ void z8530_interrupt(int irq, void *dev_id, struct pt_regs *regs) @@ -714,6 +724,7 @@ u8 intr; static volatile int locker=0; int work=0; + struct z8530_irqhandler *irqs=dev->chanA.irqs; if(locker) { @@ -721,11 +732,12 @@ return; } locker=1; - + + spin_lock(&dev->lock); + while(++work<5000) { - struct z8530_irqhandler *irqs=dev->chanA.irqs; - + intr = read_zsreg(&dev->chanA, R3); if(!(intr & (CHARxIP|CHATxIP|CHAEXT|CHBRxIP|CHBTxIP|CHBEXT))) break; @@ -758,6 +770,7 @@ irqs->status(&dev->chanB); } } + spin_unlock(&dev->lock); if(work==5000) printk(KERN_ERR "%s: interrupt jammed - abort(0x%X)!\n", dev->name, intr); /* Ok all done */ @@ -786,12 +799,17 @@ int z8530_sync_open(struct net_device *dev, struct z8530_channel *c) { + unsigned long flags; + + spin_lock_irqsave(c->lock, flags); + c->sync = 1; c->mtu = dev->mtu+64; c->count = 0; c->skb = NULL; c->skb2 = NULL; c->irqs = &z8530_sync; + /* This loads the double buffer up */ z8530_rx_done(c); /* Load the frame ring */ z8530_rx_done(c); /* Load the backup frame */ @@ -800,6 +818,8 @@ c->regs[R1]|=TxINT_ENAB; write_zsreg(c, R1, c->regs[R1]); write_zsreg(c, R3, c->regs[R3]|RxENABLE); + + spin_unlock_irqrestore(c->lock, flags); return 0; } @@ -818,7 +838,9 @@ int z8530_sync_close(struct net_device *dev, struct z8530_channel *c) { u8 chk; + unsigned long flags; + spin_lock_irqsave(c->lock, flags); c->irqs = &z8530_nop; c->max = 0; c->sync = 0; @@ -826,6 +848,8 @@ chk=read_zsreg(c,R0); write_zsreg(c, R3, c->regs[R3]); z8530_rtsdtr(c,0); + + spin_unlock_irqrestore(c->lock, flags); return 0; } @@ -887,6 +911,8 @@ /* * Enable DMA control mode */ + + spin_lock_irqsave(c->lock, flags); /* * TX DMA via DIR/REQ @@ -945,6 +971,8 @@ c->irqs = &z8530_dma_sync; z8530_rtsdtr(c,1); write_zsreg(c, R3, c->regs[R3]|RxENABLE); + + spin_unlock_irqrestore(c->lock, flags); return 0; } @@ -986,6 +1014,8 @@ c->txdma_on = 0; c->tx_dma_used = 0; + spin_lock_irqsave(c->lock, flags); + /* * Disable DMA control mode */ @@ -1011,6 +1041,9 @@ chk=read_zsreg(c,R0); write_zsreg(c, R3, c->regs[R3]); z8530_rtsdtr(c,0); + + spin_unlock_irqrestore(c->lock, flags); + return 0; } @@ -1038,20 +1071,6 @@ c->skb2 = NULL; /* - * Load the PIO receive ring - */ - - z8530_rx_done(c); - z8530_rx_done(c); - - /* - * Load the DMA interfaces up - */ - - c->rxdma_on = 0; - c->txdma_on = 0; - - /* * Allocate the DMA flip buffers. Limit by page size. * Everyone runs 1500 mtu or less on wan links so this * should be fine. @@ -1066,6 +1085,23 @@ c->tx_dma_buf[1] = c->tx_dma_buf[0] + PAGE_SIZE/2; + + spin_lock_irqsave(c->lock, flags); + + /* + * Load the PIO receive ring + */ + + z8530_rx_done(c); + z8530_rx_done(c); + + /* + * Load the DMA interfaces up + */ + + c->rxdma_on = 0; + c->txdma_on = 0; + c->tx_dma_used=0; c->dma_num=0; c->dma_ready=1; @@ -1106,10 +1142,9 @@ c->tx_dma_used = 1; c->irqs = &z8530_txdma_sync; - printk("Loading RX\n"); z8530_rtsdtr(c,1); - printk("Rx interrupts ON\n"); write_zsreg(c, R3, c->regs[R3]|RxENABLE); + spin_unlock_irqrestore(c->lock, flags); return 0; } @@ -1129,6 +1164,9 @@ { unsigned long flags; u8 chk; + + + spin_lock_irqsave(c->lock, flags); c->irqs = &z8530_nop; c->max = 0; @@ -1208,25 +1246,11 @@ EXPORT_SYMBOL(z8530_describe); -/** - * z8530_init - Initialise a Z8530 device - * @dev: Z8530 device to initialise. - * - * Configure up a Z8530/Z85C30 or Z85230 chip. We check the device - * is present, identify the type and then program it to hopefully - * keep quite and behave. This matters a lot, a Z8530 in the wrong - * state will sometimes get into stupid modes generating 10Khz - * interrupt streams and the like. - * - * We set the interrupt handler up to discard any events, in case - * we get them during reset or setp. - * - * Return 0 for success, or a negative value indicating the problem - * in errno form. +/* + * Locked operation part of the z8530 init code */ - -int z8530_init(struct z8530_dev *dev) +static int do_z8530_init(struct z8530_dev *dev) { /* NOP the interrupt handlers first - we might get a floating IRQ transition when we reset the chip */ @@ -1234,6 +1258,12 @@ dev->chanB.irqs=&z8530_nop; dev->chanA.dcdcheck=DCD; dev->chanB.dcdcheck=DCD; + + /* Set up the chip level lock */ + spin_lock_init(&dev->lock); + dev->chanA.lock = &dev->lock; + dev->chanB.lock = &dev->lock; + /* Reset the chip */ write_zsreg(&dev->chanA, R9, 0xC0); udelay(200); @@ -1287,6 +1317,40 @@ return 0; } +/** + * z8530_init - Initialise a Z8530 device + * @dev: Z8530 device to initialise. + * + * Configure up a Z8530/Z85C30 or Z85230 chip. We check the device + * is present, identify the type and then program it to hopefully + * keep quite and behave. This matters a lot, a Z8530 in the wrong + * state will sometimes get into stupid modes generating 10Khz + * interrupt streams and the like. + * + * We set the interrupt handler up to discard any events, in case + * we get them during reset or setp. + * + * Return 0 for success, or a negative value indicating the problem + * in errno form. + */ + +int z8530_init(struct z8530_dev *dev) +{ + unsigned long flags; + int ret; + + /* Set up the chip level lock */ + spin_lock_init(&dev->lock); + dev->chanA.lock = &dev->lock; + dev->chanB.lock = &dev->lock; + + spin_lock_irqsave(&dev->lock, flags); + ret = do_z8530_init(dev); + spin_unlock_irqrestore(&dev->lock, flags); + + return ret; +} + EXPORT_SYMBOL(z8530_init); @@ -1297,15 +1361,22 @@ * We set the interrupt handlers to silence any interrupts. We then * reset the chip and wait 100uS to be sure the reset completed. Just * in case the caller then tries to do stuff. + * + * This is called without the lock held */ int z8530_shutdown(struct z8530_dev *dev) { + unsigned long flags; /* Reset the chip */ + + spin_lock_irqsave(&dev->lock, flags); dev->chanA.irqs=&z8530_nop; dev->chanB.irqs=&z8530_nop; write_zsreg(&dev->chanA, R9, 0xC0); + /* We must lock the udelay, the chip is offlimits here */ udelay(100); + spin_unlock_irqrestore(&dev->lock, flags); return 0; } @@ -1324,6 +1395,10 @@ int z8530_channel_load(struct z8530_channel *c, u8 *rtable) { + unsigned long flags; + + spin_lock_irqsave(c->lock, flags); + while(*rtable!=255) { int reg=*rtable++; @@ -1344,6 +1419,8 @@ c->status=read_zsreg(c, R0); c->sync=1; write_zsreg(c, R3, c->regs[R3]|RxENABLE); + + spin_unlock_irqrestore(c->lock, flags); return 0; } @@ -1360,6 +1437,8 @@ * * Note: We are handling this code path in the interrupt path, keep it * fast or bad things will happen. + * + * Called with the lock held. */ static void z8530_tx_begin(struct z8530_channel *c) @@ -1430,8 +1509,7 @@ } else { - save_flags(flags); - cli(); + /* ABUNDER off */ write_zsreg(c, R10, c->regs[10]); write_zsctrl(c, RES_Tx_CRC); @@ -1442,7 +1520,7 @@ write_zsreg(c, R8, *c->tx_ptr++); c->txcount--; } - restore_flags(flags); + } } } @@ -1454,25 +1532,22 @@ * This is called when we complete a packet send. We wake the queue, * start the next packet going and then free the buffer of the existing * packet. This code is fairly timing sensitive. + * + * Called with the register lock held. */ static void z8530_tx_done(struct z8530_channel *c) { - unsigned long flags; struct sk_buff *skb; - spin_lock_irqsave(&z8530_buffer_lock, flags); netif_wake_queue(c->netdevice); /* Actually this can happen.*/ if(c->tx_skb==NULL) - { - spin_unlock_irqrestore(&z8530_buffer_lock, flags); return; - } + skb=c->tx_skb; c->tx_skb=NULL; z8530_tx_begin(c); - spin_unlock_irqrestore(&z8530_buffer_lock, flags); c->stats.tx_packets++; c->stats.tx_bytes+=skb->len; dev_kfree_skb_irq(skb); @@ -1489,7 +1564,7 @@ void z8530_null_rx(struct z8530_channel *c, struct sk_buff *skb) { - kfree_skb(skb); + dev_kfree_skb_any(skb); } EXPORT_SYMBOL(z8530_null_rx); @@ -1503,6 +1578,8 @@ * ESCC mode, but on the older chips we have no choice. We flip to the * new buffer immediately in DMA mode so that the DMA of the next * frame can occur while we are copying the previous buffer to an sk_buff + * + * Called with the lock held */ static void z8530_rx_done(struct z8530_channel *c) @@ -1673,6 +1750,9 @@ * hard to hit interrupt latencies for the Z85230 per packet * even in DMA mode we do the flip to DMA buffer if needed here * not in the IRQ. + * + * Called from the network code. The lock is not held at this + * point. */ int z8530_queue_xmit(struct z8530_channel *c, struct sk_buff *skb) @@ -1711,9 +1791,9 @@ c->tx_next_skb=skb; RT_UNLOCK; - spin_lock_irqsave(&z8530_buffer_lock, flags); + spin_lock_irqsave(c->lock, flags); z8530_tx_begin(c); - spin_unlock_irqrestore(&z8530_buffer_lock, flags); + spin_unlock_irqrestore(c->lock, flags); netif_wake_queue(c->netdevice); return 0; @@ -1727,6 +1807,9 @@ * * Get the statistics block. We keep the statistics in software as * the chip doesn't do it for us. + * + * Locking is ignored here - we could lock for a copy but its + * not likely to be that big an issue */ struct net_device_stats *z8530_get_stats(struct z8530_channel *c) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/wan/z85230.h linux.ac/drivers/net/wan/z85230.h --- linux.vanilla/drivers/net/wan/z85230.h Tue Feb 13 21:15:05 2001 +++ linux.ac/drivers/net/wan/z85230.h Fri Oct 12 12:56:39 2001 @@ -368,6 +368,8 @@ unsigned char tx_active; /* character is being xmitted */ unsigned char tx_stopped; /* output is suspended */ + + spinlock_t *lock; /* Devicr lock */ }; /* @@ -386,6 +388,7 @@ int irq; /* Interrupt for the device */ int active; /* Soft interrupt enable - the Mac doesn't always have a hard disable on its 8530s... */ + spinlock_t lock; }; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/wavelan.c linux.ac/drivers/net/wavelan.c --- linux.vanilla/drivers/net/wavelan.c Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/net/wavelan.c Wed Oct 10 01:48:05 2001 @@ -2059,6 +2059,10 @@ range.max_qual.qual = MMR_SGNL_QUAL; range.max_qual.level = MMR_SIGNAL_LVL; range.max_qual.noise = MMR_SILENCE_LVL; + range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */ + /* Need to get better values for those two */ + range.avg_qual.level = 30; + range.avg_qual.noise = 8; range.num_bitrates = 1; range.bitrate[0] = 2000000; /* 2 Mb/s */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/wavelan.p.h linux.ac/drivers/net/wavelan.p.h --- linux.vanilla/drivers/net/wavelan.p.h Mon Jul 2 22:03:04 2001 +++ linux.ac/drivers/net/wavelan.p.h Fri Oct 12 12:54:44 2001 @@ -447,13 +447,13 @@ /* ------------------------ PRIVATE IOCTL ------------------------ */ -#define SIOCSIPQTHR SIOCDEVPRIVATE /* Set quality threshold */ -#define SIOCGIPQTHR SIOCDEVPRIVATE + 1 /* Get quality threshold */ -#define SIOCSIPLTHR SIOCDEVPRIVATE + 2 /* Set level threshold */ -#define SIOCGIPLTHR SIOCDEVPRIVATE + 3 /* Get level threshold */ +#define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */ +#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */ +#define SIOCSIPLTHR SIOCIWFIRSTPRIV + 2 /* Set level threshold */ +#define SIOCGIPLTHR SIOCIWFIRSTPRIV + 3 /* Get level threshold */ -#define SIOCSIPHISTO SIOCDEVPRIVATE + 6 /* Set histogram ranges */ -#define SIOCGIPHISTO SIOCDEVPRIVATE + 7 /* Get histogram values */ +#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 6 /* Set histogram ranges */ +#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 7 /* Get histogram values */ /****************************** TYPES ******************************/ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/nubus/nubus_syms.c linux.ac/drivers/nubus/nubus_syms.c --- linux.vanilla/drivers/nubus/nubus_syms.c Sat Sep 4 21:08:32 1999 +++ linux.ac/drivers/nubus/nubus_syms.c Wed Oct 10 01:48:05 2001 @@ -12,6 +12,8 @@ EXPORT_SYMBOL(nubus_proc_detach_device); #endif +MODULE_LICENSE("GPL"); + EXPORT_SYMBOL(nubus_find_device); EXPORT_SYMBOL(nubus_find_type); EXPORT_SYMBOL(nubus_find_slot); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/parport/ChangeLog linux.ac/drivers/parport/ChangeLog --- linux.vanilla/drivers/parport/ChangeLog Thu Oct 11 13:52:16 2001 +++ linux.ac/drivers/parport/ChangeLog Thu Oct 11 16:26:19 2001 @@ -57,6 +57,12 @@ * share.c (parport_unregister_device): Remove device from wait list too. +2001-07-04 Tim Waugh + + * parport_pc.c (init_module): Don't try to load parport_serial. + This means that the user needs to load it (or a hardware detection + program on their behalf) if necessary. + 2001-06-20 Tim Waugh * parport_pc.c: Make 'io_hi=0' work. @@ -64,6 +70,7 @@ 2001-05-31 Tim Waugh * parport_serial.c: New file. + * parport_pc.c (init_module): Try to load parport_serial. 2001-06-05 Tim Waugh diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/parport/Config.in linux.ac/drivers/parport/Config.in --- linux.vanilla/drivers/parport/Config.in Fri Sep 14 00:04:43 2001 +++ linux.ac/drivers/parport/Config.in Wed Oct 10 01:48:05 2001 @@ -45,6 +45,12 @@ else define_tristate CONFIG_PARPORT_ATARI n fi + if [ "$CONFIG_GSC_LASI" = "y" ]; then + dep_tristate ' LASI/ASP builtin parallel-port' CONFIG_PARPORT_GSC $CONFIG_PARPORT + else + define_tristate CONFIG_PARPORT_GSC n + fi + if [ "$CONFIG_SBUS" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' Sparc hardware (EXPERIMENTAL)' CONFIG_PARPORT_SUNBPP $CONFIG_PARPORT else diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/parport/ieee1284_ops.c linux.ac/drivers/parport/ieee1284_ops.c --- linux.vanilla/drivers/parport/ieee1284_ops.c Thu Oct 11 13:52:17 2001 +++ linux.ac/drivers/parport/ieee1284_ops.c Thu Oct 11 23:33:10 2001 @@ -362,7 +362,7 @@ } else { DPRINTK (KERN_DEBUG "%s: ECP direction: failed to reverse\n", port->name); - port->ieee1284.phase = IEEE1284_PH_DIR_UNKNOWN; + port->ieee1284.phase = IEEE1284_PH_ECP_DIR_UNKNOWN; } return retval; @@ -394,7 +394,7 @@ DPRINTK (KERN_DEBUG "%s: ECP direction: failed to switch forward\n", port->name); - port->ieee1284.phase = IEEE1284_PH_DIR_UNKNOWN; + port->ieee1284.phase = IEEE1284_PH_ECP_DIR_UNKNOWN; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/parport/parport_amiga.c linux.ac/drivers/parport/parport_amiga.c --- linux.vanilla/drivers/parport/parport_amiga.c Wed May 23 03:54:04 2001 +++ linux.ac/drivers/parport/parport_amiga.c Wed Oct 10 01:48:05 2001 @@ -298,6 +298,7 @@ MODULE_AUTHOR("Joerg Dorchain "); MODULE_DESCRIPTION("Parport Driver for Amiga builtin Port"); MODULE_SUPPORTED_DEVICE("Amiga builtin Parallel Port"); +MODULE_LICENSE("GPL"); module_init(parport_amiga_init) module_exit(parport_amiga_exit) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/parport/parport_atari.c linux.ac/drivers/parport/parport_atari.c --- linux.vanilla/drivers/parport/parport_atari.c Tue Nov 28 01:11:26 2000 +++ linux.ac/drivers/parport/parport_atari.c Wed Oct 10 01:48:05 2001 @@ -249,6 +249,7 @@ MODULE_AUTHOR("Andreas Schwab"); MODULE_DESCRIPTION("Parport Driver for Atari builtin Port"); MODULE_SUPPORTED_DEVICE("Atari builtin Parallel Port"); +MODULE_LICENSE("GPL"); int init_module(void) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/parport/parport_pc.c linux.ac/drivers/parport/parport_pc.c --- linux.vanilla/drivers/parport/parport_pc.c Thu Oct 11 13:52:17 2001 +++ linux.ac/drivers/parport/parport_pc.c Thu Oct 11 16:26:23 2001 @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,10 @@ #include #include +#if defined (CONFIG_PNPBIOS) || defined (CONFIG_PNPBIOS_MODULE) +#include +#endif + #define PARPORT_PC_MAX_PORTS PARPORT_MAX /* ECR modes */ @@ -2814,6 +2819,67 @@ return count; } +#if defined (CONFIG_PNPBIOS) || defined (CONFIG_PNPBIOS_MODULE) + +#define UNSET(res) ((res).flags & IORESOURCE_UNSET) + +int init_pnp040x(struct pci_dev *dev) +{ + int io,iohi,irq,dma; + + printk(KERN_INFO + "parport: PnP BIOS reports device %s %s (node number 0x%x) is ", + dev->name, dev->slot_name, dev->devfn + ); + + if ( UNSET(dev->resource[0]) ) { + printk("not configured.\n"); + return 0; + } + io = dev->resource[0].start; + printk("configured to use io 0x%04x",io); + if ( UNSET(dev->resource[1]) ) { + iohi = 0; + } else { + iohi = dev->resource[1].start; + printk(", io 0x%04x",iohi); + } + + if ( UNSET(dev->irq_resource[0]) ) { + irq = PARPORT_IRQ_NONE; + } else { + if ( dev->irq_resource[0].start == (unsigned long)-1 ) { + irq = PARPORT_IRQ_NONE; + printk(", irq disabled"); + } else { + irq = dev->irq_resource[0].start; + printk(", irq %d",irq); + } + } + + if ( UNSET(dev->dma_resource[0]) ) { + dma = PARPORT_DMA_NONE; + } else { + if ( dev->dma_resource[0].start == (unsigned long)-1 ) { + dma = PARPORT_DMA_NONE; + printk(", dma disabled"); + } else { + dma = dev->dma_resource[0].start; + printk(", dma %d",dma); + } + } + + printk("\n"); + + if (parport_pc_probe_port(io,iohi,irq,dma,NULL)) + return 1; + + return 0; +} +#undef UNSET + +#endif + /* This function is called by parport_pc_init if the user didn't * specify any ports to probe. Its job is to find some ports. Order * is important here -- we want ISA ports to be registered first, @@ -2827,12 +2893,21 @@ static int __init parport_pc_find_ports (int autoirq, int autodma) { int count = 0, r; + struct pci_dev *dev; #ifdef CONFIG_PARPORT_PC_SUPERIO detect_and_report_winbond (); detect_and_report_smsc (); #endif +#if defined (CONFIG_PNPBIOS) || defined (CONFIG_PNPBIOS_MODULE) + dev=NULL; + while ((dev=pnpbios_find_device("PNP0400",dev))) + count+=init_pnp040x(dev); + while ((dev=pnpbios_find_device("PNP0401",dev))) + count+=init_pnp040x(dev); +#endif + /* Onboard SuperIO chipsets that show themselves on the PCI bus. */ count += parport_pc_init_superio (autoirq, autodma); @@ -2884,6 +2959,8 @@ MODULE_AUTHOR("Phil Blundell, Tim Waugh, others"); MODULE_DESCRIPTION("PC-style parallel port driver"); +MODULE_LICENSE("GPL"); + MODULE_PARM_DESC(io, "Base I/O address (SPP regs)"); MODULE_PARM(io, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i"); MODULE_PARM_DESC(io_hi, "Base I/O address (ECR)"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pci/gen-devlist.c linux.ac/drivers/pci/gen-devlist.c --- linux.vanilla/drivers/pci/gen-devlist.c Mon Oct 16 20:56:50 2000 +++ linux.ac/drivers/pci/gen-devlist.c Wed Oct 10 01:48:05 2001 @@ -68,6 +68,7 @@ bra[-1] = 0; if (vendor_len + strlen(c) + 1 > MAX_NAME_SIZE) { fprintf(stderr, "Line %d: Device name too long\n", lino); + fprintf(stderr, "%s\n", c); return 1; } } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pci/pci.c linux.ac/drivers/pci/pci.c --- linux.vanilla/drivers/pci/pci.c Tue Sep 18 06:52:35 2001 +++ linux.ac/drivers/pci/pci.c Wed Oct 10 01:48:05 2001 @@ -959,10 +959,10 @@ res = child->resource[0]; pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); - base = (io_base_lo & PCI_IO_RANGE_MASK) << 8; - limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8; + base = (unsigned long)(io_base_lo & PCI_IO_RANGE_MASK) << 8; + limit = (unsigned long)(io_limit_lo & PCI_IO_RANGE_MASK) << 8; - if ((base & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { + if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { u16 io_base_hi, io_limit_hi; pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi); pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi); @@ -987,8 +987,8 @@ res = child->resource[1]; pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo); pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo); - base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16; - limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; + base = (unsigned long)(mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16; + limit = (unsigned long)(mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; if (base && base <= limit) { res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; res->start = base; @@ -1003,16 +1003,16 @@ res = child->resource[2]; pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo); - base = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16; - limit = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16; + base = (unsigned long)(mem_base_lo & PCI_PREF_RANGE_MASK) << 16; + limit = (unsigned long)(mem_limit_lo & PCI_PREF_RANGE_MASK) << 16; if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { u32 mem_base_hi, mem_limit_hi; pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi); pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi); #if BITS_PER_LONG == 64 - base |= ((long) mem_base_hi) << 32; - limit |= ((long) mem_limit_hi) << 32; + base |= ((unsigned long) mem_base_hi) << 32; + limit |= ((unsigned long) mem_limit_hi) << 32; #else if (mem_base_hi || mem_limit_hi) { printk(KERN_ERR "PCI: Unable to handle 64-bit address space for %s\n", child->name); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pci/pci.ids linux.ac/drivers/pci/pci.ids --- linux.vanilla/drivers/pci/pci.ids Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/pci/pci.ids Thu Oct 11 18:00:50 2001 @@ -3107,34 +3107,104 @@ 0396 SDRAM controller 0397 BIOS scratchpad 127a Rockwell International - 1002 HCF 56k V90 FaxModem - 127a 1002 HCF 56k V90 Modem - 1003 HCF 56k V90 FaxModem - 127a 1003 PCI56RX Modem - 13df 1003 PCI56RX Modem - 1004 HCF 56k V90 FaxModem - 1005 HCF 56k V90 FaxModem - 122d 4008 MDP3858SP-A SVD Modem - 127a 1005 PCI56RVP Modem - 13df 1005 PCI56RVP Modem - 1436 1005 WS-5614PS3G - 1025 HCF 56k PCI Modem - 127a 1025 HCF 56k PCI Modem + 1002 HCF 56k Data/Fax Modem + 122d 4002 HPG / MDP3858-U # Aztech + 122d 4005 MDP3858-E # Aztech + 122d 4007 MDP3858-A/-NZ # Aztech + 122d 4012 MDP3858-SA # Aztech + 122d 4017 MDP3858-W # Aztech + 122d 4018 MDP3858-W # Aztech + 1003 HCF 56k Data/Fax Modem + 0e11 b0bc 229-DF Zephyr # Compaq + 0e11 b114 229-DF Cheetah # Compaq + 1033 802b 229-DF # NEC + 13df 1003 PCI56RX Modem # E-Tech Inc + 13e0 0117 IBM # GVC + 13e0 0147 IBM # GVC + 13e0 0197 IBM # GVC + 13e0 01c7 IBM # GVC + 13e0 01f7 IBM # GVC + 1436 1003 IBM # CIS + 1436 1103 IBM # CIS + 1436 1602 Compaq 229-DF Ducati + 1004 HCF 56k Data/Fax/Voice Modem + 10cf 1059 Fujitsu 229-DFRT + 1005 HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + 1033 8029 229-DFSV # NEC + 1033 8054 Modem # NEC + 10cf 103c Fujitsu + 10cf 1055 Fujitsu 229-DFSV + 10cf 1056 Fujitsu 229-DFSV + 122d 4003 MDP3858SP-U # Aztech + 122d 4006 Packard Bell MDP3858V-E # Aztech + 122d 4008 MDP3858SP-A/SP-NZ # Aztech + 122d 4009 MDP3858SP-E # Aztech + 122d 4010 MDP3858V-U # Aztech + 122d 4011 MDP3858SP-SA # Aztech + 122d 4013 MDP3858V-A/V-NZ # Aztech + 122d 4015 MDP3858SP-W # Aztech + 122d 4016 MDP3858V-W # Aztech + 122d 4019 MDP3858V-SA # Aztech + 13df 1005 PCI56RVP Modem # E-Tech Inc + 13e0 0187 IBM # GVC + 13e0 01a7 IBM # GVC + 13e0 01b7 IBM # GVC + 13e0 01d7 IBM # GVC + 1436 1005 IBM # CIS + 1436 1105 IBM # CIS + 1023 HCF 56k Data/Fax Modem + 122d 4020 Packard Bell MDP3858-WE # Aztech + 122d 4023 MDP3858-UE # Aztech + 13e0 0247 IBM # GVC + 13e0 0297 IBM # GVC + 13e0 02c7 IBM # GVC + 1436 1203 IBM # CIS + 1436 1303 IBM # CIS + 1024 HCF 56k Data/Fax/Voice Modem + 1025 HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + 10cf 106a Fujitsu 235-DFSV + 122d 4021 Packard Bell MDP3858V-WE # Aztech + 122d 4022 MDP3858SP-WE # Aztech + 122d 4024 MDP3858V-UE # Aztech + 122d 4025 MDP3858SP-UE # Aztech 1026 HCF 56k PCI Speakerphone Modem - 127a 1026 HCF 56k PCI Speakerphone Modem 1035 HCF 56k PCI Speakerphone Modem - 127a 1035 HCF 56k PCI Speakerphone Modem - 1085 Volcano HCF 56k PCI Modem - 127a 1085 Volcano HCF 56k PCI Modem - 2005 HCF 56k V90 FaxModem - 127a 2005 Conexant SoftK56 Speakerphone Modem - 2015 Conexant SoftK56 Speakerphone Modem - 127a 2015 Conexant SoftK56 Speakerphone Modem - 14c8 2115 Conexant SoftK56 Speakerphone Modem + 1085 HCF 56k Volcano PCI Modem + 2005 HCF 56k Data/Fax Modem + 104d 8044 229-DFSV # Sony + 104d 8045 229-DFSV # Sony + 104d 8055 PBE/Aztech 235W-DFSV # Sony + 104d 8056 235-DFSV # Sony + 104d 805a Modem # Sony + 104d 805f Modem # Sony + 104d 8074 Modem # Sony + 2013 HSF 56k Data/Fax Modem + 1179 0001 Modem # Toshiba + 1179 ff00 Modem # Toshiba + 2014 HSF 56k Data/Fax/Voice Modem + 10cf 1057 Fujitsu Citicorp III + 122d 4050 MSP3880-U # Aztech + 122d 4055 MSP3880-W # Aztech + 2015 HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + 10cf 1063 Fujitsu + 10cf 1064 Fujitsu + 1468 2015 Fujitsu + 2016 HSF 56k Data/Fax/Voice/Spkp Modem + 122d 4051 MSP3880V-W # Aztech + 122d 4052 MSP3880SP-W # Aztech + 122d 4054 MSP3880V-U # Aztech + 122d 4056 MSP3880SP-U # Aztech + 122d 4057 MSP3880SP-A # Aztech + 4311 Riptide HSF 56k PCI Modem + 127a 4311 Ring Modular? Riptide HSF RT HP Dom + 13e0 0210 HP-GVC 4320 Riptide PCI Audio Controller 1235 4320 Riptide PCI Audio Controller 4321 Riptide HCF 56k PCI Modem - 1235 4321 Riptide HCF 56k PCI Modem + 1235 4321 Hewlett Packard DF + 1235 4324 Hewlett Packard DF + 13e0 0210 Hewlett Packard DF + 144d 2321 Riptide # Samsung 4322 Riptide PCI Game Controller 1235 4322 Riptide PCI Game Controller 8234 RapidFire 616X ATM155 Adapter @@ -3884,6 +3954,7 @@ 148b INNOMEDIALOGIC Inc. 148c C.P. Technology Co. Ltd 148d DIGICOM Systems, Inc. + 1003 HCF 56k Data/Fax Modem 148e OSI Plus Corporation 148f Plant Equipment, Inc. 1490 Stone Microsystems PTY Ltd. @@ -4021,30 +4092,123 @@ 14ee MASPRO KENKOH Corp 14ef CARRY Computer ENG. CO Ltd 14f0 CANON RESEACH CENTRE FRANCE -14f1 CONEXANT - 1033 56K Winmodem - 13e0 02b0 56K Winmodem - 1035 PCI Modem Enumerator - 2003 SoftK56 Winmodem - 14f1 2003 SoftK56 Winmodem - 2004 SoftK56 RemoteTAM Winmodem - 14f1 2004 SoftK56 RemoteTAM Winmodem - 2005 SoftK56 Speakerphone Winmodem - 14f1 2005 SoftK56 Speakerphone Winmodem - 2006 SoftK56 Speakerphone Winmodem - 14f1 2006 SoftK56 Speakerphone Winmodem - 2013 HSP MicroModem 56K - 14f1 2013 SoftK56 Winmodem - 2014 SoftK56 RemoteTAM Winmodem - 144f 101c SoftK56 RemoteTAM Winmodem - 144f 2014 SoftK56 RemoteTAM Winmodem - 2015 SoftK56 Speakerphone Winmodem - 14c8 2115 SoftK56 Speakerphone Winmodem - 14f1 2015 SoftK56 Speakerphone Winmodem - 2016 SoftK56 Speakerphone Winmodem - 14f1 2016 SoftK56 Speakerphone Winmodem - 2443 SoftK56 Speakerphone Winmodem - 14f1 2443 SoftK56 Speakerphone Winmodem +14f1 Conexant + 1033 HCF 56k Data/Fax Modem + 1033 8077 NEC + 122d 4027 Dell Zeus - MDP3880-W(B) Data Fax Modem # Aztech + 122d 4030 Dell Mercury - MDP3880-U(B) Data Fax Modem # Aztech + 122d 4034 Dell Thor - MDP3880-W(U) Data Fax Modem # Aztech + 13e0 020d Dell Copper + 13e0 020e Dell Silver + 13e0 0261 IBM # GVC + 13e0 0290 Compaq Goldwing + 13e0 02a0 IBM # GVC + 13e0 02b0 IBM # GVC + 13e0 02c0 Compaq Scooter + 13e0 02d0 IBM # GVC + 144f 1500 IBM P85-DF # Askey + 144f 1501 IBM P85-DF # Askey + 144f 150a IBM P85-DF # Askey + 144f 150b IBM P85-DF Low Profile # Askey + 144f 1510 IBM P85-DF Low Profile # Askey + 1034 HCF 56k Data/Fax/Voice Modem + 1035 HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + 10cf 1098 Fujitsu P85-DFSV + 1036 HCF 56k Data/Fax/Voice/Spkp Modem + 122d 4029 MDP3880SP-W # Aztech + 122d 4031 MDP3880SP-U # Aztech + 13e0 0209 Dell Titanium + 13e0 020a Dell Graphite + 13e0 0260 Gateway Red Owl + 13e0 0270 Gateway White Horse + 1052 HCF 56k Data/Fax Modem (Worldwide) + 1053 HCF 56k Data/Fax Modem (Worldwide) + 1054 HCF 56k Data/Fax/Voice Modem (Worldwide) + 1055 HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Worldwide) + 1056 HCF 56k Data/Fax/Voice/Spkp Modem (Worldwide) + 1057 HCF 56k Data/Fax/Voice/Spkp Modem (Worldwide) + 1059 HCF 56k Data/Fax/Voice Modem (Worldwide) + 1063 HCF 56k Data/Fax Modem + 1064 HCF 56k Data/Fax/Voice Modem + 1065 HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + 1066 HCF 56k Data/Fax/Voice/Spkp Modem + 122d 4033 Dell Athena - MDP3900V-U # Aztech + 1433 HCF 56k Data/Fax Modem + 1434 HCF 56k Data/Fax/Voice Modem + 1435 HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + 1436 HCF 56k Data/Fax Modem + 1453 HCF 56k Data/Fax Modem + 13e0 0240 IBM # GVC + 13e0 0250 IBM # GVC + 144f 1502 IBM P95-DF # Askey + 144f 1503 IBM P95-DF # Askey + 1454 HCF 56k Data/Fax/Voice Modem + 1455 HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + 1456 HCF 56k Data/Fax/Voice/Spkp Modem + 122d 4035 Dell Europa - MDP3900V-W # Aztech + 122d 4302 MP3930V-W(C) MiniPCI # Aztech + 1803 HCF 56k Modem + 0e11 0023 623-LAN Grizzly # Compaq + 0e11 0043 623-LAN Yogi # Compaq + 1815 HCF 56k Modem + 0e11 0022 Grizzly # Compaq + 0e11 0042 Yogi # Compaq + 2003 HSF 56k Data/Fax Modem + 2004 HSF 56k Data/Fax/Voice Modem + 2005 HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + 2006 HSF 56k Data/Fax/Voice/Spkp Modem + 2013 HSF 56k Data/Fax Modem + 0e11 b195 Bear # Compaq + 0e11 b196 Seminole 1 # Compaq + 0e11 b1be Seminole 2 # Compaq + 1025 8013 Acer + 1033 809d NEC + 1033 80bc NEC + 155d 6793 HP + 155d 8850 E Machines + 2014 HSF 56k Data/Fax/Voice Modem + 2015 HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + 2016 HSF 56k Data/Fax/Voice/Spkp Modem + 2043 HSF 56k Data/Fax Modem (Worldwide SmartDAA) + 2044 HSF 56k Data/Fax/Voice Modem (Worldwide SmartDAA) + 2045 HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Worldwide SmartDAA) + 2046 HSF 56k Data/Fax/Voice/Spkp Modem (Worldwide SmartDAA) + 2063 HSF 56k Data/Fax Modem (SmartDAA) + 2064 HSF 56k Data/Fax/Voice Modem (SmartDAA) + 2065 HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (SmartDAA) + 2066 HSF 56k Data/Fax/Voice/Spkp Modem (SmartDAA) + 2093 HSF 56k Modem + 155d 2f07 Legend + 2143 HSF 56k Data/Fax/Cell Modem (Mobile Worldwide SmartDAA) + 2144 HSF 56k Data/Fax/Voice/Cell Modem (Mobile Worldwide SmartDAA) + 2145 HSF 56k Data/Fax/Voice/Spkp (w/HS)/Cell Modem (Mob WW SmartDAA) + 2146 HSF 56k Data/Fax/Voice/Spkp/Cell Modem (Mobile Worldwide SmartDAA) + 2163 HSF 56k Data/Fax/Cell Modem (Mobile SmartDAA) + 2164 HSF 56k Data/Fax/Voice/Cell Modem (Mobile SmartDAA) + 2165 HSF 56k Data/Fax/Voice/Spkp (w/Handset)/Cell Modem (Mobile SmartDAA) + 2166 HSF 56k Data/Fax/Voice/Spkp/Cell Modem (Mobile SmartDAA) + 2343 HSF 56k Data/Fax CardBus Modem (Mobile Worldwide SmartDAA) + 2344 HSF 56k Data/Fax/Voice CardBus Modem (Mobile Worldwide SmartDAA) + 2345 HSF 56k Data/Fax/Voice/Spkp (w/HS) CardBus Modem (Mob WW SmartDAA) + 2346 HSF 56k Data/Fax/Voice/Spkp CardBus Modem (Mobile Worldwide SmartDAA) + 2363 HSF 56k Data/Fax CardBus Modem (Mobile SmartDAA) + 2364 HSF 56k Data/Fax/Voice CardBus Modem (Mobile SmartDAA) + 2365 HSF 56k Data/Fax/Voice/Spkp (w/HS) CardBus Modem (Mob SmartDAA) + 2366 HSF 56k Data/Fax/Voice/Spkp CardBus Modem (Mobile SmartDAA) + 2443 HSF 56k Data/Fax Modem (Mobile Worldwide SmartDAA) + 104d 8075 Modem # Sony + 104d 8083 Modem # Sony + 104d 8097 Modem # Sony + 2444 HSF 56k Data/Fax/Voice Modem (Mobile Worldwide SmartDAA) + 2445 HSF 56k Data/Fax/Voice/Spkp (w/HS) Modem (Mobile WW SmartDAA) + 2446 HSF 56k Data/Fax/Voice/Spkp Modem (Mobile Worldwide SmartDAA) + 2463 HSF 56k Data/Fax Modem (Mobile SmartDAA) + 2464 HSF 56k Data/Fax/Voice Modem (Mobile SmartDAA) + 2465 HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Mobile SmartDAA) + 2466 HSF 56k Data/Fax/Voice/Spkp Modem (Mobile SmartDAA) + 2f00 HSF 56k HSFi Modem + 13e0 8d84 IBM HSFi V.90 + 13e0 8d85 Compaq Stinger 14f2 MOBILITY Electronics 14f3 BROADLOGIC 14f4 TOKYO Electronic Industry CO Ltd diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pci/quirks.c linux.ac/drivers/pci/quirks.c --- linux.vanilla/drivers/pci/quirks.c Tue Sep 18 22:23:15 2001 +++ linux.ac/drivers/pci/quirks.c Wed Oct 10 01:48:05 2001 @@ -411,6 +411,27 @@ pci_write_config_dword(dev, PCI_CB_LEGACY_MODE_BASE, 0); } +/* + * The AMD io apic can hang the box when an apic irq is masked. + * We check all revs >= B0 (yet not in the pre production!) as the bug + * is currently marked NoFix + * + * We have multiple reports of hangs with this chipset that went away with + * noapic specified. For the moment we assume its the errata. We may be wrong + * of course. However the advice is demonstrably good even if so.. + */ + +static void __init quirk_amd_ioapic(struct pci_dev *dev) +{ + u8 rev; + + pci_read_config_byte(dev, PCI_REVISION_ID, &rev); + if(rev >= 0x02) + { + printk(KERN_WARNING "I/O APIC: AMD Errata #22 may be present. In the event of instability try\n"); + printk(KERN_WARNING " : booting with the \"noapic\" option.\n"); + } +} /* * The main table of quirks. @@ -462,6 +483,8 @@ { PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_2, quirk_via_irqpic }, { PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, quirk_via_irqpic }, { PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_6, quirk_via_irqpic }, + + { PCI_FIXUP_FINAL, PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, quirk_amd_ioapic }, { 0 } }; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/Makefile linux.ac/drivers/pcmcia/Makefile --- linux.vanilla/drivers/pcmcia/Makefile Tue Mar 27 00:36:30 2001 +++ linux.ac/drivers/pcmcia/Makefile Wed Oct 10 01:48:05 2001 @@ -53,10 +53,33 @@ endif endif +obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o + +sa1100_cs-objs-y := sa1100_generic.o +sa1100_cs-objs-$(CONFIG_SA1100_ASSABET) += sa1100_assabet.o +sa1100_cs-objs-$(CONFIG_ASSABET_NEPONSET) += sa1100_neponset.o +sa1100_cs-objs-$(CONFIG_SA1100_H3600) += sa1100_h3600.o +sa1100_cs-objs-$(CONFIG_SA1100_CERF) += sa1100_cerf.o +sa1100_cs-objs-$(CONFIG_SA1100_GRAPHICSCLIENT) += sa1100_graphicsclient.o +sa1100_cs-objs-$(CONFIG_SA1100_XP860) += sa1100_xp860.o +sa1100_cs-objs-$(CONFIG_SA1100_PANGOLIN) += sa1100_pangolin.o +sa1100_cs-objs-$(CONFIG_SA1100_YOPY) += sa1100_yopy.o +sa1100_cs-objs-$(CONFIG_SA1100_FREEBIRD) += sa1100_freebird.o +sa1100_cs-objs-$(CONFIG_SA1100_PFS168) += sa1100_pfs168.o +sa1100_cs-objs-$(CONFIG_SA1100_JORNADA720) += sa1100_jornada720.o +sa1100_cs-objs-$(CONFIG_SA1100_FLEXANET) += sa1100_flexanet.o +sa1100_cs-objs-$(CONFIG_SA1100_SIMPAD) += sa1100_simpad.o +sa1100_cs-objs-$(CONFIG_SA1100_GRAPHICSMASTER) += sa1100_graphicsmaster.o +sa1100_cs-objs-$(CONFIG_SA1100_ADSBITSY) += sa1100_adsbitsy.o +sa1100_cs-objs-$(CONFIG_SA1100_STORK) += sa1100_stork.o + include $(TOPDIR)/Rules.make pcmcia_core.o: $(pcmcia_core-objs) $(LD) $(LD_RFLAG) -r -o $@ $(pcmcia_core-objs) + +sa1100_cs.o: $(sa1100_cs-objs-y) + $(LD) -r -o $@ $(sa1100_cs-objs-y) yenta_socket.o: $(yenta_socket-objs) $(LD) $(LD_RFLAG) -r -o $@ $(yenta_socket-objs) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/cs.c linux.ac/drivers/pcmcia/cs.c --- linux.vanilla/drivers/pcmcia/cs.c Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/pcmcia/cs.c Wed Oct 10 01:48:05 2001 @@ -789,6 +789,10 @@ *base, align); align = 0; } + if ((s->cap.features & SS_CAP_STATIC_MAP) && s->cap.io_offset) { + *base = s->cap.io_offset | (*base & 0x0fff); + return 0; + } /* Check for an already-allocated window that must conflict with what was asked for. It is a hack because it does not catch all potential conflicts, just the most obvious ones. */ @@ -833,7 +837,8 @@ ioaddr_t num) { int i; - release_region(base, num); + if(!(s->cap.features & SS_CAP_STATIC_MAP)) + release_region(base, num); for (i = 0; i < MAX_IO_WIN; i++) { if ((s->io[i].BasePort <= base) && (s->io[i].BasePort+s->io[i].NumPorts >= base+num)) { @@ -1623,7 +1628,8 @@ s->state &= ~SOCKET_WIN_REQ(win->index); /* Release system memory */ - release_mem_region(win->base, win->size); + if(!(s->cap.features & SS_CAP_STATIC_MAP)) + release_mem_region(win->base, win->size); win->handle->state &= ~CLIENT_WIN_REQ(win->index); win->magic = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100.h linux.ac/drivers/pcmcia/sa1100.h --- linux.vanilla/drivers/pcmcia/sa1100.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100.h Wed Oct 10 01:48:05 2001 @@ -0,0 +1,202 @@ +/*====================================================================== + + Device driver for the PCMCIA control functionality of StrongARM + SA-1100 microprocessors. + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is John G. Dorsey + . Portions created by John G. Dorsey are + Copyright (C) 1999 John G. Dorsey. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#if !defined(_PCMCIA_SA1100_H) +# define _PCMCIA_SA1100_H + +#include +#include +#include +#include +#include "cs_internal.h" + +#include + + +/* MECR: Expansion Memory Configuration Register + * (SA-1100 Developers Manual, p.10-13; SA-1110 Developers Manual, p.10-24) + * + * MECR layout is: + * + * FAST1 BSM1<4:0> BSA1<4:0> BSIO1<4:0> FAST0 BSM0<4:0> BSA0<4:0> BSIO0<4:0> + * + * (This layout is actually true only for the SA-1110; the FASTn bits are + * reserved on the SA-1100.) + */ + +#define MECR_SOCKET_0_SHIFT (0) +#define MECR_SOCKET_1_SHIFT (16) + +#define MECR_BS_MASK (0x1f) +#define MECR_FAST_MODE_MASK (0x01) + +#define MECR_BSIO_SHIFT (0) +#define MECR_BSA_SHIFT (5) +#define MECR_BSM_SHIFT (10) +#define MECR_FAST_SHIFT (15) + +#define MECR_SET(mecr, sock, shift, mask, bs) \ +((mecr)=((mecr)&~(((mask)<<(shift))<<\ + ((sock)==0?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT)))|\ + (((bs)<<(shift))<<((sock)==0?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT))) + +#define MECR_GET(mecr, sock, shift, mask) \ +((((mecr)>>(((sock)==0)?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT))>>\ + (shift))&(mask)) + +#define MECR_BSIO_SET(mecr, sock, bs) \ +MECR_SET((mecr), (sock), MECR_BSIO_SHIFT, MECR_BS_MASK, (bs)) + +#define MECR_BSIO_GET(mecr, sock) \ +MECR_GET((mecr), (sock), MECR_BSIO_SHIFT, MECR_BS_MASK) + +#define MECR_BSA_SET(mecr, sock, bs) \ +MECR_SET((mecr), (sock), MECR_BSA_SHIFT, MECR_BS_MASK, (bs)) + +#define MECR_BSA_GET(mecr, sock) \ +MECR_GET((mecr), (sock), MECR_BSA_SHIFT, MECR_BS_MASK) + +#define MECR_BSM_SET(mecr, sock, bs) \ +MECR_SET((mecr), (sock), MECR_BSM_SHIFT, MECR_BS_MASK, (bs)) + +#define MECR_BSM_GET(mecr, sock) \ +MECR_GET((mecr), (sock), MECR_BSM_SHIFT, MECR_BS_MASK) + +#define MECR_FAST_SET(mecr, sock, fast) \ +MECR_SET((mecr), (sock), MECR_FAST_SHIFT, MECR_FAST_MODE_MASK, (fast)) + +#define MECR_FAST_GET(mecr, sock) \ +MECR_GET((mecr), (sock), MECR_FAST_SHIFT, MECR_FAST_MODE_MASK) + + +/* This function implements the BS value calculation for setting the MECR + * using integer arithmetic: + */ +static inline unsigned int sa1100_pcmcia_mecr_bs(unsigned int pcmcia_cycle_ns, + unsigned int cpu_clock_khz){ + unsigned int t = ((pcmcia_cycle_ns * cpu_clock_khz) / 6) - 1000000; + return (t / 1000000) + (((t % 1000000) == 0) ? 0 : 1); +} + +/* This function returns the (approxmiate) command assertion period, in + * nanoseconds, for a given CPU clock frequency and MECR BS value: + */ +static inline unsigned int sa1100_pcmcia_cmd_time(unsigned int cpu_clock_khz, + unsigned int pcmcia_mecr_bs){ + return (((10000000 * 2) / cpu_clock_khz) * (3 * (pcmcia_mecr_bs + 1))) / 10; +} + + +/* SA-1100 PCMCIA Memory and I/O timing + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * The SA-1110 Developer's Manual, section 10.2.5, says the following: + * + * "To calculate the recommended BS_xx value for each address space: + * divide the command width time (the greater of twIOWR and twIORD, + * or the greater of twWE and twOE) by processor cycle time; divide + * by 2; divide again by 3 (number of BCLK's per command assertion); + * round up to the next whole number; and subtract 1." + * + * The PC Card Standard, Release 7, section 4.13.4, says that twIORD + * has a minimum value of 165ns. Section 4.13.5 says that twIOWR has + * a minimum value of 165ns, as well. Section 4.7.2 (describing + * common and attribute memory write timing) says that twWE has a + * minimum value of 150ns for a 250ns cycle time (for 5V operation; + * see section 4.7.4), or 300ns for a 600ns cycle time (for 3.3V + * operation, also section 4.7.4). Section 4.7.3 says that taOE + * has a maximum value of 150ns for a 300ns cycle time (for 5V + * operation), or 300ns for a 600ns cycle time (for 3.3V operation). + * + * When configuring memory maps, Card Services appears to adopt the policy + * that a memory access time of "0" means "use the default." The default + * PCMCIA I/O command width time is 165ns. The default PCMCIA 5V attribute + * and memory command width time is 150ns; the PCMCIA 3.3V attribute and + * memory command width time is 300ns. + */ +#define SA1100_PCMCIA_IO_ACCESS (165) +#define SA1100_PCMCIA_5V_MEM_ACCESS (150) +#define SA1100_PCMCIA_3V_MEM_ACCESS (300) + + +/* The socket driver actually works nicely in interrupt-driven form, + * so the (relatively infrequent) polling is "just to be sure." + */ +#define SA1100_PCMCIA_POLL_PERIOD (2*HZ) + + +/* This structure encapsulates per-socket state which we might need to + * use when responding to a Card Services query of some kind. + */ +struct sa1100_pcmcia_socket { + socket_state_t cs_state; + struct pcmcia_state k_state; + unsigned int irq; + void (*handler)(void *, unsigned int); + void *handler_info; + pccard_io_map io_map[MAX_IO_WIN]; + pccard_mem_map mem_map[MAX_WIN]; + ioaddr_t virt_io, phys_attr, phys_mem; + unsigned short speed_io, speed_attr, speed_mem; +}; + + +/* I/O pins replacing memory pins + * (PCMCIA System Architecture, 2nd ed., by Don Anderson, p.75) + * + * These signals change meaning when going from memory-only to + * memory-or-I/O interface: + */ +#define iostschg bvd1 +#define iospkr bvd2 + + +/* + * Declaration for all implementation specific low_level operations. + */ +extern struct pcmcia_low_level assabet_pcmcia_ops; +extern struct pcmcia_low_level neponset_pcmcia_ops; +extern struct pcmcia_low_level h3600_pcmcia_ops; +extern struct pcmcia_low_level cerf_pcmcia_ops; +extern struct pcmcia_low_level gcplus_pcmcia_ops; +extern struct pcmcia_low_level xp860_pcmcia_ops; +extern struct pcmcia_low_level yopy_pcmcia_ops; +extern struct pcmcia_low_level pangolin_pcmcia_ops; +extern struct pcmcia_low_level freebird_pcmcia_ops; +extern struct pcmcia_low_level pfs168_pcmcia_ops; +extern struct pcmcia_low_level jornada720_pcmcia_ops; +extern struct pcmcia_low_level flexanet_pcmcia_ops; +extern struct pcmcia_low_level simpad_pcmcia_ops; +extern struct pcmcia_low_level graphicsmaster_pcmcia_ops; +extern struct pcmcia_low_level adsbitsy_pcmcia_ops; +extern struct pcmcia_low_level stork_pcmcia_ops; + +#endif /* !defined(_PCMCIA_SA1100_H) */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_adsbitsy.c linux.ac/drivers/pcmcia/sa1100_adsbitsy.c --- linux.vanilla/drivers/pcmcia/sa1100_adsbitsy.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_adsbitsy.c Wed Oct 10 01:48:05 2001 @@ -0,0 +1,216 @@ +/* + * drivers/pcmcia/sa1100_adsbitsy.c + * + * PCMCIA implementation routines for ADS Bitsy + * + * 9/18/01 Woojung + * Fixed wrong PCMCIA voltage setting + * + * 7/5/01 Woojung Huh + * + */ +#include +#include + +#include +#include +#include +#include + +static int adsbitsy_pcmcia_init(struct pcmcia_init *init) +{ + int return_val=0; + + /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ + PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); + + /* Disable Power 3.3V/5V for PCMCIA/CF */ + PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3; + + INTPOL1 |= (1 << (S0_READY_NINT - SA1111_IRQ(32))) | + (1 << (S1_READY_NINT - SA1111_IRQ(32))) | + (1 << (S0_CD_VALID - SA1111_IRQ(32))) | + (1 << (S1_CD_VALID - SA1111_IRQ(32))) | + (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | + (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))); + + return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT, + "GC Master PCMCIA (0) CD", NULL); + return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT, + "GC Master CF (1) CD", NULL); + return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT, + "GC Master PCMCIA (0) BVD1", NULL); + return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT, + "GC Master CF (1) BVD1", NULL); + + MECR = 0x09430943; + + return (return_val<0) ? -1 : 2; +} + +static int adsbitsy_pcmcia_shutdown(void) +{ + + free_irq(S0_CD_VALID, NULL); + free_irq(S1_CD_VALID, NULL); + free_irq(S0_BVD1_STSCHG, NULL); + free_irq(S1_BVD1_STSCHG, NULL); + + INTPOL1 &= ~((1 << (S0_CD_VALID - SA1111_IRQ(32))) | + (1 << (S1_CD_VALID - SA1111_IRQ(32))) | + (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | + (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)))); + + return 0; +} + +static int adsbitsy_pcmcia_socket_state(struct pcmcia_state_array *state_array) +{ + unsigned long status; + int return_val=1; + + if(state_array->size<2) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + + status=PCSR; + + state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0; + + state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1; + + state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1; + + state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1; + + state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1; + + state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0; + + state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; + + state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0; + + state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1; + + state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1; + + state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1; + + state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1; + + state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0; + + state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0; + + return return_val; +} + +static int adsbitsy_pcmcia_get_irq_info(struct pcmcia_irq_info *info) +{ + + switch(info->sock){ + case 0: + info->irq=S0_READY_NINT; + break; + + case 1: + info->irq=S1_READY_NINT; + break; + + default: + return -1; + } + + return 0; +} + +static int adsbitsy_pcmcia_configure_socket(const struct pcmcia_configure *configure) +{ + unsigned long pccr=PCCR, gpio=PA_DWR; + + switch(configure->sock){ + case 0: + + switch(configure->vcc){ + case 0: + pccr = (pccr & ~PCCR_S0_FLT); + gpio |= GPIO_GPIO0 | GPIO_GPIO1; + break; + + case 33: + pccr = (pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN; + gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1); + gpio &= ~GPIO_GPIO0; + break; + + case 50: + pccr = (pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN); + gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1); + gpio |= GPIO_GPIO0; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + return -1; + } + + pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST); + + break; + + case 1: + switch(configure->vcc){ + case 0: + pccr = (pccr & ~PCCR_S1_FLT); + gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3); + break; + + case 33: + pccr = (pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN; + gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3); + gpio |= GPIO_GPIO2; + break; + + case 50: + pccr = (pccr | PCCR_S1_PSE | PCCR_S1_FLT | PCCR_S1_PWAITEN); + gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3); + gpio |= GPIO_GPIO3; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + return -1; + } + + if(configure->vpp!=configure->vcc && configure->vpp!=0){ + printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, + configure->vpp); + return -1; + } + + pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST); + + break; + + default: + return -1; + } + + PCCR = pccr; + PA_DWR = gpio; + + return 0; +} + +struct pcmcia_low_level adsbitsy_pcmcia_ops = { + adsbitsy_pcmcia_init, + adsbitsy_pcmcia_shutdown, + adsbitsy_pcmcia_socket_state, + adsbitsy_pcmcia_get_irq_info, + adsbitsy_pcmcia_configure_socket +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_assabet.c linux.ac/drivers/pcmcia/sa1100_assabet.c --- linux.vanilla/drivers/pcmcia/sa1100_assabet.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_assabet.c Wed Oct 10 01:48:05 2001 @@ -0,0 +1,149 @@ +/* + * drivers/pcmcia/sa1100_assabet.c + * + * PCMCIA implementation routines for Assabet + * + */ +#include +#include + +#include +#include +#include + + +static int assabet_pcmcia_init(struct pcmcia_init *init){ + int irq, res; + + /* Enable CF bus: */ + BCR_clear(BCR_CF_BUS_OFF); + + /* All those are inputs */ + GPDR &= ~(GPIO_CF_CD | GPIO_CF_BVD2 | GPIO_CF_BVD1 | GPIO_CF_IRQ); + + /* Set transition detect */ + set_GPIO_IRQ_edge( GPIO_CF_CD|GPIO_CF_BVD2|GPIO_CF_BVD1, GPIO_BOTH_EDGES ); + set_GPIO_IRQ_edge( GPIO_CF_IRQ, GPIO_FALLING_EDGE ); + + /* Register interrupts */ + irq = IRQ_GPIO_CF_CD; + res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_CD", NULL ); + if( res < 0 ) goto irq_err; + irq = IRQ_GPIO_CF_BVD2; + res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD2", NULL ); + if( res < 0 ) goto irq_err; + irq = IRQ_GPIO_CF_BVD1; + res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD1", NULL ); + if( res < 0 ) goto irq_err; + + /* There's only one slot, but it's "Slot 1": */ + return 2; + +irq_err: + printk( KERN_ERR "%s: Request for IRQ %u failed\n", __FUNCTION__, irq ); + return -1; +} + +static int assabet_pcmcia_shutdown(void) +{ + /* disable IRQs */ + free_irq( IRQ_GPIO_CF_CD, NULL ); + free_irq( IRQ_GPIO_CF_BVD2, NULL ); + free_irq( IRQ_GPIO_CF_BVD1, NULL ); + + /* Disable CF bus: */ + BCR_set(BCR_CF_BUS_OFF); + + return 0; +} + +static int assabet_pcmcia_socket_state(struct pcmcia_state_array + *state_array){ + unsigned long levels; + + if(state_array->size<2) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + + levels=GPLR; + + state_array->state[1].detect=((levels & GPIO_CF_CD)==0)?1:0; + + state_array->state[1].ready=(levels & GPIO_CF_IRQ)?1:0; + + state_array->state[1].bvd1=(levels & GPIO_CF_BVD1)?1:0; + + state_array->state[1].bvd2=(levels & GPIO_CF_BVD2)?1:0; + + state_array->state[1].wrprot=0; /* Not available on Assabet. */ + + state_array->state[1].vs_3v=1; /* Can only apply 3.3V on Assabet. */ + + state_array->state[1].vs_Xv=0; + + return 1; +} + +static int assabet_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ + + if(info->sock>1) return -1; + + if(info->sock==1) + info->irq=IRQ_GPIO_CF_IRQ; + + return 0; +} + +static int assabet_pcmcia_configure_socket(const struct pcmcia_configure + *configure) +{ + unsigned long value, flags; + + if(configure->sock>1) return -1; + + if(configure->sock==0) return 0; + + save_flags_cli(flags); + + value = BCR_value; + + switch(configure->vcc){ + case 0: + value &= ~BCR_CF_PWR; + break; + + case 50: + printk(KERN_WARNING "%s(): CS asked for 5V, applying 3.3V...\n", + __FUNCTION__); + + case 33: /* Can only apply 3.3V to the CF slot. */ + value |= BCR_CF_PWR; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + restore_flags(flags); + return -1; + } + + value = (configure->reset) ? (value | BCR_CF_RST) : (value & ~BCR_CF_RST); + + /* Silently ignore Vpp, output enable, speaker enable. */ + + BCR = BCR_value = value; + + restore_flags(flags); + + return 0; +} + +struct pcmcia_low_level assabet_pcmcia_ops = { + assabet_pcmcia_init, + assabet_pcmcia_shutdown, + assabet_pcmcia_socket_state, + assabet_pcmcia_get_irq_info, + assabet_pcmcia_configure_socket +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_cerf.c linux.ac/drivers/pcmcia/sa1100_cerf.c --- linux.vanilla/drivers/pcmcia/sa1100_cerf.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_cerf.c Wed Oct 10 01:48:05 2001 @@ -0,0 +1,150 @@ +/* + * drivers/pcmcia/sa1100_cerf.c + * + * PCMCIA implementation routines for CerfBoard + * Based off the Assabet. + * + */ +#include +#include + +#include +#include +#include + + +static int cerf_pcmcia_init(struct pcmcia_init *init){ + int irq, res; + + /* Enable CF bus: */ +// BCR_clear(BCR_CF_BUS_OFF); + + /* All those are inputs */ + GPDR &= ~(GPIO_CF_CD | GPIO_CF_BVD2 | GPIO_CF_BVD1 | GPIO_CF_IRQ); + + /* Set transition detect */ + set_GPIO_IRQ_edge( GPIO_CF_CD|GPIO_CF_BVD2|GPIO_CF_BVD1, GPIO_BOTH_EDGES ); + set_GPIO_IRQ_edge( GPIO_CF_IRQ, GPIO_FALLING_EDGE ); + + /* Register interrupts */ + irq = IRQ_GPIO_CF_CD; + res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_CD", NULL ); + if( res < 0 ) goto irq_err; + irq = IRQ_GPIO_CF_BVD2; + res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD2", NULL ); + if( res < 0 ) goto irq_err; + irq = IRQ_GPIO_CF_BVD1; + res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD1", NULL ); + if( res < 0 ) goto irq_err; + + /* There's only one slot, but it's "Slot 1": */ + return 2; + +irq_err: + printk( KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq ); + return -1; +} + +static int cerf_pcmcia_shutdown(void) +{ + /* disable IRQs */ + free_irq( IRQ_GPIO_CF_CD, NULL ); + free_irq( IRQ_GPIO_CF_BVD2, NULL ); + free_irq( IRQ_GPIO_CF_BVD1, NULL ); + + /* Disable CF bus: */ +// BCR_set(BCR_CF_BUS_OFF); + + return 0; +} + +static int cerf_pcmcia_socket_state(struct pcmcia_state_array + *state_array){ + unsigned long levels; + + if(state_array->size<2) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + + levels=GPLR; + + state_array->state[1].detect=((levels & GPIO_CF_CD)==0)?1:0; + + state_array->state[1].ready=(levels & GPIO_CF_IRQ)?1:0; + + state_array->state[1].bvd1=(levels & GPIO_CF_BVD1)?1:0; + + state_array->state[1].bvd2=(levels & GPIO_CF_BVD2)?1:0; + + state_array->state[1].wrprot=0; /* Not available on Assabet. */ + + state_array->state[1].vs_3v=1; /* Can only apply 3.3V on Assabet. */ + + state_array->state[1].vs_Xv=0; + + return 1; +} + +static int cerf_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ + + if(info->sock>1) return -1; + + if(info->sock==1) + info->irq=IRQ_GPIO_CF_IRQ; + + return 0; +} + +static int cerf_pcmcia_configure_socket(const struct pcmcia_configure + *configure) +{ + unsigned long value, flags; + + if(configure->sock>1) return -1; + + if(configure->sock==0) return 0; + + save_flags_cli(flags); + +// value = BCR_value; + + switch(configure->vcc){ + case 0: +// value &= ~BCR_CF_PWR; + break; + + case 50: + printk(KERN_WARNING "%s(): CS asked for 5V, applying 3.3V...\n", + __FUNCTION__); + + case 33: /* Can only apply 3.3V to the CF slot. */ +// value |= BCR_CF_PWR; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + restore_flags(flags); + return -1; + } + +// value = (configure->reset) ? (value | BCR_CF_RST) : (value & ~BCR_CF_RST); + + /* Silently ignore Vpp, output enable, speaker enable. */ + +// BCR = BCR_value = value; + + restore_flags(flags); + + return 0; +} + +struct pcmcia_low_level cerf_pcmcia_ops = { + cerf_pcmcia_init, + cerf_pcmcia_shutdown, + cerf_pcmcia_socket_state, + cerf_pcmcia_get_irq_info, + cerf_pcmcia_configure_socket +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_flexanet.c linux.ac/drivers/pcmcia/sa1100_flexanet.c --- linux.vanilla/drivers/pcmcia/sa1100_flexanet.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_flexanet.c Wed Oct 10 01:48:05 2001 @@ -0,0 +1,84 @@ +/* + * drivers/pcmcia/sa1100_flexanet.c + * + * PCMCIA implementation routines for Flexanet. + * by Jordi Colomer, 09/05/2001 + * + * Yet to be defined. + */ + +#include +#include + +#include +#include +#include + + +/* + * Socket initialization. + * + * Called by sa1100_pcmcia_driver_init on startup. + * Must return the number of slots. + * + */ +static int flexanet_pcmcia_init(struct pcmcia_init *init){ + + return 0; +} + + +/* + * Socket shutdown + * + */ +static int flexanet_pcmcia_shutdown(void) +{ + return 0; +} + + +/* + * Get the state of the sockets. + * + * Sockets in Flexanet are 3.3V only, without BVD2. + * + */ +static int flexanet_pcmcia_socket_state(struct pcmcia_state_array + *state_array){ + return -1; +} + + +/* + * Return the IRQ information for a given socket number (the IRQ number) + * + */ +static int flexanet_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ + + return -1; +} + + +/* + * + */ +static int flexanet_pcmcia_configure_socket(const struct pcmcia_configure + *configure) +{ + return -1; +} + + +/* + * The set of socket operations + * + */ +struct pcmcia_low_level flexanet_pcmcia_ops = { + flexanet_pcmcia_init, + flexanet_pcmcia_shutdown, + flexanet_pcmcia_socket_state, + flexanet_pcmcia_get_irq_info, + flexanet_pcmcia_configure_socket +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_freebird.c linux.ac/drivers/pcmcia/sa1100_freebird.c --- linux.vanilla/drivers/pcmcia/sa1100_freebird.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_freebird.c Wed Oct 10 01:48:05 2001 @@ -0,0 +1,160 @@ +/* + * drivers/pcmcia/sa1100_freebird.c + * + * Created by Eric Peng + * + */ +#include +#include + +#include +#include +#include + + +static int freebird_pcmcia_init(struct pcmcia_init *init){ + int irq, res; + + /* Enable Linkup CF card */ + LINKUP_PRC = 0xc0; + mdelay(100); + LINKUP_PRC = 0xc1; + mdelay(100); + LINKUP_PRC = 0xd1; + mdelay(100); + LINKUP_PRC = 0xd1; + mdelay(100); + LINKUP_PRC = 0xc0; + + /* All those are inputs */ + ////GPDR &= ~(GPIO_CF_CD | GPIO_CF_BVD2 | GPIO_CF_BVD1 | GPIO_CF_IRQ); + GPDR &= ~(GPIO_FREEBIRD_CF_CD | GPIO_FREEBIRD_CF_IRQ | GPIO_FREEBIRD_CF_BVD); + + /* Set transition detect */ + //set_GPIO_IRQ_edge( GPIO_CF_CD|GPIO_CF_BVD2|GPIO_CF_BVD1, GPIO_BOTH_EDGES ); + //set_GPIO_IRQ_edge( GPIO_CF_IRQ, GPIO_FALLING_EDGE ); + set_GPIO_IRQ_edge(GPIO_FREEBIRD_CF_CD|GPIO_FREEBIRD_CF_BVD,GPIO_BOTH_EDGES); + set_GPIO_IRQ_edge(GPIO_FREEBIRD_CF_IRQ, GPIO_FALLING_EDGE); + + /* Register interrupts */ + irq = IRQ_GPIO_FREEBIRD_CF_CD; + res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_CD", NULL ); + if( res < 0 ) goto irq_err; + irq = IRQ_GPIO_FREEBIRD_CF_BVD; + res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD1", NULL ); + if( res < 0 ) goto irq_err; + + /* There's only one slot, but it's "Slot 1": */ + return 2; + +irq_err: + printk( KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq ); + return -1; +} + +static int freebird_pcmcia_shutdown(void) +{ + /* disable IRQs */ + free_irq( IRQ_GPIO_FREEBIRD_CF_CD, NULL ); + free_irq( IRQ_GPIO_FREEBIRD_CF_BVD, NULL ); + + /* Disable CF card */ + LINKUP_PRC = 0x40; /* SSP=1 SOE=0 */ + mdelay(100); + + return 0; +} + +static int freebird_pcmcia_socket_state(struct pcmcia_state_array + *state_array){ + unsigned long levels; + + if(state_array->size<2) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + + levels = LINKUP_PRS; +//printk("LINKUP_PRS=%x \n",levels); + + state_array->state[0].detect= + ((levels & (LINKUP_CD1 | LINKUP_CD2))==0)?1:0; + + state_array->state[0].ready=(levels & LINKUP_RDY)?1:0; + + state_array->state[0].bvd1=(levels & LINKUP_BVD1)?1:0; + + state_array->state[0].bvd2=(levels & LINKUP_BVD2)?1:0; + + state_array->state[0].wrprot=0; /* Not available on Assabet. */ + + state_array->state[0].vs_3v=1; /* Can only apply 3.3V on Assabet. */ + + state_array->state[0].vs_Xv=0; + + return 1; +} + +static int freebird_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ + + if(info->sock>1) return -1; + + if(info->sock==0) + info->irq=IRQ_GPIO_FREEBIRD_CF_IRQ; + + return 0; +} + +static int freebird_pcmcia_configure_socket(const struct pcmcia_configure + *configure) +{ + unsigned long value, flags; + + if(configure->sock>1) return -1; + + if(configure->sock==1) return 0; + + save_flags_cli(flags); + + value = 0xc0; /* SSP=1 SOE=1 CFE=1 */ + + switch(configure->vcc){ + case 0: + + break; + + case 50: + printk(KERN_WARNING "%s(): CS asked for 5V, applying 3.3V...\n", + __FUNCTION__); + + case 33: /* Can only apply 3.3V to the CF slot. */ + value |= LINKUP_S1; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + restore_flags(flags); + return -1; + } + + if (configure->reset) + value = (configure->reset) ? (value | LINKUP_RESET) : (value & ~LINKUP_RESET); + + /* Silently ignore Vpp, output enable, speaker enable. */ + + LINKUP_PRC = value; +//printk("LINKUP_PRC=%x\n",value); + restore_flags(flags); + + return 0; +} + +struct pcmcia_low_level freebird_pcmcia_ops = { + freebird_pcmcia_init, + freebird_pcmcia_shutdown, + freebird_pcmcia_socket_state, + freebird_pcmcia_get_irq_info, + freebird_pcmcia_configure_socket +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_generic.c linux.ac/drivers/pcmcia/sa1100_generic.c --- linux.vanilla/drivers/pcmcia/sa1100_generic.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_generic.c Wed Oct 10 01:48:06 2001 @@ -0,0 +1,1168 @@ +/*====================================================================== + + Device driver for the PCMCIA control functionality of StrongARM + SA-1100 microprocessors. + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is John G. Dorsey + . Portions created by John G. Dorsey are + Copyright (C) 1999 John G. Dorsey. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "sa1100.h" + +#ifdef PCMCIA_DEBUG +static int pc_debug; +#endif + +MODULE_AUTHOR("John Dorsey "); +MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-1100 Socket Controller"); + +/* This structure maintains housekeeping state for each socket, such + * as the last known values of the card detect pins, or the Card Services + * callback value associated with the socket: + */ +static struct sa1100_pcmcia_socket +sa1100_pcmcia_socket[SA1100_PCMCIA_MAX_SOCK]; + +static int sa1100_pcmcia_socket_count; + + +/* Returned by the low-level PCMCIA interface: */ +static struct pcmcia_low_level *pcmcia_low_level; + +/* Event poll timer structure */ +static struct timer_list poll_timer; + + +/* Prototypes for routines which are used internally: */ + +static int sa1100_pcmcia_driver_init(void); +static void sa1100_pcmcia_driver_shutdown(void); +static void sa1100_pcmcia_task_handler(void *data); +static void sa1100_pcmcia_poll_event(unsigned long data); +static void sa1100_pcmcia_interrupt(int irq, void *dev, + struct pt_regs *regs); +static struct tq_struct sa1100_pcmcia_task; + +#ifdef CONFIG_PROC_FS +static int sa1100_pcmcia_proc_status(char *buf, char **start, off_t pos, + int count, int *eof, void *data); +#endif + + +/* Prototypes for operations which are exported to the + * new-and-impr^H^H^H^H^H^H^H^H^H^H in-kernel PCMCIA core: + */ + +static int sa1100_pcmcia_init(unsigned int sock); +static int sa1100_pcmcia_suspend(unsigned int sock); +static int sa1100_pcmcia_register_callback(unsigned int sock, + void (*handler)(void *, + unsigned int), + void *info); +static int sa1100_pcmcia_inquire_socket(unsigned int sock, + socket_cap_t *cap); +static int sa1100_pcmcia_get_status(unsigned int sock, u_int *value); +static int sa1100_pcmcia_get_socket(unsigned int sock, + socket_state_t *state); +static int sa1100_pcmcia_set_socket(unsigned int sock, + socket_state_t *state); +static int sa1100_pcmcia_get_io_map(unsigned int sock, + struct pccard_io_map *io); +static int sa1100_pcmcia_set_io_map(unsigned int sock, + struct pccard_io_map *io); +static int sa1100_pcmcia_get_mem_map(unsigned int sock, + struct pccard_mem_map *mem); +static int sa1100_pcmcia_set_mem_map(unsigned int sock, + struct pccard_mem_map *mem); +#ifdef CONFIG_PROC_FS +static void sa1100_pcmcia_proc_setup(unsigned int sock, + struct proc_dir_entry *base); +#endif + +static struct pccard_operations sa1100_pcmcia_operations = { + sa1100_pcmcia_init, + sa1100_pcmcia_suspend, + sa1100_pcmcia_register_callback, + sa1100_pcmcia_inquire_socket, + sa1100_pcmcia_get_status, + sa1100_pcmcia_get_socket, + sa1100_pcmcia_set_socket, + sa1100_pcmcia_get_io_map, + sa1100_pcmcia_set_io_map, + sa1100_pcmcia_get_mem_map, + sa1100_pcmcia_set_mem_map, +#ifdef CONFIG_PROC_FS + sa1100_pcmcia_proc_setup +#endif +}; + +#ifdef CONFIG_CPU_FREQ +/* forward declaration */ +static struct notifier_block sa1100_pcmcia_notifier_block; +#endif + + +/* sa1100_pcmcia_driver_init() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * + * This routine performs a basic sanity check to ensure that this + * kernel has been built with the appropriate board-specific low-level + * PCMCIA support, performs low-level PCMCIA initialization, registers + * this socket driver with Card Services, and then spawns the daemon + * thread which is the real workhorse of the socket driver. + * + * Please see linux/Documentation/arm/SA1100/PCMCIA for more information + * on the low-level kernel interface. + * + * Returns: 0 on success, -1 on error + */ +static int __init sa1100_pcmcia_driver_init(void){ + servinfo_t info; + struct pcmcia_init pcmcia_init; + struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK]; + struct pcmcia_state_array state_array; + unsigned int i, clock; + unsigned long mecr; + + printk(KERN_INFO "SA-1100 PCMCIA (CS release %s)\n", CS_RELEASE); + + CardServices(GetCardServicesInfo, &info); + + if(info.Revision!=CS_RELEASE_CODE){ + printk(KERN_ERR "Card Services release codes do not match\n"); + return -1; + } + + if(machine_is_assabet()){ +#ifdef CONFIG_SA1100_ASSABET + if(machine_has_neponset()){ +#ifdef CONFIG_ASSABET_NEPONSET + pcmcia_low_level=&neponset_pcmcia_ops; +#else + printk(KERN_ERR "Card Services disabled: missing Neponset support\n"); + return -1; +#endif + }else{ + pcmcia_low_level=&assabet_pcmcia_ops; + } +#endif + } else if (machine_is_freebird()) { +#ifdef CONFIG_SA1100_FREEBIRD + pcmcia_low_level = &freebird_pcmcia_ops; +#endif + } else if (machine_is_h3600()) { +#ifdef CONFIG_SA1100_H3600 + pcmcia_low_level = &h3600_pcmcia_ops; +#endif + } else if (machine_is_cerf()) { +#ifdef CONFIG_SA1100_CERF + pcmcia_low_level = &cerf_pcmcia_ops; +#endif + } else if (machine_is_graphicsclient()) { +#ifdef CONFIG_SA1100_GRAPHICSCLIENT + pcmcia_low_level = &gcplus_pcmcia_ops; +#endif + } else if (machine_is_xp860()) { +#ifdef CONFIG_SA1100_XP860 + pcmcia_low_level = &xp860_pcmcia_ops; +#endif + } else if (machine_is_yopy()) { +#ifdef CONFIG_SA1100_YOPY + pcmcia_low_level = &yopy_pcmcia_ops; +#endif + } else if (machine_is_pangolin()) { +#ifdef CONFIG_SA1100_PANGOLIN + pcmcia_low_level = &pangolin_pcmcia_ops; +#endif + } else if (machine_is_jornada720()) { +#ifdef CONFIG_SA1100_JORNADA720 + pcmcia_low_level = &jornada720_pcmcia_ops; +#endif + } else if(machine_is_pfs168()){ +#ifdef CONFIG_SA1100_PFS168 + pcmcia_low_level=&pfs168_pcmcia_ops; +#endif + } else if(machine_is_flexanet()){ +#ifdef CONFIG_SA1100_FLEXANET + pcmcia_low_level=&flexanet_pcmcia_ops; +#endif + } else if(machine_is_simpad()){ +#ifdef CONFIG_SA1100_SIMPAD + pcmcia_low_level=&simpad_pcmcia_ops; +#endif + } else if(machine_is_graphicsmaster()) { +#ifdef CONFIG_SA1100_GRAPHICSMASTER + pcmcia_low_level=&graphicsmaster_pcmcia_ops; +#endif + } else if(machine_is_adsbitsy()) { +#ifdef CONFIG_SA1100_ADSBITSY + pcmcia_low_level=&adsbitsy_pcmcia_ops; +#endif + } else if(machine_is_stork()) { +#ifdef CONFIG_SA1100_STORK + pcmcia_low_level=&stork_pcmcia_ops; +#endif + } + + if (!pcmcia_low_level) { + printk(KERN_ERR "This hardware is not supported by the SA1100 Card Service driver\n"); + return -ENODEV; + } + + pcmcia_init.handler=sa1100_pcmcia_interrupt; + + if((sa1100_pcmcia_socket_count=pcmcia_low_level->init(&pcmcia_init))<0){ + printk(KERN_ERR "Unable to initialize kernel PCMCIA service.\n"); + return -EIO; + } + + state_array.size=sa1100_pcmcia_socket_count; + state_array.state=state; + + if(pcmcia_low_level->socket_state(&state_array)<0){ + printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n"); + return -EIO; + } + + /* We initialize the MECR to default values here, because we are + * not guaranteed to see a SetIOMap operation at runtime. + */ + mecr=0; + + clock = get_cclk_frequency() * 100; + + for(i=0; ishutdown(); + flush_scheduled_tasks(); + + DEBUG(1, "sa1100: shutdown complete\n"); +} + +module_exit(sa1100_pcmcia_driver_shutdown); + + +/* sa1100_pcmcia_init() + * ^^^^^^^^^^^^^^^^^^^^ + * We perform all of the interesting initialization tasks in + * sa1100_pcmcia_driver_init(). + * + * Returns: 0 + */ +static int sa1100_pcmcia_init(unsigned int sock){ + + DEBUG(2, "%s(): initializing socket %u\n", __FUNCTION__, sock); + + return 0; +} + + +/* sa1100_pcmcia_suspend() + * ^^^^^^^^^^^^^^^^^^^^^^^ + * We don't currently perform any actions on a suspend. + * + * Returns: 0 + */ +static int sa1100_pcmcia_suspend(unsigned int sock){ + + DEBUG(2, "%s(): suspending socket %u\n", __FUNCTION__, sock); + + return 0; +} + + +/* sa1100_pcmcia_events() + * ^^^^^^^^^^^^^^^^^^^^^^ + * Helper routine to generate a Card Services event mask based on + * state information obtained from the kernel low-level PCMCIA layer + * in a recent (and previous) sampling. Updates `prev_state'. + * + * Returns: an event mask for the given socket state. + */ +static inline unsigned sa1100_pcmcia_events(struct pcmcia_state *state, + struct pcmcia_state *prev_state, + unsigned int mask, + unsigned int flags){ + unsigned int events=0; + + if(state->detect!=prev_state->detect){ + + DEBUG(2, "%s(): card detect value %u\n", __FUNCTION__, state->detect); + + events|=mask&SS_DETECT; + } + + if(state->ready!=prev_state->ready){ + + DEBUG(2, "%s(): card ready value %u\n", __FUNCTION__, state->ready); + + events|=mask&((flags&SS_IOCARD)?0:SS_READY); + } + + if(state->bvd1!=prev_state->bvd1){ + + DEBUG(2, "%s(): card BVD1 value %u\n", __FUNCTION__, state->bvd1); + + events|=mask&(flags&SS_IOCARD)?SS_STSCHG:SS_BATDEAD; + } + + if(state->bvd2!=prev_state->bvd2){ + + DEBUG(2, "%s(): card BVD2 value %u\n", __FUNCTION__, state->bvd2); + + events|=mask&(flags&SS_IOCARD)?0:SS_BATWARN; + } + + DEBUG(2, "events: %s%s%s%s%s%s\n", + (events==0)?"":"", + (events&SS_DETECT)?"DETECT ":"", + (events&SS_READY)?"READY ":"", + (events&SS_BATDEAD)?"BATDEAD ":"", + (events&SS_BATWARN)?"BATWARN ":"", + (events&SS_STSCHG)?"STSCHG ":""); + + *prev_state=*state; + + return events; + +} /* sa1100_pcmcia_events() */ + + +/* sa1100_pcmcia_task_handler() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * Processes serviceable socket events using the "eventd" thread context. + * + * Event processing (specifically, the invocation of the Card Services event + * callback) occurs in this thread rather than in the actual interrupt + * handler due to the use of scheduling operations in the PCMCIA core. + */ +static void sa1100_pcmcia_task_handler(void *data) { + struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK]; + struct pcmcia_state_array state_array; + int i, events, all_events, irq_status; + + DEBUG(2, "%s(): entering PCMCIA monitoring thread\n", __FUNCTION__); + + state_array.size=sa1100_pcmcia_socket_count; + state_array.state=state; + + do { + + DEBUG(3, "%s(): interrogating low-level PCMCIA service\n", __FUNCTION__); + + if((irq_status=pcmcia_low_level->socket_state(&state_array))<0) + printk(KERN_ERR "Error in kernel low-level PCMCIA service.\n"); + + all_events=0; + + if(irq_status>0){ + + for(i=0; i=sa1100_pcmcia_socket_count){ + printk(KERN_ERR "sa1100: socket %u not configured\n", sock); + return -1; + } + + /* SS_CAP_PAGE_REGS: used by setup_cis_mem() in cistpl.c to set the + * force_low argument to validate_mem() in rsrc_mgr.c -- since in + * general, the mapped * addresses of the PCMCIA memory regions + * will not be within 0xffff, setting force_low would be + * undesirable. + * + * SS_CAP_STATIC_MAP: don't bother with the (user-configured) memory + * resource database; we instead pass up physical address ranges + * and allow other parts of Card Services to deal with remapping. + * + * SS_CAP_PCCARD: we can deal with 16-bit PCMCIA & CF cards, but + * not 32-bit CardBus devices. + */ + cap->features=(SS_CAP_PAGE_REGS | SS_CAP_STATIC_MAP | SS_CAP_PCCARD); + + irq_info.sock=sock; + irq_info.irq=-1; + + if(pcmcia_low_level->get_irq_info(&irq_info)<0){ + printk(KERN_ERR "Error obtaining IRQ info from kernel for socket %u\n", + sock); + return -1; + } + + cap->irq_mask=0; + cap->map_size=PAGE_SIZE; + cap->pci_irq=irq_info.irq; + cap->io_offset=sa1100_pcmcia_socket[sock].virt_io; + + return 0; + +} /* sa1100_pcmcia_inquire_socket() */ + + +/* sa1100_pcmcia_get_status() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^ + * Implements the get_status() operation for the in-kernel PCMCIA + * service (formerly SS_GetStatus in Card Services). Essentially just + * fills in bits in `status' according to internal driver state or + * the value of the voltage detect chipselect register. + * + * As a debugging note, during card startup, the PCMCIA core issues + * three set_socket() commands in a row the first with RESET deasserted, + * the second with RESET asserted, and the last with RESET deasserted + * again. Following the third set_socket(), a get_status() command will + * be issued. The kernel is looking for the SS_READY flag (see + * setup_socket(), reset_socket(), and unreset_socket() in cs.c). + * + * Returns: 0 + */ +static int sa1100_pcmcia_get_status(unsigned int sock, + unsigned int *status){ + struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK]; + struct pcmcia_state_array state_array; + + DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock); + + state_array.size=sa1100_pcmcia_socket_count; + state_array.state=state; + + if((pcmcia_low_level->socket_state(&state_array))<0){ + printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n"); + return -1; + } + + sa1100_pcmcia_socket[sock].k_state=state[sock]; + + *status=state[sock].detect?SS_DETECT:0; + + *status|=state[sock].ready?SS_READY:0; + + /* The power status of individual sockets is not available + * explicitly from the hardware, so we just remember the state + * and regurgitate it upon request: + */ + *status|=sa1100_pcmcia_socket[sock].cs_state.Vcc?SS_POWERON:0; + + if(sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD) + *status|=state[sock].bvd1?SS_STSCHG:0; + else { + if(state[sock].bvd1==0) + *status|=SS_BATDEAD; + else if(state[sock].bvd2==0) + *status|=SS_BATWARN; + } + + *status|=state[sock].vs_3v?SS_3VCARD:0; + + *status|=state[sock].vs_Xv?SS_XVCARD:0; + + DEBUG(3, "\tstatus: %s%s%s%s%s%s%s%s\n", + (*status&SS_DETECT)?"DETECT ":"", + (*status&SS_READY)?"READY ":"", + (*status&SS_BATDEAD)?"BATDEAD ":"", + (*status&SS_BATWARN)?"BATWARN ":"", + (*status&SS_POWERON)?"POWERON ":"", + (*status&SS_STSCHG)?"STSCHG ":"", + (*status&SS_3VCARD)?"3VCARD ":"", + (*status&SS_XVCARD)?"XVCARD ":""); + + return 0; + +} /* sa1100_pcmcia_get_status() */ + + +/* sa1100_pcmcia_get_socket() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^ + * Implements the get_socket() operation for the in-kernel PCMCIA + * service (formerly SS_GetSocket in Card Services). Not a very + * exciting routine. + * + * Returns: 0 + */ +static int sa1100_pcmcia_get_socket(unsigned int sock, + socket_state_t *state){ + + DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock); + + /* This information was given to us in an earlier call to set_socket(), + * so we're just regurgitating it here: + */ + *state=sa1100_pcmcia_socket[sock].cs_state; + + return 0; +} + + +/* sa1100_pcmcia_set_socket() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^ + * Implements the set_socket() operation for the in-kernel PCMCIA + * service (formerly SS_SetSocket in Card Services). We more or + * less punt all of this work and let the kernel handle the details + * of power configuration, reset, &c. We also record the value of + * `state' in order to regurgitate it to the PCMCIA core later. + * + * Returns: 0 + */ +static int sa1100_pcmcia_set_socket(unsigned int sock, + socket_state_t *state){ + struct pcmcia_configure configure; + + DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock); + + DEBUG(3, "\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n" + "\tVcc %d Vpp %d irq %d\n", + (state->csc_mask==0)?"":"", + (state->csc_mask&SS_DETECT)?"DETECT ":"", + (state->csc_mask&SS_READY)?"READY ":"", + (state->csc_mask&SS_BATDEAD)?"BATDEAD ":"", + (state->csc_mask&SS_BATWARN)?"BATWARN ":"", + (state->csc_mask&SS_STSCHG)?"STSCHG ":"", + (state->flags==0)?"":"", + (state->flags&SS_PWR_AUTO)?"PWR_AUTO ":"", + (state->flags&SS_IOCARD)?"IOCARD ":"", + (state->flags&SS_RESET)?"RESET ":"", + (state->flags&SS_SPKR_ENA)?"SPKR_ENA ":"", + (state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":"", + state->Vcc, state->Vpp, state->io_irq); + + configure.sock=sock; + configure.vcc=state->Vcc; + configure.vpp=state->Vpp; + configure.output=(state->flags&SS_OUTPUT_ENA)?1:0; + configure.speaker=(state->flags&SS_SPKR_ENA)?1:0; + configure.reset=(state->flags&SS_RESET)?1:0; + + if(pcmcia_low_level->configure_socket(&configure)<0){ + printk(KERN_ERR "Unable to configure socket %u\n", sock); + return -1; + } + + sa1100_pcmcia_socket[sock].cs_state=*state; + + return 0; + +} /* sa1100_pcmcia_set_socket() */ + + +/* sa1100_pcmcia_get_io_map() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^ + * Implements the get_io_map() operation for the in-kernel PCMCIA + * service (formerly SS_GetIOMap in Card Services). Just returns an + * I/O map descriptor which was assigned earlier by a set_io_map(). + * + * Returns: 0 on success, -1 if the map index was out of range + */ +static int sa1100_pcmcia_get_io_map(unsigned int sock, + struct pccard_io_map *map){ + + DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); + + if(map->map>=MAX_IO_WIN){ + printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, + map->map); + return -1; + } + + *map=sa1100_pcmcia_socket[sock].io_map[map->map]; + + return 0; +} + + +/* sa1100_pcmcia_set_io_map() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^ + * Implements the set_io_map() operation for the in-kernel PCMCIA + * service (formerly SS_SetIOMap in Card Services). We configure + * the map speed as requested, but override the address ranges + * supplied by Card Services. + * + * Returns: 0 on success, -1 on error + */ +static int sa1100_pcmcia_set_io_map(unsigned int sock, + struct pccard_io_map *map){ + unsigned int clock, speed; + unsigned long mecr, start; + + DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); + + DEBUG(4, "\tmap %u speed %u\n\tstart 0x%08lx stop 0x%08lx\n" + "\tflags: %s%s%s%s%s%s%s%s\n", + map->map, map->speed, map->start, map->stop, + (map->flags==0)?"":"", + (map->flags&MAP_ACTIVE)?"ACTIVE ":"", + (map->flags&MAP_16BIT)?"16BIT ":"", + (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"", + (map->flags&MAP_0WS)?"0WS ":"", + (map->flags&MAP_WRPROT)?"WRPROT ":"", + (map->flags&MAP_USE_WAIT)?"USE_WAIT ":"", + (map->flags&MAP_PREFETCH)?"PREFETCH ":""); + + if(map->map>=MAX_IO_WIN){ + printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, + map->map); + return -1; + } + + if(map->flags&MAP_ACTIVE){ + + speed=(map->speed>0)?map->speed:SA1100_PCMCIA_IO_ACCESS; + + clock = get_cclk_frequency() * 100; + + mecr=MECR; + + MECR_BSIO_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock)); + + sa1100_pcmcia_socket[sock].speed_io=speed; + + DEBUG(4, "%s(): FAST%u %lx BSM%u %lx BSA%u %lx BSIO%u %lx\n", + __FUNCTION__, sock, MECR_FAST_GET(mecr, sock), sock, + MECR_BSM_GET(mecr, sock), sock, MECR_BSA_GET(mecr, sock), + sock, MECR_BSIO_GET(mecr, sock)); + + MECR=mecr; + + } + + start=map->start; + + if(map->stop==1) + map->stop=PAGE_SIZE-1; + + map->start=sa1100_pcmcia_socket[sock].virt_io; + map->stop=map->start+(map->stop-start); + + sa1100_pcmcia_socket[sock].io_map[map->map]=*map; + + return 0; + +} /* sa1100_pcmcia_set_io_map() */ + + +/* sa1100_pcmcia_get_mem_map() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * Implements the get_mem_map() operation for the in-kernel PCMCIA + * service (formerly SS_GetMemMap in Card Services). Just returns a + * memory map descriptor which was assigned earlier by a + * set_mem_map() request. + * + * Returns: 0 on success, -1 if the map index was out of range + */ +static int sa1100_pcmcia_get_mem_map(unsigned int sock, + struct pccard_mem_map *map){ + + DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); + + if(map->map>=MAX_WIN){ + printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, + map->map); + return -1; + } + + *map=sa1100_pcmcia_socket[sock].mem_map[map->map]; + + return 0; +} + + +/* sa1100_pcmcia_set_mem_map() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * Implements the set_mem_map() operation for the in-kernel PCMCIA + * service (formerly SS_SetMemMap in Card Services). We configure + * the map speed as requested, but override the address ranges + * supplied by Card Services. + * + * Returns: 0 on success, -1 on error + */ +static int sa1100_pcmcia_set_mem_map(unsigned int sock, + struct pccard_mem_map *map){ + unsigned int clock, speed; + unsigned long mecr, start; + + DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); + + DEBUG(4, "\tmap %u speed %u\n\tsys_start %#lx\n" + "\tsys_stop %#lx\n\tcard_start %#x\n" + "\tflags: %s%s%s%s%s%s%s%s\n", + map->map, map->speed, map->sys_start, map->sys_stop, + map->card_start, (map->flags==0)?"":"", + (map->flags&MAP_ACTIVE)?"ACTIVE ":"", + (map->flags&MAP_16BIT)?"16BIT ":"", + (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"", + (map->flags&MAP_0WS)?"0WS ":"", + (map->flags&MAP_WRPROT)?"WRPROT ":"", + (map->flags&MAP_ATTRIB)?"ATTRIB ":"", + (map->flags&MAP_USE_WAIT)?"USE_WAIT ":""); + + if(map->map>=MAX_WIN){ + printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, + map->map); + return -1; + } + + if(map->flags&MAP_ACTIVE){ + + /* When clients issue RequestMap, the access speed is not always + * properly configured: + */ + if(map->speed > 0) + speed = map->speed; + else + switch(sa1100_pcmcia_socket[sock].cs_state.Vcc){ + case 33: + speed = SA1100_PCMCIA_3V_MEM_ACCESS; + break; + default: + speed = SA1100_PCMCIA_5V_MEM_ACCESS; + } + + clock = get_cclk_frequency() * 100; + + mecr=MECR; + + if(map->flags&MAP_ATTRIB){ + + MECR_BSA_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock)); + sa1100_pcmcia_socket[sock].speed_attr=speed; + + } else { + + MECR_BSM_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock)); + sa1100_pcmcia_socket[sock].speed_mem=speed; + + } + + DEBUG(4, "%s(): FAST%u %lx BSM%u %lx BSA%u %lx BSIO%u %lx\n", + __FUNCTION__, sock, MECR_FAST_GET(mecr, sock), sock, + MECR_BSM_GET(mecr, sock), sock, MECR_BSA_GET(mecr, sock), + sock, MECR_BSIO_GET(mecr, sock)); + + MECR=mecr; + + } + + start=map->sys_start; + + if(map->sys_stop==0) + map->sys_stop=PAGE_SIZE-1; + + map->sys_start=(map->flags & MAP_ATTRIB)?\ + sa1100_pcmcia_socket[sock].phys_attr:\ + sa1100_pcmcia_socket[sock].phys_mem; + + map->sys_stop=map->sys_start+(map->sys_stop-start); + + sa1100_pcmcia_socket[sock].mem_map[map->map]=*map; + + return 0; + +} /* sa1100_pcmcia_set_mem_map() */ + + +#if defined(CONFIG_PROC_FS) + +/* sa1100_pcmcia_proc_setup() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^ + * Implements the proc_setup() operation for the in-kernel PCMCIA + * service (formerly SS_ProcSetup in Card Services). + * + * Returns: 0 on success, -1 on error + */ +static void sa1100_pcmcia_proc_setup(unsigned int sock, + struct proc_dir_entry *base){ + struct proc_dir_entry *entry; + + DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); + + if((entry=create_proc_entry("status", 0, base))==NULL){ + printk(KERN_ERR "Unable to install \"status\" procfs entry\n"); + return; + } + + entry->read_proc=sa1100_pcmcia_proc_status; + entry->data=(void *)sock; +} + + +/* sa1100_pcmcia_proc_status() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * Implements the /proc/bus/pccard/??/status file. + * + * Returns: the number of characters added to the buffer + */ +static int sa1100_pcmcia_proc_status(char *buf, char **start, off_t pos, + int count, int *eof, void *data){ + char *p=buf; + unsigned int sock=(unsigned int)data; + unsigned int clock = get_cclk_frequency() * 100; + unsigned long mecr = MECR; + + p+=sprintf(p, "k_flags : %s%s%s%s%s%s%s\n", + sa1100_pcmcia_socket[sock].k_state.detect?"detect ":"", + sa1100_pcmcia_socket[sock].k_state.ready?"ready ":"", + sa1100_pcmcia_socket[sock].k_state.bvd1?"bvd1 ":"", + sa1100_pcmcia_socket[sock].k_state.bvd2?"bvd2 ":"", + sa1100_pcmcia_socket[sock].k_state.wrprot?"wrprot ":"", + sa1100_pcmcia_socket[sock].k_state.vs_3v?"vs_3v ":"", + sa1100_pcmcia_socket[sock].k_state.vs_Xv?"vs_Xv ":""); + + p+=sprintf(p, "status : %s%s%s%s%s%s%s%s%s\n", + sa1100_pcmcia_socket[sock].k_state.detect?"SS_DETECT ":"", + sa1100_pcmcia_socket[sock].k_state.ready?"SS_READY ":"", + sa1100_pcmcia_socket[sock].cs_state.Vcc?"SS_POWERON ":"", + sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD?\ + "SS_IOCARD ":"", + (sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD && + sa1100_pcmcia_socket[sock].k_state.bvd1)?"SS_STSCHG ":"", + ((sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD)==0 && + (sa1100_pcmcia_socket[sock].k_state.bvd1==0))?"SS_BATDEAD ":"", + ((sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD)==0 && + (sa1100_pcmcia_socket[sock].k_state.bvd2==0))?"SS_BATWARN ":"", + sa1100_pcmcia_socket[sock].k_state.vs_3v?"SS_3VCARD ":"", + sa1100_pcmcia_socket[sock].k_state.vs_Xv?"SS_XVCARD ":""); + + p+=sprintf(p, "mask : %s%s%s%s%s\n", + sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_DETECT?\ + "SS_DETECT ":"", + sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_READY?\ + "SS_READY ":"", + sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_BATDEAD?\ + "SS_BATDEAD ":"", + sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_BATWARN?\ + "SS_BATWARN ":"", + sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_STSCHG?\ + "SS_STSCHG ":""); + + p+=sprintf(p, "cs_flags : %s%s%s%s%s\n", + sa1100_pcmcia_socket[sock].cs_state.flags&SS_PWR_AUTO?\ + "SS_PWR_AUTO ":"", + sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD?\ + "SS_IOCARD ":"", + sa1100_pcmcia_socket[sock].cs_state.flags&SS_RESET?\ + "SS_RESET ":"", + sa1100_pcmcia_socket[sock].cs_state.flags&SS_SPKR_ENA?\ + "SS_SPKR_ENA ":"", + sa1100_pcmcia_socket[sock].cs_state.flags&SS_OUTPUT_ENA?\ + "SS_OUTPUT_ENA ":""); + + p+=sprintf(p, "Vcc : %d\n", sa1100_pcmcia_socket[sock].cs_state.Vcc); + + p+=sprintf(p, "Vpp : %d\n", sa1100_pcmcia_socket[sock].cs_state.Vpp); + + p+=sprintf(p, "irq : %d\n", sa1100_pcmcia_socket[sock].cs_state.io_irq); + + p+=sprintf(p, "I/O : %u (%u)\n", sa1100_pcmcia_socket[sock].speed_io, + sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, sock))); + + p+=sprintf(p, "attribute: %u (%u)\n", sa1100_pcmcia_socket[sock].speed_attr, + sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, sock))); + + p+=sprintf(p, "common : %u (%u)\n", sa1100_pcmcia_socket[sock].speed_mem, + sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, sock))); + + return p-buf; +} + +#endif /* defined(CONFIG_PROC_FS) */ + + +#ifdef CONFIG_CPU_FREQ + +/* sa1100_pcmcia_update_mecr() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * When sa1100_pcmcia_notifier() decides that a MECR adjustment (due + * to a core clock frequency change) is needed, this routine establishes + * new BS_xx values consistent with the clock speed `clock'. + */ +static void sa1100_pcmcia_update_mecr(unsigned int clock){ + unsigned int sock; + unsigned long mecr = MECR; + + for(sock = 0; sock < SA1100_PCMCIA_MAX_SOCK; ++sock){ + + MECR_BSIO_SET(mecr, sock, + sa1100_pcmcia_mecr_bs(sa1100_pcmcia_socket[sock].speed_io, + clock)); + MECR_BSA_SET(mecr, sock, + sa1100_pcmcia_mecr_bs(sa1100_pcmcia_socket[sock].speed_attr, + clock)); + MECR_BSM_SET(mecr, sock, + sa1100_pcmcia_mecr_bs(sa1100_pcmcia_socket[sock].speed_mem, + clock)); + } + + MECR = mecr; + +} + +/* sa1100_pcmcia_notifier() + * ^^^^^^^^^^^^^^^^^^^^^^^^ + * When changing the processor core clock frequency, it is necessary + * to adjust the MECR timings accordingly. We've recorded the timings + * requested by Card Services, so this is just a matter of finding + * out what our current speed is, and then recomputing the new MECR + * values. + * + * Returns: 0 on success, -1 on error + */ +static int sa1100_pcmcia_notifier(struct notifier_block *nb, + unsigned long val, void *data){ + struct cpufreq_info *ci = data; + + switch(val){ + case CPUFREQ_MINMAX: + + break; + + case CPUFREQ_PRECHANGE: + + if(ci->new_freq > ci->old_freq){ + DEBUG(2, "%s(): new frequency %u.%uMHz > %u.%uMHz, pre-updating\n", + __FUNCTION__, + ci->new_freq / 1000, (ci->new_freq / 100) % 10, + ci->old_freq / 1000, (ci->old_freq / 100) % 10); + sa1100_pcmcia_update_mecr(ci->new_freq); + } + + break; + + case CPUFREQ_POSTCHANGE: + + if(ci->new_freq < ci->old_freq){ + DEBUG(2, "%s(): new frequency %u.%uMHz < %u.%uMHz, post-updating\n", + __FUNCTION__, + ci->new_freq / 1000, (ci->new_freq / 100) % 10, + ci->old_freq / 1000, (ci->old_freq / 100) % 10); + sa1100_pcmcia_update_mecr(ci->new_freq); + } + + break; + + default: + printk(KERN_ERR "%s(): unknown CPU frequency event %lx\n", __FUNCTION__, + val); + return -1; + + } + + return 0; + +} + +static struct notifier_block sa1100_pcmcia_notifier_block = { + notifier_call: sa1100_pcmcia_notifier +}; + +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_graphicsclient.c linux.ac/drivers/pcmcia/sa1100_graphicsclient.c --- linux.vanilla/drivers/pcmcia/sa1100_graphicsclient.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_graphicsclient.c Wed Oct 10 01:48:06 2001 @@ -0,0 +1,154 @@ +/* + * drivers/pcmcia/sa1100_graphicsclient.c + * + * PCMCIA implementation routines for Graphics Client Plus + * + * 9/12/01 Woojung + * Turn power OFF at startup + * 1/31/2001 Woojung Huh + * Fix for GC Plus PCMCIA Reset Problem + * 2/27/2001 Woojung Huh [whuh@applieddata.net] + * Fix + * + */ +#include +#include +#include + +#include +#include +#include + +#define S0_CD_IRQ 60 // Socket 0 Card Detect IRQ +#define S0_STS_IRQ 55 // Socket 0 PCMCIA IRQ + +static volatile unsigned long *PCMCIA_Status = + ((volatile unsigned long *) ADS_p2v(_ADS_CS_STATUS)); + +static volatile unsigned long *PCMCIA_Power = + ((volatile unsigned long *) ADS_p2v(_ADS_CS_PR)); + +static int gcplus_pcmcia_init(struct pcmcia_init *init) +{ + int irq, res; + + // Reset PCMCIA + // Reset Timing for CPLD(U2) version 8001E or later + *PCMCIA_Power &= ~ ADS_CS_PR_A_RESET; + udelay(12); // 12 uSec + + *PCMCIA_Power |= ADS_CS_PR_A_RESET; + mdelay(30); // 30 mSec + + // Turn off 5V + *PCMCIA_Power &= ~0x03; + + /* Register interrupts */ + irq = S0_CD_IRQ; + res = request_irq(irq, init->handler, SA_INTERRUPT, "PCMCIA 0 CD", NULL); + if (res < 0) { + printk(KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq); + return -1; + } + + return 1; // 1 PCMCIA Slot +} + +static int gcplus_pcmcia_shutdown(void) +{ + /* disable IRQs */ + free_irq( S0_CD_IRQ, NULL); + + /* Shutdown PCMCIA power */ + mdelay(2); // 2msec + *PCMCIA_Power &= ~0x03; + + return 0; +} + +static int gcplus_pcmcia_socket_state(struct pcmcia_state_array + *state_array){ + unsigned long levels; + + if(state_array->size<1) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + + levels=*PCMCIA_Status; + + state_array->state[0].detect=(levels & ADS_CS_ST_A_CD)?1:0; + state_array->state[0].ready=(levels & ADS_CS_ST_A_READY)?1:0; + state_array->state[0].bvd1= 0; + state_array->state[0].bvd2= 0; + state_array->state[0].wrprot=0; + state_array->state[0].vs_3v=0; + state_array->state[0].vs_Xv=0; + + return 1; +} + +static int gcplus_pcmcia_get_irq_info(struct pcmcia_irq_info *info) +{ + if (info->sock > 1) + return -1; + + if (info->sock == 0) + info->irq = S0_STS_IRQ; + + return 0; +} + +static int gcplus_pcmcia_configure_socket(const struct pcmcia_configure + *configure) +{ + unsigned long flags; + + if(configure->sock>1) return -1; + + save_flags_cli(flags); + + switch (configure->vcc) { + case 0: + *PCMCIA_Power &= ~(ADS_CS_PR_A_3V_POWER | ADS_CS_PR_A_5V_POWER); + break; + + case 50: + *PCMCIA_Power &= ~(ADS_CS_PR_A_3V_POWER | ADS_CS_PR_A_5V_POWER); + *PCMCIA_Power |= ADS_CS_PR_A_5V_POWER; + break; + + case 33: + *PCMCIA_Power &= ~(ADS_CS_PR_A_3V_POWER | ADS_CS_PR_A_5V_POWER); + *PCMCIA_Power |= ADS_CS_PR_A_3V_POWER; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + restore_flags(flags); + return -1; + } + + /* Silently ignore Vpp, output enable, speaker enable. */ + + // Reset PCMCIA + *PCMCIA_Power &= ~ ADS_CS_PR_A_RESET; + udelay(12); + + *PCMCIA_Power |= ADS_CS_PR_A_RESET; + mdelay(30); + + restore_flags(flags); + + return 0; +} + +struct pcmcia_low_level gcplus_pcmcia_ops = { + gcplus_pcmcia_init, + gcplus_pcmcia_shutdown, + gcplus_pcmcia_socket_state, + gcplus_pcmcia_get_irq_info, + gcplus_pcmcia_configure_socket +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_graphicsmaster.c linux.ac/drivers/pcmcia/sa1100_graphicsmaster.c --- linux.vanilla/drivers/pcmcia/sa1100_graphicsmaster.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_graphicsmaster.c Wed Oct 10 01:48:06 2001 @@ -0,0 +1,215 @@ +/* + * drivers/pcmcia/sa1100_graphicsmaster.c + * + * PCMCIA implementation routines for GraphicsMaster + * + * 9/18/01 Woojung + * Fixed wrong PCMCIA voltage setting + * 7/5/01 Woojung Huh + * + */ +#include +#include + +#include +#include +#include +#include + +static int graphicsmaster_pcmcia_init(struct pcmcia_init *init) +{ + int return_val=0; + + /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ + PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); + + /* Disable Power 3.3V/5V for PCMCIA/CF */ + PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3; + + INTPOL1 |= (1 << (S0_READY_NINT - SA1111_IRQ(32))) | + (1 << (S1_READY_NINT - SA1111_IRQ(32))) | + (1 << (S0_CD_VALID - SA1111_IRQ(32))) | + (1 << (S1_CD_VALID - SA1111_IRQ(32))) | + (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | + (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))); + + return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT, + "GC Master PCMCIA (0) CD", NULL); + return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT, + "GC Master CF (1) CD", NULL); + return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT, + "GC Master PCMCIA (0) BVD1", NULL); + return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT, + "GC Master CF (1) BVD1", NULL); + + MECR = 0x09430943; + + return (return_val<0) ? -1 : 2; +} + +static int graphicsmaster_pcmcia_shutdown(void) +{ + + free_irq(S0_CD_VALID, NULL); + free_irq(S1_CD_VALID, NULL); + free_irq(S0_BVD1_STSCHG, NULL); + free_irq(S1_BVD1_STSCHG, NULL); + + INTPOL1 &= ~((1 << (S0_CD_VALID - SA1111_IRQ(32))) | + (1 << (S1_CD_VALID - SA1111_IRQ(32))) | + (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | + (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)))); + + return 0; +} + +static int graphicsmaster_pcmcia_socket_state(struct pcmcia_state_array *state_array) +{ + unsigned long status; + int return_val=1; + + if(state_array->size<2) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + + status=PCSR; + + state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0; + + state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1; + + state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1; + + state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1; + + state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1; + + state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0; + + state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; + + state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0; + + state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1; + + state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1; + + state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1; + + state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1; + + state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0; + + state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0; + + return return_val; +} + +static int graphicsmaster_pcmcia_get_irq_info(struct pcmcia_irq_info *info) +{ + + switch(info->sock){ + case 0: + info->irq=S0_READY_NINT; + break; + + case 1: + info->irq=S1_READY_NINT; + break; + + default: + return -1; + } + + return 0; +} + +static int graphicsmaster_pcmcia_configure_socket(const struct pcmcia_configure *configure) +{ + unsigned long pccr=PCCR, gpio=PA_DWR; + + switch(configure->sock){ + case 0: + + switch(configure->vcc){ + case 0: + pccr = (pccr & ~PCCR_S0_FLT); + gpio |= GPIO_GPIO0 | GPIO_GPIO1; + break; + + case 33: + pccr = (pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN; + gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1); + gpio &= ~GPIO_GPIO0; + break; + + case 50: + pccr = (pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN); + gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1); + gpio |= GPIO_GPIO0; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + return -1; + } + + pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST); + + break; + + case 1: + switch(configure->vcc){ + case 0: + pccr = (pccr & ~PCCR_S1_FLT); + gpio |= GPIO_GPIO2 | GPIO_GPIO3; + break; + + case 33: + pccr = (pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN; + gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3); + gpio &= ~GPIO_GPIO2; + break; + + case 50: + pccr = (pccr | PCCR_S1_PSE | PCCR_S1_FLT | PCCR_S1_PWAITEN); + gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3); + gpio |= GPIO_GPIO2; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + return -1; + } + + if(configure->vpp!=configure->vcc && configure->vpp!=0){ + printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, + configure->vpp); + return -1; + } + + pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST); + + break; + + default: + return -1; + } + + PCCR = pccr; + PA_DWR = gpio; + + return 0; +} + +struct pcmcia_low_level graphicsmaster_pcmcia_ops = { + graphicsmaster_pcmcia_init, + graphicsmaster_pcmcia_shutdown, + graphicsmaster_pcmcia_socket_state, + graphicsmaster_pcmcia_get_irq_info, + graphicsmaster_pcmcia_configure_socket +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_h3600.c linux.ac/drivers/pcmcia/sa1100_h3600.c --- linux.vanilla/drivers/pcmcia/sa1100_h3600.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_h3600.c Wed Oct 10 01:48:06 2001 @@ -0,0 +1,147 @@ +/* + * drivers/pcmcia/sa1100_h3600.c + * + * PCMCIA implementation routines for H3600 + * + */ +#include +#include + +#include +#include +#include + + +static int h3600_pcmcia_init(struct pcmcia_init *init){ + int irq, res; + + /* Enable CF bus: */ + set_h3600_egpio(EGPIO_H3600_OPT_NVRAM_ON); + clr_h3600_egpio(EGPIO_H3600_OPT_RESET); + + /* All those are inputs */ + GPDR &= ~(GPIO_H3600_PCMCIA_CD0 | GPIO_H3600_PCMCIA_CD1 | GPIO_H3600_PCMCIA_IRQ0| GPIO_H3600_PCMCIA_IRQ1); + + /* Set transition detect */ + set_GPIO_IRQ_edge( GPIO_H3600_PCMCIA_CD0 | GPIO_H3600_PCMCIA_CD1, GPIO_BOTH_EDGES ); + set_GPIO_IRQ_edge( GPIO_H3600_PCMCIA_IRQ0| GPIO_H3600_PCMCIA_IRQ1, GPIO_FALLING_EDGE ); + + /* Register interrupts */ + irq = IRQ_GPIO_H3600_PCMCIA_CD0; + res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD0", NULL ); + if( res < 0 ) goto irq_err; + irq = IRQ_GPIO_H3600_PCMCIA_CD1; + res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD1", NULL ); + if( res < 0 ) goto irq_err; + + return 2; + +irq_err: + printk( KERN_ERR __FUNCTION__ ": Request for IRQ %u failed\n", irq ); + return -1; +} + +static int h3600_pcmcia_shutdown(void) +{ + /* disable IRQs */ + free_irq( IRQ_GPIO_H3600_PCMCIA_CD0, NULL ); + free_irq( IRQ_GPIO_H3600_PCMCIA_CD1, NULL ); + + /* Disable CF bus: */ + clr_h3600_egpio(EGPIO_H3600_OPT_NVRAM_ON|EGPIO_H3600_OPT_ON); + set_h3600_egpio(EGPIO_H3600_OPT_RESET); + + return 0; +} + +static int h3600_pcmcia_socket_state(struct pcmcia_state_array + *state_array){ + unsigned long levels; + + if(state_array->size<2) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + + levels=GPLR; + + state_array->state[0].detect=((levels & GPIO_H3600_PCMCIA_CD0)==0)?1:0; + state_array->state[0].ready=(levels & GPIO_H3600_PCMCIA_IRQ0)?1:0; + state_array->state[0].bvd1= 0; + state_array->state[0].bvd2= 0; + state_array->state[0].wrprot=0; /* Not available on H3600. */ + state_array->state[0].vs_3v=0; + state_array->state[0].vs_Xv=0; + + state_array->state[1].detect=((levels & GPIO_H3600_PCMCIA_CD1)==0)?1:0; + state_array->state[1].ready=(levels & GPIO_H3600_PCMCIA_IRQ1)?1:0; + state_array->state[1].bvd1=0; + state_array->state[1].bvd2=0; + state_array->state[1].wrprot=0; /* Not available on H3600. */ + state_array->state[1].vs_3v=0; + state_array->state[1].vs_Xv=0; + + return 1; +} + +static int h3600_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ + + switch (info->sock) { + case 0: + info->irq=IRQ_GPIO_H3600_PCMCIA_IRQ0; + break; + case 1: + info->irq=IRQ_GPIO_H3600_PCMCIA_IRQ1; + break; + default: + return -1; + } + return 0; +} + +static int h3600_pcmcia_configure_socket(const struct pcmcia_configure + *configure) +{ + unsigned long flags; + + if(configure->sock>1) return -1; + + save_flags_cli(flags); + + switch (configure->vcc) { + case 0: + clr_h3600_egpio(EGPIO_H3600_OPT_ON); + break; + + case 33: + case 50: + set_h3600_egpio(EGPIO_H3600_OPT_ON); + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + restore_flags(flags); + return -1; + } + + if (configure->reset) + set_h3600_egpio(EGPIO_H3600_CARD_RESET); + else + clr_h3600_egpio(EGPIO_H3600_CARD_RESET); + + /* Silently ignore Vpp, output enable, speaker enable. */ + + restore_flags(flags); + + return 0; +} + +struct pcmcia_low_level h3600_pcmcia_ops = { + h3600_pcmcia_init, + h3600_pcmcia_shutdown, + h3600_pcmcia_socket_state, + h3600_pcmcia_get_irq_info, + h3600_pcmcia_configure_socket +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_jornada720.c linux.ac/drivers/pcmcia/sa1100_jornada720.c --- linux.vanilla/drivers/pcmcia/sa1100_jornada720.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_jornada720.c Wed Oct 10 01:48:06 2001 @@ -0,0 +1,215 @@ +/* + * drivers/pcmcia/sa1100_jornada720.c + * + * Jornada720 PCMCIA specific routines + * + */ +#include +#include + +#include +#include +#include +#include + +#define SOCKET0_POWER GPIO_GPIO0 +#define SOCKET0_3V GPIO_GPIO2 +#define SOCKET1_POWER (GPIO_GPIO1 | GPIO_GPIO3) +#define SOCKET1_3V GPIO_GPIO3 + +static int jornada720_pcmcia_init(struct pcmcia_init *init) +{ + int return_val=0; + + GRER |= 0x00000002; + /* Set GPIO_A<3:1> to be outputs for PCMCIA/CF power controller: */ + PA_DDR = 0; + PA_DWR = 0; + PA_SDR = 0; + PA_SSR = 0; + + PB_DDR = 0; + PB_DWR = 0x01; + PB_SDR = 0; + PB_SSR = 0; + + PC_DDR = 0x88; + PC_DWR = 0x20; + PC_SDR = 0; + PC_SSR = 0; + + INTPOL1 |= + (1 << (S0_READY_NINT - SA1111_IRQ(32))) | + (1 << (S1_READY_NINT - SA1111_IRQ(32))) | + (1 << (S0_CD_VALID - SA1111_IRQ(32))) | + (1 << (S1_CD_VALID - SA1111_IRQ(32))) | + (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | + (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))); + + return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT, + "Jornada720 PCMCIA (0) CD", NULL); + return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT, + "Jornada720 CF (1) CD", NULL); + return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT, + "Jornada720 PCMCIA (0) BVD1", NULL); + return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT, + "Jornada720 CF (1) BVD1", NULL); + + return (return_val<0) ? -1 : 2; +} + +static int jornada720_pcmcia_shutdown(void) +{ + free_irq(S0_CD_VALID, NULL); + free_irq(S1_CD_VALID, NULL); + free_irq(S0_BVD1_STSCHG, NULL); + free_irq(S1_BVD1_STSCHG, NULL); + + INTPOL1 &= + ~((1 << (S0_CD_VALID - SA1111_IRQ(32))) | + (1 << (S1_CD_VALID - SA1111_IRQ(32))) | + (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | + (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)))); + + return 0; +} + +static int jornada720_pcmcia_socket_state(struct pcmcia_state_array + *state_array) +{ + unsigned long status; + int return_val=1; + + if(state_array->size<2) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + status=PCSR; + state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0; + state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1; + state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1; + state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1; + state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1; + state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0; + state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; + state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0; + state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1; + state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1; + state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1; + state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1; + state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0; + state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0; + return return_val; +} + +static int jornada720_pcmcia_get_irq_info(struct pcmcia_irq_info *info) +{ + switch(info->sock){ + case 0: + info->irq=S0_READY_NINT; + break; + + case 1: + info->irq=S1_READY_NINT; + break; + + default: + return -1; + } + return 0; +} + +static int jornada720_pcmcia_configure_socket(const struct pcmcia_configure + *configure) +{ + unsigned long pccr=PCCR, gpio=PA_DWR; + +printk("%s(): config socket %d vcc %d vpp %d\n", __FUNCTION__, + configure->sock, configure->vcc, configure->vpp); + switch(configure->sock){ + case 0: + switch(configure->vcc){ + case 0: + pccr = (pccr & ~PCCR_S0_FLT); + gpio&=~(SOCKET0_POWER | SOCKET0_3V); + break; + + case 33: + pccr = (pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN; + gpio |= SOCKET0_POWER | SOCKET0_3V; + break; + + case 50: + pccr = (pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN); + gpio = (gpio & ~SOCKET0_3V) | SOCKET0_POWER; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + return -1; + } + switch(configure->vpp){ + case 0: + break; + case 50: + printk(KERN_ERR "%s(): 5.0 Vpp %u\n", __FUNCTION__, + configure->vpp); + break; + case 120: + printk(KERN_ERR "%s(): 12 Vpp %u\n", __FUNCTION__, + configure->vpp); + break; + default: + printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__, + configure->vpp); + return -1; + } + pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST); + break; + + case 1: + switch(configure->vcc){ + case 0: + pccr = (pccr & ~PCCR_S1_FLT); + gpio &= ~(SOCKET1_POWER); + break; + + case 33: + pccr = (pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN; + gpio |= SOCKET1_POWER; + break; + + case 50: + pccr = (pccr | PCCR_S1_PSE | PCCR_S1_FLT | PCCR_S1_PWAITEN); + gpio = (gpio & ~(SOCKET1_POWER)) | SOCKET1_POWER; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + return -1; + } + if(configure->vpp!=configure->vcc && configure->vpp!=0){ + printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, + configure->vpp); + return -1; + } + pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST); + break; + default: + return -1; + } + PCCR = pccr; + PA_DWR = gpio; + return 0; +} + +struct pcmcia_low_level jornada720_pcmcia_ops = { + jornada720_pcmcia_init, + jornada720_pcmcia_shutdown, + jornada720_pcmcia_socket_state, + jornada720_pcmcia_get_irq_info, + jornada720_pcmcia_configure_socket +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_neponset.c linux.ac/drivers/pcmcia/sa1100_neponset.c --- linux.vanilla/drivers/pcmcia/sa1100_neponset.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_neponset.c Wed Oct 10 01:48:06 2001 @@ -0,0 +1,248 @@ +/* + * drivers/pcmcia/sa1100_neponset.c + * + * Neponset PCMCIA specific routines + * + */ +#include +#include + +#include +#include +#include +#include + +static int neponset_pcmcia_init(struct pcmcia_init *init){ + int return_val=0; + + /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ + PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); + + /* MAX1600 to standby mode: */ + PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); + NCR_0 &= ~(NCR_A0VPP | NCR_A1VPP); + + INTPOL1 |= + (1 << (S0_READY_NINT - SA1111_IRQ(32))) | + (1 << (S1_READY_NINT - SA1111_IRQ(32))) | + (1 << (S0_CD_VALID - SA1111_IRQ(32))) | + (1 << (S1_CD_VALID - SA1111_IRQ(32))) | + (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | + (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))); + + return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT, + "Neponset PCMCIA (0) CD", NULL); + return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT, + "Neponset CF (1) CD", NULL); + return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT, + "Neponset PCMCIA (0) BVD1", NULL); + return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT, + "Neponset CF (1) BVD1", NULL); + + return (return_val<0) ? -1 : 2; +} + +static int neponset_pcmcia_shutdown(void){ + + free_irq(S0_CD_VALID, NULL); + free_irq(S1_CD_VALID, NULL); + free_irq(S0_BVD1_STSCHG, NULL); + free_irq(S1_BVD1_STSCHG, NULL); + + INTPOL1 &= + ~((1 << (S0_CD_VALID - SA1111_IRQ(32))) | + (1 << (S1_CD_VALID - SA1111_IRQ(32))) | + (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | + (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)))); + + return 0; +} + +static int neponset_pcmcia_socket_state(struct pcmcia_state_array + *state_array){ + unsigned long status; + int return_val=1; + + if(state_array->size<2) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + + status=PCSR; + + state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0; + + state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1; + + state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1; + + state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1; + + state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1; + + state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0; + + state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; + + state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0; + + state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1; + + state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1; + + state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1; + + state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1; + + state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0; + + state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0; + + return return_val; +} + +static int neponset_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ + + switch(info->sock){ + case 0: + info->irq=S0_READY_NINT; + break; + + case 1: + info->irq=S1_READY_NINT; + break; + + default: + return -1; + } + + return 0; +} + +static int neponset_pcmcia_configure_socket(const struct pcmcia_configure + *configure){ + unsigned long pccr=PCCR, ncr=NCR_0, gpio=PA_DWR; + + /* Neponset uses the Maxim MAX1600, with the following connections: + * + * MAX1600 Neponset + * + * A0VCC SA-1111 GPIO A<1> + * A1VCC SA-1111 GPIO A<0> + * A0VPP CPLD NCR A0VPP + * A1VPP CPLD NCR A1VPP + * B0VCC SA-1111 GPIO A<2> + * B1VCC SA-1111 GPIO A<3> + * B0VPP ground (slot B is CF) + * B1VPP ground (slot B is CF) + * + * VX VCC (5V) + * VY VCC3_3 (3.3V) + * 12INA 12V + * 12INB ground (slot B is CF) + * + * The MAX1600 CODE pin is tied to ground, placing the device in + * "Standard Intel code" mode. Refer to the Maxim data sheet for + * the corresponding truth table. + */ + + switch(configure->sock){ + case 0: + + switch(configure->vcc){ + case 0: + pccr=(pccr & ~PCCR_S0_FLT); + gpio&=~(GPIO_GPIO0 | GPIO_GPIO1); + break; + + case 33: + pccr=(pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN; + gpio=(gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO1; + break; + + case 50: + pccr=(pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN); + gpio=(gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO0; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + return -1; + } + + switch(configure->vpp){ + case 0: + ncr&=~(NCR_A0VPP | NCR_A1VPP); + break; + + case 120: + ncr=(ncr & ~(NCR_A0VPP | NCR_A1VPP)) | NCR_A1VPP; + break; + + default: + if(configure->vpp == configure->vcc) + ncr=(ncr & ~(NCR_A0VPP | NCR_A1VPP)) | NCR_A0VPP; + else { + printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__, + configure->vpp); + return -1; + } + } + + pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST); + + break; + + case 1: + switch(configure->vcc){ + case 0: + pccr=(pccr & ~PCCR_S1_FLT); + gpio&=~(GPIO_GPIO2 | GPIO_GPIO3); + break; + + case 33: + pccr=(pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN; + gpio=(gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO2; + break; + + case 50: + pccr=(pccr | PCCR_S1_PSE | PCCR_S1_FLT | PCCR_S1_PWAITEN); + gpio=(gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO3; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + return -1; + } + + if(configure->vpp!=configure->vcc && configure->vpp!=0){ + printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, + configure->vpp); + return -1; + } + + pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST); + + break; + + default: + return -1; + } + + PCCR = pccr; + NCR_0 = ncr; + PA_DWR = gpio; + + return 0; +} + +struct pcmcia_low_level neponset_pcmcia_ops = { + neponset_pcmcia_init, + neponset_pcmcia_shutdown, + neponset_pcmcia_socket_state, + neponset_pcmcia_get_irq_info, + neponset_pcmcia_configure_socket +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_pangolin.c linux.ac/drivers/pcmcia/sa1100_pangolin.c --- linux.vanilla/drivers/pcmcia/sa1100_pangolin.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_pangolin.c Wed Oct 10 01:48:06 2001 @@ -0,0 +1,157 @@ +/* + * drivers/pcmcia/sa1100_pangolin.c + * + * PCMCIA implementation routines for Pangolin + * + */ +#include +#include + +#include +#include +#include + +static int pangolin_pcmcia_init(struct pcmcia_init *init){ + int irq, res; + + /* set GPIO_PCMCIA_CD & GPIO_PCMCIA_IRQ as inputs */ + GPDR &= ~(GPIO_PCMCIA_CD|GPIO_PCMCIA_IRQ); +#ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE + /* set GPIO pins GPIO_PCMCIA_BUS_ON & GPIO_PCMCIA_RESET as output */ + GPDR |= (GPIO_PCMCIA_BUS_ON|GPIO_PCMCIA_RESET); + /* Enable PCMCIA bus: */ + GPCR = GPIO_PCMCIA_BUS_ON; +#else + /* set GPIO pin GPIO_PCMCIA_RESET as output */ + GPDR |= GPIO_PCMCIA_RESET; +#endif + /* Set transition detect */ + set_GPIO_IRQ_edge( GPIO_PCMCIA_CD, GPIO_BOTH_EDGES ); + set_GPIO_IRQ_edge( GPIO_PCMCIA_IRQ, GPIO_FALLING_EDGE ); + + /* Register interrupts */ + irq = IRQ_PCMCIA_CD; + res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD", NULL ); + if( res < 0 ) goto irq_err; + + /* There's only one slot, but it's "Slot 1": */ + return 2; + +irq_err: + printk( KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq ); + return -1; +} + +static int pangolin_pcmcia_shutdown(void) +{ + /* disable IRQs */ + free_irq( IRQ_PCMCIA_CD, NULL ); +#ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE + /* Disable PCMCIA bus: */ + GPSR = GPIO_PCMCIA_BUS_ON; +#endif + return 0; +} + +static int pangolin_pcmcia_socket_state(struct pcmcia_state_array + *state_array){ + unsigned long levels; + + if(state_array->size<2) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + + levels=GPLR; +#ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE + state_array->state[1].detect=((levels & GPIO_PCMCIA_CD)==0)?1:0; + state_array->state[1].ready=(levels & GPIO_PCMCIA_IRQ)?1:0; + state_array->state[1].bvd1=1; /* Not available on Pangolin. */ + state_array->state[1].bvd2=1; /* Not available on Pangolin. */ + state_array->state[1].wrprot=0; /* Not available on Pangolin. */ + state_array->state[1].vs_3v=1; /* Can only apply 3.3V on Pangolin. */ + state_array->state[1].vs_Xv=0; +#else + state_array->state[0].detect=((levels & GPIO_PCMCIA_CD)==0)?1:0; + state_array->state[0].ready=(levels & GPIO_PCMCIA_IRQ)?1:0; + state_array->state[0].bvd1=1; /* Not available on Pangolin. */ + state_array->state[0].bvd2=1; /* Not available on Pangolin. */ + state_array->state[0].wrprot=0; /* Not available on Pangolin. */ + state_array->state[0].vs_3v=0; /* voltage level is determined by jumper setting */ + state_array->state[0].vs_Xv=0; +#endif + return 1; +} + +static int pangolin_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ + + if(info->sock>1) return -1; +#ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE + if(info->sock==1) + info->irq=IRQ_PCMCIA_IRQ; +#else + if(info->sock==0) + info->irq=IRQ_PCMCIA_IRQ; +#endif + return 0; +} + +static int pangolin_pcmcia_configure_socket(const struct pcmcia_configure + *configure) +{ + unsigned long value, flags; + + if(configure->sock>1) return -1; +#ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE + if(configure->sock==0) return 0; +#endif + save_flags_cli(flags); + + /* Murphy: BUS_ON different from POWER ? */ + + switch(configure->vcc){ + case 0: + break; +#ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE + case 50: + printk(KERN_WARNING "%s(): CS asked for 5V, applying 3.3V...\n", + __FUNCTION__); + case 33: /* Can only apply 3.3V to the CF slot. */ + break; +#else + case 50: + printk(KERN_WARNING "%s(): CS asked for 5V, determinded by jumper setting...\n", __FUNCTION__); + break; + case 33: + printk(KERN_WARNING "%s(): CS asked for 3.3V, determined by jumper setting...\n", __FUNCTION__); + break; +#endif + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + restore_flags(flags); + return -1; + } +#ifdef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE + /* reset & unreset request */ + if(configure->sock==0) { + if(configure->reset) { + GPSR |= GPIO_PCMCIA_RESET; + } else { + GPCR |= GPIO_PCMCIA_RESET; + } + } +#endif + /* Silently ignore Vpp, output enable, speaker enable. */ + restore_flags(flags); + return 0; +} + +struct pcmcia_low_level pangolin_pcmcia_ops = { + pangolin_pcmcia_init, + pangolin_pcmcia_shutdown, + pangolin_pcmcia_socket_state, + pangolin_pcmcia_get_irq_info, + pangolin_pcmcia_configure_socket +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_pfs168.c linux.ac/drivers/pcmcia/sa1100_pfs168.c --- linux.vanilla/drivers/pcmcia/sa1100_pfs168.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_pfs168.c Wed Oct 10 01:48:06 2001 @@ -0,0 +1,237 @@ +#warning "REVISIT_PFS168: Need to verify and test GPIO power encodings." +/* + * drivers/pcmcia/sa1100_pfs168.c + * + * PFS168 PCMCIA specific routines + * + */ +#include +#include + +#include +#include +#include +#include + +static int pfs168_pcmcia_init(struct pcmcia_init *init){ + int return_val=0; + + /* TPS2211 to standby mode: */ + PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); + + /* Set GPIO_A<3:0> to be outputs for PCMCIA (socket 0) power controller: */ + PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); + + INTPOL1 |= + (1 << (S0_READY_NINT - SA1111_IRQ(32))) | + (1 << (S1_READY_NINT - SA1111_IRQ(32))) | + (1 << (S0_CD_VALID - SA1111_IRQ(32))) | + (1 << (S1_CD_VALID - SA1111_IRQ(32))) | + (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | + (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))); + + return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT, + "PFS168 PCMCIA (0) CD", NULL); + return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT, + "PFS168 CF (1) CD", NULL); + return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT, + "PFS168 PCMCIA (0) BVD1", NULL); + return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT, + "PFS168 CF (1) BVD1", NULL); + + return (return_val<0) ? -1 : 2; +} + +static int pfs168_pcmcia_shutdown(void){ + + free_irq(S0_CD_VALID, NULL); + free_irq(S1_CD_VALID, NULL); + free_irq(S0_BVD1_STSCHG, NULL); + free_irq(S1_BVD1_STSCHG, NULL); + + INTPOL1 &= + ~((1 << (S0_CD_VALID - SA1111_IRQ(32))) | + (1 << (S1_CD_VALID - SA1111_IRQ(32))) | + (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | + (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)))); + + return 0; +} + +static int pfs168_pcmcia_socket_state(struct pcmcia_state_array + *state_array){ + unsigned long status; + int return_val=1; + + if(state_array->size<2) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + + status=PCSR; + + state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0; + + state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1; + + state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1; + + state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1; + + state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1; + + state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0; + + state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; + + state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0; + + state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1; + + state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1; + + state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1; + + state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1; + + state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0; + + state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0; + + return return_val; +} + +static int pfs168_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ + + switch(info->sock){ + case 0: + info->irq=S0_READY_NINT; + break; + + case 1: + info->irq=S1_READY_NINT; + break; + + default: + return -1; + } + + return 0; +} + +static int pfs168_pcmcia_configure_socket(const struct pcmcia_configure + *configure){ + unsigned long pccr=PCCR, gpio=PA_DWR; + + /* PFS168 uses the Texas Instruments TPS2211 for PCMCIA (socket 0) voltage control only, + * with the following connections: + * + * TPS2211 PFS168 + * + * -VCCD0 SA-1111 GPIO A<0> + * -VCCD0 SA-1111 GPIO A<1> + * VPPD0 SA-1111 GPIO A<2> + * VPPD0 SA-1111 GPIO A<2> + * + */ + + switch(configure->sock){ + case 0: + + switch(configure->vcc){ + case 0: + pccr = (pccr & ~PCCR_S0_FLT); + gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1); + break; + + case 33: + pccr = (pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN; + gpio = (gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO0; + break; + + case 50: + pccr = (pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN); + gpio = (gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO1; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + return -1; + } + + switch(configure->vpp){ + case 0: + gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3); + break; + + case 120: + printk(KERN_ERR "%s(): PFS-168 does not support Vpp %uV\n", __FUNCTION__, + configure->vpp/10); + return -1; + break; + + default: + if(configure->vpp == configure->vcc) + gpio = (gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO3; + else { + printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__, + configure->vpp); + return -1; + } + } + + pccr = (configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST); + + PA_DWR = gpio; + + break; + + case 1: + switch(configure->vcc){ + case 0: + pccr = (pccr & ~PCCR_S1_FLT); + break; + + case 33: + pccr = (pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN; + break; + + case 50: + printk(KERN_ERR "%s(): PFS-168 CompactFlash socket does not support Vcc %uV\n", __FUNCTION__, + configure->vcc/10); + return -1; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + return -1; + } + + if(configure->vpp!=configure->vcc && configure->vpp!=0){ + printk(KERN_ERR "%s(): CompactFlash socket does not support Vpp %uV\n", __FUNCTION__, + configure->vpp/10); + return -1; + } + + pccr = (configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST); + + break; + + default: + return -1; + } + + PCCR = pccr; + + return 0; +} + +struct pcmcia_low_level pfs168_pcmcia_ops = { + pfs168_pcmcia_init, + pfs168_pcmcia_shutdown, + pfs168_pcmcia_socket_state, + pfs168_pcmcia_get_irq_info, + pfs168_pcmcia_configure_socket +}; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_simpad.c linux.ac/drivers/pcmcia/sa1100_simpad.c --- linux.vanilla/drivers/pcmcia/sa1100_simpad.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_simpad.c Wed Oct 10 01:48:06 2001 @@ -0,0 +1,136 @@ +/* + * drivers/pcmcia/sa1100_pangolin.c + * + * PCMCIA implementation routines for simpad + * + */ +#include +#include + +#include +#include +#include + +static int simpad_pcmcia_init(struct pcmcia_init *init){ + int irq, res; + + /* set GPIO_CF_CD & GPIO_CF_IRQ as inputs */ + GPDR &= ~(GPIO_CF_CD|GPIO_CF_IRQ); + //init_simpad_cs3(); + printk("\nCS3:%x\n",cs3_shadow); + PCMCIA_setbit(PCMCIA_RESET); + PCMCIA_clearbit(PCMCIA_BUFF_DIS); + + /* Set transition detect */ + set_GPIO_IRQ_edge( GPIO_CF_CD, GPIO_BOTH_EDGES ); + set_GPIO_IRQ_edge( GPIO_CF_IRQ, GPIO_FALLING_EDGE ); + + /* Register interrupts */ + irq = IRQ_GPIO_CF_CD; + res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_CD", NULL ); + if( res < 0 ) goto irq_err; + + /* There's only one slot, but it's "Slot 1": */ + return 2; + +irq_err: + printk( KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq ); + return -1; +} + +static int simpad_pcmcia_shutdown(void) +{ + /* disable IRQs */ + free_irq( IRQ_GPIO_CF_CD, NULL ); + + /* Disable CF bus: */ + + PCMCIA_setbit(PCMCIA_BUFF_DIS); + PCMCIA_clearbit(PCMCIA_RESET); + return 0; +} + +static int simpad_pcmcia_socket_state(struct pcmcia_state_array + *state_array) +{ + unsigned long levels; + + if(state_array->size<2) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + + levels=GPLR; + + state_array->state[1].detect=((levels & GPIO_CF_CD)==0)?1:0; + + state_array->state[1].ready=(levels & GPIO_CF_IRQ)?1:0; + + state_array->state[1].bvd1=1; /* Not available on Simpad. */ + + state_array->state[1].bvd2=1; /* Not available on Simpad. */ + + state_array->state[1].wrprot=0; /* Not available on Simpad. */ + + state_array->state[1].vs_3v=1; /* Can only apply 3.3V on Simpad. */ + + state_array->state[1].vs_Xv=0; + + return 1; +} + +static int simpad_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ + + if(info->sock>1) return -1; + + if(info->sock==1) + info->irq=IRQ_GPIO_CF_IRQ; + + return 0; +} + +static int simpad_pcmcia_configure_socket(const struct pcmcia_configure + *configure) +{ + unsigned long value, flags; + + if(configure->sock>1) return -1; + + if(configure->sock==0) return 0; + + save_flags_cli(flags); + + /* Murphy: BUS_ON different from POWER ? */ + + switch(configure->vcc){ + case 0: + PCMCIA_setbit(PCMCIA_BUFF_DIS); + break; + + case 33: + case 50: + PCMCIA_setbit(PCMCIA_BUFF_DIS); + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + restore_flags(flags); + return -1; + } + + /* Silently ignore Vpp, output enable, speaker enable. */ + + restore_flags(flags); + + return 0; +} + +struct pcmcia_low_level simpad_pcmcia_ops = { + simpad_pcmcia_init, + simpad_pcmcia_shutdown, + simpad_pcmcia_socket_state, + simpad_pcmcia_get_irq_info, + simpad_pcmcia_configure_socket +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_stork.c linux.ac/drivers/pcmcia/sa1100_stork.c --- linux.vanilla/drivers/pcmcia/sa1100_stork.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_stork.c Wed Oct 10 01:48:06 2001 @@ -0,0 +1,202 @@ +/* + * drivers/pcmcia/sa1100_stork.c + * + Copyright 2001 (C) Ken Gordon + + This is derived from pre-existing drivers/pcmcia/sa1100_?????.c + + 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. + + 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. + + * + * PCMCIA implementation routines for stork + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static int debug = 0; + +static struct pcmcia_init sa1100_stork_pcmcia_init; + +static int stork_pcmcia_init(struct pcmcia_init *init) +{ + int irq, res; + printk("in stork_pcmcia_init\n"); + + sa1100_stork_pcmcia_init = *init; + + /* Enable CF bus: */ + storkSetLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON); + + /* All those are inputs */ + GPDR &= ~(GPIO_STORK_PCMCIA_A_CARD_DETECT | GPIO_STORK_PCMCIA_B_CARD_DETECT | GPIO_STORK_PCMCIA_A_RDY| GPIO_STORK_PCMCIA_B_RDY); + + /* Set transition detect */ + set_GPIO_IRQ_edge( GPIO_STORK_PCMCIA_A_CARD_DETECT | GPIO_STORK_PCMCIA_B_CARD_DETECT, GPIO_BOTH_EDGES ); + set_GPIO_IRQ_edge( GPIO_STORK_PCMCIA_A_RDY| GPIO_STORK_PCMCIA_B_RDY, GPIO_FALLING_EDGE ); + + /* Register interrupts */ + irq = IRQ_GPIO_STORK_PCMCIA_A_CARD_DETECT; + res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD0", NULL ); + if( res < 0 ) goto irq_err; + irq = IRQ_GPIO_STORK_PCMCIA_B_CARD_DETECT; + res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD1", NULL ); + if( res < 0 ) goto irq_err; + + return 2; + + irq_err: + printk( KERN_ERR __FUNCTION__ ": Request for IRQ %u failed\n", irq ); + return -1; +} + +static int stork_pcmcia_shutdown(void) +{ + printk(__FUNCTION__ "\n"); + /* disable IRQs */ + free_irq( IRQ_GPIO_STORK_PCMCIA_A_CARD_DETECT, NULL ); + free_irq( IRQ_GPIO_STORK_PCMCIA_B_CARD_DETECT, NULL ); + + /* Disable CF bus: */ + storkClearLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON); + storkClearLatchA(STORK_PCMCIA_A_POWER_ON); + storkClearLatchA(STORK_PCMCIA_B_POWER_ON); + return 0; +} + +static int stork_pcmcia_socket_state(struct pcmcia_state_array *state_array) +{ + unsigned long levels; + + if(state_array->size<2) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + + levels=GPLR; + + if (debug > 1) + printk(__FUNCTION__ " GPLR=%x IRQ[1:0]=%x\n", GPLR, (GPLR & (GPIO_STORK_PCMCIA_A_RDY|GPIO_STORK_PCMCIA_B_RDY))); + state_array->state[0].detect=((levels & GPIO_STORK_PCMCIA_A_CARD_DETECT)==0)?1:0; + state_array->state[0].ready=(levels & GPIO_STORK_PCMCIA_A_RDY)?1:0; + state_array->state[0].bvd1= 1; + state_array->state[0].bvd2= 1; + state_array->state[0].wrprot=0; + state_array->state[0].vs_3v=1; + state_array->state[0].vs_Xv=0; + + state_array->state[1].detect=((levels & GPIO_STORK_PCMCIA_B_CARD_DETECT)==0)?1:0; + state_array->state[1].ready=(levels & GPIO_STORK_PCMCIA_B_RDY)?1:0; + state_array->state[1].bvd1=1; + state_array->state[1].bvd2=1; + state_array->state[1].wrprot=0; + state_array->state[1].vs_3v=1; + state_array->state[1].vs_Xv=0; + + return 1; +} + +static int stork_pcmcia_get_irq_info(struct pcmcia_irq_info *info) +{ + + switch (info->sock) { + case 0: + info->irq=IRQ_GPIO_STORK_PCMCIA_A_RDY; + break; + case 1: + info->irq=IRQ_GPIO_STORK_PCMCIA_B_RDY; + break; + default: + return -1; + } + return 0; +} + +static int stork_pcmcia_configure_socket(const struct pcmcia_configure *configure) +{ + int card = configure->sock; + unsigned long flags; + + int DETECT, RDY, POWER, RESET; + + if (card > 1) return -1; + + printk(__FUNCTION__ ": socket=%d vcc=%d vpp=%d reset=%d\n", + card, configure->vcc, configure->vpp, configure->reset); + + save_flags_cli(flags); + + if (card == 0) { + DETECT = GPIO_STORK_PCMCIA_A_CARD_DETECT; + RDY = GPIO_STORK_PCMCIA_A_RDY; + POWER = STORK_PCMCIA_A_POWER_ON; + RESET = STORK_PCMCIA_A_RESET; + } else { + DETECT = GPIO_STORK_PCMCIA_B_CARD_DETECT; + RDY = GPIO_STORK_PCMCIA_B_RDY; + POWER = STORK_PCMCIA_B_POWER_ON; + RESET = STORK_PCMCIA_B_RESET; + } + +/* + if (storkTestGPIO(DETECT)) { + printk("no card detected - but resetting anyway\r\n"); + } +*/ + switch (configure->vcc) { + case 0: + storkClearLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON); + storkClearLatchA(POWER); + break; + + case 50: + case 33: + storkSetLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON); + storkSetLatchA(POWER); + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + restore_flags(flags); + return -1; + } + + if (configure->reset) + storkSetLatchB(RESET); + else + storkClearLatchB(RESET); + + restore_flags(flags); + + /* silently ignore vpp and speaker enables. */ + + printk(__FUNCTION__ ": finished\n"); + + return 0; +} + +struct pcmcia_low_level stork_pcmcia_ops = { + stork_pcmcia_init, + stork_pcmcia_shutdown, + stork_pcmcia_socket_state, + stork_pcmcia_get_irq_info, + stork_pcmcia_configure_socket +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_xp860.c linux.ac/drivers/pcmcia/sa1100_xp860.c --- linux.vanilla/drivers/pcmcia/sa1100_xp860.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_xp860.c Wed Oct 10 01:48:06 2001 @@ -0,0 +1,255 @@ +/* + * drivers/pcmcia/sa1100_xp860.c + * + * XP860 PCMCIA specific routines + * + */ +#include +#include +#include + +#include +#include +#include + +#define NCR_A0VPP (1<<16) +#define NCR_A1VPP (1<<17) + +static int xp860_pcmcia_init(struct pcmcia_init *init){ + int return_val=0; + + /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ + PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); + + /* MAX1600 to standby mode: */ + PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); + GPDR |= (NCR_A0VPP | NCR_A1VPP); + GPCR &= ~(NCR_A0VPP | NCR_A1VPP); + + INTPOL1 |= + (1 << (S0_READY_NINT - SA1111_IRQ(32))) | + (1 << (S1_READY_NINT - SA1111_IRQ(32))) | + (1 << (S0_CD_VALID - SA1111_IRQ(32))) | + (1 << (S1_CD_VALID - SA1111_IRQ(32))) | + (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | + (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))); + + return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT, + "XP860 PCMCIA (0) CD", NULL); + return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT, + "XP860 CF (1) CD", NULL); + return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT, + "XP860 PCMCIA (0) BVD1", NULL); + return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT, + "XP860 CF (1) BVD1", NULL); + + return (return_val<0) ? -1 : 2; +} + +static int xp860_pcmcia_shutdown(void){ + + free_irq(S0_CD_VALID, NULL); + free_irq(S1_CD_VALID, NULL); + free_irq(S0_BVD1_STSCHG, NULL); + free_irq(S1_BVD1_STSCHG, NULL); + + INTPOL1 &= + ~((1 << (S0_CD_VALID - SA1111_IRQ(32))) | + (1 << (S1_CD_VALID - SA1111_IRQ(32))) | + (1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) | + (1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)))); + + return 0; +} + +static int xp860_pcmcia_socket_state(struct pcmcia_state_array + *state_array){ + unsigned long status; + int return_val=1; + + if(state_array->size<2) return -1; + + memset(state_array->state, 0, + (state_array->size)*sizeof(struct pcmcia_state)); + + status=PCSR; + + state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0; + + state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1; + + state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1; + + state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1; + + state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1; + + state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0; + + state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; + + state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0; + + state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1; + + state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1; + + state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1; + + state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1; + + state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0; + + state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0; + + return return_val; +} + +static int xp860_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ + + switch(info->sock){ + case 0: + info->irq=S0_READY_NINT; + break; + + case 1: + info->irq=S1_READY_NINT; + break; + + default: + return -1; + } + + return 0; +} + +static int xp860_pcmcia_configure_socket(const struct pcmcia_configure + *configure){ + unsigned long pccr=PCCR, ncr=GPLR, gpio=PA_DWR; + + + /* Neponset uses the Maxim MAX1600, with the following connections: + * + * MAX1600 Neponset + * + * A0VCC SA-1111 GPIO A<1> + * A1VCC SA-1111 GPIO A<0> + * A0VPP CPLD NCR A0VPP + * A1VPP CPLD NCR A1VPP + * B0VCC SA-1111 GPIO A<2> + * B1VCC SA-1111 GPIO A<3> + * B0VPP ground (slot B is CF) + * B1VPP ground (slot B is CF) + * + * VX VCC (5V) + * VY VCC3_3 (3.3V) + * 12INA 12V + * 12INB ground (slot B is CF) + * + * The MAX1600 CODE pin is tied to ground, placing the device in + * "Standard Intel code" mode. Refer to the Maxim data sheet for + * the corresponding truth table. + */ + + switch(configure->sock){ + case 0: + + switch(configure->vcc){ + case 0: + gpio&=~(GPIO_GPIO0 | GPIO_GPIO1); + break; + + case 33: + pccr=(pccr & ~PCCR_S0_PSE); + gpio=(gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO1; + break; + + case 50: + pccr=(pccr | PCCR_S0_PSE); + gpio=(gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO0; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + return -1; + } + + switch(configure->vpp){ + case 0: + ncr&=~(NCR_A0VPP | NCR_A1VPP); + break; + + case 120: + ncr=(ncr & ~(NCR_A0VPP | NCR_A1VPP)) | NCR_A1VPP; + break; + + default: + if(configure->vpp == configure->vcc) + ncr=(ncr & ~(NCR_A0VPP | NCR_A1VPP)) | NCR_A0VPP; + else { + printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__, + configure->vpp); + return -1; + } + } + + pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST); + pccr=(configure->output)?(pccr | PCCR_S0_FLT):(pccr & ~PCCR_S0_FLT); + + break; + + case 1: + switch(configure->vcc){ + case 0: + gpio&=~(GPIO_GPIO2 | GPIO_GPIO3); + break; + + case 33: + pccr=(pccr & ~PCCR_S1_PSE); + gpio=(gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO2; + break; + + case 50: + pccr=(pccr | PCCR_S1_PSE); + gpio=(gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO3; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, + configure->vcc); + return -1; + } + + if(configure->vpp!=configure->vcc && configure->vpp!=0){ + printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, + configure->vpp); + return -1; + } + + pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST); + pccr=(configure->output)?(pccr | PCCR_S1_FLT):(pccr & ~PCCR_S1_FLT); + + break; + + default: + return -1; + } + + PCCR = pccr; + ncr &= NCR_A0VPP|NCR_A1VPP; + GPSR = ncr; + GPCR = (~ncr)&(NCR_A0VPP|NCR_A1VPP); + PA_DWR = gpio; + + return 0; +} + +struct pcmcia_low_level xp860_pcmcia_ops = { + xp860_pcmcia_init, + xp860_pcmcia_shutdown, + xp860_pcmcia_socket_state, + xp860_pcmcia_get_irq_info, + xp860_pcmcia_configure_socket +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/sa1100_yopy.c linux.ac/drivers/pcmcia/sa1100_yopy.c --- linux.vanilla/drivers/pcmcia/sa1100_yopy.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pcmcia/sa1100_yopy.c Wed Oct 10 01:48:06 2001 @@ -0,0 +1,139 @@ +/* + * drivers/pcmcia/sa1100_yopy.c + * + * PCMCIA implementation routines for Yopy + * + */ +#include +#include + +#include +#include +#include + + +static inline void pcmcia_power(int on) { + /* high for power up */ + yopy_gpio_set(GPIO_CF_POWER, on); +} + +static inline void pcmcia_reset(int reset) +{ + /* high for reset */ + yopy_gpio_set(GPIO_CF_RESET, reset); +} + +static int yopy_pcmcia_init(struct pcmcia_init *init) +{ + int irq, res; + + pcmcia_power(0); + pcmcia_reset(1); + + /* All those are inputs */ + GPDR &= ~(GPIO_CF_CD | GPIO_CF_BVD2 | GPIO_CF_BVD1 | GPIO_CF_IREQ); + GAFR &= ~(GPIO_CF_CD | GPIO_CF_BVD2 | GPIO_CF_BVD1 | GPIO_CF_IREQ); + + /* Set transition detect */ + set_GPIO_IRQ_edge( GPIO_CF_CD|GPIO_CF_BVD2|GPIO_CF_BVD1, + GPIO_BOTH_EDGES ); + set_GPIO_IRQ_edge( GPIO_CF_IREQ, GPIO_FALLING_EDGE ); + + /* Register interrupts */ + irq = IRQ_CF_CD; + res = request_irq(irq, init->handler, SA_INTERRUPT, "CF_CD", NULL); + if (res < 0) goto irq_err; + irq = IRQ_CF_BVD2; + res = request_irq(irq, init->handler, SA_INTERRUPT, "CF_BVD2", NULL); + if (res < 0) goto irq_err; + irq = IRQ_CF_BVD1; + res = request_irq(irq, init->handler, SA_INTERRUPT, "CF_BVD1", NULL); + if (res < 0) goto irq_err; + + return 1; +irq_err: + printk(KERN_ERR "%s: Request for IRQ %d failed\n", __FUNCTION__, irq); + return -1; +} + +static int yopy_pcmcia_shutdown(void) +{ + /* disable IRQs */ + free_irq( IRQ_CF_CD, NULL ); + free_irq( IRQ_CF_BVD2, NULL ); + free_irq( IRQ_CF_BVD1, NULL ); + + /* Disable CF */ + pcmcia_reset(1); + pcmcia_power(0); + + return 0; +} + +static int yopy_pcmcia_socket_state(struct pcmcia_state_array *state_array) +{ + unsigned long levels; + + if (state_array->size != 1) + return -1; + + memset(state_array->state, 0, + state_array->size * sizeof(struct pcmcia_state)); + + levels = GPLR; + + state_array->state[0].detect = (levels & GPIO_CF_CD) ? 0 : 1; + state_array->state[0].ready = (levels & GPIO_CF_READY) ? 1 : 0; + state_array->state[0].bvd1 = (levels & GPIO_CF_BVD1) ? 1 : 0; + state_array->state[0].bvd2 = (levels & GPIO_CF_BVD2) ? 1 : 0; + state_array->state[0].wrprot = 0; /* Not available on Yopy. */ + state_array->state[0].vs_3v = 0; /* FIXME Can only apply 3.3V on Yopy. */ + state_array->state[0].vs_Xv = 0; + + return 1; +} + +static int yopy_pcmcia_get_irq_info(struct pcmcia_irq_info *info) +{ + if (info->sock != 0) + return -1; + + info->irq = IRQ_CF_IREQ; + + return 0; +} + +static int yopy_pcmcia_configure_socket(const struct pcmcia_configure *configure) +{ + if (configure->sock != 0) + return -1; + + switch (configure->vcc) { + case 0: /* power off */; + pcmcia_power(0); + break; + case 50: + printk(KERN_WARNING __FUNCTION__"(): CS asked for 5V, applying 3.3V..\n"); + case 33: + pcmcia_power(1); + break; + default: + printk(KERN_ERR __FUNCTION__"(): unrecognized Vcc %u\n", + configure->vcc); + return -1; + } + + pcmcia_reset(configure->reset); + + /* Silently ignore Vpp, output enable, speaker enable. */ + + return 0; +} + +struct pcmcia_low_level yopy_pcmcia_ops = { + yopy_pcmcia_init, + yopy_pcmcia_shutdown, + yopy_pcmcia_socket_state, + yopy_pcmcia_get_irq_info, + yopy_pcmcia_configure_socket +}; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pnp/Makefile linux.ac/drivers/pnp/Makefile --- linux.vanilla/drivers/pnp/Makefile Tue Mar 27 00:36:30 2001 +++ linux.ac/drivers/pnp/Makefile Wed Oct 10 01:48:06 2001 @@ -8,17 +8,24 @@ # Note 2! The CFLAGS definitions are now inherited from the # parent makes.. -O_TARGET := pnp.o +O_TARGET := pnp.o -export-objs := isapnp.o -list-multi := isa-pnp.o +export-objs := isapnp.o pnp_bios.o +multi-objs := isa-pnp.o pnpbios.o proc-$(CONFIG_PROC_FS) = isapnp_proc.o isa-pnp-objs := isapnp.o quirks.o $(proc-y) +procpnp-$(CONFIG_PROC_FS) = pnp_proc.o +pnpbios-objs := pnp_bios.o $(procpnp-y) + obj-$(CONFIG_ISAPNP) += isa-pnp.o +obj-$(CONFIG_PNPBIOS) += pnpbios.o include $(TOPDIR)/Rules.make -isa-pnp.o: $(isa-pnp-objs) - $(LD) $(LD_RFLAG) -r -o $@ $(isa-pnp-objs) +isa-pnp.o: $(isa-pnp-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(isa-pnp-objs) + +pnpbios.o: $(pnpbios-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(pnpbios-objs) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pnp/isapnp.c linux.ac/drivers/pnp/isapnp.c --- linux.vanilla/drivers/pnp/isapnp.c Wed Jul 4 22:41:33 2001 +++ linux.ac/drivers/pnp/isapnp.c Wed Oct 10 22:39:18 2001 @@ -87,6 +87,7 @@ MODULE_PARM_DESC(isapnp_reserve_io, "ISA Plug & Play - reserve I/O region(s) - port,size"); MODULE_PARM(isapnp_reserve_mem, "1-16i"); MODULE_PARM_DESC(isapnp_reserve_mem, "ISA Plug & Play - reserve memory region(s) - address,size"); +MODULE_LICENSE("GPL"); #define _PIDXR 0x279 #define _PNPWRP 0xa79 @@ -1673,8 +1674,8 @@ } isapnp_for_each_dev(dev) { if (dev->active) { - if (dev->irq_resource[0].start == irq || - dev->irq_resource[1].start == irq) + if ((dev->irq_resource[0].flags && dev->irq_resource[0].start == irq) || + (dev->irq_resource[1].flags && dev->irq_resource[1].start == irq)) return 1; } } @@ -1763,7 +1764,8 @@ } isapnp_for_each_dev(dev) { if (dev->active) { - if (dev->dma_resource[0].start == dma || dev->dma_resource[1].start == dma) + if ((dev->dma_resource[0].flags && dev->dma_resource[0].start == dma) || + (dev->dma_resource[1].flags && dev->dma_resource[1].start == dma)) return 1; } } @@ -1784,6 +1786,10 @@ static int isapnp_valid_dma(struct isapnp_cfgtmp *cfg, int idx) { + /* DMA priority: this table is good for i386 */ + static unsigned short xtab[16] = { + 1, 3, 5, 6, 7, 0, 2, 4 + }; int err, i; unsigned long *value1, *value2; struct isapnp_dma *dma; @@ -1799,15 +1805,16 @@ value1 = &cfg->result.dma_resource[idx].start; value2 = &cfg->result.dma_resource[idx].end; if (cfg->result.dma_resource[idx].flags & IORESOURCE_AUTO) { - for (i = 0; i < 8 && !(dma->map & (1<map & (1<= 8) return -ENOENT; cfg->result.dma_resource[idx].flags &= ~IORESOURCE_AUTO; - if (!isapnp_check_dma(cfg, *value1 = *value2 = i, idx)) + if (!isapnp_check_dma(cfg, *value1 = *value2 = xtab[i], idx)) return 0; } do { - for (i = *value1 + 1; i < 8 && !(dma->map & (1<map & (1<= 8) { if (dma->res && dma->res->alt) { if ((err = isapnp_alternative_switch(cfg, dma->res, dma->res->alt))<0) @@ -1816,7 +1823,7 @@ } return -ENOENT; } else { - *value1 = *value2 = i; + *value1 = *value2 = xtab[i]; } } while (isapnp_check_dma(cfg, *value1, idx)); return 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pnp/pnp_bios.c linux.ac/drivers/pnp/pnp_bios.c --- linux.vanilla/drivers/pnp/pnp_bios.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pnp/pnp_bios.c Wed Oct 10 22:45:49 2001 @@ -0,0 +1,1157 @@ +/* + * PnP BIOS services + * + * Originally (C) 1998 Christian Schmidt (chr.schmidt@tu-bs.de) + * Modifications (c) 1998 Tom Lees + * Minor reorganizations by David Hinds + * More modifications by Thomas Hood + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * References: + * Compaq Computer Corporation, Phoenix Technologies Ltd., Intel Corporation + * Plug and Play BIOS Specification, Version 1.0A, May 5, 1994 + * Plug and Play BIOS Clarification Paper, October 6, 1994 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PnP bios signature: "$PnP" */ +#define PNP_SIGNATURE (('$' << 0) + ('P' << 8) + ('n' << 16) + ('P' << 24)) + +/* + * This is the standard structure used to identify the entry point + * to the Plug and Play bios + */ +#pragma pack(1) +union pnpbios { + struct { + u32 signature; /* "$PnP" */ + u8 version; /* in BCD */ + u8 length; /* length in bytes, currently 21h */ + u16 control; /* system capabilities */ + u8 checksum; /* all bytes must add up to 0 */ + + u32 eventflag; /* phys. address of the event flag */ + u16 rmoffset; /* real mode entry point */ + u16 rmcseg; + u16 pm16offset; /* 16 bit protected mode entry */ + u32 pm16cseg; + u32 deviceID; /* EISA encoded system ID or 0 */ + u16 rmdseg; /* real mode data segment */ + u32 pm16dseg; /* 16 bit pm data segment base */ + } fields; + char chars[0x21]; /* To calculate the checksum */ +}; +#pragma pack() + +/* + * Local Variables + */ +static struct { + u16 offset; + u16 segment; +} pnp_bios_callpoint; + +static union pnpbios * pnp_bios_inst_struc = NULL; + +/* The PnP entries in the GDT */ +#define PNP_GDT 0x0060 +#define PNP_CS32 (PNP_GDT+0x00) /* segment for calling fn */ +#define PNP_CS16 (PNP_GDT+0x08) /* code segment for bios */ +#define PNP_DS (PNP_GDT+0x10) /* data segment for bios */ +#define PNP_TS1 (PNP_GDT+0x18) /* transfer data segment */ +#define PNP_TS2 (PNP_GDT+0x20) /* another data segment */ + +/* + * These are some opcodes for a "static asmlinkage" + * As this code is *not* executed inside the linux kernel segment, but in a + * alias at offset 0, we need a far return that can not be compiled by + * default (please, prove me wrong! this is *really* ugly!) + * This is the only way to get the bios to return into the kernel code, + * because the bios code runs in 16 bit protected mode and therefore can only + * return to the caller if the call is within the first 64kB, and the linux + * kernel begins at offset 3GB... + */ + +asmlinkage void pnp_bios_callfunc(void); + +__asm__( + ".text \n" + __ALIGN_STR "\n" + SYMBOL_NAME_STR(pnp_bios_callfunc) ":\n" + " pushl %edx \n" + " pushl %ecx \n" + " pushl %ebx \n" + " pushl %eax \n" + " lcallw " SYMBOL_NAME_STR(pnp_bios_callpoint) "\n" + " addl $16, %esp \n" + " lret \n" + ".previous \n" +); + +#define Q_SET_SEL(selname, address, size) \ +set_base (gdt [(selname) >> 3], __va((u32)(address))); \ +set_limit (gdt [(selname) >> 3], size) + +#define Q2_SET_SEL(selname, address, size) \ +set_base (gdt [(selname) >> 3], (u32)(address)); \ +set_limit (gdt [(selname) >> 3], size) + +/* + * Callable PnP BIOS functions + */ +#define PNP_GET_NUM_SYS_DEV_NODES 0x00 +#define PNP_GET_SYS_DEV_NODE 0x01 +#define PNP_SET_SYS_DEV_NODE 0x02 +#define PNP_GET_EVENT 0x03 +#define PNP_SEND_MESSAGE 0x04 +#define PNP_GET_DOCKING_STATION_INFORMATION 0x05 +#define PNP_SET_STATIC_ALLOCED_RES_INFO 0x09 +#define PNP_GET_STATIC_ALLOCED_RES_INFO 0x0a +#define PNP_GET_APM_ID_TABLE 0x0b +#define PNP_GET_PNP_ISA_CONFIG_STRUC 0x40 +#define PNP_GET_ESCD_INFO 0x41 +#define PNP_READ_ESCD 0x42 +#define PNP_WRITE_ESCD 0x43 + + +/* + * At some point we want to use this stack frame pointer to unwind + * after PnP BIOS oopses. + */ + +u32 pnp_bios_fault_esp; +u32 pnp_bios_fault_eip; +u32 pnp_bios_is_utter_crap = 0; + +static spinlock_t pnp_bios_lock; + +static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3, + u16 arg4, u16 arg5, u16 arg6, u16 arg7) +{ + unsigned long flags; + u16 status; + + /* + * PnPBIOS is generally not terribly re-entrant. + * Also don't rely on it to save everything correctly + * + * On some boxes IRQ's during PnP bios calls seem fatal + */ + + if(pnp_bios_is_utter_crap) + return PNP_FUNCTION_NOT_SUPPORTED; + + spin_lock_irqsave(&pnp_bios_lock, flags); + __cli(); + __asm__ __volatile__( + "pushl %%ebp\n\t" + "pushl %%edi\n\t" + "pushl %%esi\n\t" + "pushl %%ds\n\t" + "pushl %%es\n\t" + "pushl %%fs\n\t" + "pushl %%gs\n\t" + "pushfl\n\t" + "movl %%esp, pnp_bios_fault_esp\n\t" + "movl $1f, pnp_bios_fault_eip\n\t" + "lcall %5,%6\n\t" + "1:popfl\n\t" + "popl %%gs\n\t" + "popl %%fs\n\t" + "popl %%es\n\t" + "popl %%ds\n\t" + "popl %%esi\n\t" + "popl %%edi\n\t" + "popl %%ebp\n\t" + : "=a" (status) + : "0" ((func) | (((u32)arg1) << 16)), + "b" ((arg2) | (((u32)arg3) << 16)), + "c" ((arg4) | (((u32)arg5) << 16)), + "d" ((arg6) | (((u32)arg7) << 16)), + "i" (PNP_CS32), + "i" (0) + : "memory" + ); + spin_unlock_irqrestore(&pnp_bios_lock, flags); + + /* If we got here and this is set the pnp bios faulted on us.. */ + if(pnp_bios_is_utter_crap) + { + printk(KERN_ERR "PnPBIOS: Warning! Your PnP BIOS caused a fatal error. Attempting to continue.\n"); + printk(KERN_ERR "PnPBIOS: You may need to reboot with the \"nobiospnp\" option to operate stably.\n"); + printk(KERN_ERR "PnPBIOS: Check with your vendor for an updated BIOS\n"); + } + +// if ( status ) printk(KERN_WARNING "PnPBIOS: BIOS returned error 0x%x from function 0x%x.\n", status, func); + + return status; +} + +static void *pnp_bios_kmalloc(size_t size, int f) +{ + void *p = kmalloc( size, f ); + if ( p == NULL ) + printk(KERN_ERR "PnPBIOS: kmalloc() failed.\n"); + return p; +} + +/* + * + * PnP BIOS ACCESS FUNCTIONS + * + */ + +/* + * Call PnP BIOS with function 0x00, "get number of system device nodes" + */ +static int pnp_bios_dev_node_info_silently(struct pnp_dev_node_info *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_dev_node_info)); + status = call_pnp_bios(PNP_GET_NUM_SYS_DEV_NODES, 0, PNP_TS1, 2, PNP_TS1, PNP_DS, 0, 0); + data->no_nodes &= 0xff; + return status; +} + +int pnp_bios_dev_node_info(struct pnp_dev_node_info *data) +{ + u16 status = pnp_bios_dev_node_info_silently( data ); + if ( status ) + printk(KERN_WARNING "PnPBIOS: PnP BIOS dev_node_info function returned error status 0x%x\n", status); + return status; +} + +/* + * Note that some PnP BIOSes (on Sony Vaio laptops) die a horrible + * death if they are asked to access the "current" configuration + * Therefore, if it's a matter of indifference, it's better to call + * get_dev_node() and set_dev_node() with boot=1 rather than with boot=0. + */ + +/* + * Call PnP BIOS with function 0x01, "get system device node" + * Input: *nodenum = desired node, + * boot = whether to get nonvolatile boot (!=0) + * or volatile current (0) config + * Output: *nodenum=next node or 0xff if no more nodes + */ +static int pnp_bios_get_dev_node_silently(u8 *nodenum, char boot, struct pnp_bios_node *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, nodenum, sizeof(char)); + Q2_SET_SEL(PNP_TS2, data, 64 * 1024); + status = call_pnp_bios(PNP_GET_SYS_DEV_NODE, 0, PNP_TS1, 0, PNP_TS2, boot ? 2 : 1, PNP_DS, 0); + return status; +} + +int pnp_bios_get_dev_node(u8 *nodenum, char boot, struct pnp_bios_node *data) +{ + u16 status = pnp_bios_get_dev_node_silently( nodenum, boot, data ); + if ( status ) + printk(KERN_WARNING "PnPBIOS: PnP BIOS get_dev_node function returned error status 0x%x\n", status); + return status; +} + +/* + * Call PnP BIOS with function 0x02, "set system device node" + * Input: *nodenum = desired node, + * boot = whether to set nonvolatile boot (!=0) + * or volatile current (0) config + */ +static int pnp_bios_set_dev_node_silently(u8 nodenum, char boot, struct pnp_bios_node *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, /* *((u16 *) data)*/ 65536); + status = call_pnp_bios(PNP_SET_SYS_DEV_NODE, nodenum, 0, PNP_TS1, boot ? 2 : 1, PNP_DS, 0, 0); + return status; +} + +int pnp_bios_set_dev_node(u8 nodenum, char boot, struct pnp_bios_node *data) +{ + u16 status = pnp_bios_set_dev_node_silently( nodenum, boot, data ); + if ( status ) + printk(KERN_WARNING "PnPBIOS: PnP BIOS set_dev_node function returned error status 0x%x\n", status); + return status; +} + +#if needed +/* + * Call PnP BIOS with function 0x03, "get event" + */ +static int pnp_bios_get_event(u16 *event) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, event, sizeof(u16)); + status = call_pnp_bios(PNP_GET_EVENT, 0, PNP_TS1, PNP_DS, 0, 0 ,0 ,0); + return status; +} +#endif + +#if needed +/* + * Call PnP BIOS with function 0x04, "send message" + */ +static int pnp_bios_send_message(u16 message) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_SEND_MESSAGE, message, PNP_DS, 0, 0, 0, 0, 0); + return status; +} +#endif + +#ifdef CONFIG_HOTPLUG +/* + * Call PnP BIOS with function 0x05, "get docking station information" + */ +static int pnp_bios_dock_station_info(struct pnp_docking_station_info *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_docking_station_info)); + status = call_pnp_bios(PNP_GET_DOCKING_STATION_INFORMATION, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0); + return status; +} +#endif + +#if needed +/* + * Call PnP BIOS with function 0x09, "set statically allocated resource + * information" + */ +static int pnp_bios_set_stat_res(char *info) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, info, *((u16 *) info)); + status = call_pnp_bios(PNP_SET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0); + return status; +} +#endif + +#if needed +/* + * Call PnP BIOS with function 0x0a, "get statically allocated resource + * information" + */ +static int pnp_bios_get_stat_res(char *info) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, info, 64 * 1024); + status = call_pnp_bios(PNP_GET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0); + return status; +} +#endif + +#if needed +/* + * Call PnP BIOS with function 0x0b, "get APM id table" + */ +static int pnp_bios_apm_id_table(char *table, u16 *size) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, table, *size); + Q2_SET_SEL(PNP_TS2, size, sizeof(u16)); + status = call_pnp_bios(PNP_GET_APM_ID_TABLE, 0, PNP_TS2, 0, PNP_TS1, PNP_DS, 0, 0); + return status; +} +#endif + +#if needed +/* + * Call PnP BIOS with function 0x40, "get isa pnp configuration structure" + */ +static int pnp_bios_isapnp_config(struct pnp_isa_config_struc *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_isa_config_struc)); + status = call_pnp_bios(PNP_GET_PNP_ISA_CONFIG_STRUC, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0); + return status; +} +#endif + +#if needed +/* + * Call PnP BIOS with function 0x41, "get ESCD info" + */ +static int pnp_bios_escd_info(struct escd_info_struc *data) +{ + u16 status; + if (!pnp_bios_present ()) + return ESCD_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, sizeof(struct escd_info_struc)); + status = call_pnp_bios(PNP_GET_ESCD_INFO, 0, PNP_TS1, 2, PNP_TS1, 4, PNP_TS1, PNP_DS); + return status; +} +#endif + +#if needed +/* + * Call PnP BIOS function 0x42, "read ESCD" + * nvram_base is determined by calling escd_info + */ +static int pnp_bios_read_escd(char *data, u32 nvram_base) +{ + u16 status; + if (!pnp_bios_present ()) + return ESCD_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, 64 * 1024); + set_base(gdt[PNP_TS2 >> 3], nvram_base); + set_limit(gdt[PNP_TS2 >> 3], 64 * 1024); + status = call_pnp_bios(PNP_READ_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0); + return status; +} +#endif + +#if needed +/* + * Call PnP BIOS function 0x43, "write ESCD" + */ +static int pnp_bios_write_escd(char *data, u32 nvram_base) +{ + u16 status; + if (!pnp_bios_present ()) + return ESCD_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, 64 * 1024); + set_base(gdt[PNP_TS2 >> 3], nvram_base); + set_limit(gdt[PNP_TS2 >> 3], 64 * 1024); + status = call_pnp_bios(PNP_WRITE_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0); + return status; +} +#endif + +EXPORT_SYMBOL(pnp_bios_dev_node_info); +EXPORT_SYMBOL(pnp_bios_get_dev_node); +EXPORT_SYMBOL(pnp_bios_set_dev_node); + +/* + * + * PnP DOCKING FUNCTIONS + * + */ + +#ifdef CONFIG_HOTPLUG + +static int unloading = 0; + +static struct completion unload_sem; + +/* + * (Much of this belongs in a shared routine somewhere) + */ + +static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) +{ + char *argv [3], **envp, *buf, *scratch; + int i = 0, value; + + if (!hotplug_path [0]) + return -ENOENT; + if (!current->fs->root) { + return -EAGAIN; + } + if (!(envp = (char **) pnp_bios_kmalloc (20 * sizeof (char *), GFP_KERNEL))) { + return -ENOMEM; + } + if (!(buf = pnp_bios_kmalloc (256, GFP_KERNEL))) { + kfree (envp); + return -ENOMEM; + } + + /* only one standardized param to hotplug command: type */ + argv [0] = hotplug_path; + argv [1] = "dock"; + argv [2] = 0; + + /* minimal command environment */ + envp [i++] = "HOME=/"; + envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + +#ifdef DEBUG + /* hint that policy agent should enter no-stdout debug mode */ + envp [i++] = "DEBUG=kernel"; +#endif + /* extensible set of named bus-specific parameters, + * supporting multiple driver selection algorithms. + */ + scratch = buf; + + /* action: add, remove */ + envp [i++] = scratch; + scratch += sprintf (scratch, "ACTION=%s", dock?"add":"remove") + 1; + + /* Report the ident for the dock */ + envp [i++] = scratch; + scratch += sprintf (scratch, "DOCK=%x/%x/%x", + info->location_id, info->serial, info->capabilities); + envp[i] = 0; + + value = call_usermodehelper (argv [0], argv, envp); + kfree (buf); + kfree (envp); + return 0; +} + +/* + * Poll the PnP docking at a regular interval + */ +static int pnp_dock_thread(void * unused) +{ + static struct pnp_docking_station_info now; + int docked = -1, d; + daemonize(); + reparent_to_init(); + strcpy(current->comm, "kpnpbios"); + while(!unloading && !signal_pending(current)) + { + int err; + + /* + * Poll every 2 seconds + */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ*2); + if(signal_pending(current)) + break; + + err = pnp_bios_dock_station_info(&now); + + switch(err) + { + /* + * No dock to manage + */ + case PNP_FUNCTION_NOT_SUPPORTED: + complete_and_exit(&unload_sem, 0); + case PNP_SYSTEM_NOT_DOCKED: + d = 0; + break; + case PNP_SUCCESS: + d = 1; + break; + default: + printk(KERN_WARNING "PnPBIOS: Unexpected error 0x%x returned by BIOS.\n", err); + continue; + } + if(d != docked) + { + if(pnp_dock_event(d, &now)==0) + { + docked = d; +// printk(KERN_INFO "PnPBIOS: Docking station %stached.\n", docked?"at":"de"); + } + } + } + complete_and_exit(&unload_sem, 0); +} + +#endif + +/* + * + * NODE DATA HANDLING FUNCTIONS + * + */ + +static void inline pnpbios_add_irqresource(struct pci_dev *dev, int irq) +{ + // Permit irq==-1 which signifies "disabled" + int i = 0; + while (!(dev->irq_resource[i].flags & IORESOURCE_UNSET) && i < DEVICE_COUNT_IRQ) i++; + if (i < DEVICE_COUNT_IRQ) { + dev->irq_resource[i].start = irq; + dev->irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag + } +} + +static void inline pnpbios_add_dmaresource(struct pci_dev *dev, int dma) +{ + // Permit dma==-1 which signifies "disabled" + int i = 0; + while (!(dev->dma_resource[i].flags & IORESOURCE_UNSET) && i < DEVICE_COUNT_DMA) i++; + if (i < DEVICE_COUNT_DMA) { + dev->dma_resource[i].start = dma; + dev->dma_resource[i].flags = IORESOURCE_DMA; // Also clears _UNSET flag + } +} + +static void inline pnpbios_add_ioresource(struct pci_dev *dev, int io, int len) +{ + int i = 0; + while (!(dev->resource[i].flags & IORESOURCE_UNSET) && i < DEVICE_COUNT_RESOURCE) i++; + if (i < DEVICE_COUNT_RESOURCE) { + dev->resource[i].start = io; + dev->resource[i].end = io + len; + dev->resource[i].flags = IORESOURCE_IO; // Also clears _UNSET flag + } +} + +static void inline pnpbios_add_memresource(struct pci_dev *dev, int mem, int len) +{ + int i = 0; + while (!(dev->resource[i].flags & IORESOURCE_UNSET) && i < DEVICE_COUNT_RESOURCE) i++; + if (i < DEVICE_COUNT_RESOURCE) { + dev->resource[i].start = mem; + dev->resource[i].end = mem + len; + dev->resource[i].flags = IORESOURCE_MEM; // Also clears _UNSET flag + } +} + +/* + * request I/O ports which are used according to the PnP BIOS + * to avoid I/O conflicts. + */ +static void mboard_request(char *pnpid, int io, int len) +{ + struct resource *res; + char *regionid; + + if ( + 0 != strcmp(pnpid,"PNP0c01") && /* memory controller */ + 0 != strcmp(pnpid,"PNP0c02") /* system peripheral: other */ + ) { + return; + } + + if (io < 0x100) { + /* + * Below 0x100 is only standard PC hardware + * (pics, kbd, timer, dma, ...) + * + * We should not get resource conflicts there, + * and the kernel reserves these anyway + * (see arch/i386/kernel/setup.c). + */ + return; + } + + /* + * Anything else we'll try reserve to avoid these ranges are + * assigned to someone (CardBus bridges for example) and thus are + * triggering resource conflicts. + * + * Failures at this point are usually harmless. pci quirks for + * example do reserve stuff they know about too, so we might have + * double reservations here. + * + * We really shouldn't just reserve these regions, though, since + * that prevents the device drivers from claiming them. + */ + regionid = pnp_bios_kmalloc(16, GFP_KERNEL); + if ( regionid == NULL ) + return; + sprintf(regionid, "PnPBIOS %s", pnpid); + res = request_region(io,len,regionid); + if ( res == NULL ) + kfree( regionid ); + printk( + "PnPBIOS: %s: 0x%x-0x%x %s reserved\n", + pnpid, io, io+len-1, + NULL != res ? "has been" : "was already" + ); + + return; +} + + +#define HEX(id,a) hex[((id)>>a) & 15] +#define CHAR(id,a) (0x40 + (((id)>>a) & 31)) + +static char * __init pnpid32_to_pnpid(u32 id) +{ + const char *hex = "0123456789abcdef"; + static char str[8]; + id = be32_to_cpu(id); + str[0] = CHAR(id, 26); + str[1] = CHAR(id, 21); + str[2] = CHAR(id,16); + str[3] = HEX(id, 12); + str[4] = HEX(id, 8); + str[5] = HEX(id, 4); + str[6] = HEX(id, 0); + str[7] = '\0'; + return str; +} + +#undef CHAR +#undef HEX + +/* + * parse PNPBIOS "Allocated Resources Block" and fill IO,IRQ,DMA into pci_dev + */ +static void __init pnpbios_rawdata_2_pci_dev(struct pnp_bios_node *node, struct pci_dev *dev) +{ + unsigned char *p = node->data, *lastp=NULL; + int i; + + /* + * First, set dev contents to default values + */ + memset(dev,0,sizeof(struct pci_dev)); + for (i=0;iresource[i].start = 0; */ + dev->resource[i].flags = IORESOURCE_UNSET; + } + for (i=0;iirq_resource[i].start = (unsigned long)-1; // "disabled" + dev->irq_resource[i].flags = IORESOURCE_UNSET; + } + for (i=0;idma_resource[i].start = (unsigned long)-1; // "disabled" + dev->dma_resource[i].flags = IORESOURCE_UNSET; + } + + /* + * Fill in dev info + */ + while ( (char *)p < ((char *)node->data + node->size )) { + if(p==lastp) break; + + if( p[0] & 0x80 ) {// large item + switch (p[0] & 0x7f) { + case 0x01: // memory + { + int io = *(short *) &p[4]; + int len = *(short *) &p[10]; + pnpbios_add_memresource(dev, io, len); + break; + } + case 0x02: // device name + { + int len = *(short *) &p[1]; + memcpy(dev->name, p + 3, len >= 80 ? 79 : len); + break; + } + case 0x05: // 32-bit memory + { + int io = *(int *) &p[4]; + int len = *(int *) &p[16]; + pnpbios_add_memresource(dev, io, len); + break; + } + case 0x06: // fixed location 32-bit memory + { + int io = *(int *) &p[4]; + int len = *(int *) &p[8]; + pnpbios_add_memresource(dev, io, len); + break; + } + } /* switch */ + lastp = p+3; + p = p + p[1] + p[2]*256 + 3; + continue; + } + if ((p[0]>>3) == 0x0f) // end tag + break; + switch (p[0]>>3) { + case 0x04: // irq + { + int i, mask, irq = -1; // "disabled" + mask= p[1] + p[2]*256; + for (i=0;i<16;i++, mask=mask>>1) + if(mask & 0x01) irq=i; + pnpbios_add_irqresource(dev, irq); + break; + } + case 0x05: // dma + { + int i, mask, dma = -1; // "disabled" + mask = p[1]; + for (i=0;i<8;i++, mask = mask>>1) + if(mask & 0x01) dma=i; + pnpbios_add_dmaresource(dev, dma); + break; + } + case 0x08: // io + { + int io= p[2] + p[3] *256; + int len = p[7]; + pnpbios_add_ioresource(dev, io, len); + mboard_request(pnpid32_to_pnpid(node->eisa_id),io,len); + break; + } + case 0x09: // fixed location io + { + int io = p[1] + p[2] * 256; + int len = p[3]; + pnpbios_add_ioresource(dev, io, len); + break; + } + } /* switch */ + lastp=p+1; + p = p + (p[0] & 0x07) + 1; + + } /* while */ + + return; +} + +/* + * + * PnP BIOS PUBLIC DEVICE MANAGEMENT LAYER FUNCTIONS + * + */ + +static LIST_HEAD(pnpbios_devices); + +int pnp_bios_present(void) +{ + return (pnp_bios_inst_struc != NULL); +} + +EXPORT_SYMBOL(pnp_bios_present); + +static int __init pnpbios_insert_device(struct pci_dev *dev) +{ + /* FIXME: Need to check for re-add of existing node */ + list_add_tail(&dev->global_list, &pnpbios_devices); + return 0; +} + +/* + * Build the list of pci_dev objects from the PnP table + */ + +static void __init pnpbios_build_devlist(void) +{ + int i; + int nodenum; + int nodes_got = 0; + struct pnp_bios_node *node; + struct pnp_dev_node_info node_info; + struct pci_dev *dev; + char *pnpid; + + + if (!pnp_bios_present ()) + return; + + if (pnp_bios_dev_node_info(&node_info) != 0) + return; + + node = pnp_bios_kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) + return; + + for(i=0,nodenum=0;i<0xff && nodenum!=0xff;i++) { + int thisnodenum = nodenum; + /* For now we build the list from the "boot" config + * because asking for the "current" config causes + * some BIOSes to crash. + */ + if (pnp_bios_get_dev_node((u8 *)&nodenum, (char )1 , node)) + break; + nodes_got++; + dev = pnp_bios_kmalloc(sizeof (struct pci_dev), GFP_KERNEL); + if (!dev) + break; + pnpbios_rawdata_2_pci_dev(node,dev); + dev->devfn=thisnodenum; + pnpid = pnpid32_to_pnpid(node->eisa_id); + memcpy(dev->name,"PNPBIOS",8); + memcpy(dev->slot_name,pnpid,8); + if(pnpbios_insert_device(dev)<0) + kfree(dev); + } + kfree(node); + + printk(KERN_INFO "PnPBIOS: %i node%s reported by PnP BIOS\n", nodes_got, nodes_got != 1 ? "s" : ""); +} + + +/* + * + * PUBLIC INTERFACE FUNCTIONS to PnP BIOS ENUMERATION + * + */ + +/* + * Find device in list + */ +struct pci_dev *pnpbios_find_device(char *pnpid, struct pci_dev *prev) +{ + struct pci_dev *dev; + int num; + + if(prev==NULL) + num=0; /* Start from beginning */ + else + num=prev->devfn + 1; /* Encode node number here */ + + + pnpbios_for_each_dev(dev) + { + if(dev->devfn >= num) + { + if(memcmp(dev->slot_name, pnpid, 7)==0) + return dev; + } + } + return NULL; +} + +EXPORT_SYMBOL(pnpbios_find_device); + +/* + * Registration of PnPBIOS drivers and handling of hot-pluggable devices. + */ + +static LIST_HEAD(pnpbios_drivers); + +/** + * pnpbios_match_device - Tell if a PnPBIOS device structure has a matching PnPBIOS device id structure + * @ids: array of PnPBIOS device id structures to search in + * @dev: the PnPBIOS device structure to match against + * + * Used by a driver to check whether a PnPBIOS device present in the + * system is in its list of supported devices.Returns the matching + * pnpbios_device_id structure or %NULL if there is no match. + */ + +const struct pnpbios_device_id * +pnpbios_match_device(const struct pnpbios_device_id *ids, const struct pci_dev *dev) +{ + while (*ids->id) + { + if(memcmp(ids->id, dev->slot_name, 7)==0) + return ids; + ids++; + } + return NULL; +} + +static int +pnpbios_announce_device(struct pnpbios_driver *drv, struct pci_dev *dev) +{ + const struct pnpbios_device_id *id; + int ret = 0; + + if (drv->id_table) { + id = pnpbios_match_device(drv->id_table, dev); + if (!id) { + ret = 0; + goto out; + } + } else + id = NULL; + + dev_probe_lock(); + if (drv->probe(dev, id) >= 0) { + // Hack for 2.4 - in 2.5 this needs to be generic stuff anyway + dev->driver = (void *)drv; + ret = 1; + } + dev_probe_unlock(); +out: + return ret; +} + +EXPORT_SYMBOL(pnpbios_announce_device); + +/** + * pnpbios_register_driver - register a new pci driver + * @drv: the driver structure to register + * + * Adds the driver structure to the list of registered drivers + * Returns the number of pci devices which were claimed by the driver + * during registration. The driver remains registered even if the + * return value is zero. + */ +int +pnpbios_register_driver(struct pnpbios_driver *drv) +{ + struct pci_dev *dev; + int count = 0; + + list_add_tail(&drv->node, &pnpbios_drivers); + pnpbios_for_each_dev(dev) { + if (!pnpbios_dev_driver(dev)) + count += pnpbios_announce_device(drv, dev); + } + return count; +} + +EXPORT_SYMBOL(pnpbios_register_driver); + +/** + * pnpbios_unregister_driver - unregister a pci driver + * @drv: the driver structure to unregister + * + * Deletes the driver structure from the list of registered PnPBIOS drivers, + * gives it a chance to clean up by calling its remove() function for + * each device it was responsible for, and marks those devices as + * driverless. + */ + +void +pnpbios_unregister_driver(struct pnpbios_driver *drv) +{ + struct pci_dev *dev; + + list_del(&drv->node); + pnpbios_for_each_dev(dev) { + if (dev->driver == (void *)drv) { + if (drv->remove) + drv->remove(dev); + dev->driver = NULL; + } + } +} + +EXPORT_SYMBOL(pnpbios_unregister_driver); + +/* + * + * INIT AND EXIT + * + * + * Search the defined area (0xf0000-0xffff0) for a valid PnP BIOS + * structure and, if one is found, sets up the selectors and + * entry points + */ + +extern int is_sony_vaio_laptop; + +static int pnp_bios_disabled; +static int pnp_bios_dont_use_current_config; + +static int disable_pnp_bios(char *str) +{ + pnp_bios_disabled=1; + return 0; +} + +static int disable_use_of_current_config(char *str) +{ + pnp_bios_dont_use_current_config=1; + return 0; +} + +__setup("nobiospnp", disable_pnp_bios); +__setup("nobioscurrpnp", disable_use_of_current_config); + +void pnp_bios_init(void) +{ + union pnpbios *check; + u8 sum; + int i, length; + + spin_lock_init(&pnp_bios_lock); + + if(pnp_bios_disabled) { + printk(KERN_INFO "PnPBIOS: Disabled.\n"); + return; + } + + if ( is_sony_vaio_laptop ) + pnp_bios_dont_use_current_config = 1; + + for (check = (union pnpbios *) __va(0xf0000); + check < (union pnpbios *) __va(0xffff0); + ((void *) (check)) += 16) { + if (check->fields.signature != PNP_SIGNATURE) + continue; + length = check->fields.length; + if (!length) + continue; + for (sum = 0, i = 0; i < length; i++) + sum += check->chars[i]; + if (sum) + continue; + if (check->fields.version < 0x10) { + printk(KERN_WARNING "PnPBIOS: PnP BIOS version %d.%d is not supported.\n", + check->fields.version >> 4, + check->fields.version & 15); + continue; + } + printk(KERN_INFO "PnPBIOS: Found PnP BIOS installation structure at 0x%p.\n", check); + printk(KERN_INFO "PnPBIOS: PnP BIOS version %d.%d, entry 0x%x:0x%x, dseg 0x%x.\n", + check->fields.version >> 4, check->fields.version & 15, + check->fields.pm16cseg, check->fields.pm16offset, + check->fields.pm16dseg); + Q2_SET_SEL(PNP_CS32, &pnp_bios_callfunc, 64 * 1024); + Q_SET_SEL(PNP_CS16, check->fields.pm16cseg, 64 * 1024); + Q_SET_SEL(PNP_DS, check->fields.pm16dseg, 64 * 1024); + pnp_bios_callpoint.offset = check->fields.pm16offset; + pnp_bios_callpoint.segment = PNP_CS16; + pnp_bios_inst_struc = check; + break; + } + pnpbios_build_devlist(); +#ifdef CONFIG_PROC_FS + pnp_proc_init( pnp_bios_dont_use_current_config ); +#endif +#ifdef CONFIG_HOTPLUG + init_completion(&unload_sem); + if(kernel_thread(pnp_dock_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL)>0) + unloading = 0; +#endif +} + +#ifdef MODULE + +MODULE_LICENSE("GPL"); + +/* We have to run it early and specifically in non modular.. */ +module_init(pnp_bios_init); + +#ifdef CONFIG_HOTPLUG +static void pnp_bios_exit(void) +{ + unloading = 1; + wait_for_completion(&unload_sem); +} + +module_exit(pnp_bios_exit); + +#endif +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pnp/pnp_proc.c linux.ac/drivers/pnp/pnp_proc.c --- linux.vanilla/drivers/pnp/pnp_proc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pnp/pnp_proc.c Wed Oct 10 19:22:23 2001 @@ -0,0 +1,151 @@ +/* + * pnp_proc.c: /proc/bus/pnp interface for Plug and Play devices + * + * Written by David Hinds, dahinds@users.sourceforge.net + */ + +//#include +#define __NO_VERSION__ +//#include + +#include +#include +#include +#include +#include +#include + +static struct proc_dir_entry *proc_pnp = NULL; +static struct proc_dir_entry *proc_pnp_boot = NULL; +static struct pnp_dev_node_info node_info; + +static int proc_read_devices(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + struct pnp_bios_node *node; + int i; + u8 nodenum; + char *p = buf; + + if (pos != 0) { + *eof = 1; + return 0; + } + node = kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return -ENOMEM; + for (i=0,nodenum=0;i<0xff && nodenum!=0xff; i++) { + if ( pnp_bios_get_dev_node(&nodenum, 1, node) ) + break; + p += sprintf(p, "%02x\t%08x\t%02x:%02x:%02x\t%04x\n", + node->handle, node->eisa_id, + node->type_code[0], node->type_code[1], + node->type_code[2], node->flags); + } + kfree(node); + return (p-buf); +} + +static int proc_read_node(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + struct pnp_bios_node *node; + int boot = (long)data >> 8; + u8 nodenum = (long)data; + int len; + + if (pos != 0) { + *eof = 1; + return 0; + } + node = kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return -ENOMEM; + if ( pnp_bios_get_dev_node(&nodenum, boot, node) ) + return -EIO; + len = node->size - sizeof(struct pnp_bios_node); + memcpy(buf, node->data, len); + kfree(node); + return len; +} + +static int proc_write_node(struct file *file, const char *buf, + unsigned long count, void *data) +{ + struct pnp_bios_node *node; + int boot = (long)data >> 8; + u8 nodenum = (long)data; + + node = kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return -ENOMEM; + if ( pnp_bios_get_dev_node(&nodenum, boot, node) ) + return -EIO; + if (count != node->size - sizeof(struct pnp_bios_node)) + return -EINVAL; + memcpy(node->data, buf, count); + if (pnp_bios_set_dev_node(node->handle, boot, node) != 0) + return -EINVAL; + kfree(node); + return count; +} + +static int pnp_proc_dont_use_current_config; + +void pnp_proc_init( int dont_use_current ) +{ + struct pnp_bios_node *node; + struct proc_dir_entry *ent; + char name[3]; + int i; + u8 nodenum; + + pnp_proc_dont_use_current_config = dont_use_current; + + if (!pnp_bios_present()) return; + if (pnp_bios_dev_node_info(&node_info) != 0) return; + + proc_pnp = proc_mkdir("pnp", proc_bus); + if (!proc_pnp) return; + proc_pnp_boot = proc_mkdir("boot", proc_pnp); + if (!proc_pnp_boot) return; + create_proc_read_entry("devices", 0, proc_pnp, proc_read_devices, NULL); + + node = kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return; + for (i=0,nodenum = 0; i<0xff && nodenum != 0xff; i++) { + if (pnp_bios_get_dev_node(&nodenum, 1, node) != 0) + break; + sprintf(name, "%02x", node->handle); + if ( !pnp_proc_dont_use_current_config ) { + ent = create_proc_entry(name, 0, proc_pnp); + if (ent) { + ent->read_proc = proc_read_node; + ent->write_proc = proc_write_node; + ent->data = (void *)(long)(node->handle); + } + } + ent = create_proc_entry(name, 0, proc_pnp_boot); + if (ent) { + ent->read_proc = proc_read_node; + ent->write_proc = proc_write_node; + ent->data = (void *)(long)(node->handle+0x100); + } + } + kfree(node); +} + +void pnp_proc_done(void) +{ + int i; + char name[3]; + + if (!proc_pnp) return; + + for (i=0; i<0xff; i++) { + sprintf(name, "%02x", i); + if ( !pnp_proc_dont_use_current_config ) + remove_proc_entry(name, proc_pnp); + remove_proc_entry(name, proc_pnp_boot); + } + remove_proc_entry("boot", proc_pnp); + remove_proc_entry("devices", proc_pnp); + remove_proc_entry("pnp", proc_bus); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/block/dasd.c linux.ac/drivers/s390/block/dasd.c --- linux.vanilla/drivers/s390/block/dasd.c Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/s390/block/dasd.c Wed Oct 10 01:48:06 2001 @@ -1092,9 +1092,11 @@ BUG (); } if (device->lowmem_cqr==NULL) { - DASD_MESSAGE (KERN_WARNING, device, - "Low memory! Using emergency request %p", - device->lowmem_ccws); + DASD_DRIVER_DEBUG_EVENT (2, dasd_alloc_request, + "(%04x) Low memory! Using emergency request %p.", + device->devinfo.devno, + device->lowmem_ccws); + device->lowmem_cqr=device->lowmem_ccws; rv = device->lowmem_ccws; memset (rv, 0, PAGE_SIZE); @@ -1105,10 +1107,11 @@ rv->data = (void *) ((long) rv + PAGE_SIZE - datasize); rv->cpaddr = (ccw1_t *) ((long) rv + sizeof (ccw_req_t)); } else { - DASD_MESSAGE (KERN_WARNING, device, - "Refusing emergency mem for request " - "NULL, already in use at %p.", - device->lowmem_ccws); + DASD_DRIVER_DEBUG_EVENT (2, dasd_alloc_request, + "(%04x) Refusing emergency mem for request " + "NULL, already in use at %p.", + device->devinfo.devno, + device->lowmem_ccws); } return rv; } @@ -1219,6 +1222,7 @@ CQR_STATUS_QUEUED); +#ifdef DASD_PROFILE /* save profile information for non erp cqr */ if (cqr->refers == NULL) { unsigned int counter = 0; @@ -1235,6 +1239,7 @@ dasd_global_profile.dasd_io_nr_req[counter]++; device->profile.dasd_io_nr_req[counter]++; } +#endif } /* @@ -1671,9 +1676,12 @@ chanq_max_size > 0 || (req->nr_sectors >= chanq_min_size)) { ccw_req_t *cqr = NULL; if (is_read_only(device->kdev) && req->cmd == WRITE) { - DASD_MESSAGE (KERN_WARNING, device, - "rejecting write request %p\n", - req); + + DASD_DRIVER_DEBUG_EVENT (3, dasd_int_handler, + "(%04x) Rejecting write request %p\n", + device->devinfo.devno, + req); + dasd_end_request (req, 0); dasd_dequeue_request (queue,req); } else { @@ -1683,9 +1691,12 @@ part[MINOR (req->rq_dev)].start_sect; cqr = device->discipline->build_cp_from_req (device, req); if (cqr == NULL) { - DASD_MESSAGE (KERN_WARNING, device, - "CCW creation failed on request %p\n", - req); + + DASD_DRIVER_DEBUG_EVENT (3, dasd_int_handler, + "(%04x) CCW creation failed " + "on request %p\n", + device->devinfo.devno, + req); /* revert relocation of request */ req->sector -= device->major_info->gendisk. @@ -2032,7 +2043,7 @@ } erp->cpaddr->cmd_code = CCW_CMD_TIC; - erp->cpaddr->cda = (__u32) (void *) cqr->cpaddr; + erp->cpaddr->cda = (__u32) (addr_t) cqr->cpaddr; erp->function = dasd_default_erp_action; erp->refers = cqr; erp->device = cqr->device; @@ -2232,13 +2243,7 @@ } for (i = (1 << DASD_PARTN_BITS) - 1; i >= 0; i--) { int major = device->major_info->gendisk.major; - int minor = start + i; - kdev_t devi = MKDEV (major, minor); - struct super_block *sb = get_super (devi); - //sync_dev (devi); - if (sb) - invalidate_inodes (sb); - invalidate_buffers (devi); + invalidate_device(MKDEV (major, start+i), 1); } dasd_destroy_partitions(device); dasd_setup_partitions(device); @@ -2285,20 +2290,20 @@ #endif switch (no) { case DASDAPIVER: { - int ver = DASD_API_VERSION; - rc = copy_to_user ((int *) data, &ver, sizeof (int)); - if (rc) - rc = -EFAULT; - break; + int ver = DASD_API_VERSION; + rc = put_user(ver, (int *) data); + break; } case BLKGETSIZE:{ /* Return device size */ long blocks = major_info->gendisk.sizes [MINOR (inp->i_rdev)] << 1; - rc = - copy_to_user ((long *) data, &blocks, - sizeof (long)); - if (rc) - rc = -EFAULT; + rc = put_user(blocks, (long *) data); + break; + } + case BLKGETSIZE64:{ + u64 blocks = major_info->gendisk.sizes + [MINOR (inp->i_rdev)]; + rc = put_user(blocks << 10, (u64 *) data); break; } case BLKRRPART:{ @@ -3253,10 +3258,6 @@ int rc = 0; unsigned long flags; - printk (KERN_ERR PRINTK_HEADER - "called dasd_state_accept_to_init for device %02x\n", - device->devinfo.devno); - if ( device->discipline->init_analysis ) { device->init_cqr=device->discipline->init_analysis (device); if ( device->init_cqr != NULL ) { @@ -3297,8 +3298,10 @@ rc = -EMEDIUMTYPE; } if ( device->init_cqr ) { + /* This pointer is no longer needed, BUT dont't free the */ + /* memory, because this is done in bh for finished request!!!! */ atomic_dec(&dasd_init_pending); - device->init_cqr = NULL; /* This one is no longer needed */ + device->init_cqr = NULL; } device->level = DASD_STATE_READY; return rc; @@ -3378,7 +3381,6 @@ dasd_deactivate_queue (dasd_device_t *device) { int i; - int major = MAJOR(device->kdev); int minor = MINOR(device->kdev); for (i = 0; i < (1 << DASD_PARTN_BITS); i++) { @@ -3764,7 +3766,14 @@ vfree (buffer); return -EFAULT; } - buffer[user_len] = 0; + + /* replace LF with '\0' */ + if (buffer[user_len -1] == '\n') { + buffer[user_len -1] = '\0'; + } else { + buffer[user_len] = '\0'; + } + printk (KERN_INFO PRINTK_HEADER "/proc/dasd/devices: '%s'\n", buffer); if (strncmp (buffer, "set ", 4) && strncmp (buffer, "add ", 4)) { printk (KERN_WARNING PRINTK_HEADER @@ -3798,7 +3807,7 @@ range.to == -EINVAL ) { printk (KERN_WARNING PRINTK_HEADER - "/proc/dasd/devices: parse error in '%s'", + "/proc/dasd/devices: range parse error in '%s'\n", buffer); } else { off = (long) temp - (long) buffer; @@ -3814,7 +3823,8 @@ dasd_disable_ranges (&range, NULL, 0, 1); } else { printk (KERN_WARNING PRINTK_HEADER - "/proc/dasd/devices: parse error in '%s'", buffer); + "/proc/dasd/devices: parse error in '%s'\n", + buffer); } } } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/block/dasd_3990_erp.c linux.ac/drivers/s390/block/dasd_3990_erp.c --- linux.vanilla/drivers/s390/block/dasd_3990_erp.c Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/s390/block/dasd_3990_erp.c Wed Oct 10 01:48:06 2001 @@ -761,7 +761,7 @@ * DASD_3990_HANDLE_ENV_DATA * * DESCRIPTION - * Handles 24 byte 'Enviromental data present'. + * Handles 24 byte 'Environmental data present'. * Does a analysis of the sense data (message Format) * and prints the error messages. * diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/char/Makefile linux.ac/drivers/s390/char/Makefile --- linux.vanilla/drivers/s390/char/Makefile Wed Jul 25 22:12:02 2001 +++ linux.ac/drivers/s390/char/Makefile Wed Oct 10 01:48:06 2001 @@ -5,6 +5,7 @@ O_TARGET := s390-char.o list-multi := tub3270.o tape390.o +export-objs := hwc_rw.o tub3270-objs := tuball.o tubfs.o tubtty.o \ tubttyaid.o tubttybld.o tubttyscl.o \ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/char/hwc_cpi.c linux.ac/drivers/s390/char/hwc_cpi.c --- linux.vanilla/drivers/s390/char/hwc_cpi.c Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/s390/char/hwc_cpi.c Wed Oct 10 01:48:06 2001 @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/char/tape34xx.c linux.ac/drivers/s390/char/tape34xx.c --- linux.vanilla/drivers/s390/char/tape34xx.c Wed Jul 25 22:12:02 2001 +++ linux.ac/drivers/s390/char/tape34xx.c Wed Oct 10 01:48:06 2001 @@ -1670,7 +1670,7 @@ tapestate_set (ti, TS_DONE); ti->rc = 0; ti->wanna_wakeup=1; - wake_up_interruptible (&ti->wq); + wake_up (&ti->wq); } void @@ -2274,6 +2274,8 @@ ti->wanna_wakeup=1; switch (tapestate_get(ti)) { case TS_REW_RELEASE_INIT: + case TS_RFO_INIT: + case TS_RBA_INIT: tapestate_set(ti,TS_FAILED); wake_up (&ti->wq); break; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/char/tapechar.c linux.ac/drivers/s390/char/tapechar.c --- linux.vanilla/drivers/s390/char/tapechar.c Wed Jul 25 22:12:02 2001 +++ linux.ac/drivers/s390/char/tapechar.c Wed Oct 10 01:48:06 2001 @@ -204,13 +204,9 @@ return rc; } s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - wait_event_interruptible (ti->wq,ti->wanna_wakeup); + wait_event (ti->wq,ti->wanna_wakeup); ti->cqr = NULL; ti->discipline->free_read_block (cqr, ti); - if (signal_pending (current)) { - tapestate_set (ti, TS_IDLE); - return -ERESTARTSYS; - } s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); if (tapestate_get (ti) == TS_FAILED) { tapestate_set (ti, TS_IDLE); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/char/tuball.c linux.ac/drivers/s390/char/tuball.c --- linux.vanilla/drivers/s390/char/tuball.c Fri Sep 7 17:28:38 2001 +++ linux.ac/drivers/s390/char/tuball.c Wed Oct 10 01:48:06 2001 @@ -93,8 +93,8 @@ NULL /* next */ }; -bcb_t tub3270_con_bcb; /* Buffer that receives con writes */ -spinlock_t tub3270_con_bcblock; /* Lock for the buffer */ +static bcb_t tub3270_con_bcb; /* Buffer that receives con writes */ +static spinlock_t tub3270_con_bcblock; /* Lock for the buffer */ int tub3270_con_irq = -1; /* set nonneg by _activate() */ tub_t *tub3270_con_tubp; /* set nonzero by _activate() */ struct tty_driver tty3270_con_driver; /* for /dev/console at 4, 64 */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/char/tubtty.c linux.ac/drivers/s390/char/tubtty.c --- linux.vanilla/drivers/s390/char/tubtty.c Fri Sep 7 17:28:38 2001 +++ linux.ac/drivers/s390/char/tubtty.c Wed Oct 10 01:48:06 2001 @@ -992,8 +992,8 @@ if (tty) len += sprintf(buf+len, - " write_wait=%.8x read_wait=%.8x\n", - (int)&tty->write_wait, (int)&tty->read_wait); + " write_wait=%p read_wait=%p\n", + &tty->write_wait, &tty->read_wait); if (tty && ((mp = tty->termios))) len += sprintf(buf+len," iflag=%.8x oflag=%.8x " diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/net/ctcmain.c linux.ac/drivers/s390/net/ctcmain.c --- linux.vanilla/drivers/s390/net/ctcmain.c Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/s390/net/ctcmain.c Wed Oct 10 01:48:06 2001 @@ -1,5 +1,5 @@ /* - * $Id: ctcmain.c,v 1.49 2001/08/31 14:50:05 felfert Exp $ + * $Id: ctcmain.c,v 1.51 2001/09/24 10:38:02 mschwide Exp $ * * CTC / ESCON network driver * @@ -35,7 +35,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.49 $ + * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.51 $ * */ @@ -43,7 +43,7 @@ #include #include #include -#include +#include #include #include #include @@ -96,6 +96,7 @@ #ifdef MODULE MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert@millenux.com)"); MODULE_DESCRIPTION("Linux for S/390 CTC/Escon Driver"); +MODULE_LICENSE("GPL"); #ifndef CTC_CHANDEV MODULE_PARM(ctc, "s"); MODULE_PARM_DESC(ctc, @@ -296,6 +297,8 @@ net_device *netdev; ctc_profile prof; + + unsigned char *trans_skb_data; } channel; #define CHANNEL_FLAGS_READ 0 @@ -382,7 +385,7 @@ */ static void print_banner(void) { static int printed = 0; - char vbuf[] = "$Revision: 1.49 $"; + char vbuf[] = "$Revision: 1.51 $"; char *version = vbuf; if (printed) @@ -951,6 +954,7 @@ return -ENOMEM; } ch->ccw[1].count = 0; + ch->trans_skb_data = ch->trans_skb->data; ch->flags &= ~CHANNEL_FLAGS_BUFSIZE_CHANGED; } return 0; @@ -1016,7 +1020,7 @@ spin_unlock(&ch->collect_lock); return; } - ch->trans_skb->tail = ch->trans_skb->data; + ch->trans_skb->tail = ch->trans_skb->data = ch->trans_skb_data; ch->trans_skb->len = 0; if (ch->prof.maxmulti < (ch->collect_len + 2)) ch->prof.maxmulti = ch->collect_len + 2; @@ -1089,7 +1093,6 @@ int len = ch->max_bufsize - ch->devstat->rescnt; struct sk_buff *skb = ch->trans_skb; __u16 block_len = *((__u16*)skb->data); - char *saved_data = skb->data; int check_len; int rc; @@ -1138,7 +1141,7 @@ ctc_unpack_skb(ch, skb); } again: - skb->data = skb->tail = saved_data; + skb->data = skb->tail = ch->trans_skb_data; skb->len = 0; if (ctc_checkalloc_buffer(ch, 1)) return; @@ -1548,9 +1551,9 @@ channel *ch = (channel *)arg; net_device *dev = ch->netdev; + fsm_deltimer(&ch->timer); printk(KERN_DEBUG "%s: %s channel restart\n", dev->name, (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); - fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); oldstate = fsm_getstate(fi); fsm_newstate(fi, CH_STATE_STARTWAIT); @@ -1716,8 +1719,7 @@ #ifdef DEBUG printk(KERN_DEBUG "ccw[4].cda = %08x\n", ch->ccw[4].cda); #endif - rc = do_IO(ch->irq, &ch->ccw[3], - (intparm_t)ch, 0xff, 0); + rc = do_IO(ch->irq, &ch->ccw[3], (intparm_t)ch, 0xff, 0); if (event == CH_EVENT_TIMER) s390irq_spin_unlock_irqrestore(ch->irq, saveflags); @@ -2539,7 +2541,8 @@ if (rc != 0) { fsm_deltimer(&ch->timer); ccw_check_return_code(ch, rc); - skb_dequeue_tail(&ch->io_queue); + if (ccw_idx == 3) + skb_dequeue_tail(&ch->io_queue); /** * Remove our header. It gets added * again on retransmit. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/net/iucv.c linux.ac/drivers/s390/net/iucv.c --- linux.vanilla/drivers/s390/net/iucv.c Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/s390/net/iucv.c Wed Oct 10 01:48:07 2001 @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include #include @@ -53,10 +53,6 @@ #undef DEBUG -#ifndef min -#define min(a,b) (((a)<(b))?(a):(b)) -#endif - /* FLAGS: * All flags are defined in the field IPFLAGS1 of each function * and can be found in CP Programming Services. @@ -1163,7 +1159,7 @@ parm = (iparml_db *)grab_param(); - parm->ipbfadr1 = (__u32) buffer; + parm->ipbfadr1 = (__u32) (addr_t) buffer; parm->ipbfln1f = (__u32) ((ulong) buflen); parm->ipmsgid = msgid; parm->ippathid = pathid; @@ -1186,7 +1182,7 @@ if (residual_buffer) *residual_buffer = parm->ipbfadr1; } else { - moved = min (buflen, 8); + moved = min_t (unsigned long, buflen, 8); memcpy ((char *) buffer, (char *) &parm->ipbfadr1, moved); @@ -1283,7 +1279,8 @@ while ((moved < 8) && (moved < buflen)) { dyn_len = - min ((buffer + i)->length, need_to_move); + min_t (unsigned int, + (buffer + i)->length, need_to_move); memcpy ((char *)((ulong)((buffer + i)->address)), ((char *) &parm->ipbfadr1) + moved, diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/s390/net/netiucv.c linux.ac/drivers/s390/net/netiucv.c --- linux.vanilla/drivers/s390/net/netiucv.c Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/s390/net/netiucv.c Wed Oct 10 01:48:07 2001 @@ -1,5 +1,5 @@ /* - * $Id: netiucv.c,v 1.11 2001/07/16 17:00:02 felfert Exp $ + * $Id: netiucv.c,v 1.12 2001/09/24 10:38:02 mschwide Exp $ * * IUCV network driver * @@ -28,7 +28,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * RELEASE-TAG: IUCV network driver $Revision: 1.11 $ + * RELEASE-TAG: IUCV network driver $Revision: 1.12 $ * */ @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include #include @@ -2002,7 +2002,7 @@ static void netiucv_banner(void) { - char vbuf[] = "$Revision: 1.11 $"; + char vbuf[] = "$Revision: 1.12 $"; char *version = vbuf; if ((version = strchr(version, ':'))) { @@ -2115,7 +2115,8 @@ return 0; } -module_init(netiucv_init); #ifdef MODULE +module_init(netiucv_init); module_exit(netiucv_exit); +MODULE_LICENSE("GPL"); #endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sbus/audio/amd7930.c linux.ac/drivers/sbus/audio/amd7930.c --- linux.vanilla/drivers/sbus/audio/amd7930.c Tue Jun 12 03:15:27 2001 +++ linux.ac/drivers/sbus/audio/amd7930.c Wed Oct 10 22:46:42 2001 @@ -1716,6 +1716,7 @@ module_init(amd7930_init); module_exit(amd7930_exit); +MODULE_LICENSE("GPL"); /*************************************************************/ /* Audio format conversion */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sbus/char/bpp.c linux.ac/drivers/sbus/char/bpp.c --- linux.vanilla/drivers/sbus/char/bpp.c Thu Oct 11 13:52:17 2001 +++ linux.ac/drivers/sbus/char/bpp.c Thu Oct 11 16:26:50 2001 @@ -1073,4 +1073,3 @@ module_init(bpp_init); module_exit(bpp_cleanup); -MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/53c700.c linux.ac/drivers/scsi/53c700.c --- linux.vanilla/drivers/scsi/53c700.c Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/scsi/53c700.c Wed Oct 10 01:48:07 2001 @@ -51,6 +51,12 @@ /* CHANGELOG * + * Version 2.6 + * + * Following test of the 64 bit parisc kernel by Richard Hirst, + * several problems have now been corrected. Also adds support for + * consistent memory allocation. + * * Version 2.5 * * More Compatibility changes for 710 (now actually works). Enhanced @@ -90,7 +96,7 @@ * Initial modularisation from the D700. See NCR_D700.c for the rest of * the changelog. * */ -#define NCR_700_VERSION "2.5" +#define NCR_700_VERSION "2.6" #include #include @@ -217,18 +223,39 @@ NCR_700_detect(Scsi_Host_Template *tpnt, struct NCR_700_Host_Parameters *hostdata) { - dma_addr_t pScript, pSlots; + dma_addr_t pScript, pMemory, pSlots; + __u8 *memory; __u32 *script; struct Scsi_Host *host; static int banner = 0; int j; - /* This separation of pScript and script is not strictly - * necessay, but may be useful in architectures which can - * allocate consistent memory on which virt_to_bus will not - * work */ - script = kmalloc(sizeof(SCRIPT), GFP_KERNEL); - pScript = virt_to_bus(script); +#ifdef CONFIG_53C700_USE_CONSISTENT + memory = pci_alloc_consistent(hostdata->pci_dev, TOTAL_MEM_SIZE, + &pMemory); + hostdata->consistent = 1; + if(memory == NULL ) { + printk(KERN_WARNING "53c700: consistent memory allocation failed\n"); +#endif + memory = kmalloc(TOTAL_MEM_SIZE, GFP_KERNEL); + if(memory == NULL) { + printk(KERN_ERR "53c700: Failed to allocate memory for driver, detatching\n"); + return NULL; + } + pMemory = pci_map_single(hostdata->pci_dev, memory, + TOTAL_MEM_SIZE, PCI_DMA_BIDIRECTIONAL); +#ifdef CONFIG_53C700_USE_CONSISTENT + hostdata->consistent = 0; + } +#endif + script = (__u32 *)memory; + pScript = pMemory; + hostdata->msgin = memory + MSGIN_OFFSET; + hostdata->msgout = memory + MSGOUT_OFFSET; + hostdata->status = memory + STATUS_OFFSET; + hostdata->slots = (struct NCR_700_command_slot *)(memory + SLOTS_OFFSET); + + pSlots = pMemory + SLOTS_OFFSET; /* Fill in the missing routines from the host template */ tpnt->queuecommand = NCR_700_queuecommand; @@ -251,29 +278,6 @@ if((host = scsi_register(tpnt, 4)) == NULL) return NULL; - if(script == NULL) { - printk(KERN_ERR "53c700: Failed to allocate script, detatching\n"); - scsi_unregister(host); - return NULL; - } - - /* This separation of slots and pSlots may facilitate later - * migration to consistent memory on architectures which - * support it */ - hostdata->slots = kmalloc(sizeof(struct NCR_700_command_slot) - * NCR_700_COMMAND_SLOTS_PER_HOST, - GFP_KERNEL); - pSlots = virt_to_bus(hostdata->slots); - - hostdata->msgin = kmalloc(MSG_ARRAY_SIZE, GFP_KERNEL); - hostdata->msgout = kmalloc(MSG_ARRAY_SIZE, GFP_KERNEL); - hostdata->status = kmalloc(MSG_ARRAY_SIZE, GFP_KERNEL); - if(hostdata->slots == NULL || hostdata->msgin == NULL - || hostdata->msgout == NULL || hostdata->status==NULL) { - printk(KERN_ERR "53c700: Failed to allocate command slots or message buffers, detatching\n"); - scsi_unregister(host); - return NULL; - } memset(hostdata->slots, 0, sizeof(struct NCR_700_command_slot) * NCR_700_COMMAND_SLOTS_PER_HOST); for(j = 0; j < NCR_700_COMMAND_SLOTS_PER_HOST; j++) { @@ -295,19 +299,17 @@ for(j = 0; j < PATCHES; j++) { script[LABELPATCHES[j]] = bS_to_host(pScript + SCRIPT[LABELPATCHES[j]]); } - /* now patch up fixed addresses. - * NOTE: virt_to_bus may be wrong if consistent memory is used - * for these in the future */ + /* now patch up fixed addresses. */ script_patch_32(script, MessageLocation, - virt_to_bus(&hostdata->msgout[0])); + pScript + MSGOUT_OFFSET); script_patch_32(script, StatusAddress, - virt_to_bus(&hostdata->status[0])); + pScript + STATUS_OFFSET); script_patch_32(script, ReceiveMsgAddress, - virt_to_bus(&hostdata->msgin[0])); + pScript + MSGIN_OFFSET); hostdata->script = script; hostdata->pScript = pScript; - dma_cache_wback((unsigned long)script, sizeof(SCRIPT)); + NCR_700_dma_cache_wback((unsigned long)script, sizeof(SCRIPT)); hostdata->state = NCR_700_HOST_FREE; spin_lock_init(&hostdata->lock); hostdata->cmd = NULL; @@ -344,13 +346,18 @@ struct NCR_700_Host_Parameters *hostdata = (struct NCR_700_Host_Parameters *)host->hostdata[0]; - /* NOTE: these may be NULL if we weren't fully initialised before - * the scsi_unregister was called */ - kfree(hostdata->script); - kfree(hostdata->slots); - kfree(hostdata->msgin); - kfree(hostdata->msgout); - kfree(hostdata->status); +#ifdef CONFIG_53C700_USE_CONSISTENT + if(hostdata->consistent) { + pci_free_consistent(hostdata->pci_dev, TOTAL_MEM_SIZE, + hostdata->script, hostdata->pScript); + } else { +#endif + pci_unmap_single(hostdata->pci_dev, hostdata->pScript, + TOTAL_MEM_SIZE, PCI_DMA_BIDIRECTIONAL); + kfree(hostdata->script); +#ifdef CONFIG_53C700_USE_CONSISTENT + } +#endif return 1; } @@ -620,7 +627,25 @@ } return (offset & 0x0f) | (XFERP & 0x07)<<4; } - + +STATIC inline void +NCR_700_unmap(struct NCR_700_Host_Parameters *hostdata, Scsi_Cmnd *SCp, + struct NCR_700_command_slot *slot) +{ + if(SCp->sc_data_direction != SCSI_DATA_NONE && + SCp->sc_data_direction != SCSI_DATA_UNKNOWN) { + int pci_direction = scsi_to_pci_dma_dir(SCp->sc_data_direction); + if(SCp->use_sg) { + pci_unmap_sg(hostdata->pci_dev, SCp->buffer, + SCp->use_sg, pci_direction); + } else { + pci_unmap_single(hostdata->pci_dev, + slot->dma_handle, + SCp->request_bufflen, + pci_direction); + } + } +} STATIC inline void NCR_700_scsi_done(struct NCR_700_Host_Parameters *hostdata, @@ -632,31 +657,22 @@ if(SCp != NULL) { struct NCR_700_command_slot *slot = (struct NCR_700_command_slot *)SCp->host_scribble; - + + NCR_700_unmap(hostdata, SCp, slot); + pci_unmap_single(hostdata->pci_dev, slot->pCmd, + sizeof(SCp->cmnd), PCI_DMA_TODEVICE); if(SCp->cmnd[0] == REQUEST_SENSE && SCp->cmnd[6] == NCR_700_INTERNAL_SENSE_MAGIC) { #ifdef NCR_700_DEBUG printk(" ORIGINAL CMD %p RETURNED %d, new return is %d sense is\n", SCp, SCp->cmnd[7], result); print_sense("53c700", SCp); + #endif + SCp->use_sg = SCp->cmnd[8]; if(result == 0) result = SCp->cmnd[7]; } - if(SCp->sc_data_direction != SCSI_DATA_NONE && - SCp->sc_data_direction != SCSI_DATA_UNKNOWN) { - int pci_direction = scsi_to_pci_dma_dir(SCp->sc_data_direction); - if(SCp->use_sg) { - pci_unmap_sg(hostdata->pci_dev, SCp->buffer, - SCp->use_sg, pci_direction); - } else { - pci_unmap_single(hostdata->pci_dev, - slot->dma_handle, - SCp->request_bufflen, - pci_direction); - } - } - free_slot(slot, hostdata); SCp->host_scribble = NULL; @@ -850,7 +866,7 @@ printk(KERN_WARNING "scsi%d Unexpected SDTR msg\n", host->host_no); hostdata->msgout[0] = A_REJECT_MSG; - dma_cache_wback((unsigned long)hostdata->msgout, 1); + NCR_700_dma_cache_wback((unsigned long)hostdata->msgout, 1); script_patch_16(hostdata->script, MessageCount, 1); /* SendMsgOut returns, so set up the return * address */ @@ -862,7 +878,7 @@ printk(KERN_INFO "scsi%d: (%d:%d), Unsolicited WDTR after CMD, Rejecting\n", host->host_no, pun, lun); hostdata->msgout[0] = A_REJECT_MSG; - dma_cache_wback((unsigned long)hostdata->msgout, 1); + NCR_700_dma_cache_wback((unsigned long)hostdata->msgout, 1); script_patch_16(hostdata->script, MessageCount, 1); resume_offset = hostdata->pScript + Ent_SendMessageWithATN; @@ -876,7 +892,7 @@ printk("\n"); /* just reject it */ hostdata->msgout[0] = A_REJECT_MSG; - dma_cache_wback((unsigned long)hostdata->msgout, 1); + NCR_700_dma_cache_wback((unsigned long)hostdata->msgout, 1); script_patch_16(hostdata->script, MessageCount, 1); /* SendMsgOut returns, so set up the return * address */ @@ -954,7 +970,7 @@ printk("\n"); /* just reject it */ hostdata->msgout[0] = A_REJECT_MSG; - dma_cache_wback((unsigned long)hostdata->msgout, 1); + NCR_700_dma_cache_wback((unsigned long)hostdata->msgout, 1); script_patch_16(hostdata->script, MessageCount, 1); /* SendMsgOut returns, so set up the return * address */ @@ -964,7 +980,7 @@ } NCR_700_writel(temp, host, TEMP_REG); /* set us up to receive another message */ - dma_cache_inv((unsigned long)hostdata->msgin, MSG_ARRAY_SIZE); + NCR_700_dma_cache_inv((unsigned long)hostdata->msgin, MSG_ARRAY_SIZE); return resume_offset; } @@ -1002,10 +1018,15 @@ printk(" cmd %p has status %d, requesting sense\n", SCp, hostdata->status[0]); #endif - /* we can destroy the command here because the - * contingent allegiance condition will cause a - * retry which will re-copy the command from the - * saved data_cmnd */ + /* we can destroy the command here + * because the contingent allegiance + * condition will cause a retry which + * will re-copy the command from the + * saved data_cmnd. We also unmap any + * data associated with the command + * here */ + NCR_700_unmap(hostdata, SCp, slot); + SCp->cmnd[0] = REQUEST_SENSE; SCp->cmnd[1] = (SCp->lun & 0x7) << 5; SCp->cmnd[2] = 0; @@ -1013,21 +1034,29 @@ SCp->cmnd[4] = sizeof(SCp->sense_buffer); SCp->cmnd[5] = 0; SCp->cmd_len = 6; - /* Here's a quiet hack: the REQUEST_SENSE command is - * six bytes, so store a flag indicating that this - * was an internal sense request and the original - * status at the end of the command */ + /* Here's a quiet hack: the + * REQUEST_SENSE command is six bytes, + * so store a flag indicating that + * this was an internal sense request + * and the original status at the end + * of the command */ SCp->cmnd[6] = NCR_700_INTERNAL_SENSE_MAGIC; SCp->cmnd[7] = hostdata->status[0]; + SCp->cmnd[8] = SCp->use_sg; + SCp->use_sg = 0; SCp->sc_data_direction = SCSI_DATA_READ; - dma_cache_wback((unsigned long)SCp->cmnd, SCp->cmd_len); + pci_dma_sync_single(hostdata->pci_dev, + slot->pCmd, + SCp->cmd_len, + PCI_DMA_TODEVICE); + slot->dma_handle = pci_map_single(hostdata->pci_dev, SCp->sense_buffer, sizeof(SCp->sense_buffer), PCI_DMA_FROMDEVICE); slot->SG[0].ins = bS_to_host(SCRIPT_MOVE_DATA_IN | sizeof(SCp->sense_buffer)); - slot->SG[0].pAddr = bS_to_host(virt_to_bus(SCp->sense_buffer)); + slot->SG[0].pAddr = bS_to_host(slot->dma_handle); slot->SG[1].ins = bS_to_host(SCRIPT_RETURN); slot->SG[1].pAddr = 0; slot->resume_offset = hostdata->pScript; - dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG[0])*2); - dma_cache_inv((unsigned long)SCp->sense_buffer, sizeof(SCp->sense_buffer)); + NCR_700_dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG[0])*2); + NCR_700_dma_cache_inv((unsigned long)SCp->sense_buffer, sizeof(SCp->sense_buffer)); /* queue the command for reissue */ slot->state = NCR_700_SLOT_QUEUED; @@ -1136,7 +1165,7 @@ /* re-patch for this command */ script_patch_32_abs(hostdata->script, CommandAddress, - virt_to_bus(slot->cmnd->cmnd)); + slot->pCmd); script_patch_16(hostdata->script, CommandCount, slot->cmnd->cmd_len); script_patch_32_abs(hostdata->script, SGScriptStartAddress, @@ -1149,13 +1178,13 @@ * should therefore always clear ACK */ NCR_700_writeb(NCR_700_get_SXFER(hostdata->cmd->device), host, SXFER_REG); - dma_cache_inv((unsigned long)hostdata->msgin, + NCR_700_dma_cache_inv((unsigned long)hostdata->msgin, MSG_ARRAY_SIZE); - dma_cache_wback((unsigned long)hostdata->msgout, + NCR_700_dma_cache_wback((unsigned long)hostdata->msgout, MSG_ARRAY_SIZE); /* I'm just being paranoid here, the command should * already have been flushed from the cache */ - dma_cache_wback((unsigned long)slot->cmnd->cmnd, + NCR_700_dma_cache_wback((unsigned long)slot->cmnd->cmnd, slot->cmnd->cmd_len); @@ -1219,7 +1248,8 @@ hostdata->reselection_id = reselection_id; /* just in case we have a stale simple tag message, clear it */ hostdata->msgin[1] = 0; - dma_cache_wback_inv((unsigned long)hostdata->msgin, MSG_ARRAY_SIZE); + NCR_700_dma_cache_wback_inv((unsigned long)hostdata->msgin, + MSG_ARRAY_SIZE); if(hostdata->tag_negotiated & (1<pScript + Ent_GetReselectionWithTag; } else { @@ -1334,7 +1364,7 @@ hostdata->cmd = NULL; /* clear any stale simple tag message */ hostdata->msgin[1] = 0; - dma_cache_wback_inv((unsigned long)hostdata->msgin, MSG_ARRAY_SIZE); + NCR_700_dma_cache_wback_inv((unsigned long)hostdata->msgin, MSG_ARRAY_SIZE); if(id == 0xff) { /* Selected as target, Ignore */ @@ -1404,7 +1434,7 @@ * set up so we cannot take a selection interrupt */ hostdata->msgout[0] = NCR_700_identify(SCp->cmnd[0] != REQUEST_SENSE, - SCp->lun); + SCp->lun); /* for INQUIRY or REQUEST_SENSE commands, we cannot be sure * if the negotiated transfer parameters still hold, so * always renegotiate them */ @@ -1437,7 +1467,7 @@ Device_ID, 1<target); script_patch_32_abs(hostdata->script, CommandAddress, - virt_to_bus(SCp->cmnd)); + slot->pCmd); script_patch_16(hostdata->script, CommandCount, SCp->cmd_len); /* finally plumb the beginning of the SG list into the script * */ @@ -1448,10 +1478,10 @@ if(slot->resume_offset == 0) slot->resume_offset = hostdata->pScript; /* now perform all the writebacks and invalidates */ - dma_cache_wback((unsigned long)hostdata->msgout, count); - dma_cache_inv((unsigned long)hostdata->msgin, MSG_ARRAY_SIZE); - dma_cache_wback((unsigned long)SCp->cmnd, SCp->cmd_len); - dma_cache_inv((unsigned long)hostdata->status, 1); + NCR_700_dma_cache_wback((unsigned long)hostdata->msgout, count); + NCR_700_dma_cache_inv((unsigned long)hostdata->msgin, MSG_ARRAY_SIZE); + NCR_700_dma_cache_wback((unsigned long)SCp->cmnd, SCp->cmd_len); + NCR_700_dma_cache_inv((unsigned long)hostdata->status, 1); /* set the synchronous period/offset */ NCR_700_writeb(NCR_700_get_SXFER(SCp->device), @@ -1519,7 +1549,7 @@ DEBUG(("scsi%d: istat %02x sstat0 %02x dstat %02x dsp %04x[%08x] dsps 0x%x\n", host->host_no, istat, sstat0, dstat, - (dsp - (__u32)virt_to_bus(hostdata->script))/4, + (dsp - (__u32)(hostdata->pScript))/4, dsp, dsps)); if(SCp != NULL) { @@ -1632,7 +1662,7 @@ slot->SG[i].ins = bS_to_host(SCRIPT_NOP); slot->SG[i].pAddr = 0; } - dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG)); + NCR_700_dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG)); /* and pretend we disconnected after * the command phase */ resume_offset = hostdata->pScript + Ent_MsgInDuringData; @@ -1847,7 +1877,13 @@ #endif if(old != NULL && old->tag == SCp->device->current_tag) { - printk(KERN_WARNING "scsi%d (%d:%d) Tag clock back to current, queueing\n", SCp->host->host_no, SCp->target, SCp->lun); + /* On some badly starving drives, this can be + * a frequent occurance, so print the message + * only once */ + if(NCR_700_is_flag_clear(SCp->device, NCR_700_DEV_TAG_STARVATION_WARNED)) { + printk(KERN_WARNING "scsi%d (%d:%d) Target is suffering from tag starvation.\n", SCp->host->host_no, SCp->target, SCp->lun); + NCR_700_set_flag(SCp->device, NCR_700_DEV_TAG_STARVATION_WARNED); + } return 1; } slot->tag = SCp->device->current_tag++; @@ -1900,6 +1936,18 @@ hostdata->ITL_Hash_back[hash] = slot; slot->ITL_back = NULL; + /* sanity check: some of the commands generated by the mid-layer + * have an eccentric idea of their sc_data_direction */ + if(!SCp->use_sg && !SCp->request_bufflen + && SCp->sc_data_direction != SCSI_DATA_NONE) { +#ifdef NCR_700_DEBUG + printk("53c700: Command"); + print_command(SCp->cmnd); + printk("Has wrong data direction %d\n", SCp->sc_data_direction); +#endif + SCp->sc_data_direction = SCSI_DATA_NONE; + } + switch (SCp->cmnd[0]) { case REQUEST_SENSE: /* clear the internal sense magic */ @@ -1965,12 +2013,14 @@ } slot->SG[i].ins = bS_to_host(SCRIPT_RETURN); slot->SG[i].pAddr = 0; - dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG)); + NCR_700_dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG)); DEBUG((" SETTING %08lx to %x\n", - virt_to_bus(&slot->SG[i].ins), + (&slot->pSG[i].ins), slot->SG[i].ins)); } slot->resume_offset = 0; + slot->pCmd = pci_map_single(hostdata->pci_dev, SCp->cmnd, + sizeof(SCp->cmnd), PCI_DMA_TODEVICE); NCR_700_start_command(SCp); return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/53c700.h linux.ac/drivers/scsi/53c700.h --- linux.vanilla/drivers/scsi/53c700.h Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/scsi/53c700.h Wed Oct 10 22:15:15 2001 @@ -40,6 +40,24 @@ #error "Config.in must define either CONFIG_53C700_IO_MAPPED or CONFIG_53C700_MEM_MAPPED to use this scsi core." #endif +/* macros for consistent memory allocation */ + +#ifdef CONFIG_53C700_USE_CONSISTENT +#define NCR_700_dma_cache_wback(mem, size) \ + if(!hostdata->consistent) \ + dma_cache_wback(mem, size) +#define NCR_700_dma_cache_inv(mem, size) \ + if(!hostdata->consistent) \ + dma_cache_inv(mem, size) +#define NCR_700_dma_cache_wback_inv(mem, size) \ + if(!hostdata->consistent) \ + dma_cache_wback_inv(mem, size) +#else +#define NCR_700_dma_cache_wback(mem, size) dma_cache_wback(mem,size) +#define NCR_700_dma_cache_inv(mem, size) dma_cache_inv(mem,size) +#define NCR_700_dma_cache_wback_inv(mem, size) dma_cache_wback_inv(mem,size) +#endif + struct NCR_700_Host_Parameters; @@ -86,6 +104,7 @@ #define NCR_700_DEV_NEGOTIATED_SYNC (1<<16) #define NCR_700_DEV_BEGIN_SYNC_NEGOTIATION (1<<17) #define NCR_700_DEV_BEGIN_TAG_QUEUEING (1<<18) +#define NCR_700_DEV_TAG_STARVATION_WARNED (1<<19) static inline void NCR_700_set_SXFER(Scsi_Device *SDp, __u8 sxfer) @@ -174,6 +193,8 @@ __u16 tag; __u32 resume_offset; Scsi_Cmnd *cmnd; + /* The pci_mapped address of the actual command in cmnd */ + dma_addr_t pCmd; __u32 temp; /* if this command is a pci_single mapping, holds the dma address * for later unmapping in the done routine */ @@ -191,19 +212,22 @@ int clock; /* board clock speed in MHz */ __u32 base; /* the base for the port (copied to host) */ struct pci_dev *pci_dev; - __u8 dmode_extra; /* adjustable bus settings */ - __u8 differential:1; /* if we are differential */ + __u32 dmode_extra; /* adjustable bus settings */ + __u32 differential:1; /* if we are differential */ #ifdef CONFIG_53C700_LE_ON_BE /* This option is for HP only. Set it if your chip is wired for * little endian on this platform (which is big endian) */ - __u8 force_le_on_be:1; + __u32 force_le_on_be:1; #endif - __u8 chip710:1; /* set if really a 710 not 700 */ - __u8 burst_disable:1; /* set to 1 to disable 710 bursting */ + __u32 chip710:1; /* set if really a 710 not 700 */ + __u32 burst_disable:1; /* set to 1 to disable 710 bursting */ /* NOTHING BELOW HERE NEEDS ALTERING */ - __u8 fast:1; /* if we can alter the SCSI bus clock + __u32 fast:1; /* if we can alter the SCSI bus clock speed (so can negiotiate sync) */ +#ifdef CONFIG_53C700_USE_CONSISTENT + __u32 consistent:1; +#endif int sync_clock; /* The speed of the SYNC core */ @@ -216,15 +240,23 @@ spinlock_t lock; enum NCR_700_Host_State state; /* protected by state lock */ Scsi_Cmnd *cmd; - + /* Note: pScript contains the single consistent block of + * memory. All the msgin, msgout and status are allocated in + * this memory too (at separate cache lines). TOTAL_MEM_SIZE + * represents the total size of this area */ +#define MSG_ARRAY_SIZE 8 +#define MSGOUT_OFFSET (L1_CACHE_ALIGN(sizeof(SCRIPT))) __u8 *msgout; -#define MSG_ARRAY_SIZE 16 - __u8 tag_negotiated; - __u8 *status; +#define MSGIN_OFFSET (MSGOUT_OFFSET + L1_CACHE_ALIGN(MSG_ARRAY_SIZE)) __u8 *msgin; +#define STATUS_OFFSET (MSGIN_OFFSET + L1_CACHE_ALIGN(MSG_ARRAY_SIZE)) + __u8 *status; +#define SLOTS_OFFSET (STATUS_OFFSET + L1_CACHE_ALIGN(MSG_ARRAY_SIZE)) struct NCR_700_command_slot *slots; +#define TOTAL_MEM_SIZE (SLOTS_OFFSET + L1_CACHE_ALIGN(sizeof(struct NCR_700_command_slot) * NCR_700_COMMAND_SLOTS_PER_HOST)) int saved_slot_position; int command_slot_count; /* protected by state lock */ + __u8 tag_negotiated; __u8 rev; __u8 reselection_id; /* flags for the host */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/Config.in linux.ac/drivers/scsi/Config.in --- linux.vanilla/drivers/scsi/Config.in Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/scsi/Config.in Wed Oct 10 01:48:09 2001 @@ -122,10 +122,11 @@ fi fi if [ "$CONFIG_PARISC" = "y" ]; then - dep_tristate 'HP LASI SCSI support for 53c700' CONFIG_SCSI_LASI700 $CONFIG_SCSI + dep_tristate 'HP LASI SCSI support for 53c700/710' CONFIG_SCSI_LASI700 $CONFIG_SCSI if [ "$CONFIG_SCSI_LASI700" != "n" ]; then define_bool CONFIG_53C700_MEM_MAPPED y define_bool CONFIG_53C700_LE_ON_BE y + define_bool CONFIG_53C700_USE_CONSISTENT y fi fi dep_tristate 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI $CONFIG_PCI diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/README.53c700 linux.ac/drivers/scsi/README.53c700 --- linux.vanilla/drivers/scsi/README.53c700 Sun Sep 9 19:58:36 2001 +++ linux.ac/drivers/scsi/README.53c700 Wed Oct 10 01:48:09 2001 @@ -1,17 +1,154 @@ -This driver supports the 53c700 and 53c700-66 chips only. It is full -featured and does sync (-66 only), disconnects and tag command -queueing. +General Description +=================== + +This driver supports the 53c700 and 53c700-66 chips. It also supports +the 53c710 but only in 53c700 emulation mode. It is full featured and +does sync (-66 and 710 only), disconnects and tag command queueing. Since the 53c700 must be interfaced to a bus, you need to wrapper the card detector around this driver. For an example, see the -NCR_D700.[ch] files. +NCR_D700.[ch] or lasi700.[ch] files. The comments in the 53c700.[ch] files tell you which parts you need to fill in to get the driver working. -The driver is currently I/O mapped only, but it should be easy enough -to memory map (just make the port reads #defines with MEM_MAPPED for -memory mapping or nothing for I/O mapping, specify an extra rule for -53c700-mem.o with the -DMEM_MAPPED flag and make your driver use it, -that way the make rules will generate the correct version). + +Compile Time Flags +================== + +The driver may be either io mapped or memory mapped. This is +selectable by configuration flags: + +CONFIG_53C700_MEM_MAPPED + +define if the driver is memory mapped. + +CONFIG_53C700_IO_MAPPED + +define if the driver is to be io mapped. + +One or other of the above flags *must* be defined. + +Other flags are: + +CONFIG_53C700_LE_ON_BE + +define if the chipset must be supported in little endian mode on a big +endian architecture (used for the 700 on parisc). + +CONFIG_53C700_USE_CONSISTENT + +allocate consistent memory (should only be used if your architecture +has a mixture of consistent and inconsistent memory). Fully +consistent or fully inconsistent architectures should not define this. + + +Using the Chip Core Driver +========================== + +In order to plumb the 53c700 chip core driver into a working SCSI +driver, you need to know three things about the way the chip is wired +into your system (or expansion card). + +1. The clock speed of the SCSI core +2. The interrupt line used +3. The memory (or io space) location of the 53c700 registers. + +Optionally, you may also need to know other things, like how to read +the SCSI Id from the card bios or whether the chip is wired for +differential operation. + +Usually you can find items 2. and 3. from general spec. documents or +even by examining the configuration of a working driver under another +operating system. + +The clock speed is usually buried deep in the technical literature. +It is required because it is used to set up both the synchronous and +asynchronous dividers for the chip. As a general rule of thumb, +manufacturers set the clock speed at the lowest possible setting +consistent with the best operation of the chip (although some choose +to drive it off the CPU or bus clock rather than going to the expense +of an extra clock chip). The best operation clock speeds are: + +53c700 - 25MHz +53c700-66 - 50MHz +53c710 - 40Mhz + +Writing Your Glue Driver +======================== + +This will be a standard SCSI driver (I don't know of a good document +describing this, just copy from some other driver) with at least a +detect and release entry. + +In the detect routine, you need to allocate a struct +NCR_700_Host_Parameters sized memory area and clear it (so that the +default values for everything are 0). Then you must fill in the +parameters that matter to you (see below), plumb the NCR_700_intr +routine into the interrupt line and call NCR_700_detect with the host +template and the new parameters as arguments. You should also call +the relevant request_*_region function and place the register base +address into the `base' pointer of the host parameters. + +In the release routine, you must free the NCR_700_Host_Parameters that +you allocated, call the corresponding release_*_region and free the +interrupt. + +Handling Interrupts +------------------- + +In general, you should just plumb the card's interrupt line in with + +request_irq(irq, NCR_700_intr, , , host); + +where host is the return from the relevant NCR_700_detect() routine. + +You may also write your own interrupt handling routine which calls +NCR_700_intr() directly. However, you should only really do this if +you have a card with more than one chip on it and you can read a +register to tell which set of chips wants the interrupt. + +Settable NCR_700_Host_Parameters +-------------------------------- + +The following are a list of the user settable parameters: + +clock: (MANDATORY) + +Set to the clock speed of the chip in MHz. + +base: (MANDATORY) + +set to the base of the io or mem region for the register set. On 64 +bit architectures this is only 32 bits wide, so the registers must be +mapped into the low 32 bits of memory. + +pci_dev: (OPTIONAL) + +set to the PCI board device. Leave NULL for a non-pci board. This is +used for the pci_alloc_consistent() and pci_map_*() functions. + +dmode_extra: (OPTIONAL, 53c710 only) + +extra flags for the DMODE register. These are used to control bus +output pins on the 710. The settings should be a combination of +DMODE_FC1 and DMODE_FC2. What these pins actually do is entirely up +to the board designer. Usually it is safe to ignore this setting. + +differential: (OPTIONAL) + +set to 1 if the chip drives a differential bus. + +force_le_on_be: (OPTIONAL, only if CONFIG_53C700_LE_ON_BE is set) + +set to 1 if the chip is operating in little endian mode on a big +endian architecture. + +chip710: (OPTIONAL) + +set to 1 if the chip is a 53c710. + +burst_disable: (OPTIONAL, 53c710 only) + +disable 8 byte bursting for DMA transfers. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/aha152x.c linux.ac/drivers/scsi/aha152x.c --- linux.vanilla/drivers/scsi/aha152x.c Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/scsi/aha152x.c Thu Oct 11 09:18:21 2001 @@ -390,13 +390,11 @@ #endif /* !defined(AHA152X_DEBUG) */ #ifdef __ISAPNP__ - static struct isapnp_device_id id_table[] __devinitdata = { { ISAPNP_DEVICE_SINGLE('A','D','P',0x1505, 'A','D','P',0x1505), }, { ISAPNP_DEVICE_SINGLE_END, } }; MODULE_DEVICE_TABLE(isapnp, id_table); - #endif /* ISAPNP */ #endif /* MODULE */ @@ -981,28 +979,6 @@ } printk("ok\n"); } -#ifdef __ISAPNP__ - while (setup_count <= 2 && - (dev = isapnp_find_dev (NULL, ISAPNP_VENDOR('A','D','P'), - ISAPNP_FUNCTION(0x1505), dev))) { - if (dev->prepare(dev) < 0) - continue; - if (dev->active) - continue; - if (!(dev->resource[0].flags & IORESOURCE_IO)) - continue; - dev->resource[0].flags |= IORESOURCE_AUTO; - if (dev->activate(dev) < 0) - continue; - setup[setup_count].io_port = dev->resource[0].start; - setup[setup_count].irq = dev->irq_resource[0].start; - pnpdev[num_pnpdevs++] = dev; - printk (KERN_INFO - "aha152x: found ISAPnP AVA-1505A at address 0x%03X, IRQ %d\n", - setup[setup_count].io_port, setup[setup_count].irq); - setup_count++; - } -#endif #if defined(SETUP0) if (setup_count < 2) { @@ -1132,6 +1108,41 @@ setup[setup_count].ext_trans); } #endif + +#ifdef __ISAPNP__ + while ( setup_count<2 && (dev=isapnp_find_dev(NULL, ISAPNP_VENDOR('A','D','P'), ISAPNP_FUNCTION(0x1505), dev)) ) { + if (dev->prepare(dev) < 0) + continue; + if (dev->active) + continue; + if (!(dev->resource[0].flags & IORESOURCE_IO)) + continue; + dev->resource[0].flags |= IORESOURCE_AUTO; + if (dev->activate(dev) < 0) + continue; + if ( setup_count==1 && dev->resource[0].start==setup[0].io_port) { + dev->deactivate(dev); + continue; + } + setup[setup_count].io_port = dev->resource[0].start; + setup[setup_count].irq = dev->irq_resource[0].start; + setup[setup_count].scsiid = 7; + setup[setup_count].reconnect = 1; + setup[setup_count].parity = 1; + setup[setup_count].synchronous = 1; + setup[setup_count].delay = DELAY_DEFAULT; + setup[setup_count].ext_trans = 0; +#if defined(AHA152X_DEBUG) + setup[setup_count].debug = DEBUG_DEFAULT; +#endif + pnpdev[num_pnpdevs++] = dev; + printk (KERN_INFO + "aha152x: found ISAPnP AVA-1505A at io=0x%03x, irq=%d\n", + setup[setup_count].io_port, setup[setup_count].irq); + setup_count++; + } +#endif + #if defined(AUTOCONF) if (setup_count < 2) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/aic7xxx/aic7xxx_linux.c linux.ac/drivers/scsi/aic7xxx/aic7xxx_linux.c --- linux.vanilla/drivers/scsi/aic7xxx/aic7xxx_linux.c Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/scsi/aic7xxx/aic7xxx_linux.c Thu Oct 11 18:02:35 2001 @@ -786,15 +786,17 @@ * address). For this reason, we have to reset * our dma mask when doing allocations. */ + if(ahc->dev_softc) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3) - pci_set_dma_mask(ahc->dev_softc, 0xFFFFFFFF); + pci_set_dma_mask(ahc->dev_softc, 0xFFFFFFFF); #else - ahc->dev_softc->dma_mask = 0xFFFFFFFF; + ahc->dev_softc->dma_mask = 0xFFFFFFFF; #endif *vaddr = pci_alloc_consistent(ahc->dev_softc, dmat->maxsize, &map->bus_addr); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3) - pci_set_dma_mask(ahc->dev_softc, ahc->platform_data->hw_dma_mask); + if(ahc->dev_softc) + pci_set_dma_mask(ahc->dev_softc, ahc->platform_data->hw_dma_mask); #else ahc->dev_softc->dma_mask = ahc->platform_data->hw_dma_mask; #endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/aic7xxx_old.c linux.ac/drivers/scsi/aic7xxx_old.c --- linux.vanilla/drivers/scsi/aic7xxx_old.c Thu Oct 11 13:52:17 2001 +++ linux.ac/drivers/scsi/aic7xxx_old.c Thu Oct 11 16:27:48 2001 @@ -220,11 +220,6 @@ */ #include - -#if defined(PCMCIA) -# undef MODULE -#endif - #include #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/constants.c linux.ac/drivers/scsi/constants.c --- linux.vanilla/drivers/scsi/constants.c Tue Jun 12 19:17:17 2001 +++ linux.ac/drivers/scsi/constants.c Wed Oct 10 01:48:10 2001 @@ -74,6 +74,18 @@ }; +/* The following are 16 byte commands in group 4 */ +static const char *group_4_commands[] = { +/* 80-84 */ unknown, unknown, unknown, unknown, unknown, +/* 85-89 */ "Memory Export In (16)", unknown, unknown, unknown, + "Memory Export Out (16)", +/* 8a-8f */ unknown, unknown, unknown, unknown, unknown, unknown, +/* 90-94 */ unknown, unknown, unknown, unknown, unknown, +/* 95-99 */ unknown, unknown, unknown, unknown, unknown, +/* 9a-9f */ unknown, unknown, unknown, unknown, unknown, unknown, +}; + + /* The following are 12 byte commands in group 5 */ static const char *group_5_commands[] = { /* a0-a5 */ unknown, unknown, unknown, unknown, unknown, @@ -97,7 +109,7 @@ static const char **commands[] = { group_0_commands, group_1_commands, group_2_commands, - (const char **) RESERVED_GROUP, (const char **) RESERVED_GROUP, + (const char **) RESERVED_GROUP, group_4_commands, group_5_commands, (const char **) VENDOR_GROUP, (const char **) VENDOR_GROUP }; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/dpt_i2o.c linux.ac/drivers/scsi/dpt_i2o.c --- linux.vanilla/drivers/scsi/dpt_i2o.c Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/scsi/dpt_i2o.c Thu Oct 11 09:23:30 2001 @@ -1838,7 +1838,7 @@ #endif #if defined __alpha__ -static void adpt_sparc_info(sysInfo_S* si) +static void adpt_alpha_info(sysInfo_S* si) { // This is all the info we need for now // We will add more info as our new @@ -3324,4 +3324,4 @@ static Scsi_Host_Template driver_template = DPT_I2O; #include "scsi_module.c" EXPORT_NO_SYMBOLS; -MODULE_LICENSE("BSD without advertising clause"); +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/gdth.c linux.ac/drivers/scsi/gdth.c --- linux.vanilla/drivers/scsi/gdth.c Thu Oct 11 13:52:11 2001 +++ linux.ac/drivers/scsi/gdth.c Wed Oct 10 01:48:10 2001 @@ -4574,7 +4574,7 @@ Scsi_Device sdev; #endif char cmnd[MAX_COMMAND_SIZE]; - memset(cmnd, 0xff, 12); + memset(cmnd, 0xff, MAX_COMMAND_SIZE); TRACE2(("gdth_flush() hanum %d\n",hanum)); ha = HADATA(gdth_ctr_tab[hanum]); @@ -4652,7 +4652,7 @@ #ifndef __alpha__ /* controller reset */ - memset(cmnd, 0xff, 12); + memset(cmnd, 0xff, MAX_COMMAND_SIZE); #if LINUX_VERSION_CODE >= 0x020322 sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]); scp = scsi_allocate_device(sdev, 1, FALSE); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/i60uscsi.h linux.ac/drivers/scsi/i60uscsi.h --- linux.vanilla/drivers/scsi/i60uscsi.h Sat Mar 3 02:38:38 2001 +++ linux.ac/drivers/scsi/i60uscsi.h Wed Oct 10 22:16:09 2001 @@ -61,6 +61,7 @@ **************************************************************************/ #include +#include #define ULONG unsigned long #define PVOID void * @@ -72,11 +73,7 @@ #define UBYTE unsigned char #define UWORD unsigned short #define UDWORD unsigned long -#ifdef ALPHA -#define U32 unsigned int -#else -#define U32 unsigned long -#endif +#define U32 u32 #ifndef NULL #define NULL 0 /* zero */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/i91uscsi.h linux.ac/drivers/scsi/i91uscsi.h --- linux.vanilla/drivers/scsi/i91uscsi.h Sat Mar 3 02:38:38 2001 +++ linux.ac/drivers/scsi/i91uscsi.h Wed Oct 10 22:16:07 2001 @@ -54,6 +54,7 @@ **************************************************************************/ #include +#include #define ULONG unsigned long #define USHORT unsigned short @@ -64,11 +65,7 @@ #define UBYTE unsigned char #define UWORD unsigned short #define UDWORD unsigned long -#ifdef ALPHA -#define U32 unsigned int -#else -#define U32 unsigned long -#endif +#define U32 u32 #ifndef NULL #define NULL 0 /* zero */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ini9100u.h linux.ac/drivers/scsi/ini9100u.h --- linux.vanilla/drivers/scsi/ini9100u.h Fri Jul 20 05:17:21 2001 +++ linux.ac/drivers/scsi/ini9100u.h Fri Oct 12 12:59:47 2001 @@ -74,6 +74,7 @@ #ifndef LINUX_VERSION_CODE #include #endif +#include #include "sd.h" @@ -121,17 +122,13 @@ #define ULONG unsigned long #define USHORT unsigned short #define UCHAR unsigned char -#define BYTE unsigned char +#define BYTE u8 #define WORD unsigned short #define DWORD unsigned long -#define UBYTE unsigned char +#define UBYTE u8 #define UWORD unsigned short #define UDWORD unsigned long -#ifdef ALPHA -#define U32 unsigned int -#else -#define U32 unsigned long -#endif +#define U32 u32 #ifndef NULL #define NULL 0 /* zero */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/inia100.h linux.ac/drivers/scsi/inia100.h --- linux.vanilla/drivers/scsi/inia100.h Fri Jul 20 05:17:24 2001 +++ linux.ac/drivers/scsi/inia100.h Fri Oct 12 12:59:49 2001 @@ -68,6 +68,8 @@ #include #endif +#include + #include "sd.h" extern int inia100_detect(Scsi_Host_Template *); @@ -122,11 +124,7 @@ #define UBYTE unsigned char #define UWORD unsigned short #define UDWORD unsigned long -#ifdef ALPHA -#define U32 unsigned int -#else -#define U32 unsigned long -#endif +#define U32 u32 #ifndef NULL #define NULL 0 /* zero */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ips.c linux.ac/drivers/scsi/ips.c --- linux.vanilla/drivers/scsi/ips.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/scsi/ips.c Wed Oct 10 01:48:10 2001 @@ -7566,8 +7566,10 @@ return (0); } +#if defined (MODULE) || (LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)) static Scsi_Host_Template driver_template = IPS; #include "scsi_module.c" +#endif /* * Overrides for Emacs so that we almost follow Linus's tabbing style. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pcmcia/Changelog.nsp_cs linux.ac/drivers/scsi/pcmcia/Changelog.nsp_cs --- linux.vanilla/drivers/scsi/pcmcia/Changelog.nsp_cs Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/scsi/pcmcia/Changelog.nsp_cs Wed Oct 10 01:48:11 2001 @@ -0,0 +1,157 @@ +Ver 2.7.2 +========= +Mon Sep 10 14:50:24 JST 2001 + * nsp_cs.c : ż·¤·¤¤ eh Ą¨ĄéˇĽĄĎĄóĄÉĄé¤¬Ć°¤Ż¤č¤¦¤ˤʤ俤¬ + Ą«ˇĽĄÉ¤ň°ú¤­Č´¤Ż¤ČĄŻĄéĄĂĄ·Ąĺ¤ą¤ë¤Î¤Ç¸µ¤ËĚ᤹ˇŁ + * MAINTAINER.nsp_cs : Äɲà + +Fri Sep 7 13:28:45 JST 2001 + * nsp_cs.c : *abort(), *reset() ¤ÎĘÖĂͤ¬´Ö°ă¤Ă¤Ć¤¤¤ż¤Î¤ÇÄľ¤ąˇŁ + +Tue Aug 14 12:50:53 JST 2001 + * *.[ch], COPYING : GPL µ­˝Ň¤Č COPYING ¤ňÄɲà + +Ver 2.7.1 +========= +Thu Aug 9 13:32:43 JST 2001 + * nsp_cs.[ch], nsp_debug.c : ÉÔÍפʥǥХå°ĄŞĄ×Ą·ĄçĄó¤ňŔ°Íý + (Thanks! Kazuhiko ) + +Ver 2.7 +======= +Thu Jul 5 19:56:53 JST 2001 + * nsp_message.c : nsp_message_seq.c ¤«¤éĄŐĄˇĄ¤ĄëĚľĘŃąą + * Configure.help{,.eng} : ĄŐĄˇĄ¤Ąë¤ňĆüËܸě¤Č±Ń¸ě¤Ëʬ¤±¤ż + +Wed Jul 4 23:47:54 JST 2001 + * *.[ch] : ĄáĄĂĄ»ˇĽĄ¸˝čÍý˛ţÎÉ + * *.[ch] : ĄĎˇĽĄÉĄ¦Ą§Ą˘¤ÎĄÇˇĽĄż¤Î»ý¤ÁĘý¤ňĘŃąą + +Ver 2.6.1 +========= +Thu May 31 12:07:39 JST 2001 + * nsp_cs.[ch], nsp_message_seq.c : ĄáĄĂĄ»ˇĽĄ¸˝čÍý¤Îµ­˝Ň¤ňÄɲà + +Ver 2.6 +======= +Mon May 21 14:43:24 JST 2001 + * nsp_cs.c : ĄáĄĂĄ»ˇĽĄ¸Á÷Ľőż®»ţ¤Î ATN Ŕ©¸ćľň·ď¤¬Â­¤ę¤Ę¤«¤Ă¤ż¤Î¤Ç + Äɲà + * nsp_message_seq.c : ĄáĄĂĄ»ˇĽĄ¸˝čÍý¤ňʬΥ¤·¤ĆĘ̤ΥեˇĄ¤Ąë¤Ë + +Ver 2.5 +======= +Thu Feb 15 11:39:49 JST 2001 + * nsp_cs.c : Kernel 2.2 ¤ÇĄ«ˇĽĄÉ¤ňČ´¤­şą¤·¤ą¤ë»ţ¤ËĄŻĄéĄĂĄ·Ąĺ¤ą¤ë¤Î¤Ď + procfs ¤ÎĄ¨ĄóĄČĄę¤Î˝ń¤­Ęý¤¬¤Ţ¤ş¤¤¤«¤é¤Čʬ¤«¤ëˇŁIBMMCA Ą«ˇĽĄÉ¤ÎĄ¨ĄóĄČĄę¤ň + ĽÚÍѤ·¤Ćˇ˘ĄŻĄéĄĂĄ·Ąĺ¤·¤Ę¤¤¤č¤¦¤ËÄľ¤·¤żˇŁ + +Wed Feb 14 23:19:29 JST 2001 + * nsp_cs.c : "Yushi_Okamoto" ¤µ¤ó¤Î¶¨ÎϤˤč¤ę + KXL-820AN ¸ţ¤±ĹŸ»¶ˇµëĄŞĄ×Ą·ĄçĄó¤ňĄĆĄąĄČ¤·¤Ć¤ß¤ëˇŁ¤¤¤í¤¤¤í¤ä¤Ă¤Ć¤ß¤ż¤¬ + ·ë¶ÉÂĚĚܤʻö¤¬Ę¬¤«¤ëˇŁ¤ł¤ě°Ęľĺ¤Îşî¶Č¤Ë¤Ď KXL-820AN ¤Î»ĹÍÍ˝ń¤¬É¬Í×¤Ë + ¤Ę¤ë¤Ŕ¤í¤¦ˇŁĘŃąą¤ĎĽč¤ę»ß¤á¤żˇŁ + +Ver 2.4 +======= +Thu Feb 8 16:25:47 JST 2001 + * nsp_cs.c : ĹľÁ÷Î̤¬ľŻ¤Ę¤«¤Ă¤ż¤ęˇ˘4ĄĐĄ¤ĄČ¶­ł¦¤Ë̵¤¤ľěąç¤Ď + 8ĄÓĄĂĄČĹľÁ÷¤Ë¤ą¤ë + * nsp_cs.c : KXL-820AN ¸ţ¤±ĹŸ»¶ˇµëĄŞĄ×Ą·ĄçĄó "term_power" Äɲà + Ć°¤Ż¤«¤É¤¦¤«ÉÔĚŔ + +Ver 2.3 +======= +Mon Feb 5 08:53:04 JST 2001 + * nsp_cs.c : ÂÔ¤ÁĽő¤±Ą˘ĄëĄ´ĄęĄşĄŕ¤ÎÂÔ¤Áľň·ď¤¬°ě¤ÄÉÔ­¤·¤Ć¤¤¤ż¤Î¤ÇÄɲà + +Ver 2.2 +======= +Wed Jan 31 21:48:38 JST 2001 + * nsp_cs.c : ÂÔ¤ÁĽő¤±Ą˘ĄëĄ´ĄęĄşĄŕ¤ň˛ţÎɤ·¤ĆĄ¦Ą§Ą¤ĄČ¤Ę¤·¤ÇĆ°¤Ż¤č¤¦¤Ë¤ą¤ë + * nsp_cs.c : SCpnt-> resid ¤Î»Č¤¤Ęý¤¬Ę¬¤«¤Ă¤ż + +Ver 2.1 +======= +Tue Jan 30 13:52:59 JST 2001 + * nsp_cs.c : ĹľÁ÷®Ĺ٤¬Áá˛á¤®¤ĆĄ¨ĄéˇĽ¤¬˝Đ¤ë¤Î¤ÇŬĹö¤ËĄ¦Ą§Ą¤ĄČ¤ňĆţ¤ě¤ë + +Ver 2.0 +======= +Thu Jan 25 23:37:00 JST 2001 + * nsp_cs.c : 32bit ñ°Ě¤Ç¤Ę¤¤ľěąç¤ÎĹľÁ÷¤ňµ­˝Ň + +Tue Jan 23 23:18:58 JST 2001 + * nsp_cs.c, nsp_io.h : 8bit PIO ĄâˇĽĄÉ¤«¤é 32bit PIO ĄâˇĽĄÉ¤Ë°ÜąÔˇŁ + ¤ł¤Î·ë˛Ěˇ˘ąâ®ĄÇĄĐĄ¤Ąą»ČÍŃ»ţ¤Ş¤č¤ÓąâÉé˛Ů»ţ¤Î®Ĺ٤¬˛ţÁ±¤µ¤ě¤żˇŁ + +Wed Jan 10 08:56:36 JST 2001 + * nsp_cs.c : ĄâĄ¸ĄĺˇĽĄë¤ÎŔâĚŔ¤ňÄɲà + * nsp_cs.[ch] : ÉÔÍפʽčÍý¤ňşď˝ü + +Ver 1.1.4 +========= +Tue Dec 19 18:02:54 JST 2000 + * nsp_cs.c : SCpnt->resid ¤Î»ČÍŃˡ¤¬´Ö°ă¤Ă¤Ć¤¤¤ż¤Î¤Ç¸µ¤ËĚ᤹ˇŁ + +Ver 1.1.3 +========= +Fri Dec 15 01:58:56 JST 2000 + * nsp_cs.c : ĘŁżô¤ÎĄĐĄĂĄŐĄˇ¤ň¤Ţ¤ż¤¬¤ëĆɤ߹ţ¤ß¤Î»ţ¤ËŔµ¤·¤ŻÁŕşî¤Ç¤­¤Ę¤«¤Ă¤ż + ¤Î¤ňÄľ¤ąˇŁ + +Ver 1.1.2 +========= +Thu Nov 30 18:10:23 JST 2000 + * nsp_cs.c : ĄÇˇĽĄżĆɤ߽ń¤­Ăć¤ÎĄżĄ¤ĄŕĄ˘Ą¦ĄČ˝čÍý¤¬´Ö°ă¤Ă¤Ć¤¤¤ż¤Î¤ňÄľ¤ą + +Ver 1.1.1 +========= +Wed Nov 29 12:08:32 JST 2000 + * Mafeile : ĄżĄ¤Ą×ĄßĄą˝¤Ŕµ + +Ver 1.1 +======= +Mon Nov 27 09:44:46 JST 2000 + * nsp_cs.c : scatterlist ĽÂÁőˇŁ·ăĹŞ¤Ę®Ĺٸţľĺ¤¬¸«ąţ¤Ţ¤ě¤ëˇŁ + cdparanoia ¤âŔµ¤·¤ŻĆ°¤Ż¤č¤¦¤Ę¤Ę¤Ă¤żˇŁ + +Ver 1.0 +======= +Fri Nov 24 06:09:42 JST 2000 + * nsp_cs.c : ĄÉĄéĄ¤ĄĐ¤ň°ě¤«¤éşî¤ęÄľ¤ąˇŁ¤ä¤Ă¤Č´°Ŕ®ˇŁ + +Thu Jun 8 03:10:18 JST 2000 + * nin_cs.c : Kernel 2.4.0-test1 ¤Ç¤Ď¤Ţ¤Č¤â¤ËĆ°¤«¤Ę¤¤»ö¤¬Č˝ĚŔˇŁ + +Fri Jun 2 08:25:30 JST 2000 + * nin_cs.c : cdparanoia Âбţ¤Î·ë˛Ě MO ¤¬Ć°¤«¤Ę¤Ż¤Ę¤Ă¤ż¤Î¤Ç¸µ¤ËĚ᤹ + +Fri Jun 2 04:03:22 JST 2000 + * nin_cs.c : /dev/sg? ĄÇĄĐĄ¤Ąą¤ËÂФą¤ëČż±ţ¤ň˝¤ŔµˇŁ + cdparanoia ¤¬Ć°¤Ż¤č¤¦¤Ë¤Ę¤Ă¤żˇŁ + * nin_cs.c : Kernel 2.3.99 Âбţ (Thanks! suzukis@cmpt.phys.tohoku.ac.jp) + +Sat Apr 29 18:07:52 JST 2000 + * nin_cs.c : ĄÇĄŁĄąĄŻ¤Î¸ň´ą¤ňǧĽ±¤µ¤»¤ë¤č¤¦¤Ë¤Ę¤Ă¤żˇŁ + +Fri Mar 24 11:10:01 JST 2000 + * nin_cs.4, nin_cs.4j : ĄŢĄËĄĺĄ˘ĄëşîŔ® + +Fri Mar 24 10:30:13 JST 2000 + * nin_cs.c, nin_cs.h : Ą«ˇĽĄÉ¤ÎŔßÄꥢĄëĄ´ĄęĄşĄŕ¤ňñ˝ă¤Ë¤ą¤ëˇŁ + I/O Ą˘ĄÉĄěĄą¤ňČϰϤÇÉ˝Ľ¨¤ą¤ë¤č¤¦¤Ë¤ą¤ëˇŁ + +Wed Mar 22 03:13:54 JST 2000 + * nin_cs.c : NinjaSCSI-32Bi 16bit mode ĄµĄÝˇĽĄČ + * nin_cs.c : 32bit PIO, BrindWrite ĄµĄÝˇĽĄČ + * nin_cs.c : Ć°şîĄâˇĽĄÉŔÚÂؤ¨ÍŃ¤ÎĄŞĄ×Ą·ĄçĄóĄµĄÝˇĽĄČ + * nin_cs.c : ĄâĄ¸ĄĺˇĽĄë¤ËËä¤áąţ¤ßľđĘó¤ňĆţ¤ě¤ë + * nin_cs.conf : WorkBit, IO-DATA, KME ¤Î cis ľđĘó¤ňµ­˝Ň + +Sun Jan 9 16:28:17 JST 2000 + * etc/nin_cs.conf : config ĄŐĄˇĄ¤Ąë¤Î include ʸ¤ËÂбţ¤·¤żˇŁ + +Sun Nov 7 23:25:20 JST 1999 + * doc/README.nin_cs* : Ą«ˇĽĄÍĄë¤ÎşĆą˝Ăۤιŕ¤ňÄɲä·¤ĆŔâĚŔʸ¤ň + ¤č¤ęľÜ¤·¤Ż¤·¤żˇŁąŕĚÜ [2] Äɲà [3] ˝¤Ŕµ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pcmcia/README.nsp_cs linux.ac/drivers/scsi/pcmcia/README.nsp_cs --- linux.vanilla/drivers/scsi/pcmcia/README.nsp_cs Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/scsi/pcmcia/README.nsp_cs Wed Oct 10 01:48:11 2001 @@ -0,0 +1,144 @@ + + WorkBiT NinjaSCSI-3/32Bi driver for Linux + +1. ˛ňŔâ + ¤ł¤ě¤Ď WorkbitĽŇ(http://www.workbit.co.jp/) ¤Î NinjaSCSI-3 +(http://www.workbit.co.jp/ts/z_nj3r.html) ¤ä NinjaSCSI-32Bi +(http://www.workbit.co.jp/ts/z_njsc32bi.html) ¤ňĹëşÜ¤·¤ż PCMCIA Ą«ˇĽĄÉ¤ň +Linux ¤Ç»Č¤¦¤ż¤á¤ÎĄÉĄéĄ¤ĄĐĄâĄ¸ĄĺˇĽĄë¤Ç¤ąˇŁ + +2. Ć°şî´Ä¶­ +Linux kernel: 2.4.7 / 2.2.19 +pcmcia-cs: 3.1.27 +gcc: gcc-2.95.4 +PCĄ«ˇĽĄÉ: I-O data PCSC-F (NinjaSCSI-3 ¤¬ĹëşÜ¤µ¤ě¤Ć¤¤¤ëĄ«ˇĽĄÉ) + I-O data CBSC-II (NinjaSCSI-32Bi ¤¬ĹëşÜ¤µ¤ě¤Ć¤¤¤ëĄ«ˇĽĄÉ) +SCSIĄÉĄéĄ¤ĄÖ: I-O data CDPS-PX24 (CD-ROM) + Media Intelligent MMO-640GT (MO) + +3. Ą¤ĄóĄąĄČˇĽĄë +[1] ¤Ţ¤şˇ˘¤˘¤Ę¤ż¤ÎĄ«ˇĽĄÉ¤¬ NinjaSCSI-3 ¤ÎĄ«ˇĽĄÉ¤«łÎ¤«¤á¤Ć˛Ľ¤µ¤¤ˇŁ + pcmcia-cs ¤ňĄ¤ĄóĄąĄČˇĽĄë¤·¤Ć ¤ł¤ÎĄ«ˇĽĄÉ¤ňşą¤·ąţ¤ŕ¤Č pcmcia-cs ¤ĎľÜşŮÉÔĚŔ¤Î + Ą«ˇĽĄÉ¤Č¤·¤Ć "WBT", "NinjaSCSI-3", "R1.0" Ĺů¤Č˛čĚ̤ޤż¤ĎĄíĄ°ĄŐĄˇĄ¤Ąë¤Ë + É˝Ľ¨¤ą¤ë¤Î¤ÇłÎǧ¤·¤Ć˛Ľ¤µ¤¤ˇŁ + pcmcia-cs ¤ÎĄŃĄĂĄ±ˇĽĄ¸¤Ë´Ţ¤Ţ¤ě¤Ć¤¤¤ë cardctl Ą×ĄíĄ°ĄéĄŕ¤ň»Č¤¨¤Đˇ˘ + ¤â¤Ă¤ČľÜ¤·¤¤ľđĘó¤ňÉ˝Ľ¨¤ą¤ë»ö¤â˝ĐÍč¤Ţ¤ąˇŁ + +# cat /var/log/messgaes +... +Jan 2 03:45:06 lindberg cardmgr[78]: unsupported card in socket 1 +Jan 2 03:45:06 lindberg cardmgr[78]: product info: "WBT", "NinjaSCSI-3", "R1.0" +... +# cardctl ident +Socket 0: + no product info available +Socket 1: + product info: "IO DATA", "CBSC16 ", "1" + +[2] ŬĹö¤ĘĄĐˇĽĄ¸ĄçĄó¤Î Linux Ą˝ˇĽĄąĄłˇĽĄÉ¤ňĽę¤ËĆţ¤ěˇ˘ /usr/src ¤ËŸł«¤·¤Ţ¤ąˇŁ + ¤ł¤ě¤ĎĄ«ˇĽĄÍĄë¤ÎĄ˝ˇĽĄąĄłˇĽĄÉ¤Ë´Ţ¤Ţ¤ě¤ë SCSI ´ŘϢ¤ÎĄŘĄĂĄŔĄŐĄˇĄ¤Ąë¤¬É¬Í×¤Ę + ¤ż¤á¤Ç¤ąˇŁ + PCMCIA ĄâĄ¸ĄĺˇĽĄë¤ĎĄ«ˇĽĄÍĄë¤Îą˝Â¤¤ËżĽ¤Ż°Í¸¤·¤Ć¤¤¤ë¤ż¤áˇ˘¸˝şßĆ°¤¤¤Ć¤¤¤ë + Ą«ˇĽĄÍĄë¤ÎĄĐˇĽĄ¸ĄçĄó¤Č°ă¤¦Ą˝ˇĽĄąĄłˇĽĄÉ¤ň»Č¤¦¤Č PCMCIA ĄâĄ¸ĄĺˇĽĄë¤Îą˝ĂŰ¤Ë + ĽşÇÔ¤ą¤ë¤«¤âĂΤě¤Ţ¤»¤óˇŁ¤¤¤¤µˇ˛ń¤Ç¤ą¤Î¤ÇĄ«ˇĽĄÍĄë¤ÎşĆą˝Ăۤʤɤň¤·¤Ć˛Ľ¤µ¤¤ˇŁ + ą˝Ăۤ·¤żĄ«ˇĽĄÍĄë¤ňÍ­¸ú¤Ë¤ą¤ë¤ż¤á¤ËĄęĄÖˇĽĄČ¤â¤ŞËş¤ě¤Ę¤ŻˇŁ +$ cd /usr/src +$ tar -zxvf linux-2.x.x.tar.gz +$ cd linux +$ make config +... + +[3] Kernel 2.2 ¤Ç»ČÍѤą¤ë¤Ę¤éˇ˘ pcmcia-cs ¤ÎĄ˝ˇĽĄąĄłˇĽĄÉ¤ň»ý¤Ă¤Ć¤­¤Ćˇ˘ + ¤˘¤ëĄÇĄŁĄěĄŻĄČĄę¤ËŸł«¤·¤Ćą˝Ăۤȥ¤ĄóĄąĄČˇĽĄë¤ňąÔ¤Ă¤Ć˛Ľ¤µ¤¤ˇŁ + NinjaSCSI ĄÉĄéĄ¤ĄĐ¤Ď¤ł¤Î»ţą˝Ăۤµ¤ě¤ëĄŘĄĂĄŔ¤¬É¬Íפˤʤę¤Ţ¤ąˇŁ +$ cd /usr/src +$ tar -zxvf pcmcia-cs-3.x.x.tar.gz +$ cd pcmcia-cs-3.x.x +$ make config all +$ su +# make install + +[4] ¤ł¤Î NinjaSCSI-3 ¤ÎĄŃĄĂĄ±ˇĽĄ¸¤ň ąĄ¤ß¤ÎĄÇĄŁĄěĄŻĄČĄę¤Ç + Ÿł«¤·¤Ţ¤ąˇŁ Makefile ¤ňĘÔ˝¸¤·¤Ć make ¤·¤Ć˛Ľ¤µ¤¤ˇŁ +$ tar -zxvf nsp_cs-x.x.tar.gz +$ cd nsp_cs-x.x +$ emacs Makefile +... +$ make + +[5] ¤Ç¤­¤˘¤¬¤Ă¤żĄâĄ¸ĄĺˇĽĄë nsp_cs.o ¤ňĄ·ĄąĄĆĄŕ¤Ç»ŘÄꤵ¤ě¤Ć¤¤¤ëĄÇĄŁĄěĄŻĄČĄę¤Ë + ĄłĄÔˇĽ¤·¤Ć˛Ľ¤µ¤¤ˇŁÂçÄń¤Ď /lib/modules/<Ą«ˇĽĄÍĄëĄĐˇĽĄ¸ĄçĄóČÖąć>/pcmcia/ ¤Î + ¤Ď¤ş¤Ç¤ąˇŁĄłĄÔˇĽ¤·¤ż¤é "depmod -a" ĄłĄŢĄóĄÉ¤Ç°Í¸ľđĘó¤âąąż·¤·¤Ć˛Ľ¤µ¤¤ˇŁ + +[6] °Ę˛Ľ¤Î¤č¤¦¤ĘĄ¨ĄóĄČĄę¤ň /etc/pcmcia/config ¤ËÄɲ䷤ޤąˇŁ + pcmcia-cs-3.1.8 °Ęąß¤Ę¤é source ą˝Ę¸¤¬»Č¤¨¤ë¤Î¤Çˇ˘¤˘¤é¤«¤¸¤áÍŃ°Ő¤·¤Ć¤Ş¤¤¤ż + nsp_cs.conf ĄŐĄˇĄ¤Ąë¤ň»Č¤¦¤Î¤Ç¤ł¤Îşî¶Č¤ĎɬÍפ˘¤ę¤Ţ¤»¤óˇŁĂ±˝ă¤Ë nsp_cs.conf + ¤ň /etc/pcmcia/ ¤ËĄłĄÔˇĽ¤·¤Ć˛Ľ¤µ¤¤ˇŁ + +------------------------------------- +device "nsp_cs" + class "scsi" module "nsp_cs" + +card "WorkBit NinjaSCSI-3" + version "WBT", "NinjaSCSI-3", "R1.0" + bind "nsp_cs" + +card "WorkBit NinjaSCSI-32Bi (16bit)" + version "WORKBIT", "UltraNinja-16", "1" + bind "nsp_cs" + +# OEM +card "WorkBit NinjaSCSI-32Bi (16bit) / IO-DATA" + version "IO DATA", "CBSC16 ", "1" + bind "nsp_cs" + +# OEM +card "WorkBit NinjaSCSI-32Bi (16bit) / KME-1" + version "KME ", "SCSI-CARD-001", "1" + bind "nsp_cs" +card "WorkBit NinjaSCSI-32Bi (16bit) / KME-2" + version "KME ", "SCSI-CARD-002", "1" + bind "nsp_cs" +card "WorkBit NinjaSCSI-32Bi (16bit) / KME-3" + version "KME ", "SCSI-CARD-003", "1" + bind "nsp_cs" +card "WorkBit NinjaSCSI-32Bi (16bit) / KME-4" + version "KME ", "SCSI-CARD-004", "1" + bind "nsp_cs" + +------------------------------------- + +[7] pcmcia-cs ¤ňµŻĆ°(şĆµŻĆ°)¤ą¤ě¤ĐĄ«ˇĽĄÉ¤¬»Č¤¨¤ë¤č¤¦¤Ë¤Ę¤ę¤Ţ¤ąˇŁ +# /etc/rc.d/rc.pcmcia start (BSD ·ÁĽ°) +¤Ţ¤ż¤Ď +# /etc/init.d/pcmcia start (SYSV ·ÁĽ°) + + +4. ·ĐÎň + ¤ł¤ÎĄ˝ĄŐĄČ¤Ď *BSD ÍѤΠnsp ĄÉĄéĄ¤ĄĐ¤ň»˛ąÍ¤Ë ˛ŁĹÄ Íµ»× ¤¬ Linux ÍŃ¤Ë +ż·¤ż¤ËşîŔ®¤·¤ż¤â¤Î¤Ç¤ąˇŁ + +5. Ăí°Ő + ¤ł¤ÎĄÉĄéĄ¤ĄĐ¤ň»Č¤Ă¤Ćł°Éô¤ÎSCSIµˇ´ď¤ňÁŕşî¤·¤Ć¤¤¤ë´Ö¤ËĄ«ˇĽĄÉ¤ň°ú¤­Č´¤¤¤ż¤ęˇ˘ +ĄłĄóĄÔĄĺˇĽĄż¤ňĄµĄąĄÚĄóĄÉ¤·¤ż¤ę¤ą¤ë¤ČÍ˝´ü¤·¤Ę¤¤·ë˛Ě¤ň°ú¤­µŻ¤ł¤·¤Ţ¤ąˇŁ +şÇ°­ĄŐĄˇĄ¤ĄëĄ·ĄąĄĆĄŕ¤äĄÇĄŁĄąĄŻ¤¬˛ő¤ě¤Ţ¤ą¤Î¤ÇĂí°Ő¤·¤Ć˛Ľ¤µ¤¤ˇŁ +Ŕµ¤·¤¤Ľę˝ç¤Ç»Č¤¦¸Â¤ę¤Ç¤ĎÂçľćÉפΤ褦¤Ç¤ą¤¬¤˝¤ě¤ňĘÝľÚ¤ą¤ë¤â¤Î¤Ç¤Ď¤˘¤ę¤Ţ¤»¤óˇŁ +ĄÇˇĽĄż¤ÎĘÝÁ´¤ĎłĆĽ«¤ÎŔŐǤ¤ÇąÔ¤Ę¤Ă¤Ć˛Ľ¤µ¤¤ˇŁ + +6. ʬ¤«¤Ă¤Ć¤¤¤ëĄĐĄ° + Ą«ˇĽĄÍĄë 2.4 ¤Ç 640MB MO ¤¬¤¦¤Ţ¤ŻĆ°¤«¤Ę¤¤¤Î¤Ď SCSI ľĺ°ĚĄÉĄéĄ¤ĄĐ¦¤Î + ĚäÂę¤Î¤č¤¦¤Ç¤ąˇŁ + +7. Ć°şîĄĆĄąĄČ + Âľ¤Î´Ä¶­¤Ç¤ÎĆ°şîĘóąđ¤äĄĐĄ°¤ÎĘóąđ¤ň¤·¤Ć˛Ľ¤µ¤ë¤ČÍ­Ćń¤¤¤Ç¤ąˇŁ +¤˝¤Î»ţ¤Ď + »ČÍѤ·¤żĄ«ˇĽĄÉ¤Î·żČÖ + Ą«ˇĽĄÍĄë¤ÎĄĐˇĽĄ¸ĄçĄó + »ČÍѤ·¤ż SCSI ĄÇĄĐĄ¤Ąą¤ÎĽďÎŕ(ĄĎˇĽĄÉĄÇĄŁĄąĄŻ, CD-ROM, etc...) +Ĺů¤â¶µ¤¨¤Ć˛Ľ¤µ¤¤ˇŁ + +8. Ăřşî¸˘ + ¤ł¤ÎĄ˝ĄŐĄČ¤Ď GPL ¤Ë¤č¤ęĘݸ¤ě¤Ć¤¤¤Ţ¤ąˇŁ + + +2001/08/08 yokota@netlab.is.tsukuba.ac.jp <˛ŁĹÄ Íµ»×> diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pcmcia/nsp_cs.c linux.ac/drivers/scsi/pcmcia/nsp_cs.c --- linux.vanilla/drivers/scsi/pcmcia/nsp_cs.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/scsi/pcmcia/nsp_cs.c Wed Oct 10 01:48:11 2001 @@ -23,7 +23,7 @@ ***********************************************************************/ -/* $Id: nsp_cs.c,v 1.35 2001/07/05 16:58:24 elca Exp $ */ +/* $Id: nsp_cs.c,v 1.42 2001/09/10 10:30:58 elca Exp $ */ #ifdef NSP_KERNEL_2_2 #include @@ -66,15 +66,14 @@ MODULE_AUTHOR("YOKOTA Hiroshi "); MODULE_DESCRIPTION("WorkBit NinjaSCSI-3 / NinjaSCSI-32Bi(16bit) PCMCIA SCSI host adapter module"); -MODULE_LICENSE("GPL"); - MODULE_SUPPORTED_DEVICE("sd,sr,sg,st"); +MODULE_LICENSE("GPL"); #ifdef PCMCIA_DEBUG static int pc_debug = PCMCIA_DEBUG; MODULE_PARM(pc_debug, "i"); MODULE_PARM_DESC(pc_debug, "set debug level"); -static char *version = "$Id: nsp_cs.c,v 1.35 2001/07/05 16:58:24 elca Exp $"; +static char *version = "$Id: nsp_cs.c,v 1.42 2001/09/10 10:30:58 elca Exp $"; #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) #else #define DEBUG(n, args...) /* */ @@ -93,18 +92,6 @@ struct bus_operations *bus; } scsi_info_t; -static void nsp_cs_release(u_long arg); -static int nsp_cs_event(event_t event, int priority, - event_callback_args_t *args); -static dev_link_t *nsp_cs_attach(void); -static void nsp_cs_detach(dev_link_t *); -static int nsp_detect(Scsi_Host_Template * ); -static int nsp_release(struct Scsi_Host *shpnt); -static const char * nsp_info(struct Scsi_Host *shpnt); -static int nsp_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); -static int nsp_abort(Scsi_Cmnd *); -static int nsp_reset(Scsi_Cmnd *, unsigned int); - /*----------------------------------------------------------------*/ @@ -191,7 +178,7 @@ data->CurrentSC = NULL; SCpnt->result = DID_BAD_TARGET << 16; done(SCpnt); - return FALSE; + return -1; } show_command(SCpnt); @@ -229,12 +216,12 @@ data->CurrentSC = NULL; SCpnt->result = DID_NO_CONNECT << 16; done(SCpnt); - return FALSE; + return -1; } //DEBUG(0, __FUNCTION__ "() out\n"); - return TRUE; + return 0; } /* @@ -1185,7 +1172,7 @@ return; } -#ifdef DBG_SHOWCOMMAND +#ifdef PCMCIA_DEBUG #include "nsp_debug.c" #endif /* DBG_SHOWCOMMAND */ @@ -1205,13 +1192,13 @@ host->unique_id = data->BaseAddress; host->n_io_port = data->NumAddress; host->irq = data->IrqNumber; - host->dma_channel = -1; + host->dma_channel = 0xff; /* not use dms */ sprintf(nspinfo, /* Buffer size is 100 bytes */ /* 0 1 2 3 4 5 6 7 8 9 0*/ /* 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890*/ - "NinjaSCSI-3/32Bi Driver version 2.7, I/O 0x%04lx-0x%04lx IRQ %2d", + "NinjaSCSI-3/32Bi Driver $Revision: 1.42 $, I/O 0x%04lx-0x%04lx IRQ %2d", host->io_port, host->io_port + host->n_io_port, host->irq); sht->name = nspinfo; @@ -1221,6 +1208,7 @@ return 1; /* detect done. */ } +/* nsp_cs requires own release handler because its uses dev_id (=data) */ static int nsp_release(struct Scsi_Host *shpnt) { nsp_hw_data *data = &nsp_data; @@ -1228,7 +1216,7 @@ if (shpnt->irq) { free_irq(shpnt->irq, data); } - if (shpnt->io_port) { + if (shpnt->io_port && shpnt->n_io_port) { release_region(shpnt->io_port, shpnt->n_io_port); } return 0; @@ -1249,7 +1237,9 @@ { DEBUG(0, __FUNCTION__ " SCpnt=0x%p why=%d\n", SCpnt, why); - return nsp_eh_bus_reset(SCpnt); + nsp_eh_bus_reset(SCpnt); + + return SCSI_RESET_SUCCESS; } static int nsp_abort(Scsi_Cmnd *SCpnt) @@ -1258,7 +1248,7 @@ nsp_eh_bus_reset(SCpnt); - return SUCCESS; + return SCSI_ABORT_SUCCESS; } /*static int nsp_eh_strategy(struct Scsi_Host *Shost) @@ -1271,6 +1261,7 @@ DEBUG(0, __FUNCTION__ " SCpnt=0x%p\n", SCpnt); nsp_eh_bus_reset(SCpnt); + return SUCCESS; } @@ -1309,9 +1300,8 @@ DEBUG(0, __FUNCTION__ "\n"); nsphw_init(data); - nsp_eh_bus_reset(SCpnt); - return SUCCESS; + return nsp_eh_bus_reset(SCpnt); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pcmcia/nsp_cs.h linux.ac/drivers/scsi/pcmcia/nsp_cs.h --- linux.vanilla/drivers/scsi/pcmcia/nsp_cs.h Sun Aug 5 21:12:41 2001 +++ linux.ac/drivers/scsi/pcmcia/nsp_cs.h Wed Oct 10 01:48:11 2001 @@ -6,22 +6,17 @@ Ver 0.1 : Initial version. This software may be used and distributed according to the terms of - the GNU Public License. + the GNU General Public License. =========================================================*/ -/* $Id: nsp_cs.h,v 1.21 2001/07/04 14:45:31 elca Exp $ */ +/* $Id: nsp_cs.h,v 1.27 2001/09/10 10:31:13 elca Exp $ */ #ifndef __nsp_cs__ #define __nsp_cs__ /* for debugging */ -/* -#define DBG -#define DBG_PRINT -#define DBG_SHOWCOMMAND -#define PCMCIA_DEBUG 9 -*/ +/*#define PCMCIA_DEBUG 9*/ /* #define static @@ -258,25 +253,36 @@ } nsp_hw_data; +static void nsp_cs_release(u_long arg); +static int nsp_cs_event(event_t event, int priority, event_callback_args_t *args); +static dev_link_t *nsp_cs_attach(void); +static void nsp_cs_detach(dev_link_t *); + static unsigned int nsphw_start_selection(Scsi_Cmnd *SCpnt, nsp_hw_data *data); static void nsp_start_timer(Scsi_Cmnd *SCpnt, nsp_hw_data *data, int time); +static int nsp_detect(Scsi_Host_Template * ); +static int nsp_release(struct Scsi_Host *shpnt); +static const char * nsp_info(struct Scsi_Host *shpnt); +static int nsp_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); + +static int nsp_abort(Scsi_Cmnd *); +static int nsp_reset(Scsi_Cmnd *, unsigned int); + static int nsp_eh_abort(Scsi_Cmnd * SCpnt); static int nsp_eh_device_reset(Scsi_Cmnd *SCpnt); static int nsp_eh_bus_reset(Scsi_Cmnd *SCpnt); static int nsp_eh_host_reset(Scsi_Cmnd *SCpnt); -static int nsp_fifo_count(Scsi_Cmnd *SCpnt); +static int nsp_fifo_count(Scsi_Cmnd *SCpnt); static void nsp_pio_read(Scsi_Cmnd *SCpnt, nsp_hw_data *data); -static int nsp_nexus(Scsi_Cmnd *SCpnt, nsp_hw_data *data); +static int nsp_nexus(Scsi_Cmnd *SCpnt, nsp_hw_data *data); #ifdef PCMCIA_DEBUG -# ifdef DBG_SHOWCOMMAND static void show_command(Scsi_Cmnd *ptr); static void show_phase(Scsi_Cmnd *SCpnt); static void show_busphase(unsigned char stat); static void show_message(nsp_hw_data *data); -# endif /* DBG_SHOWCOMMAND */ #else # define show_command(ptr) /* */ # define show_phase(SCpnt) /* */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pcmcia/nsp_debug.c linux.ac/drivers/scsi/pcmcia/nsp_debug.c --- linux.vanilla/drivers/scsi/pcmcia/nsp_debug.c Fri Sep 7 17:28:37 2001 +++ linux.ac/drivers/scsi/pcmcia/nsp_debug.c Wed Oct 10 01:48:11 2001 @@ -6,7 +6,7 @@ the GNU General Public License. =========================================================================*/ -/* $Id: nsp_debug.c,v 1.6 2001/07/04 14:43:53 elca Exp $ */ +/* $Id: nsp_debug.c,v 1.8 2001/09/07 04:32:28 elca Exp $ */ /* * Show the command data of a command @@ -85,7 +85,7 @@ } } -void print_commandk (unsigned char *command) +static void print_commandk (unsigned char *command) { int i,s; printk(KERN_DEBUG); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pcmcia/nsp_io.h linux.ac/drivers/scsi/pcmcia/nsp_io.h --- linux.vanilla/drivers/scsi/pcmcia/nsp_io.h Sun Aug 5 21:12:41 2001 +++ linux.ac/drivers/scsi/pcmcia/nsp_io.h Wed Oct 10 01:48:11 2001 @@ -3,11 +3,11 @@ By: YOKOTA Hiroshi This software may be used and distributed according to the terms of - the GNU Public License. + the GNU General Public License. */ -/* $Id: nsp_io.h,v 1.8 2001/01/30 05:16:02 elca Exp $ */ +/* $Id: nsp_io.h,v 1.9 2001/09/07 04:32:42 elca Exp $ */ #ifndef __NSP_IO_H__ #define __NSP_IO_H__ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pcmcia/nsp_message.c linux.ac/drivers/scsi/pcmcia/nsp_message.c --- linux.vanilla/drivers/scsi/pcmcia/nsp_message.c Fri Sep 7 17:28:37 2001 +++ linux.ac/drivers/scsi/pcmcia/nsp_message.c Wed Oct 10 01:48:11 2001 @@ -6,7 +6,7 @@ the GNU General Public License. */ -/* $Id: nsp_message.c,v 1.6 2001/07/05 10:56:37 elca Exp $ */ +/* $Id: nsp_message.c,v 1.7 2001/09/07 04:33:01 elca Exp $ */ static void nsp_message_in(Scsi_Cmnd *SCpnt, nsp_hw_data *data) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi.c linux.ac/drivers/scsi/scsi.c --- linux.vanilla/drivers/scsi/scsi.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/scsi/scsi.c Wed Oct 10 01:48:11 2001 @@ -96,17 +96,25 @@ /* Do not call reset on error if we just did a reset within 15 sec. */ #define MIN_RESET_PERIOD (15*HZ) +/* + * Macro to determine the size of SCSI command. This macro takes vendor + * unique commands into account. SCSI commands in groups 6 and 7 are + * vendor unique and we will depend upon the command length being + * supplied correctly in cmd_len. + */ +#define CDB_SIZE(SCpnt) ((((SCpnt->cmnd[0] >> 5) & 7) < 6) ? \ + COMMAND_SIZE(SCpnt->cmnd[0]) : SCpnt->cmd_len) /* * Data declarations. */ unsigned long scsi_pid; Scsi_Cmnd *last_cmnd; -/* Command groups 3 and 4 are reserved and should never be used. */ +/* Command group 3 is reserved and should never be used. */ const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, - 12, 12, 10, 10 + 16, 12, 10, 10 }; static unsigned long serial_number; static Scsi_Cmnd *scsi_bh_queue_head; @@ -679,18 +687,44 @@ * passes a meaningful return value. */ if (host->hostt->use_new_eh_code) { - spin_lock_irqsave(&io_request_lock, flags); - rtn = host->hostt->queuecommand(SCpnt, scsi_done); - spin_unlock_irqrestore(&io_request_lock, flags); - if (rtn != 0) { - scsi_delete_timer(SCpnt); - scsi_mlqueue_insert(SCpnt, SCSI_MLQUEUE_HOST_BUSY); - SCSI_LOG_MLQUEUE(3, printk("queuecommand : request rejected\n")); + /* + * Before we queue this command, check if the command + * length exceeds what the host adapter can handle. + */ + if (CDB_SIZE(SCpnt) <= SCpnt->host->max_cmd_len) { + spin_lock_irqsave(&io_request_lock, flags); + rtn = host->hostt->queuecommand(SCpnt, scsi_done); + spin_unlock_irqrestore(&io_request_lock, flags); + if (rtn != 0) { + scsi_delete_timer(SCpnt); + scsi_mlqueue_insert(SCpnt, SCSI_MLQUEUE_HOST_BUSY); + SCSI_LOG_MLQUEUE(3, printk("queuecommand : request rejected\n")); + } + } else { + SCSI_LOG_MLQUEUE(3, printk("queuecommand : command too long.\n")); + SCpnt->result = (DID_ABORT << 16); + spin_lock_irqsave(&io_request_lock, flags); + scsi_done(SCpnt); + spin_unlock_irqrestore(&io_request_lock, flags); + rtn = 1; } } else { - spin_lock_irqsave(&io_request_lock, flags); - host->hostt->queuecommand(SCpnt, scsi_old_done); - spin_unlock_irqrestore(&io_request_lock, flags); + /* + * Before we queue this command, check if the command + * length exceeds what the host adapter can handle. + */ + if (CDB_SIZE(SCpnt) <= SCpnt->host->max_cmd_len) { + spin_lock_irqsave(&io_request_lock, flags); + host->hostt->queuecommand(SCpnt, scsi_old_done); + spin_unlock_irqrestore(&io_request_lock, flags); + } else { + SCSI_LOG_MLQUEUE(3, printk("queuecommand : command too long.\n")); + SCpnt->result = (DID_ABORT << 16); + spin_lock_irqsave(&io_request_lock, flags); + scsi_old_done(SCpnt); + spin_unlock_irqrestore(&io_request_lock, flags); + rtn = 1; + } } } else { int temp; @@ -787,14 +821,15 @@ { int i; int target = SDpnt->id; + int size = COMMAND_SIZE(((const unsigned char *)cmnd)[0]); printk("scsi_do_req (host = %d, channel = %d target = %d, " "buffer =%p, bufflen = %d, done = %p, timeout = %d, " "retries = %d)\n" "command : ", host->host_no, SDpnt->channel, target, buffer, bufflen, done, timeout, retries); - for (i = 0; i < 10; ++i) - printk("%02x ", ((unsigned char *) cmnd)[i]); - printk("\n"); + for (i = 0; i < size; ++i) + printk("%02x ", ((unsigned char *) cmnd)[i]); + printk("\n"); }); if (!host) { @@ -976,14 +1011,15 @@ { int i; int target = SCpnt->target; + int size = COMMAND_SIZE(((const unsigned char *)cmnd)[0]); printk("scsi_do_cmd (host = %d, channel = %d target = %d, " "buffer =%p, bufflen = %d, done = %p, timeout = %d, " "retries = %d)\n" "command : ", host->host_no, SCpnt->channel, target, buffer, bufflen, done, timeout, retries); - for (i = 0; i < 10; ++i) - printk("%02x ", ((unsigned char *) cmnd)[i]); - printk("\n"); + for (i = 0; i < size; ++i) + printk("%02x ", ((unsigned char *) cmnd)[i]); + printk("\n"); }); if (!host) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi.h linux.ac/drivers/scsi/scsi.h --- linux.vanilla/drivers/scsi/scsi.h Sun Sep 23 18:33:41 2001 +++ linux.ac/drivers/scsi/scsi.h Fri Oct 12 12:38:17 2001 @@ -351,7 +351,7 @@ #define DRIVER_MASK 0x0f #define SUGGEST_MASK 0xf0 -#define MAX_COMMAND_SIZE 12 +#define MAX_COMMAND_SIZE 16 #define SCSI_SENSE_BUFFERSIZE 64 /* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi_error.c linux.ac/drivers/scsi/scsi_error.c --- linux.vanilla/drivers/scsi/scsi_error.c Sun Sep 9 18:52:35 2001 +++ linux.ac/drivers/scsi/scsi_error.c Wed Oct 10 01:48:11 2001 @@ -1009,13 +1009,7 @@ SCpnt->flags &= ~IS_RESETTING; goto maybe_retry; } - /* - * Examine the sense data to figure out how to proceed from here. - * If there is no sense data, we will be forced into the error - * handler thread, where we get to examine the thing in a lot more - * detail. - */ - return scsi_check_sense(SCpnt); + return SUCCESS; default: return FAILED; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi_lib.c linux.ac/drivers/scsi/scsi_lib.c --- linux.vanilla/drivers/scsi/scsi_lib.c Sun Aug 12 18:51:42 2001 +++ linux.ac/drivers/scsi/scsi_lib.c Wed Oct 10 01:48:11 2001 @@ -655,34 +655,24 @@ } #endif } - if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70) { - /* - * If the device is in the process of becoming ready, - * retry. - */ - if (SCpnt->sense_buffer[12] == 0x04 && - SCpnt->sense_buffer[13] == 0x01) { + if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70 + && (SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) { + if (SCpnt->device->removable) { + /* detected disc change. set a bit and quietly refuse + * further access. + */ + SCpnt->device->changed = 1; + SCpnt = scsi_end_request(SCpnt, 0, this_count); + return; + } else { + /* + * Must have been a power glitch, or a + * bus reset. Could not have been a + * media change, so we just retry the + * request and see what happens. + */ scsi_queue_next_request(q, SCpnt); return; - } - if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) { - if (SCpnt->device->removable) { - /* detected disc change. set a bit - * and quietly refuse further access. - */ - SCpnt->device->changed = 1; - SCpnt = scsi_end_request(SCpnt, 0, this_count); - return; - } else { - /* - * Must have been a power glitch, or a - * bus reset. Could not have been a - * media change, so we just retry the - * request and see what happens. - */ - scsi_queue_next_request(q, SCpnt); - return; - } } } /* If we had an ILLEGAL REQUEST returned, then we may have diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi_scan.c linux.ac/drivers/scsi/scsi_scan.c --- linux.vanilla/drivers/scsi/scsi_scan.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/scsi/scsi_scan.c Thu Oct 11 21:30:38 2001 @@ -421,6 +421,10 @@ max_scsi_luns : shpnt->max_lun); sparse_lun = 0; for (lun = 0, lun0_sl = SCSI_2; lun < max_dev_lun; ++lun) { + /* don't probe further for luns > 7 for targets <= SCSI_2 */ + if ((lun0_sl < SCSI_3) && (lun > 7)) + break; + if (!scan_scsis_single(channel, order_dev, lun, lun0_sl, &max_dev_lun, &sparse_lun, &SDpnt, shpnt, scsi_result) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sd.c linux.ac/drivers/scsi/sd.c --- linux.vanilla/drivers/scsi/sd.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/scsi/sd.c Wed Oct 10 01:48:11 2001 @@ -786,7 +786,7 @@ SRpnt->sr_cmd_len = 0; SRpnt->sr_sense_buffer[0] = 0; SRpnt->sr_sense_buffer[2] = 0; - SRpnt->sr_data_direction = SCSI_DATA_READ; + SRpnt->sr_data_direction = SCSI_DATA_NONE; scsi_wait_req (SRpnt, (void *) cmd, (void *) buffer, 0/*512*/, SD_TIMEOUT, MAX_RETRIES); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sg.c linux.ac/drivers/scsi/sg.c --- linux.vanilla/drivers/scsi/sg.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/scsi/sg.c Wed Oct 10 01:48:12 2001 @@ -1472,6 +1472,8 @@ static void sg_unmap_and(Sg_scatter_hold * schp, int free_also) { + int nbhs = KIO_MAX_SECTORS; + #ifdef SG_ALLOW_DIO_CODE if (schp && schp->kiobp) { if (schp->mapped) { @@ -1479,7 +1481,7 @@ schp->mapped = 0; } if (free_also) { - free_kiovec(1, &schp->kiobp); + free_kiovec_sz(1, &schp->kiobp, &nbhs); schp->kiobp = NULL; } } @@ -1498,7 +1500,9 @@ Sg_scatter_hold * schp = &srp->data; int sg_tablesize = sfp->parentdp->sg_tablesize; - res = alloc_kiovec(1, &schp->kiobp); + int nbhs = KIO_MAX_SECTORS; + + res = alloc_kiovec_sz(1, &schp->kiobp, &nbhs); if (0 != res) { SCSI_LOG_TIMEOUT(5, printk("sg_build_dir: alloc_kiovec res=%d\n", res)); return 1; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sgi/char/graphics.c linux.ac/drivers/sgi/char/graphics.c --- linux.vanilla/drivers/sgi/char/graphics.c Mon Aug 27 16:56:31 2001 +++ linux.ac/drivers/sgi/char/graphics.c Wed Oct 10 01:48:12 2001 @@ -343,6 +343,8 @@ } #ifdef MODULE +MODULE_LICENSE("GPL"); + int init_module(void) { static int initiated = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/724hwmcode.h linux.ac/drivers/sound/724hwmcode.h --- linux.vanilla/drivers/sound/724hwmcode.h Sun Nov 12 02:33:13 2000 +++ linux.ac/drivers/sound/724hwmcode.h Wed Oct 10 01:48:12 2001 @@ -9,7 +9,7 @@ #ifndef _HWMCODE_ #define _HWMCODE_ -static unsigned long int DspInst[] __initdata = { +static unsigned long int DspInst[] = { 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, @@ -20,7 +20,7 @@ 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; -static unsigned long int CntrlInst[] __initdata = { +static unsigned long int CntrlInst[] = { 0x000007, 0x240007, 0x0C0007, 0x1C0007, 0x060007, 0x700002, 0x000020, 0x030040, 0x007104, 0x004286, 0x030040, 0x000F0D, @@ -799,7 +799,7 @@ // 04/09?@creat // 04/12 stop nise fix // 06/21?@WorkingOff timming -static unsigned long int CntrlInst1E[] __initdata = { +static unsigned long int CntrlInst1E[] = { 0x000007, 0x240007, 0x0C0007, 0x1C0007, 0x060007, 0x700002, 0x000020, 0x030040, 0x007104, 0x004286, 0x030040, 0x000F0D, diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/aci.c linux.ac/drivers/sound/aci.c --- linux.vanilla/drivers/sound/aci.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/sound/aci.c Wed Oct 10 01:48:12 2001 @@ -47,6 +47,10 @@ * changed param aci_reset to reset, new params: ide, wss. * 2001-04-20 Robert Siemer * even more cleanups... + * 2001-10-08 Arnaldo Carvalho de Melo + * Get rid of check_region, release_region when attach_aci fails, + * .bss tidbits, don't use __{put,get}_user, use {put,get}_user and + * check its results. */ #include @@ -70,9 +74,9 @@ #include "aci.h" -static int aci_solo=0; /* status bit of the card that can't be * +static int aci_solo; /* status bit of the card that can't be * * checked with ACI versions prior to 0xb0 */ -static int aci_amp=0; /* status bit for power-amp/line-out level +static int aci_amp; /* status bit for power-amp/line-out level but I have no docs about what is what... */ static int aci_micpreamp=3; /* microphone preamp-level that can't be * * checked with ACI versions prior to 0xb0 */ @@ -81,7 +85,7 @@ static struct semaphore aci_sem; #ifdef MODULE -static int reset = 0; +static int reset; MODULE_PARM(reset,"i"); MODULE_PARM_DESC(reset,"When set to 1, reset aci mixer."); #else @@ -146,7 +150,7 @@ case 20 ... 30: out /= 10; default: - current->state=TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(out); break; } @@ -209,28 +213,25 @@ int aci_rw_cmd(int write1, int write2, int write3) { int write[] = {write1, write2, write3}; - int read, i; + int read = -EINTR, i; if (down_interruptible(&aci_sem)) - return -EINTR; + goto out; for (i=0; i<3; i++) { if (write[i]< 0 || write[i] > 255) break; - else - if (aci_rawwrite(write[i])<0) { - up(&aci_sem); - return -EBUSY; - } + else { + read = aci_rawwrite(write[i]); + if (read < 0) + goto out_up; + } + } - if ((read=aci_rawread())<0) { - up(&aci_sem); - return -EBUSY; - } - - up(&aci_sem); - return read; + read = aci_rawread(); +out_up: up(&aci_sem); +out: return read; } EXPORT_SYMBOL(aci_rw_cmd); @@ -240,7 +241,8 @@ { int vol, ret, uservol, buf; - __get_user(uservol, (int *)arg); + if (get_user(uservol, (int *)arg)) + return -EFAULT; /* left channel */ vol = uservol & 0xff; @@ -261,9 +263,7 @@ return buf; ret |= SCALE(0x20, 100, vol) << 8; - __put_user(ret, (int *)arg); - - return 0; + return put_user(ret, (int *)arg); } static int getvolume(caddr_t arg, @@ -282,9 +282,7 @@ return buf; vol |= SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0) << 8; - __put_user(vol, (int *)arg); - - return 0; + return put_user(vol, (int *)arg); } @@ -325,7 +323,8 @@ int buf; unsigned int vol; - __get_user(vol, (int *)arg); + if (get_user(vol, (int *)arg)) + return -EFAULT; /* left channel */ if ((buf=aci_write_cmd(left_index, eq_oss2aci(vol & 0xff)))<0) @@ -355,9 +354,7 @@ return buf; vol |= eq_aci2oss(buf) << 8; - __put_user(vol, (int *)arg); - - return 0; + return put_user(vol, (int *)arg); } static int aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) @@ -398,7 +395,8 @@ break; case SOUND_MIXER_WRITE_IGAIN: /* MIC pre-amp */ if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { - __get_user(vol, (int *)arg); + if (get_user(vol, (int *)arg)) + return -EFAULT; vol = vol & 0xff; if (vol > 100) vol = 100; @@ -408,13 +406,13 @@ aci_micpreamp = vol; vol = SCALE(3, 100, vol); vol |= (vol << 8); - __put_user(vol, (int *)arg); - return 0; + return put_user(vol, (int *)arg); } break; case SOUND_MIXER_WRITE_OGAIN: /* Power-amp/line-out level */ if (aci_idcode[1]=='A' || aci_idcode[1]=='B') { - __get_user(buf, (int *)arg); + if (get_user(buf, (int *)arg)) + return -EFAULT; buf = buf & 0xff; if (buf > 50) vol = 1; @@ -427,13 +425,13 @@ buf = (100 || 100<<8); else buf = 0; - __put_user(buf, (int *)arg); - return 0; + return put_user(buf, (int *)arg); } break; case SOUND_MIXER_WRITE_RECSRC: /* handle solo mode control */ - __get_user(buf, (int *)arg); + if (get_user(buf, (int *)arg)) + return -EFAULT; /* unset solo when RECSRC for PCM is requested */ if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { vol = !(buf & SOUND_MASK_PCM); @@ -449,8 +447,7 @@ buf |= SOUND_MASK_LINE1; if (!aci_solo) buf |= SOUND_MASK_PCM; - __put_user(buf, (int *)arg); - return 0; + return put_user(buf, (int *)arg); case SOUND_MIXER_READ_DEVMASK: buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE | @@ -471,8 +468,7 @@ default: buf |= SOUND_MASK_LINE1; } - __put_user(buf, (int *)arg); - return 0; + return put_user(buf, (int *)arg); case SOUND_MIXER_READ_STEREODEVS: buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE | @@ -486,8 +482,7 @@ default: buf |= SOUND_MASK_LINE1; } - __put_user(buf, (int *)arg); - return 0; + return put_user(buf, (int *)arg); case SOUND_MIXER_READ_RECMASK: buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE| SOUND_MASK_SYNTH| SOUND_MASK_LINE2| SOUND_MASK_PCM); @@ -496,8 +491,7 @@ else buf |= SOUND_MASK_LINE1; - __put_user(buf, (int *)arg); - return 0; + return put_user(buf, (int *)arg); case SOUND_MIXER_READ_RECSRC: buf = (SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE | SOUND_MASK_SYNTH | SOUND_MASK_LINE2); @@ -524,11 +518,9 @@ else buf |= SOUND_MASK_LINE1; - __put_user(buf, (int *)arg); - return 0; + return put_user(buf, (int *)arg); case SOUND_MIXER_READ_CAPS: - __put_user(0, (int *)arg); - return 0; + return put_user(0, (int *)arg); case SOUND_MIXER_READ_VOLUME: return getvolume(arg, 0x04, 0x03); case SOUND_MIXER_READ_CD: @@ -568,8 +560,7 @@ buf=aci_micpreamp; vol = SCALE(3, 100, buf <= 3 ? buf : 3); vol |= vol << 8; - __put_user(vol, (int *)arg); - return 0; + return put_user(vol, (int *)arg); } break; case SOUND_MIXER_READ_OGAIN: @@ -577,8 +568,7 @@ buf = (100 || 100<<8); else buf = 0; - __put_user(buf, (int *)arg); - return 0; + return put_user(buf, (int *)arg); } return -EINVAL; } @@ -602,7 +592,7 @@ static int __init attach_aci(void) { char *boardname; - int i; + int i, rc = -EBUSY; init_MUTEX(&aci_sem); @@ -610,27 +600,32 @@ aci_port = (inb(0xf90) & 0x10) ? 0x344: 0x354; /* Get aci_port from MC4_PORT */ - if (check_region(aci_port, 3)) { - printk(KERN_NOTICE "aci: I/O area 0x%03x-0x%03x already used.\n", + if (!request_region(aci_port, 3, "sound mixer (ACI)")) { + printk(KERN_NOTICE + "aci: I/O area 0x%03x-0x%03x already used.\n", aci_port, aci_port+2); - return -EBUSY; + goto out; } /* force ACI into a known state */ + rc = -EFAULT; for (i=0; i<3; i++) if (aci_rw_cmd(ACI_ERROR_OP, -1, -1)<0) - return -EFAULT; + goto out_release_region; /* official this is one aci read call: */ + rc = -EFAULT; if ((aci_idcode[0]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0 || (aci_idcode[1]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0) { - printk(KERN_ERR "aci: Failed to read idcode on 0x%03x.\n", aci_port); - return -EFAULT; + printk(KERN_ERR "aci: Failed to read idcode on 0x%03x.\n", + aci_port); + goto out_release_region; } if ((aci_version=aci_rw_cmd(ACI_READ_VERSION, -1, -1))<0) { - printk(KERN_ERR "aci: Failed to read version on 0x%03x.\n", aci_port); - return -EFAULT; + printk(KERN_ERR "aci: Failed to read version on 0x%03x.\n", + aci_port); + goto out_release_region; } if (aci_idcode[0] == 'm') { @@ -660,42 +655,40 @@ aci_idcode[0], aci_idcode[1], boardname, aci_port); + rc = -EBUSY; if (reset) { /* first write()s after reset fail with my PCM20 */ if (aci_rw_cmd(ACI_INIT, -1, -1)<0 || aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0 || aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0) - return -EBUSY; + goto out_release_region; } /* the PCM20 is muted after reset (and reboot) */ if (aci_rw_cmd(ACI_SET_MUTE, 0x00, -1)<0) - return -EBUSY; + goto out_release_region; if (ide>=0) if (aci_rw_cmd(ACI_SET_IDE, !ide, -1)<0) - return -EBUSY; + goto out_release_region; if (wss>=0 && aci_idcode[1]=='A') if (aci_rw_cmd(ACI_SET_WSS, !!wss, -1)<0) - return -EBUSY; - - if (!request_region(aci_port, 3, "sound mixer (ACI)")) - return -ENOMEM; + goto out_release_region; - if ((mixer_device = sound_install_mixer(MIXER_DRIVER_VERSION, - boardname, - &aci_mixer_operations, - sizeof(aci_mixer_operations), - NULL)) >= 0) { - /* Maybe initialize the CS4231A mixer here... */ - } else { + mixer_device = sound_install_mixer(MIXER_DRIVER_VERSION, boardname, + &aci_mixer_operations, + sizeof(aci_mixer_operations), NULL); + rc = 0; + if (mixer_device < 0) { printk(KERN_ERR "aci: Failed to install mixer.\n"); - release_region(aci_port, 3); - return mixer_device; - } - - return 0; + rc = mixer_device; + goto out_release_region; + } /* else Maybe initialize the CS4231A mixer here... */ +out: return rc; +out_release_region: + release_region(aci_port, 3); + goto out; } static void __exit unload_aci(void) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/ad1816.c linux.ac/drivers/sound/ad1816.c --- linux.vanilla/drivers/sound/ad1816.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/sound/ad1816.c Thu Oct 11 09:09:20 2001 @@ -28,6 +28,8 @@ * Christoph Hellwig: Adapted to module_init/module_exit. 2000/03/03 * * Christoph Hellwig: Added isapnp support 2000/03/15 + * + * Arnaldo Carvalho de Melo: get rid of check_region 2001/10/07 */ #include @@ -48,7 +50,7 @@ timeout--; \ } \ if (timeout==0) {\ - printk("ad1816: Check for power failed in %s line: %d\n",__FILE__,__LINE__); \ + printk(KERN_WARNING "ad1816: Check for power failed in %s line: %d\n",__FILE__,__LINE__); \ } \ } @@ -78,9 +80,9 @@ } ad1816_info; -static int nr_ad1816_devs = 0; -static int ad1816_clockfreq=33000; -static int options=0; +static int nr_ad1816_devs; +static int ad1816_clockfreq = 33000; +static int options; /* for backward mapping of irq to sound device */ @@ -558,14 +560,15 @@ if (irq < 0 || irq > 15) { - printk ("ad1816: Got bogus interrupt %d\n", irq); + printk(KERN_WARNING "ad1816: Got bogus interrupt %d\n", irq); return; } dev = irq2dev[irq]; if (dev < 0 || dev >= num_audiodevs) { - printk ("ad1816: IRQ2AD1816-mapping failed for irq %d device %d\n", irq,dev); + printk(KERN_WARNING "ad1816: IRQ2AD1816-mapping failed for " + "irq %d device %d\n", irq,dev); return; } @@ -1000,8 +1003,10 @@ int *osp=hw_config->osp; int tmp; - printk("ad1816: AD1816 sounddriver Copyright (C) 1998 by Thorsten Knabe\n"); - printk("ad1816: io=0x%x, irq=%d, dma=%d, dma2=%d, clockfreq=%d, options=%d isadmabug=%d\n", + printk(KERN_INFO "ad1816: AD1816 sounddriver " + "Copyright (C) 1998 by Thorsten Knabe\n"); + printk(KERN_INFO "ad1816: io=0x%x, irq=%d, dma=%d, dma2=%d, " + "clockfreq=%d, options=%d isadmabug=%d\n", hw_config->io_base, hw_config->irq, hw_config->dma, @@ -1010,16 +1015,17 @@ options, isa_dma_bridge_buggy); - if (check_region (io_base, 16)) { - printk ("ad1816: I/O port 0x%03x not free\n", io_base); - return 0; + if (!request_region(io_base, 16, "AD1816 Sound")) { + printk(KERN_WARNING "ad1816: I/O port 0x%03x not free\n", + io_base); + goto err; } DEBUGLOG(printk ("ad1816: detect(%x)\n", io_base)); if (nr_ad1816_devs >= MAX_AUDIO_DEV) { - printk ("ad1816: detect error - step 0\n"); - return 0; + printk(KERN_WARNING "ad1816: detect error - step 0\n"); + goto out_release_region; } devc->base = io_base; @@ -1032,7 +1038,7 @@ tmp=inb(devc->base); if ( (tmp&0x80)==0 || tmp==255 ) { DEBUGLOG (printk ("ad1816: Chip is not an AD1816 or chip is not active (Test 0)\n")); - return(0); + goto out_release_region; } @@ -1040,14 +1046,14 @@ ad_write(devc,8,12345); if (ad_read(devc,9)!=12345) { DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 1)\n")); - return(0); + goto out_release_region; } /* writes to ireg 8 are copied to ireg 9 */ ad_write(devc,8,54321); if (ad_read(devc,9)!=54321) { DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 2)\n")); - return(0); + goto out_release_region; } @@ -1055,14 +1061,14 @@ ad_write(devc,10,54321); if (ad_read(devc,11)!=54321) { DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 3)\n")); - return(0); + goto out_release_region; } /* writes to ireg 10 are copied to ireg 11 */ ad_write(devc,10,12345); if (ad_read(devc,11)!=12345) { DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 4)\n")); - return(0); + goto out_release_region; } /* bit in base +1 cannot be set to 1 */ @@ -1070,15 +1076,19 @@ outb(0xff,devc->base+1); if (inb(devc->base+1)!=tmp) { DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 5)\n")); - return(0); + goto out_release_region; } DEBUGLOG (printk ("ad1816: detect() - Detected OK\n")); DEBUGLOG (printk ("ad1816: AD1816 Version: %d\n",ad_read(devc,45))); - /* detection was successful */ + /* detection was successful */ return 1; +out_release_region: + release_region(io_base, 16); + /* detection was NOT successful */ +err: return 0; } @@ -1092,10 +1102,7 @@ int my_dev; char dev_name[100]; ad1816_info *devc = &dev_info[nr_ad1816_devs]; - - /* allocate i/o ports */ - request_region (hw_config->io_base, 16, "AD1816 Sound"); devc->base = hw_config->io_base; /* disable all interrupts */ @@ -1105,35 +1112,29 @@ outb (0, devc->base+1); /* allocate irq */ - if (hw_config->irq < 0 || hw_config->irq > 15) { - release_region(hw_config->io_base, 16); - return; - } + if (hw_config->irq < 0 || hw_config->irq > 15) + goto out_release_region; if (request_irq(hw_config->irq, ad1816_interrupt,0, - "SoundPort", - hw_config->osp) < 0) { - printk ("ad1816: IRQ in use\n"); - release_region(hw_config->io_base, 16); - return; + "SoundPort", hw_config->osp) < 0) { + printk(KERN_WARNING "ad1816: IRQ in use\n"); + goto out_release_region; } devc->irq=hw_config->irq; /* DMA stuff */ if (sound_alloc_dma (hw_config->dma, "Sound System")) { - printk ("ad1816: Can't allocate DMA%d\n", hw_config->dma); - free_irq(hw_config->irq,hw_config->osp); - release_region(hw_config->io_base, 16); - return; + printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n", + hw_config->dma); + goto out_free_irq; } devc->dma_playback=hw_config->dma; if ( hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) { - if (sound_alloc_dma (hw_config->dma2, "Sound System (capture)")) { - printk ("ad1816: Can't allocate DMA%d\n", hw_config->dma2); - sound_free_dma(hw_config->dma); - free_irq(hw_config->irq,hw_config->osp); - release_region(hw_config->io_base, 16); - return; + if (sound_alloc_dma(hw_config->dma2, + "Sound System (capture)")) { + printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n", + hw_config->dma2); + goto out_free_dma; } devc->dma_capture=hw_config->dma2; devc->audio_mode=DMA_AUTOMODE|DMA_DUPLEX; @@ -1157,15 +1158,8 @@ devc, hw_config->dma, hw_config->dma2)) < 0) { - printk ("ad1816: Can't install sound driver\n"); - if (devc->dma_capture>=0) { - sound_free_dma(hw_config->dma2); - } - sound_free_dma(hw_config->dma); - free_irq(hw_config->irq,hw_config->osp); - release_region(hw_config->io_base, 16); - return; - + printk(KERN_WARNING "ad1816: Can't install sound driver\n"); + goto out_free_dma_2; } /* fill rest of structure with reasonable default values */ @@ -1211,6 +1205,17 @@ devc)) >= 0) { audio_devs[my_dev]->min_fragment = 0; } +out: return; +out_free_dma_2: + if (devc->dma_capture >= 0) + sound_free_dma(hw_config->dma2); +out_free_dma: + sound_free_dma(hw_config->dma); +out_free_irq: + free_irq(hw_config->irq,hw_config->osp); +out_release_region: + release_region(hw_config->io_base, 16); + goto out; } static void __exit unload_card(ad1816_info *devc) @@ -1242,9 +1247,8 @@ DEBUGLOG (printk("ad1816: Unloading card at base=%x was successful\n",devc->base)); - } else { - printk ("ad1816: no device/card specified\n"); - } + } else + printk(KERN_WARNING "ad1816: no device/card specified\n"); } static struct address_info cfg; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/cmpci.c linux.ac/drivers/sound/cmpci.c --- linux.vanilla/drivers/sound/cmpci.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/sound/cmpci.c Wed Oct 10 01:48:14 2001 @@ -74,6 +74,7 @@ * 18/05/2001 - .bss nitpicks, fix a bug in set_dac_channels where it * was calling prog_dmabuf with s->lock held, call missing * unlock_kernel in cm_midi_release + * 08/10/2001 - use set_current_state in some more places * * Carlos Eduardo Gorges * Fri May 25 2001 @@ -1483,7 +1484,7 @@ if (s->dma_dac.mapped || !s->dma_dac.ready) return 0; - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&s->dma_dac.wait, &wait); for (;;) { spin_lock_irqsave(&s->lock, flags); @@ -1495,7 +1496,7 @@ break; if (nonblock) { remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; @@ -1504,7 +1505,7 @@ printk(KERN_DEBUG "cmpci: dma timed out??\n"); } remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; return 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/i810_audio.c linux.ac/drivers/sound/i810_audio.c --- linux.vanilla/drivers/sound/i810_audio.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/sound/i810_audio.c Thu Oct 11 09:44:12 2001 @@ -247,6 +247,12 @@ MODULE_DEVICE_TABLE (pci, i810_pci_tbl); +#ifdef CONFIG_PM +#define PM_SUSPENDED(card) (card->pm_suspended) +#else +#define PM_SUSPENDED(card) (0) +#endif + /* "software" or virtual channel, an instance of opened /dev/dsp */ struct i810_state { unsigned int magic; @@ -262,6 +268,9 @@ /* virtual channel number */ int virt; +#ifdef CONFIG_PM + unsigned int pm_saved_dac_rate,pm_saved_adc_rate; +#endif struct dmabuf { /* wave sample stuff */ unsigned int rate; @@ -322,7 +331,11 @@ /* PCI device stuff */ struct pci_dev * pci_dev; u16 pci_id; - +#ifdef CONFIG_PM + u16 pm_suspended; + u32 pm_save_state[64/sizeof(u32)]; + int pm_saved_mixer_settings[SOUND_MIXER_NRDEVICES][NR_AC97]; +#endif /* soundcore stuff */ int dev_audio; @@ -451,7 +464,7 @@ if(!(state->card->ac97_features & 4)) { #ifdef DEBUG - printk(KERN_WARNING "i810_audio: S/PDIF transmitter not avalible.\n"); + printk(KERN_WARNING "i810_audio: S/PDIF transmitter not available.\n"); #endif state->card->ac97_status &= ~SPDIF_ON; } else { @@ -572,6 +585,10 @@ if(!(state->card->ac97_features&0x0001)) { dmabuf->rate = clocking; +#ifdef DEBUG + printk("Asked for %d Hz, but ac97_features says we only do %dHz. Sorry!\n", + rate,clocking); +#endif return clocking; } @@ -594,11 +611,11 @@ if(new_rate != rate) { dmabuf->rate = (new_rate * 48000)/clocking; - rate = new_rate; } #ifdef DEBUG - printk("i810_audio: called i810_set_dac_rate : rate = %d/%d\n", dmabuf->rate, rate); + printk("i810_audio: called i810_set_dac_rate : asked for %d, got %d\n", rate, dmabuf->rate); #endif + rate = new_rate; return dmabuf->rate; } @@ -1066,7 +1083,7 @@ for (;;) { /* It seems that we have to set the current state to TASK_INTERRUPTIBLE every time to make the process really go to sleep */ - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&state->card->lock, flags); i810_update_ptr(state); @@ -1085,7 +1102,7 @@ if (nonblock) { remove_wait_queue(&dmabuf->wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } @@ -1099,7 +1116,7 @@ stop_dac(state); synchronize_irq(); remove_wait_queue(&dmabuf->wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; @@ -1201,16 +1218,20 @@ spin_unlock(&card->lock); } -/* in this loop, dmabuf.count signifies the amount of data that is waiting to be copied to - the user's buffer. it is filled by the dma machine and drained by this loop. */ +/* in this loop, dmabuf.count signifies the amount of data that is + waiting to be copied to the user's buffer. It is filled by the dma + machine and drained by this loop. */ + static ssize_t i810_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct i810_state *state = (struct i810_state *)file->private_data; + struct i810_card *card=state ? state->card : 0; struct dmabuf *dmabuf = &state->dmabuf; ssize_t ret; unsigned long flags; unsigned int swptr; int cnt; + DECLARE_WAITQUEUE(waita, current); #ifdef DEBUG2 printk("i810_audio: i810_read called, count = %d\n", count); @@ -1224,7 +1245,7 @@ return -ENODEV; if (!dmabuf->read_channel) { dmabuf->ready = 0; - dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card); + dmabuf->read_channel = card->alloc_rec_pcm_channel(card); if (!dmabuf->read_channel) { return -EBUSY; } @@ -1236,8 +1257,19 @@ dmabuf->trigger &= ~PCM_ENABLE_OUTPUT; ret = 0; + add_wait_queue(&dmabuf->wait, &waita); while (count > 0) { - spin_lock_irqsave(&state->card->lock, flags); + spin_lock_irqsave(&card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + if (!ret) ret = -EAGAIN; + break; + } + continue; + } swptr = dmabuf->swptr; if (dmabuf->count > dmabuf->dmasize) { dmabuf->count = dmabuf->dmasize; @@ -1246,7 +1278,7 @@ // this is to make the copy_to_user simpler below if(cnt > (dmabuf->dmasize - swptr)) cnt = dmabuf->dmasize - swptr; - spin_unlock_irqrestore(&state->card->lock, flags); + spin_unlock_irqrestore(&card->lock, flags); if (cnt > count) cnt = count; @@ -1290,15 +1322,20 @@ if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { if (!ret) ret = -EFAULT; - return ret; + goto done; } swptr = (swptr + cnt) % dmabuf->dmasize; - spin_lock_irqsave(&state->card->lock, flags); + spin_lock_irqsave(&card->lock, flags); + + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + continue; + } dmabuf->swptr = swptr; dmabuf->count -= cnt; - spin_unlock_irqrestore(&state->card->lock, flags); + spin_unlock_irqrestore(&card->lock, flags); count -= cnt; buffer += cnt; @@ -1307,6 +1344,10 @@ i810_update_lvi(state,1); if(!(dmabuf->enable & ADC_RUNNING)) start_adc(state); + done: + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + return ret; } @@ -1315,11 +1356,13 @@ static ssize_t i810_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct i810_state *state = (struct i810_state *)file->private_data; + struct i810_card *card=state ? state->card : 0; struct dmabuf *dmabuf = &state->dmabuf; ssize_t ret; unsigned long flags; unsigned int swptr = 0; int cnt, x; + DECLARE_WAITQUEUE(waita, current); #ifdef DEBUG2 printk("i810_audio: i810_write called, count = %d\n", count); @@ -1333,7 +1376,7 @@ return -ENODEV; if (!dmabuf->write_channel) { dmabuf->ready = 0; - dmabuf->write_channel = state->card->alloc_pcm_channel(state->card); + dmabuf->write_channel = card->alloc_pcm_channel(card); if(!dmabuf->write_channel) return -EBUSY; } @@ -1344,8 +1387,20 @@ dmabuf->trigger &= ~PCM_ENABLE_INPUT; ret = 0; + add_wait_queue(&dmabuf->wait, &waita); while (count > 0) { spin_lock_irqsave(&state->card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + if (!ret) ret = -EAGAIN; + break; + } + continue; + } + swptr = dmabuf->swptr; if (dmabuf->count < 0) { dmabuf->count = 0; @@ -1376,7 +1431,7 @@ i810_update_lvi(state,0); if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; - return ret; + goto ret; } /* Not strictly correct but works */ tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 4); @@ -1400,25 +1455,30 @@ } if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; - return ret; + goto ret; } continue; } if (copy_from_user(dmabuf->rawbuf+swptr,buffer,cnt)) { if (!ret) ret = -EFAULT; - return ret; + goto ret; } swptr = (swptr + cnt) % dmabuf->dmasize; spin_lock_irqsave(&state->card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + continue; + } + dmabuf->swptr = swptr; dmabuf->count += cnt; - spin_unlock_irqrestore(&state->card->lock, flags); count -= cnt; buffer += cnt; ret += cnt; + spin_unlock_irqrestore(&state->card->lock, flags); } if (swptr % dmabuf->fragsize) { x = dmabuf->fragsize - (swptr % dmabuf->fragsize); @@ -1427,6 +1487,9 @@ i810_update_lvi(state,0); if (!dmabuf->enable && dmabuf->count >= dmabuf->userfragsize) start_dac(state); + ret: + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); return ret; } @@ -2310,18 +2373,52 @@ open: i810_open_mixdev, }; -/* AC97 codec initialisation. */ -static int __init i810_ac97_init(struct i810_card *card) +/* AC97 codec initialisation. These small functions exist so we don't + duplicate code between module init and apm resume */ + +static inline int i810_ac97_exists(struct i810_card *card,int ac97_number) { - int num_ac97 = 0; - int total_channels = 0; - struct ac97_codec *codec; - u16 eid; - int i=0; - u32 reg; + u32 reg = inl(card->iobase + GLOB_STA); + return (reg & (0x100 << ac97_number)); +} - reg = inl(card->iobase + GLOB_CNT); +static inline int i810_ac97_enable_variable_rate(struct ac97_codec *codec) +{ + i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9); + i810_ac97_set(codec,AC97_EXTENDED_STATUS, + i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800); + return (i810_ac97_get(codec, AC97_EXTENDED_STATUS)&1); +} + + +static int i810_ac97_probe_and_powerup(struct i810_card *card,struct ac97_codec *codec) +{ + /* Returns 0 on failure */ + int i; + + if (ac97_probe_codec(codec) == 0) return 0; + + /* power it all up */ + i810_ac97_set(codec, AC97_POWER_CONTROL, + i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00); + /* wait for analog ready */ + for (i=10; + i && ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); + i--) + { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/20); + } + return i; +} + +/* if I knew what this did, I'd give it a better name */ +static int i810_ac97_random_init_stuff(struct i810_card *card) +{ + u32 reg = inl(card->iobase + GLOB_CNT); + int i; + if((reg&2)==0) /* Cold required */ reg|=2; else @@ -2330,13 +2427,13 @@ reg&=~8; /* ACLink on */ outl(reg , card->iobase + GLOB_CNT); - while(i<10) + for(i=0;i<10;i++) { if((inl(card->iobase+GLOB_CNT)&4)==0) break; - current->state = TASK_UNINTERRUPTIBLE; + + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/20); - i++; } if(i==10) { @@ -2344,8 +2441,22 @@ return 0; } - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(HZ/5); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/2); + reg = inl(card->iobase + GLOB_STA); + inw(card->ac97base); + return 1; +} + +static int __init i810_ac97_init(struct i810_card *card) +{ + int num_ac97 = 0; + int total_channels = 0; + struct ac97_codec *codec; + u16 eid; + u32 reg; + + if(!i810_ac97_random_init_stuff(card)) return 0; /* Number of channels supported */ /* What about the codec? Just because the ICH supports */ @@ -2371,10 +2482,10 @@ /* check the ready status before probing. So we chk */ /* What do we do if it's not ready? Wait and try */ /* again, or abort? */ - reg = inl(card->iobase + GLOB_STA); - if (!(reg & (0x100 << num_ac97))) { + if (!i810_ac97_exists(card,num_ac97)) { if(num_ac97 == 0) printk(KERN_ERR "i810_audio: Primary codec not ready.\n"); + card->ac97_codec[num_ac97] = 0; break; /* I think this works, if not ready stop */ } @@ -2390,24 +2501,13 @@ codec->codec_read = i810_ac97_get; codec->codec_write = i810_ac97_set; - if (ac97_probe_codec(codec) == 0) - break; - - /* power up everything, modify this when implementing power saving */ - i810_ac97_set(codec, AC97_POWER_CONTROL, - i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00); - /* wait for analog ready */ - for (i=10; - i && ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); - i--) - { - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(HZ/20); + if(!i810_ac97_probe_and_powerup(card,codec)) { + printk("i810_audio: timed out waiting for codec %d analog ready", num_ac97); + break; /* it didn't work */ } - /* Store state information about S/PDIF transmitter */ card->ac97_status = 0; - + /* Don't attempt to get eid until powerup is complete */ eid = i810_ac97_get(codec, AC97_EXTENDED_ID); @@ -2427,16 +2527,10 @@ printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n"); else { - /* Enable variable rate mode */ - i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9); - i810_ac97_set(codec,AC97_EXTENDED_STATUS, - i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800); - - if(!(i810_ac97_get(codec, AC97_EXTENDED_STATUS)&1)) - { + if(!i810_ac97_enable_variable_rate(codec)) { printk(KERN_WARNING "i810_audio: Codec refused to allow VRA, using 48Khz only.\n"); card->ac97_features&=~1; - } + } } /* Determine how many channels the codec(s) support */ @@ -2609,6 +2703,9 @@ card->irq = pci_dev->irq; card->next = devs; card->magic = I810_CARD_MAGIC; +#ifdef CONFIG_PM + card->pm_suspended=0; +#endif spin_lock_init(&card->lock); devs = card; @@ -2697,6 +2794,131 @@ kfree(card); } +#ifdef CONFIG_PM +static int i810_pm_suspend(struct pci_dev *dev, u32 pm_state) +{ + struct i810_card *card = dev->driver_data; + struct i810_state *state; + unsigned long flags; + struct dmabuf *dmabuf; + int i,num_ac97; +#ifdef DEBUG + printk("i810_audio: i810_pm_suspend called\n"); +#endif + if(!card) return 0; + spin_lock_irqsave(&card->lock, flags); + card->pm_suspended=1; + for(i=0;istates[i]; + if(!state) continue; + /* this happens only if there are open files */ + dmabuf = &state->dmabuf; + if(dmabuf->enable & DAC_RUNNING || + (dmabuf->count && (dmabuf->trigger & PCM_ENABLE_OUTPUT))) { + state->pm_saved_dac_rate=dmabuf->rate; + stop_dac(state); + } else { + state->pm_saved_dac_rate=0; + } + if(dmabuf->enable & ADC_RUNNING) { + state->pm_saved_adc_rate=dmabuf->rate; + stop_adc(state); + } else { + state->pm_saved_adc_rate=0; + } + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + } + + spin_unlock_irqrestore(&card->lock, flags); + + /* save mixer settings */ + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + struct ac97_codec *codec = card->ac97_codec[num_ac97]; + if(!codec) continue; + for(i=0;i< SOUND_MIXER_NRDEVICES ;i++) { + if((supported_mixer(codec,i)) && + (codec->read_mixer)) { + card->pm_saved_mixer_settings[i][num_ac97]= + codec->read_mixer(codec,i); + } + } + } + pci_save_state(dev,card->pm_save_state); /* XXX do we need this? */ + pci_disable_device(dev); /* disable busmastering */ + pci_set_power_state(dev,3); /* Zzz. */ + + return 0; +} + + +static int i810_pm_resume(struct pci_dev *dev) +{ + int num_ac97,i=0; + struct i810_card *card=(struct i810_card *)dev->driver_data; + pci_enable_device(dev); + pci_restore_state (dev,card->pm_save_state); + + /* observation of a toshiba portege 3440ct suggests that the + hardware has to be more or less completely reinitialized from + scratch after an apm suspend. Works For Me. -dan */ + + i810_ac97_random_init_stuff(card); + + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + struct ac97_codec *codec = card->ac97_codec[num_ac97]; + /* check they haven't stolen the hardware while we were + away */ + if(!i810_ac97_exists(card,num_ac97)) { + if(num_ac97) continue; + else BUG(); + } + if(!i810_ac97_probe_and_powerup(card,codec)) BUG(); + + if((card->ac97_features&0x0001)) { + /* at probe time we found we could do variable + rates, but APM suspend has made it forget + its magical powers */ + if(!i810_ac97_enable_variable_rate(codec)) BUG(); + } + /* we lost our mixer settings, so restore them */ + for(i=0;i< SOUND_MIXER_NRDEVICES ;i++) { + if(supported_mixer(codec,i)){ + int val=card-> + pm_saved_mixer_settings[i][num_ac97]; + codec->mixer_state[i]=val; + codec->write_mixer(codec,i, + (val & 0xff) , + ((val >> 8) & 0xff) ); + } + } + } + + /* we need to restore the sample rate from whatever it was */ + for(i=0;istates[i]; + if(state) { + if(state->pm_saved_adc_rate) + i810_set_adc_rate(state,state->pm_saved_adc_rate); + if(state->pm_saved_dac_rate) + i810_set_dac_rate(state,state->pm_saved_dac_rate); + } + } + + + card->pm_suspended = 0; + + /* any processes that were reading/writing during the suspend + probably ended up here */ + for(i=0;istates[i]; + if(state) wake_up(&state->dmabuf.wait); + } + + return 0; +} +#endif /* CONFIG_PM */ MODULE_AUTHOR(""); MODULE_DESCRIPTION("Intel 810 audio support"); @@ -2713,6 +2935,10 @@ id_table: i810_pci_tbl, probe: i810_probe, remove: i810_remove, +#ifdef CONFIG_PM + suspend: i810_pm_suspend, + resume: i810_pm_resume, +#endif /* CONFIG_PM */ }; @@ -2753,3 +2979,9 @@ module_init(i810_init_module); module_exit(i810_cleanup_module); + +/* +Local Variables: +c-basic-offset: 8 +End: +*/ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/ite8172.c linux.ac/drivers/sound/ite8172.c --- linux.vanilla/drivers/sound/ite8172.c Fri Sep 7 17:28:37 2001 +++ linux.ac/drivers/sound/ite8172.c Wed Oct 10 01:48:15 2001 @@ -58,7 +58,7 @@ #include #include #include -#include +#include #include #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/maestro3.c linux.ac/drivers/sound/maestro3.c --- linux.vanilla/drivers/sound/maestro3.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/sound/maestro3.c Wed Oct 10 01:48:15 2001 @@ -172,7 +172,7 @@ #define SND_DEV_DSP16 5 #ifdef M_DEBUG -static int debug=0; +static int debug; #define DPMOD 1 /* per module load */ #define DPSTR 2 /* per 'stream' */ #define DPSYS 3 /* per syscall */ @@ -365,7 +365,7 @@ return r; } -static struct m3_card *devs = NULL; +static struct m3_card *devs; /* * I'm not very good at laying out functions in a file :) @@ -1289,7 +1289,7 @@ if (s->dma_dac.mapped || !s->dma_dac.ready) return 0; - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&s->dma_dac.wait, &wait); for (;;) { spin_lock_irqsave(&s->lock, flags); @@ -1301,7 +1301,7 @@ break; if (nonblock) { remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } tmo = (count * HZ) / s->ratedac; @@ -1312,7 +1312,7 @@ DPRINTK(DPCRAP,"dma timed out?? %ld\n",jiffies); } remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; return 0; @@ -2251,7 +2251,7 @@ if(busywait) { mdelay(delay1); } else { - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((delay1 * HZ) / 1000); } @@ -2264,7 +2264,7 @@ if(busywait) { mdelay(delay2); } else { - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((delay2 * HZ) / 1000); } if(! try_read_vendor(card)) @@ -2958,8 +2958,8 @@ card->in_suspend++; add_wait_queue(&card->suspend_queue, &wait); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule(); remove_wait_queue(&card->suspend_queue, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/nec_vrc5477.c linux.ac/drivers/sound/nec_vrc5477.c --- linux.vanilla/drivers/sound/nec_vrc5477.c Fri Sep 7 17:28:37 2001 +++ linux.ac/drivers/sound/nec_vrc5477.c Wed Oct 10 01:48:15 2001 @@ -67,7 +67,7 @@ #include #include #include -#include +#include #include #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/opl3sa2.c linux.ac/drivers/sound/opl3sa2.c --- linux.vanilla/drivers/sound/opl3sa2.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/sound/opl3sa2.c Wed Oct 10 01:48:15 2001 @@ -862,9 +862,9 @@ /* Our own config: */ hw_cfg->io_base = dev->resource[4].start; - hw_cfg->irq = 0; - hw_cfg->dma = -1; - hw_cfg->dma2 = -1; + hw_cfg->irq = dev->irq_resource[0].start; + hw_cfg->dma = dev->dma_resource[0].start; + hw_cfg->dma2 = dev->dma_resource[1].start; /* The MSS config: */ mss_cfg->io_base = dev->resource[1].start; @@ -944,9 +944,9 @@ * give pretty output from conf_printf. :) */ cfg[card].io_base = io; - cfg[card].irq = 0; - cfg[card].dma = -1; - cfg[card].dma2 = -1; + cfg[card].irq = irq; + cfg[card].dma = dma; + cfg[card].dma2 = dma2; /* The MSS config: */ cfg_mss[card].io_base = mss_io; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/sb_card.c linux.ac/drivers/sound/sb_card.c --- linux.vanilla/drivers/sound/sb_card.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/sound/sb_card.c Wed Oct 10 01:48:15 2001 @@ -53,6 +53,9 @@ * * 28-10-2000 Added pnplegacy support * Daniel Church + * + * 01-10-2001 Added a new flavor of Creative SB AWE64 PnP (CTL00E9). + * Jerome Cornet */ #include @@ -434,6 +437,11 @@ ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0,0,0,0, 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E9), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0,0,0, + 0,1,1,-1}, {"ESS 1688", ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0968), ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), @@ -621,6 +629,9 @@ ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E4), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E9), ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0968), diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/sb_ess.c linux.ac/drivers/sound/sb_ess.c --- linux.vanilla/drivers/sound/sb_ess.c Sat Feb 17 00:02:37 2001 +++ linux.ac/drivers/sound/sb_ess.c Wed Oct 10 01:48:16 2001 @@ -352,7 +352,8 @@ *speedp = speed1; retval = 1; } else { - *divp = div2; + /* *divp = div2; */ + *divp = 0x80 | div2; *speedp = speed2; retval = 2; } @@ -376,10 +377,19 @@ /* * The 0x80 is important for the first audio channel */ - div = 0x80 | ess_calc_div (795500, 128, speedp, &diff); + if (devc->submodel == SUBMDL_ES1888) { + div = 0x80 | ess_calc_div (795500, 256, speedp, &diff); + } else { + div = 0x80 | ess_calc_div (795500, 128, speedp, &diff); + } } else if(devc->caps & SB_CAP_ES18XX_RATE) { - ess_calc_best_speed(ES18XX_CLOCK1, 128, ES18XX_CLOCK2, 256, + if (devc->submodel == SUBMDL_ES1888) { + ess_calc_best_speed(397700, 128, 795500, 256, &div, speedp); + } else { + ess_calc_best_speed(ES18XX_CLOCK1, 128, ES18XX_CLOCK2, 256, + &div, speedp); + } } else { if (*speedp > 22000) { div = 0x80 | ess_calc_div (ES1688_CLOCK1, 256, speedp, &diff); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/trident.c linux.ac/drivers/sound/trident.c --- linux.vanilla/drivers/sound/trident.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/sound/trident.c Wed Oct 10 01:48:16 2001 @@ -36,6 +36,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * History + * v0.14.9d + * October 8 2001 Arnaldo Carvalho de Melo + * use set_current_state, properly release resources on failure in + * trident_probe, get rid of check_region * v0.14.9c * August 10 2001 Peter Wächtler * added support for Tvia (formerly Integraphics/IGST) CyberPro5050 @@ -176,7 +180,7 @@ #include -#define DRIVER_VERSION "0.14.9c" +#define DRIVER_VERSION "0.14.9d" /* magic numbers to protect our data structures */ #define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */ @@ -1358,7 +1362,7 @@ for (;;) { /* It seems that we have to set the current state to TASK_INTERRUPTIBLE every time to make the process really go to sleep */ - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&state->card->lock, flags); count = dmabuf->count; @@ -1372,7 +1376,7 @@ if (nonblock) { remove_wait_queue(&dmabuf->wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } @@ -1395,7 +1399,7 @@ } } remove_wait_queue(&dmabuf->wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; @@ -3695,7 +3699,7 @@ } #ifdef CONFIG_PROC_FS -struct proc_dir_entry *res = NULL; +struct proc_dir_entry *res; static int ali_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { struct trident_card *card = (struct trident_card *)data; @@ -3936,14 +3940,15 @@ int i = 0; u16 temp; struct pci_dev *pci_dev_m1533 = NULL; + int rc = -ENODEV; if (pci_enable_device(pci_dev)) - return -ENODEV; + goto out; if (pci_set_dma_mask(pci_dev, TRIDENT_DMA_MASK)) { printk(KERN_ERR "trident: architecture does not support" " 30bit PCI busmaster DMA\n"); - return -ENODEV; + goto out; } pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision); @@ -3952,15 +3957,16 @@ else iobase = pci_resource_start(pci_dev, 0); - if (check_region(iobase, 256)) { + if (!request_region(iobase, 256, card_names[pci_id->driver_data])) { printk(KERN_ERR "trident: can't allocate I/O space at 0x%4.4lx\n", iobase); - return -ENODEV; + goto out; } + rc = -ENOMEM; if ((card = kmalloc(sizeof(struct trident_card), GFP_KERNEL)) == NULL) { printk(KERN_ERR "trident: out of memory\n"); - return -ENOMEM; + goto out_release_region; } memset(card, 0, sizeof(*card)); @@ -4014,8 +4020,9 @@ /* Add H/W Volume Control By Matt Wu Jul. 06, 2001 */ card->hwvolctl = 0; pci_dev_m1533 = pci_find_device(PCI_VENDOR_ID_AL,PCI_DEVICE_ID_AL_M1533, pci_dev_m1533); + rc = -ENODEV; if (pci_dev_m1533 == NULL) - return -ENODEV; + goto out_proc_fs; pci_read_config_byte(pci_dev_m1533, 0x63, &bits); if (bits & (1<<5)) card->hwvolctl = 1; @@ -4044,22 +4051,17 @@ card->address_interrupt = trident_address_interrupt; } - /* claim our iospace and irq */ - request_region(card->iobase, 256, card_names[pci_id->driver_data]); + /* claim our irq */ + rc = -ENODEV; if (request_irq(card->irq, &trident_interrupt, SA_SHIRQ, card_names[pci_id->driver_data], card)) { printk(KERN_ERR "trident: unable to allocate irq %d\n", card->irq); - release_region(card->iobase, 256); - kfree(card); - return -ENODEV; + goto out_proc_fs; } /* register /dev/dsp */ if ((card->dev_audio = register_sound_dsp(&trident_audio_fops, -1)) < 0) { printk(KERN_ERR "trident: couldn't register DSP device!\n"); - release_region(iobase, 256); - free_irq(card->irq, card); - kfree(card); - return -ENODEV; + goto out_free_irq; } card->mixer_regs_ready = 0; /* initialize AC97 codec and register /dev/mixer */ @@ -4071,11 +4073,7 @@ kfree (card->ac97_codec[i]); } } - unregister_sound_dsp(card->dev_audio); - release_region(iobase, 256); - free_irq(card->irq, card); - kfree(card); - return -ENODEV; + goto out_unregister_sound_dsp; } card->mixer_regs_ready = 1; outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL)); @@ -4112,13 +4110,28 @@ #endif /* edited by HMSEO for GT sound*/ } - + rc = 0; pci_set_drvdata(pci_dev, card); /* Enable Address Engine Interrupts */ trident_enable_loop_interrupts(card); - - return 0; +out: return rc; +out_unregister_sound_dsp: + unregister_sound_dsp(card->dev_audio); +out_free_irq: + free_irq(card->irq, card); +out_proc_fs: +#ifdef CONFIG_PROC_FS + if (res) { + remove_proc_entry("ALi5451", NULL); + res = NULL; + } +#endif + kfree(card); + devs = NULL; +out_release_region: + release_region(iobase, 256); + goto out; } static void __exit trident_remove(struct pci_dev *pci_dev) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/vidc.c linux.ac/drivers/sound/vidc.c --- linux.vanilla/drivers/sound/vidc.c Sat Apr 14 04:26:07 2001 +++ linux.ac/drivers/sound/vidc.c Wed Oct 10 01:48:16 2001 @@ -542,3 +542,8 @@ module_init(init_vidc); module_exit(cleanup_vidc); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("VIDC20 audio driver"); +MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/tc/tc.c linux.ac/drivers/tc/tc.c --- linux.vanilla/drivers/tc/tc.c Mon Aug 27 16:56:31 2001 +++ linux.ac/drivers/tc/tc.c Wed Oct 10 01:48:16 2001 @@ -25,6 +25,7 @@ #define TC_DEBUG +MODULE_LICENSE("GPL"); slot_info tc_bus[MAX_SLOT]; static int max_tcslot; static tcinfo *info; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/Config.in linux.ac/drivers/usb/Config.in --- linux.vanilla/drivers/usb/Config.in Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/usb/Config.in Fri Oct 12 11:10:55 2001 @@ -4,8 +4,8 @@ mainmenu_option next_comment comment 'USB support' -tristate 'Support for USB' CONFIG_USB -if [ ! "$CONFIG_USB" = "n" ]; then +dep_tristate 'Support for USB' CONFIG_USB $CONFIG_PCI +if [ "$CONFIG_USB" = "y" -o "$CONFIG_USB" = "m" ]; then bool ' USB verbose debug messages' CONFIG_USB_DEBUG comment 'Miscellaneous USB options' @@ -15,73 +15,87 @@ else define_bool CONFIG_USB_BANDWIDTH n fi + bool ' Long timeout for slow-responding devices (some MGE Ellipse UPSes)' CONFIG_USB_LONG_TIMEOUT + bool ' Large report fetching for "broken" devices (some APC UPSes)' CONFIG_USB_LARGE_CONFIG +fi comment 'USB Controllers' - if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then - dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB - fi - if [ "$CONFIG_USB_UHCI" != "y" ]; then - dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB - fi - dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB +if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then + dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB +fi +if [ "$CONFIG_USB_UHCI" != "y" ]; then + dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB +else + define_bool CONFIG_USB_UHCI_ALT n +fi +dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB - comment 'USB Device Class drivers' - dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND - dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL - dep_tristate ' USB Mass Storage support' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI - if [ "$CONFIG_USB_STORAGE" != "n" ]; then - bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG - bool ' Freecom USB/ATAPI Bridge support' CONFIG_USB_STORAGE_FREECOM - bool ' ISD-200 USB/ATA Bridge support' CONFIG_USB_STORAGE_ISD200 - bool ' Microtech CompactFlash/SmartMedia support' CONFIG_USB_STORAGE_DPCM - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool ' HP CD-Writer 82xx support' CONFIG_USB_STORAGE_HP8200e - bool ' SanDisk SDDR-09 (and other SmartMedia) support' CONFIG_USB_STORAGE_SDDR09 - fi +comment 'USB Device Class drivers' +dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND +dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL +dep_tristate ' USB Mass Storage support' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI + dep_mbool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG $CONFIG_USB_STORAGE + dep_mbool ' Datafab MDCFE-B Compact Flash Reader' CONFIG_USB_STORAGE_DATAFAB $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL + dep_mbool ' Freecom USB/ATAPI Bridge support' CONFIG_USB_STORAGE_FREECOM $CONFIG_USB_STORAGE + dep_mbool ' ISD-200 USB/ATA Bridge support' CONFIG_USB_STORAGE_ISD200 $CONFIG_USB_STORAGE + dep_mbool ' Lexar Jumpshot Compact Flash Reader' CONFIG_USB_STORAGE_JUMPSHOT $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL + dep_mbool ' Microtech CompactFlash/SmartMedia reader' CONFIG_USB_STORAGE_DPCM $CONFIG_USB_STORAGE + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_mbool ' HP CD-Writer 82xx support' CONFIG_USB_STORAGE_HP8200e $CONFIG_USB_STORAGE + dep_mbool ' SanDisk SDDR-09 (and other SmartMedia) support' CONFIG_USB_STORAGE_SDDR09 $CONIG_USB_STORAGE fi - dep_tristate ' USB Modem (CDC ACM) support' CONFIG_USB_ACM $CONFIG_USB - dep_tristate ' USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB +dep_tristate ' USB Modem (CDC ACM) support' CONFIG_USB_ACM $CONFIG_USB +dep_tristate ' USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB - comment 'USB Human Interface Devices (HID)' - if [ "$CONFIG_INPUT" = "n" ]; then - comment ' Input core support is needed for USB HID' - else - dep_tristate ' USB Human Interface Device (full HID) support' CONFIG_USB_HID $CONFIG_USB $CONFIG_INPUT - if [ "$CONFIG_USB_HID" != "y" ]; then - dep_tristate ' USB HIDBP Keyboard (basic) support' CONFIG_USB_KBD $CONFIG_USB $CONFIG_INPUT - dep_tristate ' USB HIDBP Mouse (basic) support' CONFIG_USB_MOUSE $CONFIG_USB $CONFIG_INPUT - fi - dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB $CONFIG_INPUT +comment 'USB Human Interface Devices (HID)' +if [ "$CONFIG_INPUT" = "n" ]; then + comment ' Input core support is needed for USB HID' +else + dep_tristate ' USB Human Interface Device (full HID) support' CONFIG_USB_HID $CONFIG_USB $CONFIG_INPUT + dep_mbool ' /dev/hiddev raw HID device support (EXPERIMENTAL)' CONFIG_USB_HIDDEV $CONFIG_USB_HID + if [ "$CONFIG_USB_HID" != "y" ]; then + dep_tristate ' USB HIDBP Keyboard (basic) support' CONFIG_USB_KBD $CONFIG_USB $CONFIG_INPUT + dep_tristate ' USB HIDBP Mouse (basic) support' CONFIG_USB_MOUSE $CONFIG_USB $CONFIG_INPUT fi + dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB $CONFIG_INPUT +fi - comment 'USB Imaging devices' - dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB - dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB $CONFIG_EXPERIMENTAL - dep_tristate ' USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB - dep_tristate ' Microtek X6USB scanner support' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI - dep_tristate ' HP53xx USB scanner support (EXPERIMENTAL)' CONFIG_USB_HPUSBSCSI $CONFIG_USB $CONFIG_SCSI $CONFIG_EXPERIMENTAL - - comment 'USB Multimedia devices' +comment 'USB Imaging devices' +dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB +dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB $CONFIG_EXPERIMENTAL +dep_tristate ' USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB +dep_tristate ' Microtek X6USB scanner support' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI +dep_tristate ' HP53xx USB scanner support (EXPERIMENTAL)' CONFIG_USB_HPUSBSCSI $CONFIG_USB $CONFIG_SCSI $CONFIG_EXPERIMENTAL + +comment 'USB Multimedia devices' +if [ "$CONFIG_VIDEO_DEV" = "n" ]; then + comment ' Video4Linux support is needed for USB Multimedia device support' +else dep_tristate ' USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' USB Philips Cameras' CONFIG_USB_PWC $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' USB SE401 Camera support' CONFIG_USB_SE401 $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB +fi - comment 'USB Network adaptors' +comment 'USB Network adaptors' +if [ "$CONFIG_NET" = "n" ]; then + comment ' Networking support is needed for USB Networking device support' +else + dep_tristate ' PLUSB Prolific USB-Network driver (EXPERIMENTAL)' CONFIG_USB_PLUSB $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL dep_tristate ' USB ADMtek Pegasus-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL - dep_tristate ' USB CATC NetMate-based Ethernet driver (EXPERIMENTAL)' CONFIG_USB_CATC $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL - dep_tristate ' USB CDC Ethernet class (USB cable modem) support (EXPERIMENTAL)' CONFIG_USB_CDCETHER $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL dep_tristate ' USB KLSI KL5USB101-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_KAWETH $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL - dep_tristate ' USB-to-USB Networking (NetChip, Prolific, ...) (EXPERIMENTAL)' CONFIG_USB_USBNET $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL - - comment 'USB port drivers' - dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT - source drivers/usb/serial/Config.in - - comment 'USB misc drivers' - dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB $CONFIG_EXPERIMENTAL + dep_tristate ' USB CATC NetMate-based Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CATC $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USB Communication Class Ethernet driver (EXPERIMENTAL)' CONFIG_USB_CDCETHER $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USB-to-USB Networking cable device support (EXPERIMENTAL)' CONFIG_USB_USBNET $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL fi +comment 'USB port drivers' +dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT +source drivers/usb/serial/Config.in + +comment 'Miscellaneous USB drivers' +dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB $CONFIG_EXPERIMENTAL +dep_tristate ' USB MassWorks ID-75 (EXPERIMENTAL)' CONFIG_USB_ID75 $CONFIG_USB $CONFIG_EXPERIMENTAL endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/Makefile linux.ac/drivers/usb/Makefile --- linux.vanilla/drivers/usb/Makefile Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/usb/Makefile Wed Oct 10 01:48:16 2001 @@ -59,23 +59,27 @@ obj-$(CONFIG_USB_ACM) += acm.o obj-$(CONFIG_USB_PRINTER) += printer.o obj-$(CONFIG_USB_AUDIO) += audio.o -obj-$(CONFIG_USB_IBMCAM) += ibmcam.o usbvideo.o ultracam.o obj-$(CONFIG_USB_PWC) += pwc.o +obj-$(CONFIG_USB_IBMCAM) += ibmcam.o usbvideo.o ultracam.o obj-$(CONFIG_USB_DC2XX) += dc2xx.o obj-$(CONFIG_USB_MDC800) += mdc800.o obj-$(CONFIG_USB_USS720) += uss720.o obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_SE401) += se401.o -obj-$(CONFIG_USB_PEGASUS) += pegasus.o -obj-$(CONFIG_USB_CATC) += catc.o -obj-$(CONFIG_USB_KAWETH) += kaweth.o -obj-$(CONFIG_USB_CDCETHER) += CDCEther.o obj-$(CONFIG_USB_RIO500) += rio500.o obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_USB_MICROTEK) += microtek.o obj-$(CONFIG_USB_HPUSBSCSI) += hpusbscsi.o obj-$(CONFIG_USB_BLUETOOTH) += bluetooth.o +obj-$(CONFIG_USB_ID75) += id75.o + +# network drivers + +obj-$(CONFIG_USB_CATC) += catc.o +obj-$(CONFIG_USB_CDCETHER) += CDCEther.o +obj-$(CONFIG_USB_KAWETH) += kaweth.o +obj-$(CONFIG_USB_PEGASUS) += pegasus.o obj-$(CONFIG_USB_USBNET) += usbnet.o # Object files in subdirectories diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/devices.c linux.ac/drivers/usb/devices.c --- linux.vanilla/drivers/usb/devices.c Fri Sep 7 18:56:50 2001 +++ linux.ac/drivers/usb/devices.c Wed Oct 10 01:48:16 2001 @@ -488,7 +488,6 @@ return -EFAULT; /* enumerate busses */ - read_lock_irq (&usb_bus_list_lock); for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next) { /* print devices for this bus */ bus = list_entry(buslist, struct usb_bus, bus_list); @@ -498,7 +497,6 @@ return ret; total_written += ret; } - read_unlock_irq (&usb_bus_list_lock); return total_written; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/hub.c linux.ac/drivers/usb/hub.c --- linux.vanilla/drivers/usb/hub.c Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/usb/hub.c Thu Oct 11 09:39:34 2001 @@ -356,7 +356,7 @@ INIT_LIST_HEAD(&hub->event_list); hub->dev = dev; - atomic_set(&hub->refcnt, 1); + init_MUTEX(&hub->khubd_sem); /* Record the new hub's existence */ spin_lock_irqsave(&hub_event_lock, flags); @@ -385,34 +385,11 @@ return NULL; } -static void hub_get(struct usb_hub *hub) -{ - atomic_inc(&hub->refcnt); -} - -static void hub_put(struct usb_hub *hub) -{ - if (atomic_dec_and_test(&hub->refcnt)) { - if (hub->descriptor) { - kfree(hub->descriptor); - hub->descriptor = NULL; - } - - kfree(hub); - } -} - static void hub_disconnect(struct usb_device *dev, void *ptr) { struct usb_hub *hub = (struct usb_hub *)ptr; unsigned long flags; - if (hub->urb) { - usb_unlink_urb(hub->urb); - usb_free_urb(hub->urb); - hub->urb = NULL; - } - spin_lock_irqsave(&hub_event_lock, flags); /* Delete it and then reset it */ @@ -423,7 +400,22 @@ spin_unlock_irqrestore(&hub_event_lock, flags); - hub_put(hub); + down(&hub->khubd_sem); /* Wait for khubd to leave this hub alone. */ + up(&hub->khubd_sem); + + if (hub->urb) { + usb_unlink_urb(hub->urb); + usb_free_urb(hub->urb); + hub->urb = NULL; + } + + if (hub->descriptor) { + kfree(hub->descriptor); + hub->descriptor = NULL; + } + + /* Free the memory */ + kfree(hub); } static int hub_ioctl(struct usb_device *hub, unsigned int code, void *user_data) @@ -534,10 +526,6 @@ dbg("port %d, portstatus %x, change %x, %s", port + 1, portstatus, portchange, portspeed (portstatus)); - /* Device went away? */ - if (!(portstatus & USB_PORT_STAT_CONNECTION)) - return 1; - /* bomb out completely if something weird happened */ if ((portchange & USB_PORT_STAT_C_CONNECTION)) return -1; @@ -745,7 +733,7 @@ list_del(tmp); INIT_LIST_HEAD(tmp); - hub_get(hub); + down(&hub->khubd_sem); /* never blocks, we were on list */ spin_unlock_irqrestore(&hub_event_lock, flags); if (hub->error) { @@ -753,8 +741,8 @@ if (usb_hub_reset(hub)) { err("error resetting hub %d - disconnecting", dev->devnum); + up(&hub->khubd_sem); usb_hub_disconnect(dev); - hub_put(hub); continue; } @@ -830,7 +818,7 @@ usb_hub_power_on(hub); } } - hub_put(hub); + up(&hub->khubd_sem); } /* end while (1) */ spin_unlock_irqrestore(&hub_event_lock, flags); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/hub.h linux.ac/drivers/usb/hub.h --- linux.vanilla/drivers/usb/hub.h Thu Oct 11 13:52:12 2001 +++ linux.ac/drivers/usb/hub.h Thu Oct 11 22:09:09 2001 @@ -135,7 +135,7 @@ struct usb_hub_descriptor *descriptor; - atomic_t refcnt; + struct semaphore khubd_sem; }; -#endif +#endif /* __LINUX_HUB_H */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/id75.c linux.ac/drivers/usb/id75.c --- linux.vanilla/drivers/usb/id75.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/usb/id75.c Wed Oct 10 01:48:16 2001 @@ -0,0 +1,725 @@ +/* + * MassWorks ID-75 USB Driver based on + * USB Skeleton driver + * + * Copyright (c) 2001 MassWorks (contact@massworks.com) + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * + * 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 driver is to used to open up the endpoints of the ID-75 to user + * applications. All the 'smarts' of the driver comes from streaming commands + * to the ID-75 from user space. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_USB_DEBUG + static int debug = 1; +#else + static int debug; +#endif + +/* Use our own dbg macro */ +#undef dbg +#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg); } while (0) + + +/* Version Information */ +#define DRIVER_VERSION "v1.0" +#define DRIVER_AUTHOR "MassWorks, contact@massworks.com" +#define DRIVER_DESC "MassWorks ID-75 USB Driver" + +/* 'ioctl' calls */ +/* #define IOCTL_ID75_RESETI _IO('i', 0x01) */ + +/* Module paramaters */ +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debug enabled or not"); + + +/* Define these values to match your device */ +#define USB_ID75_VENDOR_ID 0x088b +#define USB_ID75_PRODUCT_ID 0x4944 + +/* table of devices that work with this driver */ +static struct usb_device_id id75_table [] = { + { USB_DEVICE(USB_ID75_VENDOR_ID, USB_ID75_PRODUCT_ID) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, id75_table); + + + +/* Get a minor range for your devices from the usb maintainer */ +#define USB_ID75_MINOR_BASE 65 + +/* we can have up to this number of device plugged in at once */ +#define MAX_DEVICES 4 + +/* Structure to hold all of our device specific stuff */ +struct usb_id75 { + struct usb_device * udev; /* save off the usb device pointer */ + struct usb_interface * interface; /* the interface for this device */ + devfs_handle_t devfs; /* devfs device node */ + unsigned char minor; /* the starting minor number for this device */ + unsigned char num_ports; /* the number of ports this device has */ + char num_interrupt_in; /* number of interrupt in endpoints we have */ + char num_bulk_in; /* number of bulk in endpoints we have */ + char num_bulk_out; /* number of bulk out endpoints we have */ + + unsigned char * int_in_buffer; /* the buffer to receive data */ + int int_in_size; /* the size of the receive buffer */ + struct urb * read_urb; /* the urb used to receive data */ + __u8 int_in_endpointAddr; /* the address of the interrupt in endpoint */ + + unsigned char * bulk_out_buffer; /* the buffer to send data */ + int bulk_out_size; /* the size of the send buffer */ + struct urb * write_urb; /* the urb used to send data */ + __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ + + struct tq_struct tqueue; /* task queue for line discipline waking up */ + int open_count; /* number of times this port has been opened */ + struct semaphore sem; /* locks this structure */ + + unsigned char irqbufr[8]; /* Last captured sample */ + int nirqbufr; /* Number of valid bytes in buffer */ +}; + + +/* the global usb devfs handle */ +extern devfs_handle_t usb_devfs_handle; + + +/* local function prototypes */ +static ssize_t id75_read (struct file *file, char *buffer, size_t count, loff_t *ppos); +static ssize_t id75_write (struct file *file, const char *buffer, size_t count, loff_t *ppos); +static int id75_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static int id75_open (struct inode *inode, struct file *file); +static int id75_release (struct inode *inode, struct file *file); + +static void * id75_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id); +static void id75_disconnect (struct usb_device *dev, void *ptr); + +static void id75_write_bulk_callback (struct urb *urb); + + +/* array of pointers to our devices that are currently connected */ +static struct usb_id75 *minor_table[MAX_DEVICES]; + +/* lock to protect the minor_table structure */ +static DECLARE_MUTEX (minor_table_mutex); + +/* file operations needed when we register this driver */ +static struct file_operations id75_fops = { + owner: THIS_MODULE, + read: id75_read, + write: id75_write, + ioctl: id75_ioctl, + open: id75_open, + release: id75_release, +}; + + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver id75_driver = { + name: "id75", + probe: id75_probe, + disconnect: id75_disconnect, + fops: &id75_fops, + minor: USB_ID75_MINOR_BASE, + id_table: id75_table, +}; + + + + + +/** + * usb_id75_debug_data + */ +static inline void usb_id75_debug_data (const char *function, int size, const unsigned char *data) +{ + int i; + + if (!debug) + return; + + printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", + function, size); + for (i = 0; i < size; ++i) { + printk ("%.2x ", data[i]); + } + printk ("\n"); +} + + +/** + * id75_irq + */ +static void id75_irq (struct urb *urb) +{ + struct usb_id75 *dev = (urb->context); + + /* Process data */ + if (!urb->status) { + /* Copy to buffer if available */ + if (!dev->nirqbufr) { + dev->nirqbufr = urb->actual_length; + memcpy (dev->irqbufr, dev->int_in_buffer, dev->nirqbufr); + } + } else { + /* Error status ? */ + if (urb->status != -ENOENT) { + /* On error, restart urb */ + err (__FUNCTION__":Error, status %d\n", urb->status ); + usb_submit_urb (dev->read_urb); + } + + } +} + + +/** + * id75_open + */ +static int id75_open (struct inode *inode, struct file *file) +{ + struct usb_id75 *dev = NULL; + int subminor; + int retval = 0; + + dbg(__FUNCTION__); + + subminor = MINOR (inode->i_rdev) - USB_ID75_MINOR_BASE; + if ((subminor < 0) || + (subminor >= MAX_DEVICES)) { + return -ENODEV; + } + + /* increment our usage count for the module */ + MOD_INC_USE_COUNT; + + /* lock our minor table and get our local data for this minor */ + down (&minor_table_mutex); + dev = minor_table[subminor]; + if (dev == NULL) { + up (&minor_table_mutex); + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + /* lock this device */ + down (&dev->sem); + + /* unlock the minor table */ + up (&minor_table_mutex); + + /* increment our usage count for the driver */ + ++dev->open_count; + + /* save our object in the file's private structure */ + file->private_data = dev; + + /* unlock this device */ + up (&dev->sem); + + return retval; +} + + +/** + * id75_release + */ +static int id75_release (struct inode *inode, struct file *file) +{ + struct usb_id75 *dev; + int retval = 0; + + dev = (struct usb_id75 *)file->private_data; + if (dev == NULL) { + dbg (__FUNCTION__ " - object is NULL"); + return -ENODEV; + } + + dbg(__FUNCTION__ " - minor %d", dev->minor); + + /* lock our minor table */ + down (&minor_table_mutex); + + /* lock our device */ + down (&dev->sem); + + if (dev->open_count <= 0) { + dbg (__FUNCTION__ " - device not opened"); + retval = -ENODEV; + goto exit_not_opened; + } + + if (dev->udev == NULL) { + /* the device was unplugged before the file was released */ + minor_table[dev->minor] = NULL; + if (dev->int_in_buffer != NULL) + kfree (dev->int_in_buffer); + if (dev->bulk_out_buffer != NULL) + kfree (dev->bulk_out_buffer); + if (dev->write_urb != NULL) + usb_free_urb (dev->write_urb); + if (dev->read_urb != NULL) + usb_free_urb (dev->read_urb); + kfree (dev); + goto exit; + } + + /* decrement our usage count for the device */ + --dev->open_count; + if (dev->open_count <= 0) { + /* shutdown any transfers that might be going on */ + usb_unlink_urb (dev->write_urb); + usb_unlink_urb (dev->read_urb); + dev->open_count = 0; + } + +exit: + /* decrement our usage count for the module */ + MOD_DEC_USE_COUNT; + +exit_not_opened: + up (&dev->sem); + up (&minor_table_mutex); + + return retval; +} + + +/** + * id75_read + */ +static ssize_t id75_read (struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct usb_id75 *dev; + int retval = 0; + + dev = (struct usb_id75 *)file->private_data; + + dbg(__FUNCTION__ " - minor %d, count = %d", dev->minor, count); + + /* lock this object */ + down (&dev->sem); + + /* verify that the device wasn't unplugged */ + if (dev->udev == NULL) { + up (&dev->sem); + return -ENODEV; + } + + /* Any data to return ? */ + if (dev->nirqbufr) { + if (copy_to_user (buffer, dev->irqbufr, dev->nirqbufr)) + retval = -EFAULT; + else + retval = dev->nirqbufr; + dev->nirqbufr = 0; + } + + /* unlock the device */ + up (&dev->sem); + + return retval; +} + + +/** + * id75_write + */ +static ssize_t id75_write (struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct usb_id75 *dev; + ssize_t bytes_written = 0; + int retval = 0; + + dev = (struct usb_id75 *)file->private_data; + + dbg(__FUNCTION__ " - minor %d, count = %d", dev->minor, count); + + /* lock this object */ + down (&dev->sem); + + /* verify that the device wasn't unplugged */ + if (dev->udev == NULL) { + retval = -ENODEV; + goto exit; + } + + /* verify that we actually have some data to write */ + if (count == 0) { + dbg(__FUNCTION__ " - write request of 0 bytes"); + goto exit; + } + + /* see if we are already in the middle of a write */ + if (dev->write_urb->status == -EINPROGRESS) { + dbg (__FUNCTION__ " - already writing"); + goto exit; + } + + /* we can only write as much as 1 urb will hold */ + bytes_written = (count > dev->bulk_out_size) ? + dev->bulk_out_size : count; + + /* copy the data from userspace into our urb */ + if (copy_from_user(dev->write_urb->transfer_buffer, buffer, + bytes_written)) { + retval = -EFAULT; + goto exit; + } + + usb_id75_debug_data (__FUNCTION__, bytes_written, + dev->write_urb->transfer_buffer); + + /* set up our urb */ + FILL_BULK_URB(dev->write_urb, dev->udev, + usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), + dev->write_urb->transfer_buffer, bytes_written, + id75_write_bulk_callback, dev); + + /* send the data out the bulk port */ + retval = usb_submit_urb(dev->write_urb); + if (retval) { + err(__FUNCTION__ " - failed submitting write urb, error %d", + retval); + } else { + retval = bytes_written; + } + +exit: + /* unlock the device */ + up (&dev->sem); + + return retval; +} + + +/** + * id75_ioctl + */ +static int id75_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct usb_id75 *dev; + int ret = -ENOTTY; + + dev = (struct usb_id75 *)file->private_data; + + /* lock this object */ + down (&dev->sem); + + /* verify that the device wasn't unplugged */ + if (dev->udev == NULL) { + up (&dev->sem); + return -ENODEV; + } + + dbg(__FUNCTION__ " - minor %d, cmd 0x%.4x, arg %ld", + dev->minor, cmd, arg); + +#if 0 + /* Process 'ioctl' request */ + switch (cmd) { + /* + * TODO: Until this reset works, developers may have to + * power on/off the device to get it back into sync if + * they confuse the internal processor. It expects the + * exact number of bytes for each command. + */ + + /* Reset input ? */ + case IOCTL_ID75_RESETI : + dbg (__FUNCTION__": IOCTL_ID75_RESETI"); + + /* A input 'reset' consists of any packet on the INT(OUT) endpoint */ + FILL_INT_URB(dev->reset_urb, dev->udev, + usb_sndintpipe(dev->udev,dev->int_out_endpointAddr), + /* NULL, 0, NULL, dev, -1); */ + dev->int_out_buffer, 1, id75_irq, dev, -1); + + /* Transmit */ + r = usb_submit_urb ( dev->reset_urb ); + dbg (__FUNCTION__":r %d\n", r); + if (r) { + err("Couldn't submit reset URB"); + } + + ret = 0; + break; + } +#endif + + /* fill in your device specific stuff here */ + + /* unlock the device */ + up (&dev->sem); + + /* return that we did not understand this ioctl call */ + return ret; +} + + +/** + * id75_write_bulk_callback + */ +static void id75_write_bulk_callback (struct urb *urb) +{ + struct usb_id75 *dev = (struct usb_id75 *)urb->context; + + dbg(__FUNCTION__ " - minor %d", dev->minor); + + if ((urb->status != -ENOENT) && + (urb->status != -ECONNRESET)) { + dbg(__FUNCTION__ " - nonzero write bulk status received: %d", + urb->status); + return; + } + + return; +} + + +static void * id75_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id) +{ + struct usb_id75 *dev = NULL; + struct usb_interface *interface; + struct usb_interface_descriptor *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int minor; + int buffer_size; + int i; + char name[10]; + + + /* See if the device offered us matches what we can accept */ + if ((udev->descriptor.idVendor != USB_ID75_VENDOR_ID) || + (udev->descriptor.idProduct != USB_ID75_PRODUCT_ID)) { + return NULL; + } + + /* select a "subminor" number (part of a minor number) */ + down (&minor_table_mutex); + for (minor = 0; minor < MAX_DEVICES; ++minor) { + if (minor_table[minor] == NULL) + break; + } + if (minor >= MAX_DEVICES) { + info ("Too many devices plugged in, can not handle this device."); + goto exit; + } + + /* allocate memory for our device state and intialize it */ + dev = kmalloc (sizeof(struct usb_id75), GFP_KERNEL); + if (dev == NULL) { + err ("Out of memory"); + goto exit; + } + minor_table[minor] = dev; + + interface = &udev->actconfig->interface[ifnum]; + + init_MUTEX (&dev->sem); + dev->udev = udev; + dev->interface = interface; + dev->minor = minor; + + /* set up the endpoint information */ + /* check out the endpoints */ + iface_desc = &interface->altsetting[0]; + for (i = 0; i < iface_desc->bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i]; + + /* Bulk output for device command */ + if (((endpoint->bEndpointAddress & 0x80) == 0x00) && + ((endpoint->bmAttributes & 3) == 0x02)) { + /* we found a bulk out endpoint */ + dev->write_urb = usb_alloc_urb(0); + if (!dev->write_urb) { + err("No free urbs available"); + goto error; + } + buffer_size = endpoint->wMaxPacketSize; + dev->bulk_out_size = buffer_size; + dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; + dev->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL); + if (!dev->bulk_out_buffer) { + err("Couldn't allocate bulk_out_buffer"); + goto error; + } + FILL_BULK_URB(dev->write_urb, udev, + usb_sndbulkpipe(udev, + endpoint->bEndpointAddress), + dev->bulk_out_buffer, buffer_size, + id75_write_bulk_callback, dev); + } + + /* Interrupt input for device data */ + if ((endpoint->bEndpointAddress & 0x80) && + ((endpoint->bmAttributes & 3) == 0x03)) { + /* we found an interrupt in endpoint */ + dev->read_urb = usb_alloc_urb(0); + if (!dev->read_urb) { + err("No free urbs available"); + goto error; + } + buffer_size = endpoint->wMaxPacketSize; + dev->int_in_size = buffer_size; + dev->int_in_endpointAddr = endpoint->bEndpointAddress; + dev->int_in_buffer = kmalloc (buffer_size, GFP_KERNEL); + if (!dev->int_in_buffer) { + err("Couldn't allocate int_in_buffer"); + goto error; + } + FILL_INT_URB(dev->read_urb, dev->udev, + usb_rcvintpipe(udev,endpoint->bEndpointAddress), + dev->int_in_buffer, buffer_size, + id75_irq, dev, 100); + if (usb_submit_urb ( dev->read_urb )) { + err("Couldn't submit INT URB"); + goto error; + } + + } + + } + + /* initialize the devfs node for this device and register it */ + sprintf(name, "id75%d", dev->minor); + + dev->devfs = devfs_register (usb_devfs_handle, name, + DEVFS_FL_DEFAULT, USB_MAJOR, + USB_ID75_MINOR_BASE + dev->minor, + S_IFCHR | S_IRUSR | S_IWUSR | + S_IRGRP | S_IWGRP | S_IROTH, + &id75_fops, NULL); + + /* let the user know what node this device is now attached to */ + info ("MassWorks ID-75 USB device now attached to id75%d", dev->minor); + goto exit; + +error: + minor_table [dev->minor] = NULL; + if (dev->int_in_buffer != NULL) + kfree (dev->int_in_buffer); + if (dev->bulk_out_buffer != NULL) + kfree (dev->bulk_out_buffer); + if (dev->write_urb != NULL) + usb_free_urb (dev->write_urb); + kfree (dev); + dev = NULL; + +exit: + up (&minor_table_mutex); + return dev; +} + + +/** + * id75_disconnect + */ +static void id75_disconnect(struct usb_device *udev, void *ptr) +{ + struct usb_id75 *dev; + + int minor; + + dev = (struct usb_id75 *)ptr; + + down (&minor_table_mutex); + down (&dev->sem); + + minor = dev->minor; + + /* Unlink the interrupt urb */ + if (dev->read_urb != NULL) { + dbg ( __FUNCTION__": Unlinking INT urb\n" ); + usb_unlink_urb (dev->read_urb); + } + + /* if the device is not opened, then we clean up right now */ + if (!dev->open_count) { + minor_table[dev->minor] = NULL; + if (dev->int_in_buffer != NULL) + kfree (dev->int_in_buffer); + if (dev->bulk_out_buffer != NULL) + kfree (dev->bulk_out_buffer); + if (dev->write_urb != NULL) + usb_free_urb (dev->write_urb); + kfree (dev); + } else { + dev->udev = NULL; + up (&dev->sem); + } + + /* remove our devfs node */ + devfs_unregister(dev->devfs); + + info("MassWorks ID-75 USB #%d now disconnected", minor); + up (&minor_table_mutex); +} + + +/** + * usb_id75_init + */ +static int __init usb_id75_init(void) +{ + int result; + + /* register this driver with the USB subsystem */ + result = usb_register(&id75_driver); + if (result < 0) { + err("usb_register failed for the "__FILE__" driver. Error number %d", + result); + return -1; + } + + info(DRIVER_VERSION " " DRIVER_AUTHOR); + info(DRIVER_DESC); + return 0; +} + + +/** + * usb_id75_exit + */ +static void __exit usb_id75_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&id75_driver); +} + + +module_init (usb_id75_init); +module_exit (usb_id75_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/inode.c linux.ac/drivers/usb/inode.c --- linux.vanilla/drivers/usb/inode.c Tue Aug 28 18:56:06 2001 +++ linux.ac/drivers/usb/inode.c Wed Oct 10 01:48:16 2001 @@ -260,15 +260,11 @@ struct list_head *list; struct usb_bus *bus; - read_lock_irq (&usb_bus_list_lock); for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) { bus = list_entry(list, struct usb_bus, bus_list); - if (bus->busnum == busnr) { - read_unlock_irq (&usb_bus_list_lock); + if (bus->busnum == busnr) return bus; - } } - read_unlock_irq (&usb_bus_list_lock); return NULL; } @@ -416,7 +412,7 @@ if (i < 2+NRSPECIAL) return 0; i -= 2+NRSPECIAL; - read_lock_irq (&usb_bus_list_lock); + lock_kernel(); for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) { if (i > 0) { i--; @@ -428,7 +424,7 @@ break; filp->f_pos++; } - read_unlock_irq (&usb_bus_list_lock); + unlock_kernel(); return 0; } } @@ -639,13 +635,13 @@ list_add_tail(&inode->u.usbdev_i.slist, &s->u.usbdevfs_sb.ilist); list_add_tail(&inode->u.usbdev_i.dlist, &special[i].inodes); } - read_lock_irq (&usb_bus_list_lock); + lock_kernel(); for (blist = usb_bus_list.next; blist != &usb_bus_list; blist = blist->next) { bus = list_entry(blist, struct usb_bus, bus_list); new_bus_inode(bus, s); recurse_new_dev_inode(bus->root_hub, s); } - read_unlock_irq (&usb_bus_list_lock); + unlock_kernel(); return s; out_no_root: diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/Config.in linux.ac/drivers/usb/serial/Config.in --- linux.vanilla/drivers/usb/serial/Config.in Thu Oct 11 13:52:13 2001 +++ linux.ac/drivers/usb/serial/Config.in Thu Oct 11 09:14:07 2001 @@ -5,36 +5,32 @@ comment 'USB Serial Converter support' dep_tristate 'USB Serial Converter support' CONFIG_USB_SERIAL $CONFIG_USB -if [ "$CONFIG_USB_SERIAL" != "n" ]; then - if [ "$CONFIG_USB_SERIAL" = "y" ]; then - bool ' USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG - fi - bool ' USB Generic Serial Driver' CONFIG_USB_SERIAL_GENERIC - dep_tristate ' USB Belkin and Peracom Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_BELKIN $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL - dep_tristate ' USB ConnectTech WhiteHEAT Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_WHITEHEAT $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL - dep_tristate ' USB Digi International AccelePort USB Serial Driver' CONFIG_USB_SERIAL_DIGI_ACCELEPORT $CONFIG_USB_SERIAL - dep_tristate ' USB Empeg empeg-car Mark I/II Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_EMPEG $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL - dep_tristate ' USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI_SIO $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL - dep_tristate ' USB Handspring Visor / Palm m50x / Sony Clie Driver' CONFIG_USB_SERIAL_VISOR $CONFIG_USB_SERIAL - dep_tristate ' USB IR Dongle Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_IR $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL - dep_tristate ' USB Inside Out Edgeport Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_EDGEPORT $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL - dep_tristate ' USB Keyspan PDA Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN_PDA $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL - dep_tristate ' USB Keyspan USA-xxx Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL - if [ "$CONFIG_USB_SERIAL_KEYSPAN" != "n" ]; then - bool ' USB Keyspan USA-28 Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA28 - bool ' USB Keyspan USA-28X Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA28X - bool ' USB Keyspan USA-28XA Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA28XA - bool ' USB Keyspan USA-28XB Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA28XB - bool ' USB Keyspan USA-19 Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA19 - bool ' USB Keyspan USA-18X Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA18X - bool ' USB Keyspan USA-19W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA19W - bool ' USB Keyspan USA-49W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA49W - fi - dep_tristate ' USB MCT Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_MCT_U232 $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL - dep_tristate ' USB Prolific 2303 Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_PL2303 $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL - dep_tristate ' USB REINER SCT cyberJack pinpad/e-com chipcard reader (EXPERIMENTAL)' CONFIG_USB_SERIAL_CYBERJACK $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL - dep_tristate ' USB Xircom / Entregra Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_XIRCOM $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL - dep_tristate ' USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_OMNINET $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL -fi +if [ "$CONFIG_USB_SERIAL" = "y" ]; then + dep_mbool ' USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG $CONFIG_USB_SERIAL +fi +dep_mbool ' USB Generic Serial Driver' CONFIG_USB_SERIAL_GENERIC $CONFIG_USB_SERIAL +dep_tristate ' USB Belkin and Peracom Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_BELKIN $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL +dep_tristate ' USB ConnectTech WhiteHEAT Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_WHITEHEAT $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL +dep_tristate ' USB Digi International AccelePort USB Serial Driver' CONFIG_USB_SERIAL_DIGI_ACCELEPORT $CONFIG_USB_SERIAL +dep_tristate ' USB Empeg empeg-car Mark I/II Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_EMPEG $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL +dep_tristate ' USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI_SIO $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL +dep_tristate ' USB Handspring Visor / Palm m50x / Sony Clie Driver' CONFIG_USB_SERIAL_VISOR $CONFIG_USB_SERIAL +dep_tristate ' USB IR Dongle Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_IR $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL +dep_tristate ' USB Inside Out Edgeport Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_EDGEPORT $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL +dep_tristate ' USB Keyspan PDA Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN_PDA $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL +dep_tristate ' USB Keyspan USA-xxx Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL +dep_mbool ' USB Keyspan USA-28 Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA28 $CONFIG_USB_SERIAL_KEYSPAN +dep_mbool ' USB Keyspan USA-28X Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA28X $CONFIG_USB_SERIAL_KEYSPAN +dep_mbool ' USB Keyspan USA-28XA Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA28XA $CONFIG_USB_SERIAL_KEYSPAN +dep_mbool ' USB Keyspan USA-28XB Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA28XB $CONFIG_USB_SERIAL_KEYSPAN +dep_mbool ' USB Keyspan USA-19 Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA19 $CONFIG_USB_SERIAL_KEYSPAN +dep_mbool ' USB Keyspan USA-18X Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA18X $CONFIG_USB_SERIAL_KEYSPAN +dep_mbool ' USB Keyspan USA-19W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA19W $CONFIG_USB_SERIAL_KEYSPAN +dep_mbool ' USB Keyspan USA-49W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA49W $CONFIG_USB_SERIAL_KEYSPAN +dep_tristate ' USB MCT Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_MCT_U232 $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL +dep_tristate ' USB Prolific 2303 Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_PL2303 $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL +dep_tristate ' USB REINER SCT cyberJack pinpad/e-com chipcard reader (EXPERIMENTAL)' CONFIG_USB_SERIAL_CYBERJACK $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL +dep_tristate ' USB Xircom / Entregra Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_XIRCOM $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL +dep_tristate ' USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_OMNINET $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/storage/unusual_devs.h linux.ac/drivers/usb/storage/unusual_devs.h --- linux.vanilla/drivers/usb/storage/unusual_devs.h Sun Sep 23 18:34:02 2001 +++ linux.ac/drivers/usb/storage/unusual_devs.h Thu Oct 11 09:27:16 2001 @@ -60,6 +60,11 @@ US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), #endif +UNUSUAL_DEV( 0x04cb, 0x0100, 0x0000, 0x2210, + "Fujifilm", + "FinePix 1400Zoom", + US_SC_8070, US_PR_CBI, NULL, US_FL_FIX_INQUIRY), + #ifdef CONFIG_USB_STORAGE_DPCM UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100, "Microtech", @@ -293,18 +298,17 @@ US_FL_SINGLE_LUN ), #ifdef CONFIG_USB_STORAGE_SDDR09 -UNUSUAL_DEV( 0x0781, 0x0200, 0x0000, 0x9999, +UNUSUAL_DEV( 0x0781, 0x0200, 0x0100, 0x0208, "Sandisk", "ImageMate SDDR-09", US_SC_SCSI, US_PR_EUSB_SDDR09, NULL, US_FL_SINGLE_LUN | US_FL_START_STOP ), -#endif -#ifdef CONFIG_USB_STORAGE_FREECOM -UNUSUAL_DEV( 0x07ab, 0xfc01, 0x0000, 0x9999, - "Freecom", - "USB-IDE", - US_SC_QIC, US_PR_FREECOM, freecom_init, 0), +UNUSUAL_DEV( 0x0781, 0x0200, 0x0000, 0x9999, + "Sandisk", + "ImageMate SDDR-09", + US_SC_SCSI, US_PR_EUSB_SDDR09, NULL, + US_FL_SINGLE_LUN | US_FL_START_STOP ), #endif UNUSUAL_DEV( 0x07af, 0x0004, 0x0100, 0x0100, @@ -313,6 +317,13 @@ US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init, US_FL_SCM_MULT_TARG ), +#ifdef CONFIG_USB_STORAGE_FREECOM +UNUSUAL_DEV( 0x07ab, 0xfc01, 0x0000, 0x9999, + "Freecom", + "USB-IDE", + US_SC_QIC, US_PR_FREECOM, freecom_init, 0), +#endif + UNUSUAL_DEV( 0x07af, 0x0005, 0x0100, 0x0100, "Microtech", "USB-SCSI-HD50", @@ -383,6 +394,12 @@ * - They don't like the INQUIRY command. So we must handle this command * of the SCSI layer ourselves. */ +UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x1000, + "Casio", + "QV DigitalCamera", + US_SC_8070, US_PR_CB, NULL, + US_FL_FIX_INQUIRY ), + UNUSUAL_DEV( 0x07cf, 0x1001, 0x9009, 0x9009, "Casio", "QV DigitalCamera", diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/Config.in linux.ac/drivers/video/Config.in --- linux.vanilla/drivers/video/Config.in Fri Sep 14 22:04:07 2001 +++ linux.ac/drivers/video/Config.in Wed Oct 10 01:48:17 2001 @@ -199,13 +199,6 @@ if [ "$CONFIG_NINO" = "y" ]; then bool ' TMPTX3912/PR31700 frame buffer support' CONFIG_FB_TX3912 fi - if [ "$CONFIG_DECSTATION" = "y" ]; then - if [ "$CONFIG_TC" = "y" ]; then - bool ' PMAG-BA TURBOchannel framebuffer support' CONFIG_FB_PMAG_BA - bool ' PMAGB-B TURBOchannel framebuffer spport' CONFIG_FB_PMAGB_B - bool ' Maxine (Personal DECstation) onboard framebuffer spport' CONFIG_FB_MAXINE - fi - fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate ' Virtual Frame Buffer support (ONLY FOR TESTING!)' CONFIG_FB_VIRTUAL fi diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/acornfb.c linux.ac/drivers/video/acornfb.c --- linux.vanilla/drivers/video/acornfb.c Fri Sep 14 00:04:43 2001 +++ linux.ac/drivers/video/acornfb.c Wed Oct 10 22:40:01 2001 @@ -1772,4 +1772,7 @@ return 0; } +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("VIDC 1/1a/20 framebuffer driver"); MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/aty/atyfb_base.c linux.ac/drivers/video/aty/atyfb_base.c --- linux.vanilla/drivers/video/aty/atyfb_base.c Fri Sep 14 00:04:43 2001 +++ linux.ac/drivers/video/aty/atyfb_base.c Wed Oct 10 01:48:17 2001 @@ -251,7 +251,7 @@ static int default_mclk __initdata = 0; #ifndef MODULE -static const char *mode_option __initdata = NULL; +static char *mode_option __initdata = NULL; #endif #ifdef CONFIG_PPC @@ -271,33 +271,33 @@ static unsigned long phys_guiregbase[FB_MAX] __initdata = { 0, }; #endif -static const char m64n_gx[] __initdata = "mach64GX (ATI888GX00)"; -static const char m64n_cx[] __initdata = "mach64CX (ATI888CX00)"; -static const char m64n_ct[] __initdata = "mach64CT (ATI264CT)"; -static const char m64n_et[] __initdata = "mach64ET (ATI264ET)"; -static const char m64n_vta3[] __initdata = "mach64VTA3 (ATI264VT)"; -static const char m64n_vta4[] __initdata = "mach64VTA4 (ATI264VT)"; -static const char m64n_vtb[] __initdata = "mach64VTB (ATI264VTB)"; -static const char m64n_vt4[] __initdata = "mach64VT4 (ATI264VT4)"; -static const char m64n_gt[] __initdata = "3D RAGE (GT)"; -static const char m64n_gtb[] __initdata = "3D RAGE II+ (GTB)"; -static const char m64n_iic_p[] __initdata = "3D RAGE IIC (PCI)"; -static const char m64n_iic_a[] __initdata = "3D RAGE IIC (AGP)"; -static const char m64n_lt[] __initdata = "3D RAGE LT"; -static const char m64n_ltg[] __initdata = "3D RAGE LT-G"; -static const char m64n_gtc_ba[] __initdata = "3D RAGE PRO (BGA, AGP)"; -static const char m64n_gtc_ba1[] __initdata = "3D RAGE PRO (BGA, AGP, 1x only)"; -static const char m64n_gtc_bp[] __initdata = "3D RAGE PRO (BGA, PCI)"; -static const char m64n_gtc_pp[] __initdata = "3D RAGE PRO (PQFP, PCI)"; -static const char m64n_gtc_ppl[] __initdata = "3D RAGE PRO (PQFP, PCI, limited 3D)"; -static const char m64n_xl[] __initdata = "3D RAGE (XL)"; -static const char m64n_ltp_a[] __initdata = "3D RAGE LT PRO (AGP)"; -static const char m64n_ltp_p[] __initdata = "3D RAGE LT PRO (PCI)"; -static const char m64n_mob_p[] __initdata = "3D RAGE Mobility (PCI)"; -static const char m64n_mob_a[] __initdata = "3D RAGE Mobility (AGP)"; +static char m64n_gx[] __initdata = "mach64GX (ATI888GX00)"; +static char m64n_cx[] __initdata = "mach64CX (ATI888CX00)"; +static char m64n_ct[] __initdata = "mach64CT (ATI264CT)"; +static char m64n_et[] __initdata = "mach64ET (ATI264ET)"; +static char m64n_vta3[] __initdata = "mach64VTA3 (ATI264VT)"; +static char m64n_vta4[] __initdata = "mach64VTA4 (ATI264VT)"; +static char m64n_vtb[] __initdata = "mach64VTB (ATI264VTB)"; +static char m64n_vt4[] __initdata = "mach64VT4 (ATI264VT4)"; +static char m64n_gt[] __initdata = "3D RAGE (GT)"; +static char m64n_gtb[] __initdata = "3D RAGE II+ (GTB)"; +static char m64n_iic_p[] __initdata = "3D RAGE IIC (PCI)"; +static char m64n_iic_a[] __initdata = "3D RAGE IIC (AGP)"; +static char m64n_lt[] __initdata = "3D RAGE LT"; +static char m64n_ltg[] __initdata = "3D RAGE LT-G"; +static char m64n_gtc_ba[] __initdata = "3D RAGE PRO (BGA, AGP)"; +static char m64n_gtc_ba1[] __initdata = "3D RAGE PRO (BGA, AGP, 1x only)"; +static char m64n_gtc_bp[] __initdata = "3D RAGE PRO (BGA, PCI)"; +static char m64n_gtc_pp[] __initdata = "3D RAGE PRO (PQFP, PCI)"; +static char m64n_gtc_ppl[] __initdata = "3D RAGE PRO (PQFP, PCI, limited 3D)"; +static char m64n_xl[] __initdata = "3D RAGE (XL)"; +static char m64n_ltp_a[] __initdata = "3D RAGE LT PRO (AGP)"; +static char m64n_ltp_p[] __initdata = "3D RAGE LT PRO (PCI)"; +static char m64n_mob_p[] __initdata = "3D RAGE Mobility (PCI)"; +static char m64n_mob_a[] __initdata = "3D RAGE Mobility (AGP)"; -static const struct { +static struct { u16 pci_id, chip_type; u8 rev_mask, rev_val; const char *name; @@ -353,28 +353,29 @@ /* 3D RAGE Mobility */ { 0x4c4d, 0x4c4d, 0x00, 0x00, m64n_mob_p, 230, 50, M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | M64F_GTB_DSP | M64F_MOBIL_BUS }, + { 0x4c52, 0x4c52, 0x00, 0x00, m64n_mob_p, 230, 40, M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | M64F_GTB_DSP | M64F_MOBIL_BUS | M64F_MAGIC_POSTDIV | M64F_SDRAM_MAGIC_PLL | M64F_XL_DLL }, { 0x4c4e, 0x4c4e, 0x00, 0x00, m64n_mob_a, 230, 50, M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | M64F_GTB_DSP | M64F_MOBIL_BUS }, #endif /* CONFIG_FB_ATY_CT */ }; -static const char ram_dram[] __initdata = "DRAM"; -static const char ram_vram[] __initdata = "VRAM"; -static const char ram_edo[] __initdata = "EDO"; -static const char ram_sdram[] __initdata = "SDRAM"; -static const char ram_sgram[] __initdata = "SGRAM"; -static const char ram_wram[] __initdata = "WRAM"; -static const char ram_off[] __initdata = "OFF"; -static const char ram_resv[] __initdata = "RESV"; +static char ram_dram[] __initdata = "DRAM"; +static char ram_vram[] __initdata = "VRAM"; +static char ram_edo[] __initdata = "EDO"; +static char ram_sdram[] __initdata = "SDRAM"; +static char ram_sgram[] __initdata = "SGRAM"; +static char ram_wram[] __initdata = "WRAM"; +static char ram_off[] __initdata = "OFF"; +static char ram_resv[] __initdata = "RESV"; #ifdef CONFIG_FB_ATY_GX -static const char *aty_gx_ram[8] __initdata = { +static char *aty_gx_ram[8] __initdata = { ram_dram, ram_vram, ram_vram, ram_dram, ram_dram, ram_vram, ram_vram, ram_resv }; #endif /* CONFIG_FB_ATY_GX */ #ifdef CONFIG_FB_ATY_CT -static const char *aty_ct_ram[8] __initdata = { +static char *aty_ct_ram[8] __initdata = { ram_off, ram_dram, ram_edo, ram_edo, ram_sdram, ram_sgram, ram_wram, ram_resv }; @@ -423,7 +424,7 @@ #endif /* defined(CONFIG_PPC) */ -#if defined(CONFIG_PMAC_PBOOK) || defined(CONFIG_PMAC_BACKLIGHT) +#if defined(CONFIG_PMAC_PBOOK) || defined(CONFIG_PMAC_BACKLIGHT) || defined(CONFIG_FB_ATY_CT_VAIO_LCD) static void aty_st_lcd(int index, u32 val, const struct fb_info_aty *info) { unsigned long temp; @@ -445,7 +446,7 @@ /* read the register value */ return aty_ld_le32(LCD_DATA, info); } -#endif /* CONFIG_PMAC_PBOOK || CONFIG_PMAC_BACKLIGHT */ +#endif /* CONFIG_PMAC_PBOOK || CONFIG_PMAC_BACKLIGHT || CONFIG_FB_ATY_CT_VAIO_LCD */ /* ------------------------------------------------------------------------- */ @@ -1744,6 +1745,9 @@ #if defined(CONFIG_PPC) int sense; #endif +#if defined(CONFIG_FB_ATY_CT_VAIO_LCD) + u32 pm, hs; +#endif u8 pll_ref_div; info->aty_cmap_regs = (struct aty_cmap_regs *)(info->ati_regbase+0xc0); @@ -2068,6 +2072,35 @@ var = default_var; #endif /* !__sparc__ */ #endif /* !CONFIG_PPC */ +#if defined(CONFIG_FB_ATY_CT_VAIO_LCD) + /* Power Management */ + pm=aty_ld_lcd(POWER_MANAGEMENT, info); + pm=(pm & ~PWR_MGT_MODE_MASK) | PWR_MGT_MODE_PCI; + pm|=PWR_MGT_ON; + aty_st_lcd(POWER_MANAGEMENT, pm, info); + udelay(10); + + /* OVR_WID_LEFT_RIGHT */ + hs=aty_ld_le32(OVR_WID_LEFT_RIGHT,info); + hs= 0x00000000; + aty_st_le32(OVR_WID_LEFT_RIGHT, hs, info); + udelay(10); + + /* CONFIG_PANEL */ + hs=aty_ld_lcd(CONFIG_PANEL,info); + hs|=DONT_SHADOW_HEND ; + aty_st_lcd(CONFIG_PANEL, hs, info); + udelay(10); + +#if defined(DEBUG) + printk("LCD_INDEX CONFIG_PANEL LCD_GEN_CTRL POWER_MANAGEMENT\n" + "%08x %08x %08x %08x\n", + aty_ld_le32(LCD_INDEX, info), + aty_ld_lcd(CONFIG_PANEL, info), + aty_ld_lcd(LCD_GEN_CTRL, info), + aty_ld_lcd(POWER_MANAGEMENT, info), +#endif /* DEBUG */ +#endif /* CONFIG_FB_ATY_CT_VAIO_LCD */ #endif /* !MODULE */ if (noaccel) var.accel_flags &= ~FB_ACCELF_TEXT; @@ -2677,6 +2710,23 @@ /* * Blank the display. */ +#if defined(CONFIG_FB_ATY_CT_VAIO_LCD) +static int set_backlight_enable(int on, struct fb_info_aty *info) +{ + unsigned int reg = aty_ld_lcd(POWER_MANAGEMENT, info); + if(on) { + reg=(reg & ~SUSPEND_NOW) | PWR_BLON; + } else { + reg=(reg & ~PWR_BLON) | SUSPEND_NOW; + } + aty_st_lcd(POWER_MANAGEMENT, reg, info); + udelay(10); +#ifdef DEBUG + printk(KERN_INFO "set_backlight_enable(%i): %08x\n", on, aty_ld_lcd(POWER_MANAGEMENT, info) ); +#endif + return 0; +} +#endif /* CONFIG_FB_ATY_CT_VAIO_LCD */ static void atyfbcon_blank(int blank, struct fb_info *fb) { @@ -2688,6 +2738,9 @@ set_backlight_enable(0); #endif /* CONFIG_PMAC_BACKLIGHT */ +#if defined(CONFIG_FB_ATY_CT_VAIO_LCD) + set_backlight_enable(!blank, info); +#endif /* CONFIG_FB_ATY_CT_VAIO_LCD */ gen_cntl = aty_ld_8(CRTC_GEN_CNTL, info); if (blank > 0) switch (blank-1) { @@ -2892,3 +2945,4 @@ } #endif +MODULE_LICENSE("GPL"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/aty/mach64.h linux.ac/drivers/video/aty/mach64.h --- linux.vanilla/drivers/video/aty/mach64.h Tue Jul 31 22:43:29 2001 +++ linux.ac/drivers/video/aty/mach64.h Wed Oct 10 01:48:17 2001 @@ -1148,6 +1148,8 @@ #define APC_LUT_MN 0x39 #define APC_LUT_OP 0x3A +/* Values in CONFIG_PANEL */ +#define DONT_SHADOW_HEND 0x00004000 /* Values in LCD_MISC_CNTL */ #define BIAS_MOD_LEVEL_MASK 0x0000ff00 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/aty/mach64_ct.c linux.ac/drivers/video/aty/mach64_ct.c --- linux.vanilla/drivers/video/aty/mach64_ct.c Tue Jul 31 22:43:29 2001 +++ linux.ac/drivers/video/aty/mach64_ct.c Wed Oct 10 01:48:17 2001 @@ -178,11 +178,14 @@ } pll->pll_gen_cntl |= mpostdiv<<4; /* mclk */ - if (M64_HAS(MAGIC_POSTDIV)) - pll->pll_ext_cntl = 0; - else +#if defined(CONFIG_FB_ATY_CT_VAIO_LCD) pll->pll_ext_cntl = mpostdiv; /* xclk == mclk */ - +#else + if ( M64_HAS(MAGIC_POSTDIV) ) + pll->pll_ext_cntl = 0; + else + pll->pll_ext_cntl = mpostdiv; /* xclk == mclk */ +#endif switch (pll->vclk_post_div_real) { case 2: vpostdiv = 1; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/aty/mach64_cursor.c linux.ac/drivers/video/aty/mach64_cursor.c --- linux.vanilla/drivers/video/aty/mach64_cursor.c Tue Jul 31 22:43:29 2001 +++ linux.ac/drivers/video/aty/mach64_cursor.c Wed Oct 10 01:48:17 2001 @@ -3,7 +3,7 @@ * ATI Mach64 CT/VT/GT/LT Cursor Support */ -#include +#include #include #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/aty128fb.c linux.ac/drivers/video/aty128fb.c --- linux.vanilla/drivers/video/aty128fb.c Mon Jul 2 22:13:40 2001 +++ linux.ac/drivers/video/aty128fb.c Wed Oct 10 01:48:18 2001 @@ -7,6 +7,9 @@ * Ani Joshi / Jeff Garzik * - Code cleanup * + * Andreas Hundt + * - FB_ACTIVATE fixes + * * Based off of Geert's atyfb.c and vfb.c. * * TODO: @@ -143,7 +146,7 @@ }; /* supported Rage128 chipsets */ -static const struct aty128_chip_info aty128_pci_probe_list[] __initdata = +static struct aty128_chip_info aty128_pci_probe_list[] __initdata = { {"Rage128 RE (PCI)", PCI_DEVICE_ID_ATI_RAGE128_RE, rage_128}, {"Rage128 RF (AGP)", PCI_DEVICE_ID_ATI_RAGE128_RF, rage_128}, @@ -217,7 +220,7 @@ static char *mode __initdata = NULL; static int nomtrr __initdata = 0; -static const char *mode_option __initdata = NULL; +static char *mode_option __initdata = NULL; #ifdef CONFIG_PPC static int default_vmode __initdata = VMODE_1024_768_60; @@ -880,7 +883,11 @@ crtc->pitch = vxres >> 3; crtc->offset = 0; - crtc->offset_cntl = 0; + + if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) + crtc->offset_cntl = 0x00010000; + else + crtc->offset_cntl = 0; crtc->vxres = vxres; crtc->vyres = vyres; @@ -1363,7 +1370,7 @@ aty128_encode_var(var, &par, info); - if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) + if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST) return 0; oldxres = display->var.xres; @@ -2592,6 +2599,7 @@ #ifdef MODULE MODULE_AUTHOR("(c)1999-2000 Brad Douglas "); MODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards"); +MODULE_LICENSE("GPL"); MODULE_PARM(noaccel, "i"); MODULE_PARM_DESC(noaccel, "Disable hardware acceleration (0 or 1=disabled) (default=0)"); MODULE_PARM(font, "s"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/cgsixfb.c linux.ac/drivers/video/cgsixfb.c --- linux.vanilla/drivers/video/cgsixfb.c Thu Sep 20 22:11:58 2001 +++ linux.ac/drivers/video/cgsixfb.c Wed Oct 10 01:48:18 2001 @@ -364,13 +364,15 @@ unsigned long flags; int i, x, y; u8 *fd1, *fd2, *fd3, *fd4; + u16 c; spin_lock_irqsave(&fb->lock, flags); do { i = sbus_readl(&fbc->s); } while (i & 0x10000000); - sbus_writel(attr_fgcol(p, scr_readw(s)), &fbc->fg); - sbus_writel(attr_bgcol(p, scr_readw(s)), &fbc->bg); + c = scr_readw(s); + sbus_writel(attr_fgcol(p, c), &fbc->fg); + sbus_writel(attr_bgcol(p, c), &fbc->bg); sbus_writel(0x140000, &fbc->mode); sbus_writel(0xe880fc30, &fbc->alu); sbus_writel(~0, &fbc->pixelm); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/clgenfb.c linux.ac/drivers/video/clgenfb.c --- linux.vanilla/drivers/video/clgenfb.c Thu Oct 11 13:52:13 2001 +++ linux.ac/drivers/video/clgenfb.c Thu Oct 11 09:21:25 2001 @@ -56,6 +56,12 @@ #ifdef CONFIG_AMIGA #include #endif +#ifdef CONFIG_ALL_PPC +#include +#define isPReP (_machine == _MACH_prep) +#else +#define isPReP 0 +#endif #include